mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-10-07 08:09:39 +02:00
Compare commits
150 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3b8d74f1f9 | ||
![]() |
3fd9cabd43 | ||
![]() |
113efd1150 | ||
![]() |
f159808df6 | ||
![]() |
e9148b4caf | ||
![]() |
611880414b | ||
![]() |
36321c7cb8 | ||
![]() |
6c0f8e2800 | ||
![]() |
71e75c0900 | ||
![]() |
0400a597ef | ||
![]() |
0e923ec771 | ||
![]() |
79ce18c9bc | ||
![]() |
91f863ad9f | ||
![]() |
1415c8eda0 | ||
![]() |
9eae97ddab | ||
![]() |
df28de62c1 | ||
![]() |
72ada38417 | ||
![]() |
5d30d46535 | ||
![]() |
4dd3d3bc76 | ||
![]() |
348fa3318b | ||
![]() |
84ae430806 | ||
![]() |
61c1b0aebb | ||
![]() |
e3df38e078 | ||
![]() |
44b2fae0db | ||
![]() |
c147404d1c | ||
![]() |
7a4acb5c36 | ||
![]() |
90d187c48a | ||
![]() |
1851e81794 | ||
![]() |
aeb6e860bb | ||
![]() |
3300ad09f3 | ||
![]() |
c6e689255e | ||
![]() |
8aff0d6321 | ||
![]() |
a4ead2f259 | ||
![]() |
a569d5cbf6 | ||
![]() |
fb50a3a249 | ||
![]() |
c8cd61f0f8 | ||
![]() |
d1e51abe2d | ||
![]() |
b8c0cd9564 | ||
![]() |
56f2073ad5 | ||
![]() |
794d46beec | ||
![]() |
8e157423e0 | ||
![]() |
ef9740bb38 | ||
![]() |
bd459fadd3 | ||
![]() |
bb5baa65cb | ||
![]() |
ee61a26faf | ||
![]() |
ef943d4b0b | ||
![]() |
4c25b95124 | ||
![]() |
3451a87d64 | ||
![]() |
560b44daef | ||
![]() |
c7c211e031 | ||
![]() |
de3cbeeff3 | ||
![]() |
689b204327 | ||
![]() |
ea265bc28e | ||
![]() |
63f145f5c0 | ||
![]() |
66f2f42294 | ||
![]() |
1148039c6b | ||
![]() |
b82b2d2b78 | ||
![]() |
6eef26f107 | ||
![]() |
2ab1e162ea | ||
![]() |
56f79692a3 | ||
![]() |
ef66cf77e1 | ||
![]() |
4b72539e33 | ||
![]() |
da31880319 | ||
![]() |
96dfa1dbed | ||
![]() |
dcedfdb674 | ||
![]() |
bcb0cdfb3f | ||
![]() |
6beab4f286 | ||
![]() |
2f150b0c82 | ||
![]() |
8c29007c69 | ||
![]() |
02aafcabba | ||
![]() |
81a7e6bc05 | ||
![]() |
bf977311e1 | ||
![]() |
79636af9f9 | ||
![]() |
c956fb6570 | ||
![]() |
48b2a15fd0 | ||
![]() |
ce5bb4fef7 | ||
![]() |
06081c73e9 | ||
![]() |
6c3c6a1e65 | ||
![]() |
e8f551964b | ||
![]() |
95407eea80 | ||
![]() |
61e35edf27 | ||
![]() |
fe32845197 | ||
![]() |
077e6621b9 | ||
![]() |
7372ca5e8e | ||
![]() |
949f6dd4f9 | ||
![]() |
938b47bead | ||
![]() |
ef2fea157b | ||
![]() |
eb20782aa4 | ||
![]() |
ce3ba4f772 | ||
![]() |
0fd11562bc | ||
![]() |
a3878528c6 | ||
![]() |
3cb37ab802 | ||
![]() |
7d52e99aba | ||
![]() |
91d5c7ed71 | ||
![]() |
85062ac570 | ||
![]() |
6f6755ec02 | ||
![]() |
6d2c6c13a7 | ||
![]() |
bda13c41fd | ||
![]() |
ea58bb3359 | ||
![]() |
390ec89045 | ||
![]() |
4906460bf4 | ||
![]() |
28ca1420f9 | ||
![]() |
9ba8e6e491 | ||
![]() |
24eb2123e4 | ||
![]() |
a67b86b73b | ||
![]() |
95a2ce244c | ||
![]() |
35e2dd4047 | ||
![]() |
6ee632f245 | ||
![]() |
f9eb552382 | ||
![]() |
deff411f85 | ||
![]() |
e531997868 | ||
![]() |
7d658403c2 | ||
![]() |
015c112103 | ||
![]() |
a1ef3f0f4a | ||
![]() |
0c65ea25cb | ||
![]() |
53c63b31c8 | ||
![]() |
18114ab9ce | ||
![]() |
32a2e5fc47 | ||
![]() |
af4c0591f9 | ||
![]() |
d7b1cd912c | ||
![]() |
3a56bbed2e | ||
![]() |
b7b02fcc2c | ||
![]() |
9365138206 | ||
![]() |
61771b97ad | ||
![]() |
bc4ecdc791 | ||
![]() |
bc58c8e03c | ||
![]() |
8394bcd4a9 | ||
![]() |
c6cde959b3 | ||
![]() |
e74e51fb25 | ||
![]() |
4a0321fe3e | ||
![]() |
e306fcc795 | ||
![]() |
20c4a68aa8 | ||
![]() |
4fabf91e16 | ||
![]() |
8ed7ea6b36 | ||
![]() |
99f0eecb86 | ||
![]() |
996e8ef9b8 | ||
![]() |
734cef9017 | ||
![]() |
0856d2e82c | ||
![]() |
a8000bf51b | ||
![]() |
24f181afc8 | ||
![]() |
97cc8a4afe | ||
![]() |
4c1cf345fc | ||
![]() |
01cdd28938 | ||
![]() |
7198b6884c | ||
![]() |
c8e5525899 | ||
![]() |
f7c28ff31e | ||
![]() |
ca8080a371 | ||
![]() |
0df80d2b52 | ||
![]() |
67b6414693 | ||
![]() |
8210e3c80a |
@@ -1,2 +1,3 @@
|
||||
dist/**
|
||||
.github/**
|
||||
docs/Setup.md
|
||||
|
@@ -13,17 +13,25 @@
|
||||
},
|
||||
"sourceType": "module"
|
||||
},
|
||||
"extends": ["eslint:recommended", "plugin:jsdoc/recommended", "plugin:markdown/recommended", "plugin:prettier/recommended"],
|
||||
"plugins": ["html", "jest", "jsdoc", "prettier"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
//"plugin:jsdoc/recommended",
|
||||
"plugin:json/recommended",
|
||||
// "plugin:markdown/recommended",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"plugins": ["html", "jest", "jsdoc", "json", "prettier"],
|
||||
"rules": {
|
||||
"no-prototype-builtins": 0,
|
||||
"no-unused-vars": 0,
|
||||
"jsdoc/check-indentation": 0,
|
||||
"jsdoc/check-alignment": 0,
|
||||
"jsdoc/check-line-alignment": 0,
|
||||
"jsdoc/multiline-blocks": 0,
|
||||
"jsdoc/newline-after-description": 0,
|
||||
"jsdoc/tag-lines": 0,
|
||||
"no-prototype-builtins": "off",
|
||||
"no-unused-vars": "off",
|
||||
"jsdoc/check-indentation": "off",
|
||||
"jsdoc/check-alignment": "off",
|
||||
"jsdoc/check-line-alignment": "off",
|
||||
"jsdoc/multiline-blocks": "off",
|
||||
"jsdoc/newline-after-description": "off",
|
||||
"jsdoc/tag-lines": "off",
|
||||
"cypress/no-async-tests": "off",
|
||||
"json/*": ["error", "allowComments"],
|
||||
"no-empty": ["error", { "allowEmptyCatch": true }]
|
||||
},
|
||||
"overrides": [
|
||||
@@ -33,6 +41,12 @@
|
||||
"no-undef": "off",
|
||||
"jsdoc/require-jsdoc": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": "./**/*.md/*.html",
|
||||
"rules": {
|
||||
"prettier/prettier": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
* text=auto
|
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@@ -1,6 +1,12 @@
|
||||
name: Build
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push: {}
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -37,7 +43,3 @@ jobs:
|
||||
with:
|
||||
name: dist
|
||||
path: dist
|
||||
|
||||
- name: Run Unit Tests
|
||||
run: |
|
||||
yarn test --coverage
|
||||
|
8
.github/workflows/checks.yml
vendored
8
.github/workflows/checks.yml
vendored
@@ -1,4 +1,10 @@
|
||||
on: [push]
|
||||
on:
|
||||
push: {}
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
|
||||
name: Static analysis
|
||||
|
||||
|
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@@ -7,6 +7,10 @@ on:
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ develop ]
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
|
39
.github/workflows/lint.yml
vendored
Normal file
39
.github/workflows/lint.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
push: {}
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: yarn
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install Yarn
|
||||
run: npm i yarn --global
|
||||
|
||||
- name: Install Packages
|
||||
run: |
|
||||
yarn install --frozen-lockfile
|
||||
env:
|
||||
CYPRESS_CACHE_FOLDER: .cache/Cypress
|
||||
|
||||
- name: Run Linting
|
||||
run: yarn lint
|
@@ -1,5 +1,11 @@
|
||||
name: Validate PR Labeler Configuration
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push: {}
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
|
||||
jobs:
|
||||
pr-labeler:
|
||||
|
34
.github/workflows/test.yml
vendored
Normal file
34
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
name: Unit Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: yarn
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install Yarn
|
||||
run: npm i yarn --global
|
||||
|
||||
- name: Install Packages
|
||||
run: |
|
||||
yarn install --frozen-lockfile
|
||||
env:
|
||||
CYPRESS_CACHE_FOLDER: .cache/Cypress
|
||||
|
||||
- name: Run Unit Tests
|
||||
run: |
|
||||
yarn ci --coverage
|
0
.husky/commit-msg
Normal file → Executable file
0
.husky/commit-msg
Normal file → Executable file
0
.husky/pre-commit
Normal file → Executable file
0
.husky/pre-commit
Normal file → Executable file
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"*.{js,html,md}": [
|
||||
"*.{js,json,html,md}": [
|
||||
"yarn lint:fix"
|
||||
]
|
||||
}
|
@@ -3,12 +3,9 @@
|
||||
"libs": [
|
||||
"browser"
|
||||
],
|
||||
"loadEagerly": [
|
||||
"path/to/your/js/**/*.js"
|
||||
],
|
||||
"loadEagerly": [],
|
||||
"dontLoad": [
|
||||
"node_modules/**",
|
||||
"path/to/your/js/**/*.js"
|
||||
"node_modules/**"
|
||||
],
|
||||
"plugins": {
|
||||
"modules": {},
|
||||
|
@@ -23,7 +23,7 @@
|
||||
- Missing fontawesome icon support [\#830](https://github.com/knsv/mermaid/issues/830)
|
||||
- Docs for integration with wiki.js? [\#829](https://github.com/knsv/mermaid/issues/829)
|
||||
- Is this project still maintained? [\#826](https://github.com/knsv/mermaid/issues/826)
|
||||
- typroa [\#823](https://github.com/knsv/mermaid/issues/823)
|
||||
- typora [\#823](https://github.com/knsv/mermaid/issues/823)
|
||||
- Maintain the order of the nodes in Flowchart [\#815](https://github.com/knsv/mermaid/issues/815)
|
||||
- Overlap, Overflow and cut titles in flowchart [\#814](https://github.com/knsv/mermaid/issues/814)
|
||||
- How load mermaidApi notejs electron [\#813](https://github.com/knsv/mermaid/issues/813)
|
||||
|
@@ -114,7 +114,7 @@ Finally, if it is not in the documentation, no one will know about it and then *
|
||||
|
||||
The docs are located in the docs folder and are ofc written in markdown. Just pick the right section and start typing. If you want to add to the structure as in adding a new section and new file you do that via the _navbar.md.
|
||||
|
||||
The changes in master is reflected in http://mermaid-js.github.io/mermaid/ once released the updates are committed to https://mermaid-js.github.io/#/
|
||||
The changes in master is reflected in https://mermaid-js.github.io/mermaid/ once released the updates are committed to https://mermaid-js.github.io/#/
|
||||
|
||||
## Last words
|
||||
|
||||
|
904
classDiagram.js
904
classDiagram.js
File diff suppressed because one or more lines are too long
@@ -1,264 +0,0 @@
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
context('Actions', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/commands/actions');
|
||||
});
|
||||
|
||||
// https://on.cypress.io/interacting-with-elements
|
||||
|
||||
it('.type() - type into a DOM element', () => {
|
||||
// https://on.cypress.io/type
|
||||
cy.get('.action-email')
|
||||
.type('fake@email.com')
|
||||
.should('have.value', 'fake@email.com')
|
||||
|
||||
// .type() with special character sequences
|
||||
.type('{leftarrow}{rightarrow}{uparrow}{downarrow}')
|
||||
.type('{del}{selectall}{backspace}')
|
||||
|
||||
// .type() with key modifiers
|
||||
.type('{alt}{option}') //these are equivalent
|
||||
.type('{ctrl}{control}') //these are equivalent
|
||||
.type('{meta}{command}{cmd}') //these are equivalent
|
||||
.type('{shift}')
|
||||
|
||||
// Delay each keypress by 0.1 sec
|
||||
.type('slow.typing@email.com', { delay: 100 })
|
||||
.should('have.value', 'slow.typing@email.com');
|
||||
|
||||
cy.get('.action-disabled')
|
||||
// Ignore error checking prior to type
|
||||
// like whether the input is visible or disabled
|
||||
.type('disabled error checking', { force: true })
|
||||
.should('have.value', 'disabled error checking');
|
||||
});
|
||||
|
||||
it('.focus() - focus on a DOM element', () => {
|
||||
// https://on.cypress.io/focus
|
||||
cy.get('.action-focus')
|
||||
.focus()
|
||||
.should('have.class', 'focus')
|
||||
.prev()
|
||||
.should('have.attr', 'style', 'color: orange;');
|
||||
});
|
||||
|
||||
it('.blur() - blur off a DOM element', () => {
|
||||
// https://on.cypress.io/blur
|
||||
cy.get('.action-blur')
|
||||
.type('About to blur')
|
||||
.blur()
|
||||
.should('have.class', 'error')
|
||||
.prev()
|
||||
.should('have.attr', 'style', 'color: red;');
|
||||
});
|
||||
|
||||
it('.clear() - clears an input or textarea element', () => {
|
||||
// https://on.cypress.io/clear
|
||||
cy.get('.action-clear')
|
||||
.type('Clear this text')
|
||||
.should('have.value', 'Clear this text')
|
||||
.clear()
|
||||
.should('have.value', '');
|
||||
});
|
||||
|
||||
it('.submit() - submit a form', () => {
|
||||
// https://on.cypress.io/submit
|
||||
cy.get('.action-form').find('[type="text"]').type('HALFOFF');
|
||||
cy.get('.action-form').submit().next().should('contain', 'Your form has been submitted!');
|
||||
});
|
||||
|
||||
it('.click() - click on a DOM element', () => {
|
||||
// https://on.cypress.io/click
|
||||
cy.get('.action-btn').click();
|
||||
|
||||
// You can click on 9 specific positions of an element:
|
||||
// -----------------------------------
|
||||
// | topLeft top topRight |
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// | left center right |
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// | bottomLeft bottom bottomRight |
|
||||
// -----------------------------------
|
||||
|
||||
// clicking in the center of the element is the default
|
||||
cy.get('#action-canvas').click();
|
||||
|
||||
cy.get('#action-canvas').click('topLeft');
|
||||
cy.get('#action-canvas').click('top');
|
||||
cy.get('#action-canvas').click('topRight');
|
||||
cy.get('#action-canvas').click('left');
|
||||
cy.get('#action-canvas').click('right');
|
||||
cy.get('#action-canvas').click('bottomLeft');
|
||||
cy.get('#action-canvas').click('bottom');
|
||||
cy.get('#action-canvas').click('bottomRight');
|
||||
|
||||
// .click() accepts an x and y coordinate
|
||||
// that controls where the click occurs :)
|
||||
|
||||
cy.get('#action-canvas')
|
||||
.click(80, 75) // click 80px on x coord and 75px on y coord
|
||||
.click(170, 75)
|
||||
.click(80, 165)
|
||||
.click(100, 185)
|
||||
.click(125, 190)
|
||||
.click(150, 185)
|
||||
.click(170, 165);
|
||||
|
||||
// click multiple elements by passing multiple: true
|
||||
cy.get('.action-labels>.label').click({ multiple: true });
|
||||
|
||||
// Ignore error checking prior to clicking
|
||||
cy.get('.action-opacity>.btn').click({ force: true });
|
||||
});
|
||||
|
||||
it('.dblclick() - double click on a DOM element', () => {
|
||||
// https://on.cypress.io/dblclick
|
||||
|
||||
// Our app has a listener on 'dblclick' event in our 'scripts.js'
|
||||
// that hides the div and shows an input on double click
|
||||
cy.get('.action-div').dblclick().should('not.be.visible');
|
||||
cy.get('.action-input-hidden').should('be.visible');
|
||||
});
|
||||
|
||||
it('.check() - check a checkbox or radio element', () => {
|
||||
// https://on.cypress.io/check
|
||||
|
||||
// By default, .check() will check all
|
||||
// matching checkbox or radio elements in succession, one after another
|
||||
cy.get('.action-checkboxes [type="checkbox"]').not('[disabled]').check().should('be.checked');
|
||||
|
||||
cy.get('.action-radios [type="radio"]').not('[disabled]').check().should('be.checked');
|
||||
|
||||
// .check() accepts a value argument
|
||||
cy.get('.action-radios [type="radio"]').check('radio1').should('be.checked');
|
||||
|
||||
// .check() accepts an array of values
|
||||
cy.get('.action-multiple-checkboxes [type="checkbox"]')
|
||||
.check(['checkbox1', 'checkbox2'])
|
||||
.should('be.checked');
|
||||
|
||||
// Ignore error checking prior to checking
|
||||
cy.get('.action-checkboxes [disabled]').check({ force: true }).should('be.checked');
|
||||
|
||||
cy.get('.action-radios [type="radio"]').check('radio3', { force: true }).should('be.checked');
|
||||
});
|
||||
|
||||
it('.uncheck() - uncheck a checkbox element', () => {
|
||||
// https://on.cypress.io/uncheck
|
||||
|
||||
// By default, .uncheck() will uncheck all matching
|
||||
// checkbox elements in succession, one after another
|
||||
cy.get('.action-check [type="checkbox"]').not('[disabled]').uncheck().should('not.be.checked');
|
||||
|
||||
// .uncheck() accepts a value argument
|
||||
cy.get('.action-check [type="checkbox"]')
|
||||
.check('checkbox1')
|
||||
.uncheck('checkbox1')
|
||||
.should('not.be.checked');
|
||||
|
||||
// .uncheck() accepts an array of values
|
||||
cy.get('.action-check [type="checkbox"]')
|
||||
.check(['checkbox1', 'checkbox3'])
|
||||
.uncheck(['checkbox1', 'checkbox3'])
|
||||
.should('not.be.checked');
|
||||
|
||||
// Ignore error checking prior to unchecking
|
||||
cy.get('.action-check [disabled]').uncheck({ force: true }).should('not.be.checked');
|
||||
});
|
||||
|
||||
it('.select() - select an option in a <select> element', () => {
|
||||
// https://on.cypress.io/select
|
||||
|
||||
// Select option(s) with matching text content
|
||||
cy.get('.action-select').select('apples');
|
||||
|
||||
cy.get('.action-select-multiple').select(['apples', 'oranges', 'bananas']);
|
||||
|
||||
// Select option(s) with matching value
|
||||
cy.get('.action-select').select('fr-bananas');
|
||||
|
||||
cy.get('.action-select-multiple').select(['fr-apples', 'fr-oranges', 'fr-bananas']);
|
||||
});
|
||||
|
||||
it('.scrollIntoView() - scroll an element into view', () => {
|
||||
// https://on.cypress.io/scrollintoview
|
||||
|
||||
// normally all of these buttons are hidden,
|
||||
// because they're not within
|
||||
// the viewable area of their parent
|
||||
// (we need to scroll to see them)
|
||||
cy.get('#scroll-horizontal button').should('not.be.visible');
|
||||
|
||||
// scroll the button into view, as if the user had scrolled
|
||||
cy.get('#scroll-horizontal button').scrollIntoView().should('be.visible');
|
||||
|
||||
cy.get('#scroll-vertical button').should('not.be.visible');
|
||||
|
||||
// Cypress handles the scroll direction needed
|
||||
cy.get('#scroll-vertical button').scrollIntoView().should('be.visible');
|
||||
|
||||
cy.get('#scroll-both button').should('not.be.visible');
|
||||
|
||||
// Cypress knows to scroll to the right and down
|
||||
cy.get('#scroll-both button').scrollIntoView().should('be.visible');
|
||||
});
|
||||
|
||||
it('.trigger() - trigger an event on a DOM element', () => {
|
||||
// https://on.cypress.io/trigger
|
||||
|
||||
// To interact with a range input (slider)
|
||||
// we need to set its value & trigger the
|
||||
// event to signal it changed
|
||||
|
||||
// Here, we invoke jQuery's val() method to set
|
||||
// the value and trigger the 'change' event
|
||||
cy.get('.trigger-input-range')
|
||||
.invoke('val', 25)
|
||||
.trigger('change')
|
||||
.get('input[type=range]')
|
||||
.siblings('p')
|
||||
.should('have.text', '25');
|
||||
});
|
||||
|
||||
it('cy.scrollTo() - scroll the window or element to a position', () => {
|
||||
// https://on.cypress.io/scrollTo
|
||||
|
||||
// You can scroll to 9 specific positions of an element:
|
||||
// -----------------------------------
|
||||
// | topLeft top topRight |
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// | left center right |
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// | bottomLeft bottom bottomRight |
|
||||
// -----------------------------------
|
||||
|
||||
// if you chain .scrollTo() off of cy, we will
|
||||
// scroll the entire window
|
||||
cy.scrollTo('bottom');
|
||||
|
||||
cy.get('#scrollable-horizontal').scrollTo('right');
|
||||
|
||||
// or you can scroll to a specific coordinate:
|
||||
// (x axis, y axis) in pixels
|
||||
cy.get('#scrollable-vertical').scrollTo(250, 250);
|
||||
|
||||
// or you can scroll to a specific percentage
|
||||
// of the (width, height) of the element
|
||||
cy.get('#scrollable-both').scrollTo('75%', '25%');
|
||||
|
||||
// control the easing of the scroll (default is 'swing')
|
||||
cy.get('#scrollable-vertical').scrollTo('center', { easing: 'linear' });
|
||||
|
||||
// control the duration of the scroll (in ms)
|
||||
cy.get('#scrollable-both').scrollTo('center', { duration: 2000 });
|
||||
});
|
||||
});
|
@@ -1,36 +0,0 @@
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
context('Aliasing', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/commands/aliasing');
|
||||
});
|
||||
|
||||
it('.as() - alias a DOM element for later use', () => {
|
||||
// https://on.cypress.io/as
|
||||
|
||||
// Alias a DOM element for use later
|
||||
// We don't have to traverse to the element
|
||||
// later in our code, we reference it with @
|
||||
|
||||
cy.get('.as-table').find('tbody>tr').first().find('td').first().find('button').as('firstBtn');
|
||||
|
||||
// when we reference the alias, we place an
|
||||
// @ in front of its name
|
||||
cy.get('@firstBtn').click();
|
||||
|
||||
cy.get('@firstBtn').should('have.class', 'btn-success').and('contain', 'Changed');
|
||||
});
|
||||
|
||||
it('.as() - alias a route for later use', () => {
|
||||
// Alias the route to wait for its response
|
||||
cy.server();
|
||||
cy.route('GET', 'comments/*').as('getComment');
|
||||
|
||||
// we have code that gets a comment when
|
||||
// the button is clicked in scripts.js
|
||||
cy.get('.network-btn').click();
|
||||
|
||||
// https://on.cypress.io/wait
|
||||
cy.wait('@getComment').its('status').should('eq', 200);
|
||||
});
|
||||
});
|
@@ -1,169 +0,0 @@
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
context('Assertions', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/commands/assertions');
|
||||
});
|
||||
|
||||
describe('Implicit Assertions', () => {
|
||||
it('.should() - make an assertion about the current subject', () => {
|
||||
// https://on.cypress.io/should
|
||||
cy.get('.assertion-table')
|
||||
.find('tbody tr:last')
|
||||
.should('have.class', 'success')
|
||||
.find('td')
|
||||
.first()
|
||||
// checking the text of the <td> element in various ways
|
||||
.should('have.text', 'Column content')
|
||||
.should('contain', 'Column content')
|
||||
.should('have.html', 'Column content')
|
||||
// chai-jquery uses "is()" to check if element matches selector
|
||||
.should('match', 'td')
|
||||
// to match text content against a regular expression
|
||||
// first need to invoke jQuery method text()
|
||||
// and then match using regular expression
|
||||
.invoke('text')
|
||||
.should('match', /column content/i);
|
||||
|
||||
// a better way to check element's text content against a regular expression
|
||||
// is to use "cy.contains"
|
||||
// https://on.cypress.io/contains
|
||||
cy.get('.assertion-table')
|
||||
.find('tbody tr:last')
|
||||
// finds first <td> element with text content matching regular expression
|
||||
.contains('td', /column content/i)
|
||||
.should('be.visible');
|
||||
|
||||
// for more information about asserting element's text
|
||||
// see https://on.cypress.io/using-cypress-faq#How-do-I-get-an-element’s-text-contents
|
||||
});
|
||||
|
||||
it('.and() - chain multiple assertions together', () => {
|
||||
// https://on.cypress.io/and
|
||||
cy.get('.assertions-link')
|
||||
.should('have.class', 'active')
|
||||
.and('have.attr', 'href')
|
||||
.and('include', 'cypress.io');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Explicit Assertions', () => {
|
||||
// https://on.cypress.io/assertions
|
||||
it('expect - make an assertion about a specified subject', () => {
|
||||
// We can use Chai's BDD style assertions
|
||||
expect(true).to.be.true;
|
||||
const o = { foo: 'bar' };
|
||||
|
||||
expect(o).to.equal(o);
|
||||
expect(o).to.deep.equal({ foo: 'bar' });
|
||||
// matching text using regular expression
|
||||
expect('FooBar').to.match(/bar$/i);
|
||||
});
|
||||
|
||||
it('pass your own callback function to should()', () => {
|
||||
// Pass a function to should that can have any number
|
||||
// of explicit assertions within it.
|
||||
// The ".should(cb)" function will be retried
|
||||
// automatically until it passes all your explicit assertions or times out.
|
||||
cy.get('.assertions-p')
|
||||
.find('p')
|
||||
.should(($p) => {
|
||||
// https://on.cypress.io/$
|
||||
// return an array of texts from all of the p's
|
||||
// @ts-ignore TS6133 unused variable
|
||||
const texts = $p.map((i, el) => Cypress.$(el).text());
|
||||
|
||||
// jquery map returns jquery object
|
||||
// and .get() convert this to simple array
|
||||
const paragraphs = texts.get();
|
||||
|
||||
// array should have length of 3
|
||||
expect(paragraphs, 'has 3 paragraphs').to.have.length(3);
|
||||
|
||||
// use second argument to expect(...) to provide clear
|
||||
// message with each assertion
|
||||
expect(paragraphs, 'has expected text in each paragraph').to.deep.eq([
|
||||
'Some text from first p',
|
||||
'More text from second p',
|
||||
'And even more text from third p',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('finds element by class name regex', () => {
|
||||
cy.get('.docs-header')
|
||||
.find('div')
|
||||
// .should(cb) callback function will be retried
|
||||
.should(($div) => {
|
||||
expect($div).to.have.length(1);
|
||||
|
||||
const className = $div[0].className;
|
||||
|
||||
expect(className).to.match(/heading-/);
|
||||
})
|
||||
// .then(cb) callback is not retried,
|
||||
// it either passes or fails
|
||||
.then(($div) => {
|
||||
expect($div, 'text content').to.have.text('Introduction');
|
||||
});
|
||||
});
|
||||
|
||||
it('can throw any error', () => {
|
||||
cy.get('.docs-header')
|
||||
.find('div')
|
||||
.should(($div) => {
|
||||
if ($div.length !== 1) {
|
||||
// you can throw your own errors
|
||||
throw new Error('Did not find 1 element');
|
||||
}
|
||||
|
||||
const className = $div[0].className;
|
||||
|
||||
if (!className.match(/heading-/)) {
|
||||
throw new Error(`Could not find class "heading-" in ${className}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('matches unknown text between two elements', () => {
|
||||
/**
|
||||
* Text from the first element.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
let text;
|
||||
|
||||
/**
|
||||
* Normalizes passed text, useful before comparing text with spaces and different capitalization.
|
||||
*
|
||||
* @param {string} s Text to normalize
|
||||
*/
|
||||
const normalizeText = (s) => s.replace(/\s/g, '').toLowerCase();
|
||||
|
||||
cy.get('.two-elements')
|
||||
.find('.first')
|
||||
.then(($first) => {
|
||||
// save text from the first element
|
||||
text = normalizeText($first.text());
|
||||
});
|
||||
|
||||
cy.get('.two-elements')
|
||||
.find('.second')
|
||||
.should(($div) => {
|
||||
// we can massage text before comparing
|
||||
const secondText = normalizeText($div.text());
|
||||
|
||||
expect(secondText, 'second text').to.equal(text);
|
||||
});
|
||||
});
|
||||
|
||||
it('assert - assert shape of an object', () => {
|
||||
const person = {
|
||||
name: 'Joe',
|
||||
age: 20,
|
||||
};
|
||||
|
||||
assert.isObject(person, 'value is object');
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,55 +0,0 @@
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
context('Connectors', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/commands/connectors');
|
||||
});
|
||||
|
||||
it('.each() - iterate over an array of elements', () => {
|
||||
// https://on.cypress.io/each
|
||||
cy.get('.connectors-each-ul>li').each(($el, index, $list) => {
|
||||
console.log($el, index, $list);
|
||||
});
|
||||
});
|
||||
|
||||
it('.its() - get properties on the current subject', () => {
|
||||
// https://on.cypress.io/its
|
||||
cy.get('.connectors-its-ul>li')
|
||||
// calls the 'length' property yielding that value
|
||||
.its('length')
|
||||
.should('be.gt', 2);
|
||||
});
|
||||
|
||||
it('.invoke() - invoke a function on the current subject', () => {
|
||||
// our div is hidden in our script.js
|
||||
// $('.connectors-div').hide()
|
||||
|
||||
// https://on.cypress.io/invoke
|
||||
cy.get('.connectors-div')
|
||||
.should('be.hidden')
|
||||
// call the jquery method 'show' on the 'div.container'
|
||||
.invoke('show')
|
||||
.should('be.visible');
|
||||
});
|
||||
|
||||
it('.spread() - spread an array as individual args to callback function', () => {
|
||||
// https://on.cypress.io/spread
|
||||
const arr = ['foo', 'bar', 'baz'];
|
||||
|
||||
cy.wrap(arr).spread((foo, bar, baz) => {
|
||||
expect(foo).to.eq('foo');
|
||||
expect(bar).to.eq('bar');
|
||||
expect(baz).to.eq('baz');
|
||||
});
|
||||
});
|
||||
|
||||
it('.then() - invoke a callback function with the current subject', () => {
|
||||
// https://on.cypress.io/then
|
||||
cy.get('.connectors-list > li').then(($lis) => {
|
||||
expect($lis, '3 items').to.have.length(3);
|
||||
expect($lis.eq(0), 'first item').to.contain('Walk the dog');
|
||||
expect($lis.eq(1), 'second item').to.contain('Feed the cat');
|
||||
expect($lis.eq(2), 'third item').to.contain('Write JavaScript');
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,79 +0,0 @@
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
context('Cookies', () => {
|
||||
beforeEach(() => {
|
||||
Cypress.Cookies.debug(true);
|
||||
|
||||
cy.visit('https://example.cypress.io/commands/cookies');
|
||||
|
||||
// clear cookies again after visiting to remove
|
||||
// any 3rd party cookies picked up such as cloudflare
|
||||
cy.clearCookies();
|
||||
});
|
||||
|
||||
it('cy.getCookie() - get a browser cookie', () => {
|
||||
// https://on.cypress.io/getcookie
|
||||
cy.get('#getCookie .set-a-cookie').click();
|
||||
|
||||
// cy.getCookie() yields a cookie object
|
||||
cy.getCookie('token').should('have.property', 'value', '123ABC');
|
||||
});
|
||||
|
||||
it('cy.getCookies() - get browser cookies', () => {
|
||||
// https://on.cypress.io/getcookies
|
||||
cy.getCookies().should('be.empty');
|
||||
|
||||
cy.get('#getCookies .set-a-cookie').click();
|
||||
|
||||
// cy.getCookies() yields an array of cookies
|
||||
cy.getCookies()
|
||||
.should('have.length', 1)
|
||||
.should((cookies) => {
|
||||
// each cookie has these properties
|
||||
expect(cookies[0]).to.have.property('name', 'token');
|
||||
expect(cookies[0]).to.have.property('value', '123ABC');
|
||||
expect(cookies[0]).to.have.property('httpOnly', false);
|
||||
expect(cookies[0]).to.have.property('secure', false);
|
||||
expect(cookies[0]).to.have.property('domain');
|
||||
expect(cookies[0]).to.have.property('path');
|
||||
});
|
||||
});
|
||||
|
||||
it('cy.setCookie() - set a browser cookie', () => {
|
||||
// https://on.cypress.io/setcookie
|
||||
cy.getCookies().should('be.empty');
|
||||
|
||||
cy.setCookie('foo', 'bar');
|
||||
|
||||
// cy.getCookie() yields a cookie object
|
||||
cy.getCookie('foo').should('have.property', 'value', 'bar');
|
||||
});
|
||||
|
||||
it('cy.clearCookie() - clear a browser cookie', () => {
|
||||
// https://on.cypress.io/clearcookie
|
||||
cy.getCookie('token').should('be.null');
|
||||
|
||||
cy.get('#clearCookie .set-a-cookie').click();
|
||||
|
||||
cy.getCookie('token').should('have.property', 'value', '123ABC');
|
||||
|
||||
// cy.clearCookies() yields null
|
||||
cy.clearCookie('token').should('be.null');
|
||||
|
||||
cy.getCookie('token').should('be.null');
|
||||
});
|
||||
|
||||
it('cy.clearCookies() - clear browser cookies', () => {
|
||||
// https://on.cypress.io/clearcookies
|
||||
cy.getCookies().should('be.empty');
|
||||
|
||||
cy.get('#clearCookies .set-a-cookie').click();
|
||||
|
||||
cy.getCookies().should('have.length', 1);
|
||||
|
||||
// cy.clearCookies() yields null
|
||||
cy.clearCookies();
|
||||
|
||||
cy.getCookies().should('be.empty');
|
||||
});
|
||||
});
|
@@ -1,225 +0,0 @@
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
context('Cypress.Commands', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/cypress-api');
|
||||
});
|
||||
|
||||
// https://on.cypress.io/custom-commands
|
||||
|
||||
it('.add() - create a custom command', () => {
|
||||
Cypress.Commands.add(
|
||||
'console',
|
||||
{
|
||||
prevSubject: true,
|
||||
},
|
||||
(subject, method) => {
|
||||
// the previous subject is automatically received
|
||||
// and the commands arguments are shifted
|
||||
|
||||
// allow us to change the console method used
|
||||
method = method || 'log';
|
||||
|
||||
// log the subject to the console
|
||||
// @ts-ignore TS7017
|
||||
console[method]('The subject is', subject);
|
||||
|
||||
// whatever we return becomes the new subject
|
||||
// we don't want to change the subject so
|
||||
// we return whatever was passed in
|
||||
return subject;
|
||||
}
|
||||
);
|
||||
|
||||
// @ts-ignore TS2339
|
||||
cy.get('button')
|
||||
.console('info')
|
||||
.then(($button) => {
|
||||
// subject is still $button
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Cypress.Cookies', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/cypress-api');
|
||||
});
|
||||
|
||||
// https://on.cypress.io/cookies
|
||||
it('.debug() - enable or disable debugging', () => {
|
||||
Cypress.Cookies.debug(true);
|
||||
|
||||
// Cypress will now log in the console when
|
||||
// cookies are set or cleared
|
||||
cy.setCookie('fakeCookie', '123ABC');
|
||||
cy.clearCookie('fakeCookie');
|
||||
cy.setCookie('fakeCookie', '123ABC');
|
||||
cy.clearCookie('fakeCookie');
|
||||
cy.setCookie('fakeCookie', '123ABC');
|
||||
});
|
||||
|
||||
it('.preserveOnce() - preserve cookies by key', () => {
|
||||
// normally cookies are reset after each test
|
||||
cy.getCookie('fakeCookie').should('not.be.ok');
|
||||
|
||||
// preserving a cookie will not clear it when
|
||||
// the next test starts
|
||||
cy.setCookie('lastCookie', '789XYZ');
|
||||
Cypress.Cookies.preserveOnce('lastCookie');
|
||||
});
|
||||
|
||||
it('.defaults() - set defaults for all cookies', () => {
|
||||
// now any cookie with the name 'session_id' will
|
||||
// not be cleared before each new test runs
|
||||
Cypress.Cookies.defaults({
|
||||
whitelist: 'session_id',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Cypress.Server', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/cypress-api');
|
||||
});
|
||||
|
||||
// Permanently override server options for
|
||||
// all instances of cy.server()
|
||||
|
||||
// https://on.cypress.io/cypress-server
|
||||
it('.defaults() - change default config of server', () => {
|
||||
Cypress.Server.defaults({
|
||||
delay: 0,
|
||||
force404: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Cypress.arch', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/cypress-api');
|
||||
});
|
||||
|
||||
it('Get CPU architecture name of underlying OS', () => {
|
||||
// https://on.cypress.io/arch
|
||||
expect(Cypress.arch).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
context('Cypress.config()', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/cypress-api');
|
||||
});
|
||||
|
||||
it('Get and set configuration options', () => {
|
||||
// https://on.cypress.io/config
|
||||
let myConfig = Cypress.config();
|
||||
|
||||
expect(myConfig).to.have.property('animationDistanceThreshold', 5);
|
||||
expect(myConfig).to.have.property('baseUrl', null);
|
||||
expect(myConfig).to.have.property('defaultCommandTimeout', 4000);
|
||||
expect(myConfig).to.have.property('requestTimeout', 5000);
|
||||
expect(myConfig).to.have.property('responseTimeout', 30000);
|
||||
expect(myConfig).to.have.property('viewportHeight', 660);
|
||||
expect(myConfig).to.have.property('viewportWidth', 1000);
|
||||
expect(myConfig).to.have.property('pageLoadTimeout', 60000);
|
||||
expect(myConfig).to.have.property('waitForAnimations', true);
|
||||
|
||||
expect(Cypress.config('pageLoadTimeout')).to.eq(60000);
|
||||
|
||||
// this will change the config for the rest of your tests!
|
||||
Cypress.config('pageLoadTimeout', 20000);
|
||||
|
||||
expect(Cypress.config('pageLoadTimeout')).to.eq(20000);
|
||||
|
||||
Cypress.config('pageLoadTimeout', 60000);
|
||||
});
|
||||
});
|
||||
|
||||
context('Cypress.dom', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/cypress-api');
|
||||
});
|
||||
|
||||
// https://on.cypress.io/dom
|
||||
it('.isHidden() - determine if a DOM element is hidden', () => {
|
||||
let hiddenP = Cypress.$('.dom-p p.hidden').get(0);
|
||||
let visibleP = Cypress.$('.dom-p p.visible').get(0);
|
||||
|
||||
// our first paragraph has css class 'hidden'
|
||||
expect(Cypress.dom.isHidden(hiddenP)).to.be.true;
|
||||
expect(Cypress.dom.isHidden(visibleP)).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
context('Cypress.env()', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/cypress-api');
|
||||
});
|
||||
|
||||
// We can set environment variables for highly dynamic values
|
||||
|
||||
// https://on.cypress.io/environment-variables
|
||||
it('Get environment variables', () => {
|
||||
// https://on.cypress.io/env
|
||||
// set multiple environment variables
|
||||
Cypress.env({
|
||||
host: 'veronica.dev.local',
|
||||
api_server: 'http://localhost:8888/v1/',
|
||||
});
|
||||
|
||||
// get environment variable
|
||||
expect(Cypress.env('host')).to.eq('veronica.dev.local');
|
||||
|
||||
// set environment variable
|
||||
Cypress.env('api_server', 'http://localhost:8888/v2/');
|
||||
expect(Cypress.env('api_server')).to.eq('http://localhost:8888/v2/');
|
||||
|
||||
// get all environment variable
|
||||
expect(Cypress.env()).to.have.property('host', 'veronica.dev.local');
|
||||
expect(Cypress.env()).to.have.property('api_server', 'http://localhost:8888/v2/');
|
||||
});
|
||||
});
|
||||
|
||||
context('Cypress.log', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/cypress-api');
|
||||
});
|
||||
|
||||
it('Control what is printed to the Command Log', () => {
|
||||
// https://on.cypress.io/cypress-log
|
||||
});
|
||||
});
|
||||
|
||||
context('Cypress.platform', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/cypress-api');
|
||||
});
|
||||
|
||||
it('Get underlying OS name', () => {
|
||||
// https://on.cypress.io/platform
|
||||
expect(Cypress.platform).to.be.exist;
|
||||
});
|
||||
});
|
||||
|
||||
context('Cypress.version', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/cypress-api');
|
||||
});
|
||||
|
||||
it('Get current version of Cypress being run', () => {
|
||||
// https://on.cypress.io/version
|
||||
expect(Cypress.version).to.be.exist;
|
||||
});
|
||||
});
|
||||
|
||||
context('Cypress.spec', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/cypress-api');
|
||||
});
|
||||
|
||||
it('Get current spec information', () => {
|
||||
// https://on.cypress.io/spec
|
||||
// wrap the object so we can inspect it easily by clicking in the command log
|
||||
cy.wrap(Cypress.spec).should('have.keys', ['name', 'relative', 'absolute']);
|
||||
});
|
||||
});
|
@@ -1,114 +0,0 @@
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
/// JSON fixture file can be loaded directly using
|
||||
// the built-in JavaScript bundler
|
||||
// @ts-ignore
|
||||
const requiredExample = require('../../fixtures/example');
|
||||
|
||||
context('Files', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/commands/files');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// load example.json fixture file and store
|
||||
// in the test context object
|
||||
cy.fixture('example.json').as('example');
|
||||
});
|
||||
|
||||
it('cy.fixture() - load a fixture', () => {
|
||||
// https://on.cypress.io/fixture
|
||||
|
||||
// Instead of writing a response inline you can
|
||||
// use a fixture file's content.
|
||||
|
||||
cy.server();
|
||||
cy.fixture('example.json').as('comment');
|
||||
// when application makes an Ajax request matching "GET comments/*"
|
||||
// Cypress will intercept it and reply with object
|
||||
// from the "comment" alias
|
||||
cy.route('GET', 'comments/*', '@comment').as('getComment');
|
||||
|
||||
// we have code that gets a comment when
|
||||
// the button is clicked in scripts.js
|
||||
cy.get('.fixture-btn').click();
|
||||
|
||||
cy.wait('@getComment')
|
||||
.its('responseBody')
|
||||
.should('have.property', 'name')
|
||||
.and('include', 'Using fixtures to represent data');
|
||||
|
||||
// you can also just write the fixture in the route
|
||||
cy.route('GET', 'comments/*', 'fixture:example.json').as('getComment');
|
||||
|
||||
// we have code that gets a comment when
|
||||
// the button is clicked in scripts.js
|
||||
cy.get('.fixture-btn').click();
|
||||
|
||||
cy.wait('@getComment')
|
||||
.its('responseBody')
|
||||
.should('have.property', 'name')
|
||||
.and('include', 'Using fixtures to represent data');
|
||||
|
||||
// or write fx to represent fixture
|
||||
// by default it assumes it's .json
|
||||
cy.route('GET', 'comments/*', 'fx:example').as('getComment');
|
||||
|
||||
// we have code that gets a comment when
|
||||
// the button is clicked in scripts.js
|
||||
cy.get('.fixture-btn').click();
|
||||
|
||||
cy.wait('@getComment')
|
||||
.its('responseBody')
|
||||
.should('have.property', 'name')
|
||||
.and('include', 'Using fixtures to represent data');
|
||||
});
|
||||
|
||||
it('cy.fixture() or require - load a fixture', function () {
|
||||
// we are inside the "function () { ... }"
|
||||
// callback and can use test context object "this"
|
||||
// "this.example" was loaded in "beforeEach" function callback
|
||||
expect(this.example, 'fixture in the test context').to.deep.equal(requiredExample);
|
||||
|
||||
// or use "cy.wrap" and "should('deep.equal', ...)" assertion
|
||||
// @ts-ignore
|
||||
cy.wrap(this.example, 'fixture vs require').should('deep.equal', requiredExample);
|
||||
});
|
||||
|
||||
it('cy.readFile() - read a files contents', () => {
|
||||
// https://on.cypress.io/readfile
|
||||
|
||||
// You can read a file and yield its contents
|
||||
// The filePath is relative to your project's root.
|
||||
cy.readFile('cypress.json').then((json) => {
|
||||
expect(json).to.be.an('object');
|
||||
});
|
||||
});
|
||||
|
||||
it('cy.writeFile() - write to a file', () => {
|
||||
// https://on.cypress.io/writefile
|
||||
|
||||
// You can write to a file
|
||||
|
||||
// Use a response from a request to automatically
|
||||
// generate a fixture file for use later
|
||||
cy.request('https://jsonplaceholder.cypress.io/users').then((response) => {
|
||||
cy.writeFile('cypress/fixtures/users.json', response.body);
|
||||
});
|
||||
cy.fixture('users').should((users) => {
|
||||
expect(users[0].name).to.exist;
|
||||
});
|
||||
|
||||
// JavaScript arrays and objects are stringified
|
||||
// and formatted into text.
|
||||
cy.writeFile('cypress/fixtures/profile.json', {
|
||||
id: 8739,
|
||||
name: 'Jane',
|
||||
email: 'jane@example.com',
|
||||
});
|
||||
|
||||
cy.fixture('profile').should((profile) => {
|
||||
expect(profile.name).to.eq('Jane');
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,58 +0,0 @@
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
context('Local Storage', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/commands/local-storage');
|
||||
});
|
||||
// Although local storage is automatically cleared
|
||||
// in between tests to maintain a clean state
|
||||
// sometimes we need to clear the local storage manually
|
||||
|
||||
it('cy.clearLocalStorage() - clear all data in local storage', () => {
|
||||
// https://on.cypress.io/clearlocalstorage
|
||||
cy.get('.ls-btn')
|
||||
.click()
|
||||
.should(() => {
|
||||
expect(localStorage.getItem('prop1')).to.eq('red');
|
||||
expect(localStorage.getItem('prop2')).to.eq('blue');
|
||||
expect(localStorage.getItem('prop3')).to.eq('magenta');
|
||||
});
|
||||
|
||||
// clearLocalStorage() yields the localStorage object
|
||||
cy.clearLocalStorage().should((ls) => {
|
||||
expect(ls.getItem('prop1')).to.be.null;
|
||||
expect(ls.getItem('prop2')).to.be.null;
|
||||
expect(ls.getItem('prop3')).to.be.null;
|
||||
});
|
||||
|
||||
// Clear key matching string in Local Storage
|
||||
cy.get('.ls-btn')
|
||||
.click()
|
||||
.should(() => {
|
||||
expect(localStorage.getItem('prop1')).to.eq('red');
|
||||
expect(localStorage.getItem('prop2')).to.eq('blue');
|
||||
expect(localStorage.getItem('prop3')).to.eq('magenta');
|
||||
});
|
||||
|
||||
cy.clearLocalStorage('prop1').should((ls) => {
|
||||
expect(ls.getItem('prop1')).to.be.null;
|
||||
expect(ls.getItem('prop2')).to.eq('blue');
|
||||
expect(ls.getItem('prop3')).to.eq('magenta');
|
||||
});
|
||||
|
||||
// Clear keys matching regex in Local Storage
|
||||
cy.get('.ls-btn')
|
||||
.click()
|
||||
.should(() => {
|
||||
expect(localStorage.getItem('prop1')).to.eq('red');
|
||||
expect(localStorage.getItem('prop2')).to.eq('blue');
|
||||
expect(localStorage.getItem('prop3')).to.eq('magenta');
|
||||
});
|
||||
|
||||
cy.clearLocalStorage(/prop1|2/).should((ls) => {
|
||||
expect(ls.getItem('prop1')).to.be.null;
|
||||
expect(ls.getItem('prop2')).to.be.null;
|
||||
expect(ls.getItem('prop3')).to.eq('magenta');
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,32 +0,0 @@
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
context('Location', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/commands/location');
|
||||
});
|
||||
|
||||
it('cy.hash() - get the current URL hash', () => {
|
||||
// https://on.cypress.io/hash
|
||||
cy.hash().should('be.empty');
|
||||
});
|
||||
|
||||
it('cy.location() - get window.location', () => {
|
||||
// https://on.cypress.io/location
|
||||
cy.location().should((location) => {
|
||||
expect(location.hash).to.be.empty;
|
||||
expect(location.href).to.eq('https://example.cypress.io/commands/location');
|
||||
expect(location.host).to.eq('example.cypress.io');
|
||||
expect(location.hostname).to.eq('example.cypress.io');
|
||||
expect(location.origin).to.eq('https://example.cypress.io');
|
||||
expect(location.pathname).to.eq('/commands/location');
|
||||
expect(location.port).to.eq('');
|
||||
expect(location.protocol).to.eq('https:');
|
||||
expect(location.search).to.be.empty;
|
||||
});
|
||||
});
|
||||
|
||||
it('cy.url() - get the current URL', () => {
|
||||
// https://on.cypress.io/url
|
||||
cy.url().should('eq', 'https://example.cypress.io/commands/location');
|
||||
});
|
||||
});
|
@@ -1,77 +0,0 @@
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
context('Misc', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/commands/misc');
|
||||
});
|
||||
|
||||
it('.end() - end the command chain', () => {
|
||||
// https://on.cypress.io/end
|
||||
|
||||
// cy.end is useful when you want to end a chain of commands
|
||||
// and force Cypress to re-query from the root element
|
||||
cy.get('.misc-table').within(() => {
|
||||
// ends the current chain and yields null
|
||||
cy.contains('Cheryl').click().end();
|
||||
|
||||
// queries the entire table again
|
||||
cy.contains('Charles').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('cy.exec() - execute a system command', () => {
|
||||
// https://on.cypress.io/exec
|
||||
|
||||
// execute a system command.
|
||||
// so you can take actions necessary for
|
||||
// your test outside the scope of Cypress.
|
||||
cy.exec('echo Jane Lane').its('stdout').should('contain', 'Jane Lane');
|
||||
|
||||
// we can use Cypress.platform string to
|
||||
// select appropriate command
|
||||
// https://on.cypress/io/platform
|
||||
cy.log(`Platform ${Cypress.platform} architecture ${Cypress.arch}`);
|
||||
|
||||
if (Cypress.platform === 'win32') {
|
||||
cy.exec('print cypress.json').its('stderr').should('be.empty');
|
||||
} else {
|
||||
cy.exec('cat cypress.json').its('stderr').should('be.empty');
|
||||
|
||||
cy.exec('pwd').its('code').should('eq', 0);
|
||||
}
|
||||
});
|
||||
|
||||
it('cy.focused() - get the DOM element that has focus', () => {
|
||||
// https://on.cypress.io/focused
|
||||
cy.get('.misc-form').find('#name').click();
|
||||
cy.focused().should('have.id', 'name');
|
||||
|
||||
cy.get('.misc-form').find('#description').click();
|
||||
cy.focused().should('have.id', 'description');
|
||||
});
|
||||
|
||||
context('Cypress.Screenshot', function () {
|
||||
it('cy.screenshot() - take a screenshot', () => {
|
||||
// https://on.cypress.io/screenshot
|
||||
cy.screenshot('my-image');
|
||||
});
|
||||
|
||||
it('Cypress.Screenshot.defaults() - change default config of screenshots', function () {
|
||||
Cypress.Screenshot.defaults({
|
||||
blackout: ['.foo'],
|
||||
capture: 'viewport',
|
||||
clip: { x: 0, y: 0, width: 200, height: 200 },
|
||||
scale: false,
|
||||
disableTimersAndAnimations: true,
|
||||
screenshotOnRunFailure: true,
|
||||
beforeScreenshot() {},
|
||||
afterScreenshot() {},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('cy.wrap() - wrap an object', () => {
|
||||
// https://on.cypress.io/wrap
|
||||
cy.wrap({ foo: 'bar' }).should('have.property', 'foo').and('include', 'bar');
|
||||
});
|
||||
});
|
@@ -1,56 +0,0 @@
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
context('Navigation', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io');
|
||||
cy.get('.navbar-nav').contains('Commands').click();
|
||||
cy.get('.dropdown-menu').contains('Navigation').click();
|
||||
});
|
||||
|
||||
it("cy.go() - go back or forward in the browser's history", () => {
|
||||
// https://on.cypress.io/go
|
||||
|
||||
cy.location('pathname').should('include', 'navigation');
|
||||
|
||||
cy.go('back');
|
||||
cy.location('pathname').should('not.include', 'navigation');
|
||||
|
||||
cy.go('forward');
|
||||
cy.location('pathname').should('include', 'navigation');
|
||||
|
||||
// clicking back
|
||||
cy.go(-1);
|
||||
cy.location('pathname').should('not.include', 'navigation');
|
||||
|
||||
// clicking forward
|
||||
cy.go(1);
|
||||
cy.location('pathname').should('include', 'navigation');
|
||||
});
|
||||
|
||||
it('cy.reload() - reload the page', () => {
|
||||
// https://on.cypress.io/reload
|
||||
cy.reload();
|
||||
|
||||
// reload the page without using the cache
|
||||
cy.reload(true);
|
||||
});
|
||||
|
||||
it('cy.visit() - visit a remote url', () => {
|
||||
// https://on.cypress.io/visit
|
||||
|
||||
// Visit any sub-domain of your current domain
|
||||
|
||||
// Pass options to the visit
|
||||
cy.visit('https://example.cypress.io/commands/navigation', {
|
||||
timeout: 50000, // increase total time for the visit to resolve
|
||||
onBeforeLoad(contentWindow) {
|
||||
// contentWindow is the remote page's window object
|
||||
expect(typeof contentWindow === 'object').to.be.true;
|
||||
},
|
||||
onLoad(contentWindow) {
|
||||
// contentWindow is the remote page's window object
|
||||
expect(typeof contentWindow === 'object').to.be.true;
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,192 +0,0 @@
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
context('Network Requests', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/commands/network-requests');
|
||||
});
|
||||
|
||||
// Manage AJAX / XHR requests in your app
|
||||
|
||||
it('cy.server() - control behavior of network requests and responses', () => {
|
||||
// https://on.cypress.io/server
|
||||
|
||||
cy.server().should((server) => {
|
||||
// the default options on server
|
||||
// you can override any of these options
|
||||
expect(server.delay).to.eq(0);
|
||||
expect(server.method).to.eq('GET');
|
||||
expect(server.status).to.eq(200);
|
||||
expect(server.headers).to.be.null;
|
||||
expect(server.response).to.be.null;
|
||||
expect(server.onRequest).to.be.undefined;
|
||||
expect(server.onResponse).to.be.undefined;
|
||||
expect(server.onAbort).to.be.undefined;
|
||||
|
||||
// These options control the server behavior
|
||||
// affecting all requests
|
||||
|
||||
// pass false to disable existing route stubs
|
||||
expect(server.enable).to.be.true;
|
||||
// forces requests that don't match your routes to 404
|
||||
expect(server.force404).to.be.false;
|
||||
// whitelists requests from ever being logged or stubbed
|
||||
expect(server.whitelist).to.be.a('function');
|
||||
});
|
||||
|
||||
cy.server({
|
||||
method: 'POST',
|
||||
delay: 1000,
|
||||
status: 422,
|
||||
response: {},
|
||||
});
|
||||
|
||||
// any route commands will now inherit the above options
|
||||
// from the server. anything we pass specifically
|
||||
// to route will override the defaults though.
|
||||
});
|
||||
|
||||
it('cy.request() - make an XHR request', () => {
|
||||
// https://on.cypress.io/request
|
||||
cy.request('https://jsonplaceholder.cypress.io/comments').should((response) => {
|
||||
expect(response.status).to.eq(200);
|
||||
expect(response.body).to.have.length(500);
|
||||
expect(response).to.have.property('headers');
|
||||
expect(response).to.have.property('duration');
|
||||
});
|
||||
});
|
||||
|
||||
it('cy.request() - verify response using BDD syntax', () => {
|
||||
cy.request('https://jsonplaceholder.cypress.io/comments').then((response) => {
|
||||
// https://on.cypress.io/assertions
|
||||
expect(response).property('status').to.equal(200);
|
||||
expect(response).property('body').to.have.length(500);
|
||||
expect(response).to.include.keys('headers', 'duration');
|
||||
});
|
||||
});
|
||||
|
||||
it('cy.request() with query parameters', () => {
|
||||
// will execute request
|
||||
// https://jsonplaceholder.cypress.io/comments?postId=1&id=3
|
||||
cy.request({
|
||||
url: 'https://jsonplaceholder.cypress.io/comments',
|
||||
qs: {
|
||||
postId: 1,
|
||||
id: 3,
|
||||
},
|
||||
})
|
||||
.its('body')
|
||||
.should('be.an', 'array')
|
||||
.and('have.length', 1)
|
||||
.its('0') // yields first element of the array
|
||||
.should('contain', {
|
||||
postId: 1,
|
||||
id: 3,
|
||||
});
|
||||
});
|
||||
|
||||
it('cy.request() - pass result to the second request', () => {
|
||||
// first, let's find out the userId of the first user we have
|
||||
cy.request('https://jsonplaceholder.cypress.io/users?_limit=1')
|
||||
.its('body.0') // yields the first element of the returned list
|
||||
.then((user) => {
|
||||
expect(user).property('id').to.be.a('number');
|
||||
// make a new post on behalf of the user
|
||||
cy.request('POST', 'https://jsonplaceholder.cypress.io/posts', {
|
||||
userId: user.id,
|
||||
title: 'Cypress Test Runner',
|
||||
body: 'Fast, easy and reliable testing for anything that runs in a browser.',
|
||||
});
|
||||
})
|
||||
// note that the value here is the returned value of the 2nd request
|
||||
// which is the new post object
|
||||
.then((response) => {
|
||||
expect(response).property('status').to.equal(201); // new entity created
|
||||
expect(response).property('body').to.contain({
|
||||
id: 101, // there are already 100 posts, so new entity gets id 101
|
||||
title: 'Cypress Test Runner',
|
||||
});
|
||||
// we don't know the user id here - since it was in above closure
|
||||
// so in this test just confirm that the property is there
|
||||
expect(response.body).property('userId').to.be.a('number');
|
||||
});
|
||||
});
|
||||
|
||||
it('cy.request() - save response in the shared test context', () => {
|
||||
// https://on.cypress.io/variables-and-aliases
|
||||
cy.request('https://jsonplaceholder.cypress.io/users?_limit=1')
|
||||
.its('body.0') // yields the first element of the returned list
|
||||
.as('user') // saves the object in the test context
|
||||
.then(function () {
|
||||
// NOTE 👀
|
||||
// By the time this callback runs the "as('user')" command
|
||||
// has saved the user object in the test context.
|
||||
// To access the test context we need to use
|
||||
// the "function () { ... }" callback form,
|
||||
// otherwise "this" points at a wrong or undefined object!
|
||||
cy.request('POST', 'https://jsonplaceholder.cypress.io/posts', {
|
||||
userId: this.user.id,
|
||||
title: 'Cypress Test Runner',
|
||||
body: 'Fast, easy and reliable testing for anything that runs in a browser.',
|
||||
})
|
||||
.its('body')
|
||||
.as('post'); // save the new post from the response
|
||||
})
|
||||
.then(function () {
|
||||
// When this callback runs, both "cy.request" API commands have finished
|
||||
// and the test context has "user" and "post" objects set.
|
||||
// Let's verify them.
|
||||
expect(this.post, 'post has the right user id').property('userId').to.equal(this.user.id);
|
||||
});
|
||||
});
|
||||
|
||||
it('cy.route() - route responses to matching requests', () => {
|
||||
// https://on.cypress.io/route
|
||||
|
||||
let message = 'whoa, this comment does not exist';
|
||||
|
||||
cy.server();
|
||||
|
||||
// Listen to GET to comments/1
|
||||
cy.route('GET', 'comments/*').as('getComment');
|
||||
|
||||
// we have code that gets a comment when
|
||||
// the button is clicked in scripts.js
|
||||
cy.get('.network-btn').click();
|
||||
|
||||
// https://on.cypress.io/wait
|
||||
cy.wait('@getComment').its('status').should('eq', 200);
|
||||
|
||||
// Listen to POST to comments
|
||||
cy.route('POST', '/comments').as('postComment');
|
||||
|
||||
// we have code that posts a comment when
|
||||
// the button is clicked in scripts.js
|
||||
cy.get('.network-post').click();
|
||||
cy.wait('@postComment');
|
||||
|
||||
// get the route
|
||||
cy.get('@postComment').should((xhr) => {
|
||||
expect(xhr.requestBody).to.include('email');
|
||||
expect(xhr.requestHeaders).to.have.property('Content-Type');
|
||||
expect(xhr.responseBody).to.have.property('name', 'Using POST in cy.route()');
|
||||
});
|
||||
|
||||
// Stub a response to PUT comments/ ****
|
||||
cy.route({
|
||||
method: 'PUT',
|
||||
url: 'comments/*',
|
||||
status: 404,
|
||||
response: { error: message },
|
||||
delay: 500,
|
||||
}).as('putComment');
|
||||
|
||||
// we have code that puts a comment when
|
||||
// the button is clicked in scripts.js
|
||||
cy.get('.network-put').click();
|
||||
|
||||
cy.wait('@putComment');
|
||||
|
||||
// our 404 statusCode logic in scripts.js executed
|
||||
cy.get('.network-put-comment').should('contain', message);
|
||||
});
|
||||
});
|
@@ -1,75 +0,0 @@
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
context('Querying', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/commands/querying');
|
||||
});
|
||||
|
||||
// The most commonly used query is 'cy.get()', you can
|
||||
// think of this like the '$' in jQuery
|
||||
|
||||
it('cy.get() - query DOM elements', () => {
|
||||
// https://on.cypress.io/get
|
||||
|
||||
cy.get('#query-btn').should('contain', 'Button');
|
||||
|
||||
cy.get('.query-btn').should('contain', 'Button');
|
||||
|
||||
cy.get('#querying .well>button:first').should('contain', 'Button');
|
||||
// ↲
|
||||
// Use CSS selectors just like jQuery
|
||||
|
||||
cy.get('[data-test-id="test-example"]').should('have.class', 'example');
|
||||
|
||||
// 'cy.get()' yields jQuery object, you can get its attribute
|
||||
// by invoking `.attr()` method
|
||||
cy.get('[data-test-id="test-example"]')
|
||||
.invoke('attr', 'data-test-id')
|
||||
.should('equal', 'test-example');
|
||||
|
||||
// or you can get element's CSS property
|
||||
cy.get('[data-test-id="test-example"]').invoke('css', 'position').should('equal', 'static');
|
||||
|
||||
// or use assertions directly during 'cy.get()'
|
||||
// https://on.cypress.io/assertions
|
||||
cy.get('[data-test-id="test-example"]')
|
||||
.should('have.attr', 'data-test-id', 'test-example')
|
||||
.and('have.css', 'position', 'static');
|
||||
});
|
||||
|
||||
it('cy.contains() - query DOM elements with matching content', () => {
|
||||
// https://on.cypress.io/contains
|
||||
cy.get('.query-list').contains('bananas').should('have.class', 'third');
|
||||
|
||||
// we can pass a regexp to `.contains()`
|
||||
cy.get('.query-list').contains(/^b\w+/).should('have.class', 'third');
|
||||
|
||||
cy.get('.query-list').contains('apples').should('have.class', 'first');
|
||||
|
||||
// passing a selector to contains will
|
||||
// yield the selector containing the text
|
||||
cy.get('#querying').contains('ul', 'oranges').should('have.class', 'query-list');
|
||||
|
||||
cy.get('.query-button').contains('Save Form').should('have.class', 'btn');
|
||||
});
|
||||
|
||||
it('.within() - query DOM elements within a specific element', () => {
|
||||
// https://on.cypress.io/within
|
||||
cy.get('.query-form').within(() => {
|
||||
cy.get('input:first').should('have.attr', 'placeholder', 'Email');
|
||||
cy.get('input:last').should('have.attr', 'placeholder', 'Password');
|
||||
});
|
||||
});
|
||||
|
||||
it('cy.root() - query the root DOM element', () => {
|
||||
// https://on.cypress.io/root
|
||||
|
||||
// By default, root is the document
|
||||
cy.root().should('match', 'html');
|
||||
|
||||
cy.get('.query-ul').within(() => {
|
||||
// In this within, the root is now the ul DOM element
|
||||
cy.root().should('have.class', 'query-ul');
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,94 +0,0 @@
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
context('Spies, Stubs, and Clock', () => {
|
||||
it('cy.spy() - wrap a method in a spy', () => {
|
||||
// https://on.cypress.io/spy
|
||||
cy.visit('https://example.cypress.io/commands/spies-stubs-clocks');
|
||||
|
||||
const obj = {
|
||||
foo() {},
|
||||
};
|
||||
|
||||
const spy = cy.spy(obj, 'foo').as('anyArgs');
|
||||
|
||||
obj.foo();
|
||||
|
||||
expect(spy).to.be.called;
|
||||
});
|
||||
|
||||
it('cy.spy() retries until assertions pass', () => {
|
||||
cy.visit('https://example.cypress.io/commands/spies-stubs-clocks');
|
||||
|
||||
const obj = {
|
||||
/**
|
||||
* Prints the argument passed
|
||||
*
|
||||
* @param x {any}
|
||||
*/
|
||||
foo(x) {
|
||||
console.log('obj.foo called with', x);
|
||||
},
|
||||
};
|
||||
|
||||
cy.spy(obj, 'foo').as('foo');
|
||||
|
||||
setTimeout(() => {
|
||||
obj.foo('first');
|
||||
}, 500);
|
||||
|
||||
setTimeout(() => {
|
||||
obj.foo('second');
|
||||
}, 2500);
|
||||
|
||||
cy.get('@foo').should('have.been.calledTwice');
|
||||
});
|
||||
|
||||
it('cy.stub() - create a stub and/or replace a function with stub', () => {
|
||||
// https://on.cypress.io/stub
|
||||
cy.visit('https://example.cypress.io/commands/spies-stubs-clocks');
|
||||
|
||||
const obj = {
|
||||
/**
|
||||
* Prints both arguments to the console
|
||||
*
|
||||
* @param a {string}
|
||||
* @param b {string}
|
||||
*/
|
||||
foo(a, b) {
|
||||
console.log('a', a, 'b', b);
|
||||
},
|
||||
};
|
||||
|
||||
const stub = cy.stub(obj, 'foo').as('foo');
|
||||
|
||||
obj.foo('foo', 'bar');
|
||||
|
||||
expect(stub).to.be.called;
|
||||
});
|
||||
|
||||
it('cy.clock() - control time in the browser', () => {
|
||||
// https://on.cypress.io/clock
|
||||
|
||||
// create the date in UTC so its always the same
|
||||
// no matter what local timezone the browser is running in
|
||||
const now = new Date(Date.UTC(2017, 2, 14)).getTime();
|
||||
|
||||
cy.clock(now);
|
||||
cy.visit('https://example.cypress.io/commands/spies-stubs-clocks');
|
||||
cy.get('#clock-div').click().should('have.text', '1489449600');
|
||||
});
|
||||
|
||||
it('cy.tick() - move time in the browser', () => {
|
||||
// https://on.cypress.io/tick
|
||||
|
||||
// create the date in UTC so its always the same
|
||||
// no matter what local timezone the browser is running in
|
||||
const now = new Date(Date.UTC(2017, 2, 14)).getTime();
|
||||
|
||||
cy.clock(now);
|
||||
cy.visit('https://example.cypress.io/commands/spies-stubs-clocks');
|
||||
cy.get('#tick-div').click().should('have.text', '1489449600');
|
||||
cy.tick(10000); // 10 seconds passed
|
||||
cy.get('#tick-div').click().should('have.text', '1489449610');
|
||||
});
|
||||
});
|
@@ -1,97 +0,0 @@
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
context('Traversal', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/commands/traversal');
|
||||
});
|
||||
|
||||
it('.children() - get child DOM elements', () => {
|
||||
// https://on.cypress.io/children
|
||||
cy.get('.traversal-breadcrumb').children('.active').should('contain', 'Data');
|
||||
});
|
||||
|
||||
it('.closest() - get closest ancestor DOM element', () => {
|
||||
// https://on.cypress.io/closest
|
||||
cy.get('.traversal-badge').closest('ul').should('have.class', 'list-group');
|
||||
});
|
||||
|
||||
it('.eq() - get a DOM element at a specific index', () => {
|
||||
// https://on.cypress.io/eq
|
||||
cy.get('.traversal-list>li').eq(1).should('contain', 'siamese');
|
||||
});
|
||||
|
||||
it('.filter() - get DOM elements that match the selector', () => {
|
||||
// https://on.cypress.io/filter
|
||||
cy.get('.traversal-nav>li').filter('.active').should('contain', 'About');
|
||||
});
|
||||
|
||||
it('.find() - get descendant DOM elements of the selector', () => {
|
||||
// https://on.cypress.io/find
|
||||
cy.get('.traversal-pagination').find('li').find('a').should('have.length', 7);
|
||||
});
|
||||
|
||||
it('.first() - get first DOM element', () => {
|
||||
// https://on.cypress.io/first
|
||||
cy.get('.traversal-table td').first().should('contain', '1');
|
||||
});
|
||||
|
||||
it('.last() - get last DOM element', () => {
|
||||
// https://on.cypress.io/last
|
||||
cy.get('.traversal-buttons .btn').last().should('contain', 'Submit');
|
||||
});
|
||||
|
||||
it('.next() - get next sibling DOM element', () => {
|
||||
// https://on.cypress.io/next
|
||||
cy.get('.traversal-ul').contains('apples').next().should('contain', 'oranges');
|
||||
});
|
||||
|
||||
it('.nextAll() - get all next sibling DOM elements', () => {
|
||||
// https://on.cypress.io/nextall
|
||||
cy.get('.traversal-next-all').contains('oranges').nextAll().should('have.length', 3);
|
||||
});
|
||||
|
||||
it('.nextUntil() - get next sibling DOM elements until next el', () => {
|
||||
// https://on.cypress.io/nextuntil
|
||||
cy.get('#veggies').nextUntil('#nuts').should('have.length', 3);
|
||||
});
|
||||
|
||||
it('.not() - remove DOM elements from set of DOM elements', () => {
|
||||
// https://on.cypress.io/not
|
||||
cy.get('.traversal-disabled .btn').not('[disabled]').should('not.contain', 'Disabled');
|
||||
});
|
||||
|
||||
it('.parent() - get parent DOM element from DOM elements', () => {
|
||||
// https://on.cypress.io/parent
|
||||
cy.get('.traversal-mark').parent().should('contain', 'Morbi leo risus');
|
||||
});
|
||||
|
||||
it('.parents() - get parent DOM elements from DOM elements', () => {
|
||||
// https://on.cypress.io/parents
|
||||
cy.get('.traversal-cite').parents().should('match', 'blockquote');
|
||||
});
|
||||
|
||||
it('.parentsUntil() - get parent DOM elements from DOM elements until el', () => {
|
||||
// https://on.cypress.io/parentsuntil
|
||||
cy.get('.clothes-nav').find('.active').parentsUntil('.clothes-nav').should('have.length', 2);
|
||||
});
|
||||
|
||||
it('.prev() - get previous sibling DOM element', () => {
|
||||
// https://on.cypress.io/prev
|
||||
cy.get('.birds').find('.active').prev().should('contain', 'Lorikeets');
|
||||
});
|
||||
|
||||
it('.prevAll() - get all previous sibling DOM elements', () => {
|
||||
// https://on.cypress.io/prevAll
|
||||
cy.get('.fruits-list').find('.third').prevAll().should('have.length', 2);
|
||||
});
|
||||
|
||||
it('.prevUntil() - get all previous sibling DOM elements until el', () => {
|
||||
// https://on.cypress.io/prevUntil
|
||||
cy.get('.foods-list').find('#nuts').prevUntil('#veggies').should('have.length', 3);
|
||||
});
|
||||
|
||||
it('.siblings() - get all sibling DOM elements', () => {
|
||||
// https://on.cypress.io/siblings
|
||||
cy.get('.traversal-pills .active').siblings().should('have.length', 2);
|
||||
});
|
||||
});
|
@@ -1,129 +0,0 @@
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
context('Utilities', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/utilities');
|
||||
});
|
||||
|
||||
it('Cypress._ - call a lodash method', () => {
|
||||
// https://on.cypress.io/_
|
||||
cy.request('https://jsonplaceholder.cypress.io/users').then((response) => {
|
||||
let ids = Cypress._.chain(response.body).map('id').take(3).value();
|
||||
|
||||
expect(ids).to.deep.eq([1, 2, 3]);
|
||||
});
|
||||
});
|
||||
|
||||
it('Cypress.$ - call a jQuery method', () => {
|
||||
// https://on.cypress.io/$
|
||||
let $li = Cypress.$('.utility-jquery li:first');
|
||||
|
||||
cy.wrap($li).should('not.have.class', 'active').click().should('have.class', 'active');
|
||||
});
|
||||
|
||||
it('Cypress.Blob - blob utilities and base64 string conversion', () => {
|
||||
// https://on.cypress.io/blob
|
||||
cy.get('.utility-blob').then(($div) =>
|
||||
// https://github.com/nolanlawson/blob-util#imgSrcToDataURL
|
||||
// get the dataUrl string for the javascript-logo
|
||||
Cypress.Blob.imgSrcToDataURL(
|
||||
'https://example.cypress.io/assets/img/javascript-logo.png',
|
||||
undefined,
|
||||
'anonymous'
|
||||
).then((dataUrl) => {
|
||||
// create an <img> element and set its src to the dataUrl
|
||||
let img = Cypress.$('<img />', { src: dataUrl });
|
||||
|
||||
// need to explicitly return cy here since we are initially returning
|
||||
// the Cypress.Blob.imgSrcToDataURL promise to our test
|
||||
// append the image
|
||||
$div.append(img);
|
||||
|
||||
cy.get('.utility-blob img').click().should('have.attr', 'src', dataUrl);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('Cypress.minimatch - test out glob patterns against strings', () => {
|
||||
// https://on.cypress.io/minimatch
|
||||
let matching = Cypress.minimatch('/users/1/comments', '/users/*/comments', {
|
||||
matchBase: true,
|
||||
});
|
||||
|
||||
expect(matching, 'matching wildcard').to.be.true;
|
||||
|
||||
matching = Cypress.minimatch('/users/1/comments/2', '/users/*/comments', {
|
||||
matchBase: true,
|
||||
});
|
||||
expect(matching, 'comments').to.be.false;
|
||||
|
||||
// ** matches against all downstream path segments
|
||||
matching = Cypress.minimatch('/foo/bar/baz/123/quux?a=b&c=2', '/foo/**', {
|
||||
matchBase: true,
|
||||
});
|
||||
expect(matching, 'comments').to.be.true;
|
||||
|
||||
// whereas * matches only the next path segment
|
||||
|
||||
matching = Cypress.minimatch('/foo/bar/baz/123/quux?a=b&c=2', '/foo/*', {
|
||||
matchBase: false,
|
||||
});
|
||||
expect(matching, 'comments').to.be.false;
|
||||
});
|
||||
|
||||
it('Cypress.moment() - format or parse dates using a moment method', () => {
|
||||
// https://on.cypress.io/moment
|
||||
const time = Cypress.moment().utc('2014-04-25T19:38:53.196Z').format('h:mm A');
|
||||
|
||||
expect(time).to.be.a('string');
|
||||
|
||||
cy.get('.utility-moment').contains('3:38 PM').should('have.class', 'badge');
|
||||
|
||||
// the time in the element should be between 3pm and 5pm
|
||||
const start = Cypress.moment('3:00 PM', 'LT');
|
||||
const end = Cypress.moment('5:00 PM', 'LT');
|
||||
|
||||
cy.get('.utility-moment .badge').should(($el) => {
|
||||
// parse American time like "3:38 PM"
|
||||
const m = Cypress.moment($el.text().trim(), 'LT');
|
||||
|
||||
// display hours + minutes + AM|PM
|
||||
const f = 'h:mm A';
|
||||
|
||||
expect(
|
||||
m.isBetween(start, end),
|
||||
`${m.format(f)} should be between ${start.format(f)} and ${end.format(f)}`
|
||||
).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
it('Cypress.Promise - instantiate a bluebird promise', () => {
|
||||
// https://on.cypress.io/promise
|
||||
let waited = false;
|
||||
|
||||
/** @returns Bluebird<string> */
|
||||
function waitOneSecond() {
|
||||
// return a promise that resolves after 1 second
|
||||
// @ts-ignore TS2351 (new Cypress.Promise)
|
||||
return new Cypress.Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
// set waited to true
|
||||
waited = true;
|
||||
|
||||
// resolve with 'foo' string
|
||||
resolve('foo');
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
cy.then(() =>
|
||||
// return a promise to cy.then() that
|
||||
// is awaited until it resolves
|
||||
// @ts-ignore TS7006
|
||||
waitOneSecond().then((str) => {
|
||||
expect(str).to.eq('foo');
|
||||
expect(waited).to.be.true;
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
@@ -1,59 +0,0 @@
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
context('Viewport', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/commands/viewport');
|
||||
});
|
||||
|
||||
it('cy.viewport() - set the viewport size and dimension', () => {
|
||||
// https://on.cypress.io/viewport
|
||||
|
||||
cy.get('#navbar').should('be.visible');
|
||||
cy.viewport(320, 480);
|
||||
|
||||
// the navbar should have collapse since our screen is smaller
|
||||
cy.get('#navbar').should('not.be.visible');
|
||||
cy.get('.navbar-toggle').should('be.visible').click();
|
||||
cy.get('.nav').find('a').should('be.visible');
|
||||
|
||||
// lets see what our app looks like on a super large screen
|
||||
cy.viewport(2999, 2999);
|
||||
|
||||
// cy.viewport() accepts a set of preset sizes
|
||||
// to easily set the screen to a device's width and height
|
||||
|
||||
// We added a cy.wait() between each viewport change so you can see
|
||||
// the change otherwise it is a little too fast to see :)
|
||||
|
||||
cy.viewport('macbook-15');
|
||||
cy.wait(200);
|
||||
cy.viewport('macbook-13');
|
||||
cy.wait(200);
|
||||
cy.viewport('macbook-11');
|
||||
cy.wait(200);
|
||||
cy.viewport('ipad-2');
|
||||
cy.wait(200);
|
||||
cy.viewport('ipad-mini');
|
||||
cy.wait(200);
|
||||
cy.viewport('iphone-6+');
|
||||
cy.wait(200);
|
||||
cy.viewport('iphone-6');
|
||||
cy.wait(200);
|
||||
cy.viewport('iphone-5');
|
||||
cy.wait(200);
|
||||
cy.viewport('iphone-4');
|
||||
cy.wait(200);
|
||||
cy.viewport('iphone-3');
|
||||
cy.wait(200);
|
||||
|
||||
// cy.viewport() accepts an orientation for all presets
|
||||
// the default orientation is 'portrait'
|
||||
cy.viewport('ipad-2', 'portrait');
|
||||
cy.wait(200);
|
||||
cy.viewport('iphone-4', 'landscape');
|
||||
cy.wait(200);
|
||||
|
||||
// The viewport will be reset back to the default dimensions
|
||||
// in between tests (the default can be set in cypress.json)
|
||||
});
|
||||
});
|
@@ -1,33 +0,0 @@
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
context('Waiting', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/commands/waiting');
|
||||
});
|
||||
// BE CAREFUL of adding unnecessary wait times.
|
||||
// https://on.cypress.io/best-practices#Unnecessary-Waiting
|
||||
|
||||
// https://on.cypress.io/wait
|
||||
it('cy.wait() - wait for a specific amount of time', () => {
|
||||
cy.get('.wait-input1').type('Wait 1000ms after typing');
|
||||
cy.wait(1000);
|
||||
cy.get('.wait-input2').type('Wait 1000ms after typing');
|
||||
cy.wait(1000);
|
||||
cy.get('.wait-input3').type('Wait 1000ms after typing');
|
||||
cy.wait(1000);
|
||||
});
|
||||
|
||||
it('cy.wait() - wait for a specific route', () => {
|
||||
cy.server();
|
||||
|
||||
// Listen to GET to comments/1
|
||||
cy.route('GET', 'comments/*').as('getComment');
|
||||
|
||||
// we have code that gets a comment when
|
||||
// the button is clicked in scripts.js
|
||||
cy.get('.network-btn').click();
|
||||
|
||||
// wait for GET comments/1
|
||||
cy.wait('@getComment').its('status').should('eq', 200);
|
||||
});
|
||||
});
|
@@ -1,22 +0,0 @@
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
context('Window', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('https://example.cypress.io/commands/window');
|
||||
});
|
||||
|
||||
it('cy.window() - get the global window object', () => {
|
||||
// https://on.cypress.io/window
|
||||
cy.window().should('have.property', 'top');
|
||||
});
|
||||
|
||||
it('cy.document() - get the document object', () => {
|
||||
// https://on.cypress.io/document
|
||||
cy.document().should('have.property', 'charset').and('eq', 'UTF-8');
|
||||
});
|
||||
|
||||
it('cy.title() - get the title', () => {
|
||||
// https://on.cypress.io/title
|
||||
cy.title().should('include', 'Kitchen Sink');
|
||||
});
|
||||
});
|
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
@@ -102,4 +102,109 @@ describe('Git Graph diagram', () => {
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('8: should render a simple gitgraph with more than 8 branchs & overriding variables', () => {
|
||||
imgSnapshotTest(
|
||||
`%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': {
|
||||
'gitBranchLabel0': '#ffffff',
|
||||
'gitBranchLabel1': '#ffffff',
|
||||
'gitBranchLabel2': '#ffffff',
|
||||
'gitBranchLabel3': '#ffffff',
|
||||
'gitBranchLabel4': '#ffffff',
|
||||
'gitBranchLabel5': '#ffffff',
|
||||
'gitBranchLabel6': '#ffffff',
|
||||
'gitBranchLabel7': '#ffffff',
|
||||
} } }%%
|
||||
gitGraph
|
||||
checkout main
|
||||
branch branch1
|
||||
branch branch2
|
||||
branch branch3
|
||||
branch branch4
|
||||
branch branch5
|
||||
branch branch6
|
||||
branch branch7
|
||||
branch branch8
|
||||
branch branch9
|
||||
checkout branch1
|
||||
commit id: "1"
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('9: should render a simple gitgraph with rotated labels', () => {
|
||||
imgSnapshotTest(
|
||||
`%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'gitGraph': {
|
||||
'rotateCommitLabel': true
|
||||
} } }%%
|
||||
gitGraph
|
||||
commit id: "75f7219e83b321cd3fdde7dcf83bc7c1000a6828"
|
||||
commit id: "0db4784daf82736dec4569e0dc92980d328c1f2e"
|
||||
commit id: "7067e9973f9eaa6cd4a4b723c506d1eab598e83e"
|
||||
commit id: "66972321ad6c199013b5b31f03b3a86fa3f9817d"
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('10: should render a simple gitgraph with horizontal labels', () => {
|
||||
imgSnapshotTest(
|
||||
`%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'gitGraph': {
|
||||
'rotateCommitLabel': false
|
||||
} } }%%
|
||||
gitGraph
|
||||
commit id: "Alpha"
|
||||
commit id: "Beta"
|
||||
commit id: "Gamma"
|
||||
commit id: "Delta"
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('11: should render a simple gitgraph with cherry pick commit', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
gitGraph
|
||||
commit id: "ZERO"
|
||||
branch develop
|
||||
commit id:"A"
|
||||
checkout main
|
||||
commit id:"ONE"
|
||||
checkout develop
|
||||
commit id:"B"
|
||||
checkout main
|
||||
commit id:"TWO"
|
||||
cherry-pick id:"A"
|
||||
commit id:"THREE"
|
||||
checkout develop
|
||||
commit id:"C"
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('11: should render a simple gitgraph with two cherry pick commit', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
gitGraph
|
||||
commit id: "ZERO"
|
||||
branch develop
|
||||
commit id:"A"
|
||||
checkout main
|
||||
commit id:"ONE"
|
||||
checkout develop
|
||||
commit id:"B"
|
||||
branch featureA
|
||||
commit id:"FIX"
|
||||
commit id: "FIX-2"
|
||||
checkout main
|
||||
commit id:"TWO"
|
||||
cherry-pick id:"A"
|
||||
commit id:"THREE"
|
||||
cherry-pick id:"FIX"
|
||||
checkout develop
|
||||
commit id:"C"
|
||||
merge featureA
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@@ -452,6 +452,42 @@ context('Sequence diagram', () => {
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('should render rect around and inside criticals', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
A ->> B: 1
|
||||
rect rgb(204, 0, 102)
|
||||
critical yes
|
||||
C ->> C: 1
|
||||
option no
|
||||
rect rgb(0, 204, 204)
|
||||
C ->> C: 0
|
||||
end
|
||||
end
|
||||
end
|
||||
B ->> A: Return
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('should render rect around and inside breaks', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
A ->> B: 1
|
||||
rect rgb(204, 0, 102)
|
||||
break yes
|
||||
rect rgb(0, 204, 204)
|
||||
C ->> C: 0
|
||||
end
|
||||
end
|
||||
end
|
||||
B ->> A: Return
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('should render autonumber when configured with such', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
|
@@ -346,6 +346,21 @@ describe('State diagram', () => {
|
||||
}
|
||||
);
|
||||
});
|
||||
it('v2 A compound state should be able to link to itself', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
stateDiagram
|
||||
state Active {
|
||||
Idle
|
||||
}
|
||||
Inactive --> Idle: ACT
|
||||
Active --> Active: LOG
|
||||
`,
|
||||
{
|
||||
logLevel: 0,
|
||||
}
|
||||
);
|
||||
});
|
||||
it('v2 width of compond state should grow with title if title is wider', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
|
@@ -78,7 +78,7 @@
|
||||
a_a --> c --> d_d --> c_c
|
||||
classDef apa fill:#f9f,stroke:#333,stroke-width:4px;
|
||||
class a_a apa;
|
||||
click a_a "http://www.aftonbladet.se" "apa"
|
||||
click a_a "https://www.aftonbladet.se" "apa"
|
||||
|
||||
</div>
|
||||
|
||||
|
@@ -30,7 +30,54 @@
|
||||
|
||||
|
||||
|
||||
<div class="mermaid" style="width: 50%;">
|
||||
<div class="mermaid2" style="width: 50%;">
|
||||
journey
|
||||
title Adding journey diagram functionality to mermaid
|
||||
accTitle: Adding acc journey diagram functionality to mermaid
|
||||
accDescr {
|
||||
My multi-line description
|
||||
of the diagram
|
||||
}
|
||||
section Order from website
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 50%;">
|
||||
pie
|
||||
accTitle: My Pie Chart Accessibility Title
|
||||
accDescr: My Pie Chart Accessibility Description
|
||||
|
||||
title Key elements in Product X
|
||||
"Calcium" : 42.96
|
||||
"Potassium" : 50.05
|
||||
"Magnesium" : 10.01
|
||||
"Iron" : 5
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 50%;">
|
||||
gitGraph
|
||||
accTitle: My Gitgraph Accessibility Title
|
||||
accDescr: My Gitgraph Accessibility Description
|
||||
|
||||
commit
|
||||
commit
|
||||
branch develop
|
||||
checkout develop
|
||||
commit
|
||||
commit
|
||||
checkout main
|
||||
merge develop
|
||||
commit
|
||||
commit
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 50%;">
|
||||
sequenceDiagram
|
||||
title: My Sequence Diagram Title
|
||||
accTitle: My Acc Sequence Diagram
|
||||
accDescr: My Sequence Diagram Description
|
||||
|
||||
Alice->>John: Hello John, how are you?
|
||||
John-->>Alice: Great!
|
||||
Alice-)John: See you later!
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 50%;">
|
||||
graph TD
|
||||
A -->|000| B
|
||||
B -->|111| C
|
||||
@@ -38,18 +85,23 @@ graph TD
|
||||
linkStyle 1 stroke:#ff3,stroke-width:4px,color:red;
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 100%;">
|
||||
journey
|
||||
title My working day
|
||||
section Go to work
|
||||
Make tea: 5: Me
|
||||
Go upstairs: 3: Me
|
||||
Do work: 1: Me, Cat
|
||||
section Go home
|
||||
Go downstairs: 5: Me
|
||||
Sit down: 5: Me
|
||||
journey
|
||||
accTitle: My User Journey Diagram
|
||||
accDescr: My User Journey Diagram Description
|
||||
|
||||
title My working day
|
||||
section Go to work
|
||||
Make tea: 5: Me
|
||||
Go upstairs: 3: Me
|
||||
Do work: 1: Me, Cat
|
||||
section Go home
|
||||
Go downstairs: 5: Me
|
||||
Sit down: 5: Me
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 100%;">
|
||||
requirementDiagram
|
||||
accTitle: My req Diagram
|
||||
accDescr: My req Diagram Description
|
||||
|
||||
requirement test_req {
|
||||
id: 1
|
||||
@@ -87,65 +139,38 @@ requirementDiagram
|
||||
test_req - contains -> test_req3
|
||||
test_req <- copies - test_entity2
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 100%;">
|
||||
erDiagram
|
||||
CUSTOMER }|..|{ DELIVERY-ADDRESS : has
|
||||
CUSTOMER ||--o{ ORDER : places
|
||||
CUSTOMER ||--o{ INVOICE : "liable for"
|
||||
DELIVERY-ADDRESS ||--o{ ORDER : receives
|
||||
INVOICE ||--|{ ORDER : covers
|
||||
ORDER ||--|{ ORDER-ITEM : includes
|
||||
PRODUCT-CATEGORY ||--|{ PRODUCT : contains
|
||||
PRODUCT ||--o{ ORDER-ITEM : "ordered in"
|
||||
<div class="mermaid" style="width: 100%;">
|
||||
stateDiagram
|
||||
state Active {
|
||||
Idle
|
||||
}
|
||||
Inactive --> Idle: ACT
|
||||
Active --> Active: LOG
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 100%;">
|
||||
graph TB
|
||||
accTitle: My flowchart
|
||||
accDescr: My flowchart Description
|
||||
subgraph One
|
||||
a1-->a2-->a3
|
||||
end
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 100%;">
|
||||
gantt
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %d/%m
|
||||
title Adding GANTT diagram to mermaid
|
||||
excludes weekdays 2014-01-10
|
||||
|
||||
section A section
|
||||
Completed task :done, des1, 2014-01-06,2014-01-08
|
||||
Active task :active, des2, 2014-01-09, 3d
|
||||
Future task : des3, after des2, 5d
|
||||
Future task2 : des4, after des3, 5d
|
||||
|
||||
section Critical tasks
|
||||
Completed task in the critical line :crit, done, 2014-01-06,24h
|
||||
Implement parser and jison :crit, done, after des1, 2d
|
||||
Create tests for parser :crit, active, 3d
|
||||
Future task in critical line :crit, 5d
|
||||
Create tests for renderer :2d
|
||||
Add to mermaid :1d
|
||||
|
||||
section Documentation
|
||||
Describe gantt syntax :active, a1, after des1, 3d
|
||||
Add gantt diagram to demo page :after a1 , 20h
|
||||
Add another diagram to demo page :doc1, after a1 , 48h
|
||||
|
||||
section Clickable
|
||||
Visit mermaidjs :active, cl1, 2014-01-07,2014-01-10
|
||||
Calling a Callback (look at the console log) :cl2, after cl1, 3d
|
||||
Calling a Callback with args :cl3, after cl1, 3d
|
||||
|
||||
click cl1 href "https://mermaid-js.github.io/mermaid/#/"
|
||||
click cl2 call clickByGantt()
|
||||
click cl3 call clickByGantt("test1", test2, test3)
|
||||
|
||||
section Last section
|
||||
Describe gantt syntax :after doc1, 3d
|
||||
Add gantt diagram to demo page : 20h
|
||||
Add another diagram to demo page : 48h
|
||||
sequenceDiagram
|
||||
A ->> B: 1
|
||||
rect rgb(204, 0, 102)
|
||||
break yes
|
||||
rect rgb(0, 204, 204)
|
||||
C ->> C: 0
|
||||
end
|
||||
end
|
||||
end
|
||||
B ->> A: Return
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 100%;">
|
||||
classDiagram
|
||||
accTitle: My class diagram
|
||||
accDescr: My class diagram Description
|
||||
Class01 <|-- AveryLongClass : Cool
|
||||
Class09 --> C2 : Where am i?
|
||||
Class09 --* C3
|
||||
@@ -162,6 +187,8 @@ class Class10 {
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 100%;">
|
||||
stateDiagram
|
||||
accTitle: Apa
|
||||
accDescr: One that can climb better then any man
|
||||
[*] --> S1
|
||||
state "Some long name" as S1
|
||||
</div>
|
||||
@@ -203,6 +230,7 @@ class Class10 {
|
||||
nodeSpacing: 10,
|
||||
curve: 'cardinal',
|
||||
htmlLabels: true,
|
||||
useMaxWidth: false,
|
||||
// defaultRenderer: 'dagre-d3',
|
||||
},
|
||||
class: {
|
||||
|
@@ -11,52 +11,207 @@
|
||||
body {
|
||||
/* background: rgb(221, 208, 208); */
|
||||
/* background:#333; */
|
||||
font-family: 'Arial';
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
/* font-size: 18px !important; */
|
||||
}
|
||||
h1 { color: grey;}
|
||||
.mermaid2 {
|
||||
display: none;
|
||||
}
|
||||
.mermaid svg {
|
||||
.mermaid {
|
||||
border: 1px solid red;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
/* font-size: 18px !important; */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>info below</div>
|
||||
<div class="flex">
|
||||
<div class="">
|
||||
<div class="mermaid2" style="width: 100%; height: 400px;">
|
||||
%%{init: { "logLevel": 1, "er": {"fontSize":18 }} }%%
|
||||
erDiagram
|
||||
CUSTOMER }|..|{ DELIVERY-ADDRESS : has
|
||||
CUSTOMER ||--o{ ORDER : places
|
||||
CUSTOMER ||--o{ INVOICE : "liable for"
|
||||
DELIVERY-ADDRESS ||--o{ ORDER : receives
|
||||
INVOICE ||--|{ ORDER : covers
|
||||
ORDER ||--|{ ORDER-ITEM : includes
|
||||
PRODUCT-CATEGORY ||--|{ PRODUCT : contains
|
||||
PRODUCT ||--o{ ORDER-ITEM : "ordered in"
|
||||
flowchart TB;subgraph "number as labels";1;end;
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 50%; height: 400px;">
|
||||
<div class="mermaid2" style="width: 100%; height: 400px;">
|
||||
flowchart TB;a[APA];
|
||||
</div>
|
||||
<div class="mermaid2" style="margin-left:100px;">
|
||||
graph TD
|
||||
work --> sleep
|
||||
sleep --> work
|
||||
eat --> sleep
|
||||
work --> eat
|
||||
</div>
|
||||
<div class="mermaid2" style="margin-left:100px;">
|
||||
flowchart TD
|
||||
A[Christmas] ==> D
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
A[Christmas] ==> C
|
||||
subgraph T ["Test"]
|
||||
A
|
||||
B
|
||||
C
|
||||
end
|
||||
work --> sleep
|
||||
sleep --> work
|
||||
eat --> sleep
|
||||
work --> eat
|
||||
</div>
|
||||
<div class="mermaid2" style="">
|
||||
graph TB
|
||||
A
|
||||
B
|
||||
subgraph foo[Foo SubGraph]
|
||||
C
|
||||
D
|
||||
end
|
||||
subgraph bar[Bar SubGraph]
|
||||
E
|
||||
F
|
||||
end
|
||||
G
|
||||
|
||||
classDef Test fill:#F84E68,stroke:#333,color:white;
|
||||
class A,T Test
|
||||
classDef TestSub fill:green;
|
||||
class T TestSub
|
||||
linkStyle 0,1 color:orange, stroke: orange;
|
||||
A-->B
|
||||
B-->C
|
||||
C-->D
|
||||
B-->D
|
||||
D-->E
|
||||
E-->A
|
||||
E-->F
|
||||
F-->D
|
||||
F-->G
|
||||
B-->G
|
||||
G-->D
|
||||
|
||||
style foo fill:#F99,stroke-width:2px,stroke:#F0F,color:darkred
|
||||
style bar fill:#999,stroke-width:2px,stroke:#0F0,color:blue
|
||||
</div>
|
||||
<div class="mermaid2" style="">
|
||||
graph TB
|
||||
%%{init: { "logLevel": 1, "flowchart": {"htmlLabels":true }} }%%
|
||||
A
|
||||
B
|
||||
subgraph foo[Foo SubGraph]
|
||||
C
|
||||
D
|
||||
end
|
||||
subgraph bar[Bar SubGraph]
|
||||
E
|
||||
F
|
||||
end
|
||||
G
|
||||
|
||||
A-->B
|
||||
B-->C
|
||||
C-->D
|
||||
B-->D
|
||||
D-->E
|
||||
E-->A
|
||||
E-->F
|
||||
F-->D
|
||||
F-->G
|
||||
B-->G
|
||||
G-->D
|
||||
|
||||
style foo fill:#F99,stroke-width:2px,stroke:#F0F,color:darkred
|
||||
style bar fill:#999,stroke-width:10px,stroke:#0F0,color:blue
|
||||
</div>
|
||||
<div class="mermaid2" style="">
|
||||
graph TD
|
||||
A[Christmas] ==> D
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
A[Christmas] ==> C
|
||||
</div>
|
||||
<div class="mermaid2" style="">
|
||||
graph TD
|
||||
%%{init: { "logLevel": 1, "flowchart": {"htmlLabels":true }} }%%
|
||||
A[Christmas] ==> D
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
A[Christmas] ==> C
|
||||
</div>
|
||||
<div class="mermaid2" style="">
|
||||
flowchart TD
|
||||
A[Christmas] ==> D
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
A[Christmas] ==> C
|
||||
</div>
|
||||
<div class="mermaid2" style="">
|
||||
flowchart TD
|
||||
%%{init: { "logLevel": 1, "flowchart": {"htmlLabels":true }} }%%
|
||||
A[Christmas] ==> D
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
A[Christmas] ==> C
|
||||
</div>
|
||||
<div class="mermaid2" style="">
|
||||
flowchart LR
|
||||
a["<strong>Haiya</strong>"]---->b
|
||||
</div>
|
||||
<div class="mermaid" style="width: 100%; height: 20%;">
|
||||
flowchart TB
|
||||
<div class="mermaid2" style="">
|
||||
flowchart LR
|
||||
%%{init: { "logLevel": 1, "flowchart": {"htmlLabels":true }} }%%
|
||||
a["<strong>Haiya</strong>"]---->b
|
||||
</div>
|
||||
<div class="mermaid2" style="">
|
||||
flowchart TD
|
||||
A[Christmas] ==> D
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
A[Christmas] ==> C
|
||||
</div>
|
||||
<div class="mermaid2" style="">
|
||||
flowchart TD
|
||||
%%{init: { "logLevel": 1, "flowchart": {"htmlLabels":true }} }%%
|
||||
A[Christmas] ==> D
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
A[Christmas] ==> C
|
||||
</div>
|
||||
<div class="mermaid2" style="">
|
||||
%%{init: { "logLevel": 1, "flowchart": {"htmlLabels":true }} }%%
|
||||
classDiagram-v2
|
||||
Class01 <|-- AveryLongClass : Cool
|
||||
<<interface>> Class01
|
||||
Class03 *-- Class04
|
||||
Class05 o-- Class06
|
||||
Class07 .. Class08
|
||||
Class09 --> C2 : Where am i?
|
||||
Class09 --* C3
|
||||
Class09 --|> Class07
|
||||
Class12 <|.. Class08
|
||||
Class11 ..>Class12
|
||||
Class07 : equals()
|
||||
Class07 : Object[] elementData
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class01 : -int privateChimp
|
||||
Class01 : +int publicGorilla
|
||||
Class01 : #int protectedMarmoset
|
||||
Class08 <--> C2: Cool label
|
||||
class Class10 {
|
||||
<<service>>
|
||||
int id
|
||||
test()
|
||||
}
|
||||
</div>
|
||||
<div class="mermaid2" style="">
|
||||
classDiagram-v2
|
||||
Class01 <|-- AveryLongClass : Cool
|
||||
<<interface>> Class01
|
||||
Class03 *-- Class04
|
||||
Class05 o-- Class06
|
||||
Class07 .. Class08
|
||||
Class09 --> C2 : Where am i?
|
||||
Class09 --* C3
|
||||
Class09 --|> Class07
|
||||
Class12 <|.. Class08
|
||||
Class11 ..>Class12
|
||||
Class07 : equals()
|
||||
Class07 : Object[] elementData
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class01 : -int privateChimp
|
||||
Class01 : +int publicGorilla
|
||||
Class01 : #int protectedMarmoset
|
||||
Class08 <--> C2: Cool label
|
||||
class Class10 {
|
||||
<<service>>
|
||||
int id
|
||||
test()
|
||||
}
|
||||
</div>
|
||||
<div class="mermaid" style="">
|
||||
flowchart BT
|
||||
subgraph S1
|
||||
sub1 -->sub2
|
||||
end
|
||||
@@ -66,141 +221,25 @@ flowchart TB
|
||||
S1 --> S2
|
||||
sub1 --> sub4
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 100%; height: 20%;">
|
||||
flowchart TB
|
||||
c1-->a2
|
||||
subgraph one
|
||||
a1-->a2
|
||||
end
|
||||
subgraph two
|
||||
b1-->b2
|
||||
end
|
||||
subgraph three
|
||||
c1-->c2
|
||||
end
|
||||
one --> two
|
||||
three --> two
|
||||
two --> c2
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 100%; height: 20%;">
|
||||
stateDiagram-v2
|
||||
state S1 {
|
||||
sub1 -->sub2
|
||||
}
|
||||
state S2 {
|
||||
sub4
|
||||
}
|
||||
S1 --> S2
|
||||
sub1 --> sub4
|
||||
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 100%; height: 20%;">
|
||||
requirementDiagram
|
||||
requirement test_req {
|
||||
id: 1
|
||||
text: the test text.
|
||||
risk: high
|
||||
verifymethod: test
|
||||
}
|
||||
|
||||
functionalRequirement test_req2 {
|
||||
id: 1.1
|
||||
text: the second test text.
|
||||
risk: low
|
||||
verifymethod: inspection
|
||||
}
|
||||
|
||||
performanceRequirement test_req3 {
|
||||
id: 1.2
|
||||
text: the third test text.
|
||||
risk: medium
|
||||
verifymethod: demonstration
|
||||
}
|
||||
|
||||
element test_entity {
|
||||
type: simulation
|
||||
}
|
||||
|
||||
element test_entity2 {
|
||||
type: word doc
|
||||
docRef: reqs/test_entity
|
||||
}
|
||||
|
||||
|
||||
test_entity - satisfies -> test_req2
|
||||
test_req - traces -> test_req2
|
||||
test_req - contains -> test_req3
|
||||
test_req <- copies - test_entity2
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 50%; height: 20%;">
|
||||
flowchart LR
|
||||
classDef dark fill:#000,stroke:#000,stroke-width:4px,color:#fff
|
||||
Lorem --> Ipsum --> Dolor
|
||||
class Lorem,Dolor dark
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 50%; height: 20%;">
|
||||
%%{init: {'theme': 'base' }}%%
|
||||
%%{init2: { 'logLevel': 0, 'theme': 'forest'} }%%
|
||||
flowchart TD
|
||||
L1 --- L2
|
||||
L2 --- C
|
||||
M1 ---> C
|
||||
R1 .-> R2
|
||||
R2 <.-> C
|
||||
C -->|Label 1| E1
|
||||
C <-- Label 2 ---> E2
|
||||
C ----> E3
|
||||
C <-...-> E4
|
||||
C ======> E5
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 50%; height: 21%;">
|
||||
flowchart LR
|
||||
A[red text] -->|default style| B(blue text)
|
||||
C([red text]) -->|default style| D[[blue text]]
|
||||
E[(red text)] -->|default style| F((blue text))
|
||||
G>red text] -->|default style| H{blue text}
|
||||
I{{red text}} -->|default style| J[/blue text/]
|
||||
K[
|
||||
ed text] -->|default style| L[/blue text]
|
||||
M[
|
||||
ed text/] -->|default style| N[blue text]
|
||||
linkStyle default color:Sienna;
|
||||
style A stroke:#ff0000,fill:#ffcccc,color:#ff0000
|
||||
style B stroke:#0000ff,fill:#ccccff,color:#0000ff
|
||||
style C stroke:#ff0000,fill:#ffcccc,color:#ff0000
|
||||
style D stroke:#0000ff,fill:#ccccff,color:#0000ff
|
||||
style E stroke:#ff0000,fill:#ffcccc,color:#ff0000
|
||||
style F stroke:#0000ff,fill:#ccccff,color:#0000ff
|
||||
style G stroke:#ff0000,fill:#ffcccc,color:#ff0000
|
||||
style H stroke:#0000ff,fill:#ccccff,color:#0000ff
|
||||
style I stroke:#ff0000,fill:#ffcccc,color:#ff0000
|
||||
style J stroke:#0000ff,fill:#ccccff,color:#0000ff
|
||||
style K stroke:#ff0000,fill:#ffcccc,color:#ff0000
|
||||
style L stroke:#0000ff,fill:#ccccff,color:#0000ff
|
||||
style M stroke:#ff0000,fill:#ffcccc,color:#ff0000
|
||||
style N stroke:#0000ff,fill:#ccccff,color:#0000ff
|
||||
</div>
|
||||
|
||||
|
||||
<script src="./mermaid.js"></script>
|
||||
<script>
|
||||
mermaid.parseError = function (err, hash) {
|
||||
// console.error('Mermaid error: ', err);
|
||||
};
|
||||
mermaid.initialize({
|
||||
theme: 'neutral',
|
||||
arrowMarkerAbsolute: true,
|
||||
// theme: 'neutral',
|
||||
// arrowMarkerAbsolute: true,
|
||||
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
|
||||
logLevel: 0,
|
||||
flowchart: { nodeSpacing: 10, curve: 'cardinal', htmlLabels: true },
|
||||
htmlLabels: true,
|
||||
flowchart: { curve: 'cardinal', htmlLabels: false },
|
||||
// htmlLabels: true,
|
||||
// gantt: { axisFormat: '%m/%d/%Y' },
|
||||
sequence: { actorFontFamily: 'courier', actorMargin: 50, showSequenceNumbers: false },
|
||||
// sequence: { actorFontFamily: 'courier', actorMargin: 50, showSequenceNumbers: false },
|
||||
// sequenceDiagram: { actorMargin: 300 } // deprecated
|
||||
// fontFamily: '"times", sans-serif',
|
||||
// fontFamily: 'courier',
|
||||
fontSize: 18,
|
||||
curve: 'cardinal',
|
||||
fontFamily: 'courier',
|
||||
// fontSize: 18,
|
||||
// curve: 'cardinal',
|
||||
securityLevel: 'loose',
|
||||
// themeVariables: {relationLabelColor: 'red'}
|
||||
});
|
||||
|
159
demos/index.html
159
demos/index.html
@@ -20,6 +20,165 @@
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<div class="mermaid">
|
||||
C4Context
|
||||
title System Context diagram for Internet Banking System
|
||||
Enterprise_Boundary(b0, "BankBoundary0") {
|
||||
Person(customerA, "Banking Customer A", "A customer of the bank, with personal bank accounts.")
|
||||
Person(customerB, "Banking Customer B")
|
||||
Person_Ext(customerC, "Banking Customer C", "desc")
|
||||
|
||||
Person(customerD, "Banking Customer D", "A customer of the bank, <br/> with personal bank accounts.")
|
||||
|
||||
System(SystemAA, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments.")
|
||||
|
||||
Enterprise_Boundary(b1, "BankBoundary") {
|
||||
|
||||
SystemDb_Ext(SystemE, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.")
|
||||
|
||||
System_Boundary(b2, "BankBoundary2") {
|
||||
System(SystemA, "Banking System A")
|
||||
System(SystemB, "Banking System B", "A system of the bank, with personal bank accounts. next line.")
|
||||
}
|
||||
|
||||
System_Ext(SystemC, "E-mail system", "The internal Microsoft Exchange e-mail system.")
|
||||
SystemDb(SystemD, "Banking System D Database", "A system of the bank, with personal bank accounts.")
|
||||
|
||||
Boundary(b3, "BankBoundary3", "boundary") {
|
||||
SystemQueue(SystemF, "Banking System F Queue", "A system of the bank.")
|
||||
SystemQueue_Ext(SystemG, "Banking System G Queue", "A system of the bank, with personal bank accounts.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BiRel(customerA, SystemAA, "Uses")
|
||||
BiRel(SystemAA, SystemE, "Uses")
|
||||
Rel(SystemAA, SystemC, "Sends e-mails", "SMTP")
|
||||
Rel(SystemC, customerA, "Sends e-mails to")
|
||||
</div>
|
||||
|
||||
<div class="mermaid">
|
||||
C4Container
|
||||
title Container diagram for Internet Banking System
|
||||
|
||||
System_Ext(email_system, "E-Mail System", "The internal Microsoft Exchange system")
|
||||
Person(customer, Customer, "A customer of the bank, with personal bank accounts")
|
||||
|
||||
Container_Boundary(c1, "Internet Banking") {
|
||||
Container(spa, "Single-Page App", "JavaScript, Angular", "Provides all the Internet banking functionality to cutomers via their web browser")
|
||||
Container_Ext(mobile_app, "Mobile App", "C#, Xamarin", "Provides a limited subset of the Internet banking functionality to customers via their mobile device")
|
||||
Container(web_app, "Web Application", "Java, Spring MVC", "Delivers the static content and the Internet banking SPA")
|
||||
ContainerDb(database, "Database", "SQL Database", "Stores user registration information, hashed auth credentials, access logs, etc.")
|
||||
ContainerDb_Ext(backend_api, "API Application", "Java, Docker Container", "Provides Internet banking functionality via API")
|
||||
|
||||
}
|
||||
|
||||
System_Ext(banking_system, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.")
|
||||
|
||||
Rel(customer, web_app, "Uses", "HTTPS")
|
||||
Rel(customer, spa, "Uses", "HTTPS")
|
||||
Rel(customer, mobile_app, "Uses")
|
||||
|
||||
Rel(web_app, spa, "Delivers")
|
||||
Rel(spa, backend_api, "Uses", "async, JSON/HTTPS")
|
||||
Rel(mobile_app, backend_api, "Uses", "async, JSON/HTTPS")
|
||||
Rel_Back(database, backend_api, "Reads from and writes to", "sync, JDBC")
|
||||
|
||||
Rel(email_system, customer, "Sends e-mails to")
|
||||
Rel(backend_api, email_system, "Sends e-mails using", "sync, SMTP")
|
||||
Rel(backend_api, banking_system, "Uses", "sync/async, XML/HTTPS")
|
||||
</div>
|
||||
|
||||
<div class="mermaid">
|
||||
C4Component
|
||||
title Component diagram for Internet Banking System - API Application
|
||||
|
||||
Container(spa, "Single Page Application", "javascript and angular", "Provides all the internet banking functionality to customers via their web browser.")
|
||||
Container(ma, "Mobile App", "Xamarin", "Provides a limited subset ot the internet banking functionality to customers via their mobile mobile device.")
|
||||
ContainerDb(db, "Database", "Relational Database Schema", "Stores user registration information, hashed authentication credentials, access logs, etc.")
|
||||
System_Ext(mbs, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.")
|
||||
|
||||
Container_Boundary(api, "API Application") {
|
||||
Component(sign, "Sign In Controller", "MVC Rest Controlle", "Allows users to sign in to the internet banking system")
|
||||
Component(accounts, "Accounts Summary Controller", "MVC Rest Controller", "Provides customers with a summary of their bank accounts")
|
||||
Component(security, "Security Component", "Spring Bean", "Provides functionality related to singing in, changing passwords, etc.")
|
||||
Component(mbsfacade, "Mainframe Banking System Facade", "Spring Bean", "A facade onto the mainframe banking system.")
|
||||
|
||||
Rel(sign, security, "Uses")
|
||||
Rel(accounts, mbsfacade, "Uses")
|
||||
Rel(security, db, "Read & write to", "JDBC")
|
||||
Rel(mbsfacade, mbs, "Uses", "XML/HTTPS")
|
||||
}
|
||||
|
||||
Rel_Back(spa, sign, "Uses", "JSON/HTTPS")
|
||||
Rel(spa, accounts, "Uses", "JSON/HTTPS")
|
||||
|
||||
Rel(ma, sign, "Uses", "JSON/HTTPS")
|
||||
Rel(ma, accounts, "Uses", "JSON/HTTPS")
|
||||
</div>
|
||||
|
||||
<div class="mermaid">
|
||||
C4Dynamic
|
||||
title Dynamic diagram for Internet Banking System - API Application
|
||||
|
||||
ContainerDb(c4, "Database", "Relational Database Schema", "Stores user registration information, hashed authentication credentials, access logs, etc.")
|
||||
Container(c1, "Single-Page Application", "JavaScript and Angular", "Provides all of the Internet banking functionality to customers via their web browser.")
|
||||
Container_Boundary(b, "API Application") {
|
||||
Component(c3, "Security Component", "Spring Bean", "Provides functionality Related to signing in, changing passwords, etc.")
|
||||
Component(c2, "Sign In Controller", "Spring MVC Rest Controller", "Allows users to sign in to the Internet Banking System.")
|
||||
}
|
||||
Rel(c1, c2, "Submits credentials to", "JSON/HTTPS")
|
||||
Rel(c2, c3, "Calls isAuthenticated() on")
|
||||
Rel(c3, c4, "select * from users where username = ?", "JDBC")
|
||||
</div>
|
||||
|
||||
<div class="mermaid">
|
||||
C4Deployment
|
||||
title Deployment Diagram for Internet Banking System - Live
|
||||
|
||||
Deployment_Node(mob, "Customer's mobile device", "Apple IOS or Android"){
|
||||
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(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.")
|
||||
}
|
||||
}
|
||||
|
||||
Deployment_Node(plc, "Big Bank plc", "Big Bank plc data center"){
|
||||
Deployment_Node(dn, "bigbank-api*** x8", "Ubuntu 16.04 LTS"){
|
||||
Deployment_Node(apache, "Apache Tomcat", "Apache Tomcat 8.x"){
|
||||
Container(api, "API Application", "Java and Spring MVC", "Provides Internet Banking functionality via a JSON/HTTPS API.")
|
||||
}
|
||||
}
|
||||
Deployment_Node(bb2, "bigbank-web*** x4", "Ubuntu 16.04 LTS"){
|
||||
Deployment_Node(apache2, "Apache Tomcat", "Apache Tomcat 8.x"){
|
||||
Container(web, "Web Application", "Java and Spring MVC", "Delivers the static content and the Internet Banking single page application.")
|
||||
}
|
||||
}
|
||||
Deployment_Node(bigbankdb01, "bigbank-db01", "Ubuntu 16.04 LTS"){
|
||||
Deployment_Node(oracle, "Oracle - Primary", "Oracle 12c"){
|
||||
ContainerDb(db, "Database", "Relational Database Schema", "Stores user registration information, hashed authentication credentials, access logs, etc.")
|
||||
}
|
||||
}
|
||||
Deployment_Node(bigbankdb02, "bigbank-db02", "Ubuntu 16.04 LTS") {
|
||||
Deployment_Node(oracle2, "Oracle - Secondary", "Oracle 12c") {
|
||||
ContainerDb(db2, "Database", "Relational Database Schema", "Stores user registration information, hashed authentication credentials, access logs, etc.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rel(mobile, api, "Makes API calls to", "json/HTTPS")
|
||||
Rel(spa, api, "Makes API calls to", "json/HTTPS")
|
||||
Rel_U(web, spa, "Delivers to the customer's web browser")
|
||||
Rel(api, db, "Reads from and writes to", "JDBC")
|
||||
Rel(api, db2, "Reads from and writes to", "JDBC")
|
||||
Rel_R(db, db2, "Replicates data to")
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="mermaid">
|
||||
pie
|
||||
title Key elements in Product X
|
||||
|
31940
dist/mermaid.core.js
vendored
31940
dist/mermaid.core.js
vendored
File diff suppressed because one or more lines are too long
1
dist/mermaid.core.js.map
vendored
1
dist/mermaid.core.js.map
vendored
File diff suppressed because one or more lines are too long
129094
dist/mermaid.js
vendored
129094
dist/mermaid.js
vendored
File diff suppressed because one or more lines are too long
1
dist/mermaid.js.map
vendored
1
dist/mermaid.js.map
vendored
File diff suppressed because one or more lines are too long
@@ -203,7 +203,7 @@ Ensures options parameter does not attempt to override siteConfig secure keys.
|
||||
|
||||
| Parameter | Description |Type | Required | Values|
|
||||
| --- | --- | --- | --- | --- |
|
||||
| conf| base set of values, which currentConfig coul be reset to.| Dictionary | Required | Any Values, with respect to the secure Array|
|
||||
| conf| base set of values, which currentConfig could be reset to.| Dictionary | Required | Any Values, with respect to the secure Array|
|
||||
|
||||
```note
|
||||
default: current siteConfig (optional, default `getSiteConfig()`)
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
**Mermaid lets you create diagrams and visualizations using text and code.**
|
||||
|
||||
It is a Javascript based diagramming and charting tool that renders Markdown-inspired text definitions to create and modify diagrams dynamically.
|
||||
It is a JavaScript based diagramming and charting tool that renders Markdown-inspired text definitions to create and modify diagrams dynamically.
|
||||
|
||||
>If you are familiar with Markdown you should have no problem learning [Mermaid's Syntax](./n00b-syntaxReference.md).
|
||||
|
||||
@@ -18,7 +18,7 @@ It is a Javascript based diagramming and charting tool that renders Markdown-ins
|
||||
|
||||
|
||||
<!-- <Main description> -->
|
||||
Mermaid is a Javascript based diagramming and charting tool that uses Markdown-inspired text definitions and a renderer to create and modify complex diagrams. The main purpose of Mermaid is to help documentation catch up with development.
|
||||
Mermaid is a JavaScript based diagramming and charting tool that uses Markdown-inspired text definitions and a renderer to create and modify complex diagrams. The main purpose of Mermaid is to help documentation catch up with development.
|
||||
|
||||
> Doc-Rot is a Catch-22 that Mermaid helps to solve.
|
||||
|
||||
@@ -195,7 +195,7 @@ To Deploy Mermaid:
|
||||
<script>mermaid.initialize({startOnLoad:true});
|
||||
</script>
|
||||
```
|
||||
**Doing so will command the mermaid parser to look for the `<div>` tags with `class="mermaid"`. From these tags mermaid will try to read the diagram/chart definitions and render them into svg charts.**
|
||||
**Doing so will command the mermaid parser to look for the `<div>` tags with `class="mermaid"`. From these tags mermaid will try to read the diagram/chart definitions and render them into SVG charts.**
|
||||
|
||||
**Examples can be found at** [Other examples](/examples)
|
||||
|
||||
@@ -277,9 +277,9 @@ Detailed information about how to contribute can be found in the [contribution g
|
||||
|
||||
## Security and safe diagrams
|
||||
|
||||
For public sites, it can be precarious to retrieve text from users on the internet, storing that content for presentation in a browser at a later stage. The reason is that the user content can contain embedded malicious scripts that will run when the data is presented. For Mermaid this is a risk, specially as mermaid diagrams contain many characters that are used in html which makes the standard sanitation unusable as it also breaks the diagrams. We still make an effort to sanitise the incoming code and keep refining the process but it is hard to guarantee that there are no loop holes.
|
||||
For public sites, it can be precarious to retrieve text from users on the internet, storing that content for presentation in a browser at a later stage. The reason is that the user content can contain embedded malicious scripts that will run when the data is presented. For Mermaid this is a risk, specially as mermaid diagrams contain many characters that are used in html which makes the standard sanitation unusable as it also breaks the diagrams. We still make an effort to sanitise the incoming code and keep refining the process but it is hard to guarantee that there are no loop holes.
|
||||
|
||||
As an extra level of security for sites with external users we are happy to introduce a new security level in which the diagram is rendered in a sandboxed iframe preventing javascript in the code from being executed. This is a great step forward for better security.
|
||||
As an extra level of security for sites with external users we are happy to introduce a new security level in which the diagram is rendered in a sandboxed iframe preventing JavaScript in the code from being executed. This is a great step forward for better security.
|
||||
|
||||
*Unfortunately you can not have a cake and eat it at the same time which in this case means that some of the interactive functionality gets blocked along with the possible malicious code.*
|
||||
|
||||
|
@@ -818,13 +818,13 @@ Default value: 'TB'
|
||||
|
||||
### entityPadding
|
||||
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| ------------- | ----------------------------------------------------------- | ------- | -------- | ------------------ |
|
||||
| entityPadding | Minimum internal padding betweentext in box and box borders | Integer | 4 | Any Positive Value |
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| ------------- | ------------------------------------------------------------ | ------- | -------- | ------------------ |
|
||||
| entityPadding | Minimum internal padding between text in box and box borders | Integer | 4 | Any Positive Value |
|
||||
|
||||
**Notes:**
|
||||
|
||||
The minimum internal padding betweentext in an entity box and the enclosing box borders,
|
||||
The minimum internal padding between text in an entity box and the enclosing box borders,
|
||||
expressed in pixels.
|
||||
|
||||
Default value: 15
|
||||
@@ -902,6 +902,81 @@ available space. If set to false, the diagram reserves its absolute width.
|
||||
|
||||
Default value: true
|
||||
|
||||
## c4
|
||||
|
||||
The object containing configurations specific for c4 diagrams
|
||||
|
||||
### diagramMarginX
|
||||
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| -------------- | ---------------------------------------------- | ------- | -------- | ------------------ |
|
||||
| diagramMarginX | Margin to the right and left of the c4 diagram | Integer | Required | Any Positive Value |
|
||||
|
||||
**Notes:** Default value: 50
|
||||
|
||||
### diagramMarginY
|
||||
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| -------------- | ------------------------------------------- | ------- | -------- | ------------------ |
|
||||
| diagramMarginY | Margin to the over and under the c4 diagram | Integer | Required | Any Positive Value |
|
||||
|
||||
**Notes:** Default value: 10
|
||||
|
||||
### c4ShapeMargin
|
||||
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| ----------- | --------------------- | ------- | -------- | ------------------ |
|
||||
| shapeMargin | Margin between shapes | Integer | Required | Any Positive Value |
|
||||
|
||||
**Notes:** Default value: 50
|
||||
|
||||
### width
|
||||
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| --------- | --------------------- | ------- | -------- | ------------------ |
|
||||
| width | Width of person boxes | Integer | Required | Any Positive Value |
|
||||
|
||||
**Notes:** Default value: 216
|
||||
|
||||
### height
|
||||
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| --------- | ---------------------- | ------- | -------- | ------------------ |
|
||||
| height | Height of person boxes | Integer | Required | Any Positive Value |
|
||||
|
||||
**Notes:** Default value: 60
|
||||
|
||||
### boxMargin
|
||||
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| --------- | ------------------------ | ------- | -------- | ------------------ |
|
||||
| boxMargin | Margin around loop boxes | Integer | Required | Any Positive Value |
|
||||
|
||||
**Notes:** Default value: 10
|
||||
|
||||
### useMaxWidth
|
||||
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| ----------- | ----------- | ------- | -------- | ----------- |
|
||||
| useMaxWidth | See Notes | boolean | Required | true, false |
|
||||
|
||||
**Notes:** When this flag is set to true, the height and width is set to 100% and is then
|
||||
scaling with the available space. If set to false, the absolute space required is used.
|
||||
|
||||
Default value: true
|
||||
|
||||
### wrap
|
||||
|
||||
This sets the auto-wrap state for the diagram
|
||||
|
||||
**Notes:** Default value: true.
|
||||
|
||||
### wrapPadding
|
||||
|
||||
This sets the auto-wrap padding for the diagram (sides only)
|
||||
|
||||
**Notes:** Default value: 0.
|
||||
|
||||
## setSiteConfig
|
||||
|
||||
## setSiteConfig
|
||||
@@ -1004,9 +1079,9 @@ Pushes in a directive to the configuration
|
||||
|
||||
## conf
|
||||
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| --------- | ------------------------------------------------------------- | ---------- | -------- | -------------------------------------------- |
|
||||
| conf | base set of values, which currentConfig coul be **reset** to. | Dictionary | Required | Any Values, with respect to the secure Array |
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| --------- | -------------------------------------------------------------- | ---------- | -------- | -------------------------------------------- |
|
||||
| conf | base set of values, which currentConfig could be **reset** to. | Dictionary | Required | Any Values, with respect to the secure Array |
|
||||
|
||||
**Notes**: (default: current siteConfig ) (optional, default `getSiteConfig()`)
|
||||
|
||||
|
@@ -15,6 +15,7 @@
|
||||
- [Pie Chart](pie.md)
|
||||
- [Requirement Diagram](requirementDiagram.md)
|
||||
- [Gitgraph (Git) Diagram 🔥](gitgraph.md)
|
||||
- [C4C Diagram (Context) Diagram 🦺⚠️](c4c.md)
|
||||
- [Other Examples](examples.md)
|
||||
|
||||
- ⚙️ Deployment and Configuration
|
||||
|
46
docs/c4c.md
Normal file
46
docs/c4c.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# C4C Diagrams
|
||||
|
||||
**Edit this Page** [](https://github.com/mermaid-js/mermaid/blob/develop/docs/gitgraph.md)
|
||||
> C4 Diagram: This is an experimental diagram for now. The syntax and properties can change in future releases. Proper documentation will be provided when the syntax is stable.
|
||||
|
||||
|
||||
|
||||
Mermaid's c4 diagram sytax is compatible with plantUML. See example below:
|
||||
|
||||
```mermaid-example
|
||||
C4Context
|
||||
title System Context diagram for Internet Banking System
|
||||
|
||||
Person(customerA, "Banking Customer A", "A customer of the bank, with personal bank accounts.")
|
||||
Person(customerB, "Banking Customer B")
|
||||
Person_Ext(customerC, "Banking Customer C")
|
||||
System(SystemAA, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments.")
|
||||
|
||||
Person(customerD, "Banking Customer D", "A customer of the bank, <br/> with personal bank accounts.")
|
||||
|
||||
Enterprise_Boundary(b1, "BankBoundary") {
|
||||
|
||||
SystemDb_Ext(SystemE, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.")
|
||||
|
||||
System_Boundary(b2, "BankBoundary2") {
|
||||
System(SystemA, "Banking System A")
|
||||
System(SystemB, "Banking System B", "A system of the bank, with personal bank accounts.")
|
||||
}
|
||||
|
||||
System_Ext(SystemC, "E-mail system", "The internal Microsoft Exchange e-mail system.")
|
||||
SystemDb(SystemD, "Banking System D Database", "A system of the bank, with personal bank accounts.")
|
||||
|
||||
Boundary(b3, "BankBoundary3", "boundary") {
|
||||
SystemQueue(SystemF, "Banking System F Queue", "A system of the bank, with personal bank accounts.")
|
||||
SystemQueue_Ext(SystemG, "Banking System G Queue", "A system of the bank, with personal bank accounts.")
|
||||
}
|
||||
}
|
||||
|
||||
BiRel(customerA, SystemAA, "Uses")
|
||||
BiRel(SystemAA, SystemE, "Uses")
|
||||
Rel(SystemAA, SystemC, "Sends e-mails", "SMTP")
|
||||
Rel(SystemC, customerA, "Sends e-mails to")
|
||||
|
||||
|
||||
```
|
||||
|
@@ -8,28 +8,27 @@ The class diagram is the main building block of object-oriented modeling. It is
|
||||
Mermaid can render class diagrams.
|
||||
|
||||
```mermaid-example
|
||||
classDiagram
|
||||
Animal <|-- Duck
|
||||
Animal <|-- Fish
|
||||
Animal <|-- Zebra
|
||||
Animal : +int age
|
||||
Animal : +String gender
|
||||
Animal: +isMammal()
|
||||
Animal: +mate()
|
||||
class Duck{
|
||||
+String beakColor
|
||||
+swim()
|
||||
+quack()
|
||||
}
|
||||
class Fish{
|
||||
-int sizeInFeet
|
||||
-canEat()
|
||||
}
|
||||
class Zebra{
|
||||
+bool is_wild
|
||||
+run()
|
||||
}
|
||||
|
||||
classDiagram
|
||||
Animal <|-- Duck
|
||||
Animal <|-- Fish
|
||||
Animal <|-- Zebra
|
||||
Animal : +int age
|
||||
Animal : +String gender
|
||||
Animal: +isMammal()
|
||||
Animal: +mate()
|
||||
class Duck{
|
||||
+String beakColor
|
||||
+swim()
|
||||
+quack()
|
||||
}
|
||||
class Fish{
|
||||
-int sizeInFeet
|
||||
-canEat()
|
||||
}
|
||||
class Zebra{
|
||||
+bool is_wild
|
||||
+run()
|
||||
}
|
||||
```
|
||||
|
||||
## Syntax
|
||||
@@ -79,12 +78,12 @@ There are two ways to define the members of a class, and regardless of whichever
|
||||
- Associate a member of a class using **:** (colon) followed by member name, useful to define one member at a time. For example:
|
||||
|
||||
```mermaid-example
|
||||
classDiagram
|
||||
class BankAccount
|
||||
BankAccount : +String owner
|
||||
BankAccount : +BigDecimal balance
|
||||
BankAccount : +deposit(amount)
|
||||
BankAccount : +withdrawal(amount)
|
||||
classDiagram
|
||||
class BankAccount
|
||||
BankAccount : +String owner
|
||||
BankAccount : +BigDecimal balance
|
||||
BankAccount : +deposit(amount)
|
||||
BankAccount : +withdrawal(amount)
|
||||
```
|
||||
|
||||
- Associate members of a class using **{}** brackets, where members are grouped within curly brackets. Suitable for defining multiple members at once. For example:
|
||||
@@ -385,9 +384,9 @@ _URL Link:_
|
||||
```mmd
|
||||
classDiagram
|
||||
class Shape
|
||||
link Shape "http://www.github.com" "This is a tooltip for a link"
|
||||
link Shape "https://www.github.com" "This is a tooltip for a link"
|
||||
class Shape2
|
||||
click Shape2 href "http://www.github.com" "This is a tooltip for a link"
|
||||
click Shape2 href "https://www.github.com" "This is a tooltip for a link"
|
||||
```
|
||||
|
||||
_Callback:_
|
||||
@@ -402,10 +401,10 @@ click Shape2 call callbackFunction() "This is a tooltip for a callback"
|
||||
|
||||
```html
|
||||
<script>
|
||||
var callbackFunction = function(){
|
||||
alert('A callback was triggered');
|
||||
}
|
||||
<script>
|
||||
var callbackFunction = function () {
|
||||
alert('A callback was triggered');
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
```mermaid
|
||||
@@ -413,11 +412,11 @@ classDiagram
|
||||
class Class01
|
||||
class Class02
|
||||
callback Class01 "callbackFunction" "Callback tooltip"
|
||||
link Class02 "http://www.github.com" "This is a link"
|
||||
link Class02 "https://www.github.com" "This is a link"
|
||||
class Class03
|
||||
class Class04
|
||||
click Class03 call callbackFunction() "Callback tooltip"
|
||||
click Class04 href "http://www.github.com" "This is a link"
|
||||
click Class04 href "https://www.github.com" "This is a link"
|
||||
```
|
||||
|
||||
> **Success** The tooltip functionality and the ability to link to urls are available from version 0.5.2.
|
||||
@@ -450,18 +449,17 @@ Beginners tip, a full example using interactive links in an html context:
|
||||
}
|
||||
|
||||
callback Duck callback "Tooltip"
|
||||
link Zebra "http://www.github.com" "This is a link"
|
||||
link Zebra "https://www.github.com" "This is a link"
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var callback = function(){
|
||||
var callback = function () {
|
||||
alert('A callback was triggered');
|
||||
}
|
||||
var config = {
|
||||
startOnLoad:true,
|
||||
securityLevel:'loose',
|
||||
};
|
||||
|
||||
var config = {
|
||||
startOnLoad: true,
|
||||
securityLevel: 'loose'
|
||||
};
|
||||
mermaid.initialize(config);
|
||||
</script>
|
||||
</body>
|
||||
|
@@ -1,9 +1,9 @@
|
||||
# Configuration
|
||||
|
||||
When mermaid starts configuration is extracted to a configuration to be used for a diagram. There a 3 sources for configuration:
|
||||
When mermaid starts configuration is extracted to a configuration to be used for a diagram. There are 3 sources for configuration:
|
||||
|
||||
* The default configuration
|
||||
* Overrides on the site level, set is set by the initialize call and will be applied for all diagrams in the site/app. The term for this is the **siteConfig**.
|
||||
* Overrides on the site level is set by the initialize call and will be applied to all diagrams in the site/app. The term for this is the **siteConfig**.
|
||||
* Directives - diagram authors can update select configuration parameters directly in the diagram code via directives and these are applied to the render config.
|
||||
|
||||
**The render config** is configuration that is used when rendering by applying these configurations.
|
||||
|
@@ -1,135 +1,135 @@
|
||||
# Development and Contribution 🙌
|
||||
|
||||
So you want to help? That's great!
|
||||
|
||||

|
||||
|
||||
Here are a few things to know to get you started on the right path.
|
||||
|
||||
**The Docs Structure is dictated by [sidebar.md](https://github.com/mermaid-js/mermaid/edit/develop/docs/_sidebar.md)**
|
||||
|
||||
**Note: Commits and Pull Requests should be directed to the develop branch.**
|
||||
|
||||
## Branching
|
||||
|
||||
Mermaid uses a [Git Flow](https://guides.github.com/introduction/flow/)–inspired approach to branching. So development is done in the `develop` branch.
|
||||
|
||||
Once development is done we branch a `release` branch from `develop` for testing.
|
||||
|
||||
Once the release happens we merge the `release` branch with `master` and kill the `release` branch.
|
||||
|
||||
This means that **you should branch off your pull request from develop** and direct all Pull Requests to it.
|
||||
|
||||
## Contributing Code
|
||||
|
||||
We make all changes via Pull Requests. As we have many Pull Requests from developers new to mermaid, we have put in place a process, wherein *knsv, Knut Sveidqvist* is the primary reviewer of changes and merging pull requests. The process is as follows:
|
||||
|
||||
* Large changes reviewed by knsv or other developer asked to review by knsv
|
||||
* Smaller, low-risk changes like dependencies, documentation, etc. can be merged by active collaborators
|
||||
* Documentation (we encourage updates to the docs folder; you can submit them via direct commits)
|
||||
|
||||
When you commit code, create a branch with the following naming convention:
|
||||
|
||||
Start with the type, such as **feature** or **bug**, followed by the issue number for reference, and a text that describes the issue.
|
||||
|
||||
**One example:**
|
||||
|
||||
`feature/945_state_diagrams`
|
||||
|
||||
**Another example:**
|
||||
|
||||
`bug/123_nasty_bug_branch`
|
||||
|
||||
## Contributing to Documentation
|
||||
|
||||
If it is not in the documentation, it's like it never happened. Wouldn't that be sad? With all the effort that was put into the feature?
|
||||
|
||||
The docs are located in the `docs` folder and are written in Markdown. Just pick the right section and start typing. If you want to propose changes to the structure of the documentation, such as adding a new section or a new file you do that via the **[sidebar](https://github.com/mermaid-js/mermaid/edit/develop/docs/_sidebar.md)**.
|
||||
|
||||
> **All the documents displayed in the github.io page are listed in [sidebar.md](https://github.com/mermaid-js/mermaid/edit/develop/docs/_sidebar.md)**.
|
||||
|
||||
The contents of [https://mermaid-js.github.io/mermaid/](https://mermaid-js.github.io/mermaid/) are based on the docs from the `master` branch. Updates committed to the `master` branch are reflected in the [Mermaid Docs](https://mermaid-js.github.io/mermaid/) once released.
|
||||
|
||||
## How to Contribute to Documentation
|
||||
|
||||
We are a little less strict here, it is OK to commit directly in the `develop` branch if you are a collaborator.
|
||||
|
||||
The documentation is located in the `docs` directory and organized according to relevant subfolder.
|
||||
|
||||
We encourage contributions to the documentation at [mermaid-js/mermaid/docs](https://github.com/mermaid-js/mermaid/tree/develop/docs). We publish documentation using GitHub Pages with [Docsify](https://www.youtube.com/watch?v=TV88lp7egMw&t=3s)
|
||||
|
||||
### Add Unit Tests for Parsing
|
||||
|
||||
This is important so that, if someone that does not know about this great feature suggests a change to the grammar, they get notified early on when that change breaks the parser. Another important aspect is that, without proper parsing, tests refactoring is pretty much impossible.
|
||||
|
||||
### Add E2E Tests
|
||||
|
||||
This tests the rendering and visual apearance of the diagrams. This ensures that the rendering of that feature in the e2e will be reviewed in the release process going forward. Less chance that it breaks!
|
||||
|
||||
To start working with the e2e tests:
|
||||
|
||||
1. Run `yarn dev` to start the dev server
|
||||
2. Start **Cypress** by running `cypress open` in the **mermaid** folder.
|
||||
(Make sure you have path to Cypress in order, the binary is located in `node_modules/.bin`).
|
||||
|
||||
The rendering tests are very straightforward to create. There is a function `imgSnapshotTest`, which takes a diagram in text form and the mermaid options, and it renders that diagram in Cypress.
|
||||
|
||||
When running in CI it will take a snapshot of the rendered diagram and compare it with the snapshot from last build and flag it for review if it differs.
|
||||
|
||||
This is what a rendering test looks like:
|
||||
|
||||
```js
|
||||
it('should render forks and joins', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
stateDiagram
|
||||
state fork_state <<fork>>
|
||||
[*] --> fork_state
|
||||
fork_state --> State2
|
||||
fork_state --> State3
|
||||
|
||||
state join_state <<join>>
|
||||
State2 --> join_state
|
||||
State3 --> join_state
|
||||
join_state --> State4
|
||||
State4 --> [*]
|
||||
`,
|
||||
{ logLevel: 0 }
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Any Questions or Suggestions?
|
||||
|
||||
After logging in at [GitHub.com](https://www.github.com), open or append to an issue [using the GitHub issue tracker of the mermaid-js repository](https://github.com/mermaid-js/mermaid/issues?q=is%3Aissue+is%3Aopen+label%3A%22Area%3A+Documentation%22).
|
||||
|
||||
### How to Contribute a Suggestion
|
||||
|
||||
Markdown is used to format the text, for more information about Markdown [see the GitHub Markdown help page](https://help.github.com/en/github/writing-on-github/basic-writing-and-formatting-syntax).
|
||||
|
||||
To edit Docs on your computer:
|
||||
|
||||
1. Find the Markdown file (.md) to edit in the [mermaid-js/mermaid/docs](https://github.com/mermaid-js/mermaid/tree/develop/docs) directory in the `develop` branch.
|
||||
2. Create a fork of the develop branch.
|
||||
3. Make changes or add new documentation.
|
||||
4. Commit changes to your fork and push it to GitHub.
|
||||
5. Create a Pull Request of your fork.
|
||||
|
||||
To edit Docs on GitHub:
|
||||
|
||||
1. Login to [GitHub.com](https://www.github.com).
|
||||
2. Navigate to [mermaid-js/mermaid/docs](https://github.com/mermaid-js/mermaid/tree/develop/docs).
|
||||
3. To edit a file, click the pencil icon at the top-right of the file contents panel.
|
||||
4. Describe what you changed in the **Propose file change** section, located at the bottom of the page.
|
||||
5. Submit your changes by clicking the button **Propose file change** at the bottom (by automatic creation of a fork and a new branch).
|
||||
6. Create a Pull Request of your newly forked branch by clicking the green **Create Pull Request** button.
|
||||
|
||||
## Last Words
|
||||
|
||||
Don't get daunted if it is hard in the beginning. We have a great community with only encouraging words. So, if you get stuck, ask for help and hints in the Slack forum. If you want to show off something good, show it off there.
|
||||
|
||||
[Join our Slack community if you want closer contact!](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)
|
||||
|
||||

|
||||
# Development and Contribution 🙌
|
||||
|
||||
So you want to help? That's great!
|
||||
|
||||

|
||||
|
||||
Here are a few things to know to get you started on the right path.
|
||||
|
||||
**The Docs Structure is dictated by [sidebar.md](https://github.com/mermaid-js/mermaid/edit/develop/docs/_sidebar.md)**
|
||||
|
||||
**Note: Commits and Pull Requests should be directed to the develop branch.**
|
||||
|
||||
## Branching
|
||||
|
||||
Mermaid uses a [Git Flow](https://guides.github.com/introduction/flow/)–inspired approach to branching. So development is done in the `develop` branch.
|
||||
|
||||
Once development is done we branch a `release` branch from `develop` for testing.
|
||||
|
||||
Once the release happens we merge the `release` branch with `master` and kill the `release` branch.
|
||||
|
||||
This means that **you should branch off your pull request from develop** and direct all Pull Requests to it.
|
||||
|
||||
## Contributing Code
|
||||
|
||||
We make all changes via Pull Requests. As we have many Pull Requests from developers new to mermaid, we have put in place a process, wherein *knsv, Knut Sveidqvist* is the primary reviewer of changes and merging pull requests. The process is as follows:
|
||||
|
||||
* Large changes reviewed by knsv or other developer asked to review by knsv
|
||||
* Smaller, low-risk changes like dependencies, documentation, etc. can be merged by active collaborators
|
||||
* Documentation (we encourage updates to the docs folder; you can submit them via direct commits)
|
||||
|
||||
When you commit code, create a branch with the following naming convention:
|
||||
|
||||
Start with the type, such as **feature** or **bug**, followed by the issue number for reference, and a text that describes the issue.
|
||||
|
||||
**One example:**
|
||||
|
||||
`feature/945_state_diagrams`
|
||||
|
||||
**Another example:**
|
||||
|
||||
`bug/123_nasty_bug_branch`
|
||||
|
||||
## Contributing to Documentation
|
||||
|
||||
If it is not in the documentation, it's like it never happened. Wouldn't that be sad? With all the effort that was put into the feature?
|
||||
|
||||
The docs are located in the `docs` folder and are written in Markdown. Just pick the right section and start typing. If you want to propose changes to the structure of the documentation, such as adding a new section or a new file you do that via the **[sidebar](https://github.com/mermaid-js/mermaid/edit/develop/docs/_sidebar.md)**.
|
||||
|
||||
> **All the documents displayed in the github.io page are listed in [sidebar.md](https://github.com/mermaid-js/mermaid/edit/develop/docs/_sidebar.md)**.
|
||||
|
||||
The contents of [https://mermaid-js.github.io/mermaid/](https://mermaid-js.github.io/mermaid/) are based on the docs from the `master` branch. Updates committed to the `master` branch are reflected in the [Mermaid Docs](https://mermaid-js.github.io/mermaid/) once released.
|
||||
|
||||
## How to Contribute to Documentation
|
||||
|
||||
We are a little less strict here, it is OK to commit directly in the `develop` branch if you are a collaborator.
|
||||
|
||||
The documentation is located in the `docs` directory and organized according to relevant subfolder.
|
||||
|
||||
We encourage contributions to the documentation at [mermaid-js/mermaid/docs](https://github.com/mermaid-js/mermaid/tree/develop/docs). We publish documentation using GitHub Pages with [Docsify](https://www.youtube.com/watch?v=TV88lp7egMw&t=3s)
|
||||
|
||||
### Add Unit Tests for Parsing
|
||||
|
||||
This is important so that, if someone that does not know about this great feature suggests a change to the grammar, they get notified early on when that change breaks the parser. Another important aspect is that, without proper parsing, tests refactoring is pretty much impossible.
|
||||
|
||||
### Add E2E Tests
|
||||
|
||||
This tests the rendering and visual appearance of the diagrams. This ensures that the rendering of that feature in the e2e will be reviewed in the release process going forward. Less chance that it breaks!
|
||||
|
||||
To start working with the e2e tests:
|
||||
|
||||
1. Run `yarn dev` to start the dev server
|
||||
2. Start **Cypress** by running `cypress open` in the **mermaid** folder.
|
||||
(Make sure you have path to Cypress in order, the binary is located in `node_modules/.bin`).
|
||||
|
||||
The rendering tests are very straightforward to create. There is a function `imgSnapshotTest`, which takes a diagram in text form and the mermaid options, and it renders that diagram in Cypress.
|
||||
|
||||
When running in CI it will take a snapshot of the rendered diagram and compare it with the snapshot from last build and flag it for review if it differs.
|
||||
|
||||
This is what a rendering test looks like:
|
||||
|
||||
```js
|
||||
it('should render forks and joins', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
stateDiagram
|
||||
state fork_state <<fork>>
|
||||
[*] --> fork_state
|
||||
fork_state --> State2
|
||||
fork_state --> State3
|
||||
|
||||
state join_state <<join>>
|
||||
State2 --> join_state
|
||||
State3 --> join_state
|
||||
join_state --> State4
|
||||
State4 --> [*]
|
||||
`,
|
||||
{ logLevel: 0 }
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Any Questions or Suggestions?
|
||||
|
||||
After logging in at [GitHub.com](https://www.github.com), open or append to an issue [using the GitHub issue tracker of the mermaid-js repository](https://github.com/mermaid-js/mermaid/issues?q=is%3Aissue+is%3Aopen+label%3A%22Area%3A+Documentation%22).
|
||||
|
||||
### How to Contribute a Suggestion
|
||||
|
||||
Markdown is used to format the text, for more information about Markdown [see the GitHub Markdown help page](https://help.github.com/en/github/writing-on-github/basic-writing-and-formatting-syntax).
|
||||
|
||||
To edit Docs on your computer:
|
||||
|
||||
1. Find the Markdown file (.md) to edit in the [mermaid-js/mermaid/docs](https://github.com/mermaid-js/mermaid/tree/develop/docs) directory in the `develop` branch.
|
||||
2. Create a fork of the develop branch.
|
||||
3. Make changes or add new documentation.
|
||||
4. Commit changes to your fork and push it to GitHub.
|
||||
5. Create a Pull Request of your fork.
|
||||
|
||||
To edit Docs on GitHub:
|
||||
|
||||
1. Login to [GitHub.com](https://www.github.com).
|
||||
2. Navigate to [mermaid-js/mermaid/docs](https://github.com/mermaid-js/mermaid/tree/develop/docs).
|
||||
3. To edit a file, click the pencil icon at the top-right of the file contents panel.
|
||||
4. Describe what you changed in the **Propose file change** section, located at the bottom of the page.
|
||||
5. Submit your changes by clicking the button **Propose file change** at the bottom (by automatic creation of a fork and a new branch).
|
||||
6. Create a Pull Request of your newly forked branch by clicking the green **Create Pull Request** button.
|
||||
|
||||
## Last Words
|
||||
|
||||
Don't get daunted if it is hard in the beginning. We have a great community with only encouraging words. So, if you get stuck, ask for help and hints in the Slack forum. If you want to show off something good, show it off there.
|
||||
|
||||
[Join our Slack community if you want closer contact!](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)
|
||||
|
||||

|
||||
|
@@ -400,7 +400,7 @@ flowchart TB
|
||||
|
||||
## Interaction
|
||||
|
||||
It is possible to bind a click event to a node, the click can lead to either a javascript callback or to a link which will be opened in a new browser tab. **Note**: This functionality is disabled when using `securityLevel='strict'` and enabled when using `securityLevel='loose'`.
|
||||
It is possible to bind a click event to a node, the click can lead to either a JavaScript callback or to a link which will be opened in a new browser tab. **Note**: This functionality is disabled when using `securityLevel='strict'` and enabled when using `securityLevel='loose'`.
|
||||
|
||||
```
|
||||
click nodeId callback
|
||||
@@ -408,15 +408,15 @@ click nodeId call callback()
|
||||
```
|
||||
|
||||
* nodeId is the id of the node
|
||||
* callback is the name of a javascript function defined on the page displaying the graph, the function will be called with the nodeId as parameter.
|
||||
* callback is the name of a JavaScript function defined on the page displaying the graph, the function will be called with the nodeId as parameter.
|
||||
|
||||
Examples of tooltip usage below:
|
||||
|
||||
```html
|
||||
<script>
|
||||
var callback = function(){
|
||||
alert('A callback was triggered');
|
||||
}
|
||||
var callback = function () {
|
||||
alert('A callback was triggered');
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
@@ -428,9 +428,9 @@ graph LR;
|
||||
B-->C;
|
||||
C-->D;
|
||||
click A callback "Tooltip for a callback"
|
||||
click B "http://www.github.com" "This is a tooltip for a link"
|
||||
click B "https://www.github.com" "This is a tooltip for a link"
|
||||
click A call callback() "Tooltip for a callback"
|
||||
click B href "http://www.github.com" "This is a tooltip for a link"
|
||||
click B href "https://www.github.com" "This is a tooltip for a link"
|
||||
```
|
||||
> **Success** The tooltip functionality and the ability to link to urls are available from version 0.5.2.
|
||||
|
||||
@@ -443,13 +443,14 @@ graph LR;
|
||||
B-->C;
|
||||
C-->D;
|
||||
D-->E;
|
||||
click A "http://www.github.com" _blank
|
||||
click B "http://www.github.com" "Open this in a new tab" _blank
|
||||
click C href "http://www.github.com" _blank
|
||||
click D href "http://www.github.com" "Open this in a new tab" _blank
|
||||
click A "https://www.github.com" _blank
|
||||
click B "https://www.github.com" "Open this in a new tab" _blank
|
||||
click C href "https://www.github.com" _blank
|
||||
click D href "https://www.github.com" "Open this in a new tab" _blank
|
||||
```
|
||||
|
||||
Beginners tip, a full example using interactive links in a html context:
|
||||
|
||||
```html
|
||||
<body>
|
||||
<div class="mermaid">
|
||||
@@ -458,25 +459,24 @@ Beginners tip, a full example using interactive links in a html context:
|
||||
B-->C;
|
||||
C-->D;
|
||||
click A callback "Tooltip"
|
||||
click B "http://www.github.com" "This is a link"
|
||||
click B "https://www.github.com" "This is a link"
|
||||
click C call callback() "Tooltip"
|
||||
click D href "http://www.github.com" "This is a link"
|
||||
click D href "https://www.github.com" "This is a link"
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var callback = function(){
|
||||
var callback = function () {
|
||||
alert('A callback was triggered');
|
||||
}
|
||||
var config = {
|
||||
startOnLoad:true,
|
||||
flowchart:{
|
||||
useMaxWidth:true,
|
||||
htmlLabels:true,
|
||||
curve:'cardinal',
|
||||
},
|
||||
securityLevel:'loose',
|
||||
};
|
||||
|
||||
var config = {
|
||||
startOnLoad: true,
|
||||
flowchart: {
|
||||
useMaxWidth: true,
|
||||
htmlLabels: true,
|
||||
curve: 'cardinal'
|
||||
},
|
||||
securityLevel: 'loose'
|
||||
};
|
||||
mermaid.initialize(config);
|
||||
</script>
|
||||
</body>
|
||||
@@ -578,14 +578,12 @@ below:
|
||||
|
||||
**Example style**
|
||||
|
||||
```html
|
||||
<style>
|
||||
.cssClass > rect{
|
||||
fill:#FF0000;
|
||||
stroke:#FFFF00;
|
||||
stroke-width:4px;
|
||||
}
|
||||
</style>
|
||||
```css
|
||||
.cssClass > rect {
|
||||
fill: #FF0000;
|
||||
stroke: #FFFF00;
|
||||
stroke-width: 4px;
|
||||
}
|
||||
```
|
||||
|
||||
**Example definition**
|
||||
@@ -649,6 +647,6 @@ mermaid.flowchartConfig can be set to a JSON string with config parameters or th
|
||||
|
||||
```javascript
|
||||
mermaid.flowchartConfig = {
|
||||
width: 100%
|
||||
width: '100%'
|
||||
}
|
||||
```
|
||||
|
@@ -5,9 +5,9 @@
|
||||
## Directives
|
||||
Directives gives a diagram author the capability to alter the appearance of a diagram before rendering by changing the applied configuration.
|
||||
|
||||
The significance of having directives is that you have them available while writing the diagram, and can modify the defualt global and diagram specific configurations. So, directives are applied on top of the default configurations. The beauty of directives is that you can use them to alter configuration settings for a specific diagram, i.e. at an individual level.
|
||||
The significance of having directives is that you have them available while writing the diagram, and can modify the default global and diagram specific configurations. So, directives are applied on top of the default configurations. The beauty of directives is that you can use them to alter configuration settings for a specific diagram, i.e. at an individual level.
|
||||
|
||||
While directives allow you to change most of the default configuation settings, there are some that are not available, that too for security reasons. Also, you do have the *option to define the set of configurations* that you would allow to be available to the diagram author for overriding with help of directives.
|
||||
While directives allow you to change most of the default configuration settings, there are some that are not available, that too for security reasons. Also, you do have the *option to define the set of configurations* that you would allow to be available to the diagram author for overriding with help of directives.
|
||||
|
||||
## Types of Directives options
|
||||
Mermaid basically supports two types of configuration options to be overridden by directives.
|
||||
@@ -21,7 +21,7 @@ Mermaid basically supports two types of configuration options to be overridden b
|
||||
|
||||
|
||||
2) *Diagram specific configurations* : These are the configurations that are available and applied to a specific diagram. For each diagram there are specific configuration that will alter how that particular diagram looks and behaves.
|
||||
For example, `mirrroActors` is a configuration that is specific to the `SequenceDiagram` and alter whether the actors are mirrored or not. So this config is available only for the `SequenceDiagram` type.
|
||||
For example, `mirrorActors` is a configuration that is specific to the `SequenceDiagram` and alter whether the actors are mirrored or not. So this config is available only for the `SequenceDiagram` type.
|
||||
|
||||
**NOTE:** These options listed here are not all the configuration options. To get hold of all the configuration options, please refer to the [defaultConfig.js](https://github.com/mermaid-js/mermaid/blob/develop/src/defaultConfig.js) in the source code.
|
||||
```
|
||||
@@ -173,7 +173,7 @@ Some common flowchart configurations are:
|
||||
- *useMaxWidth*: number
|
||||
|
||||
For complete list of flowchart configurations, see [defaultConfig.js](https://github.com/mermaid-js/mermaid/blob/develop/src/defaultConfig.js) in the source code.
|
||||
*Soon we plan to publis a complete list all diagram specific configurations updated in the docs*
|
||||
*Soon we plan to publish a complete list all diagram specific configurations updated in the docs*
|
||||
|
||||
The following code snippet changes flowchart config:
|
||||
|
||||
@@ -196,15 +196,15 @@ A --> C[End]
|
||||
Some common sequence configurations are:
|
||||
- *width*: number
|
||||
- *height*: number
|
||||
- *messgageAlign*: left, center, right
|
||||
- *messageAlign*: left, center, right
|
||||
- *mirrorActors*: boolean
|
||||
- *useMaxWidth*: boolean
|
||||
- *rightAngles*: boolean
|
||||
- *showSequenceNumbers*: boolean
|
||||
- *wrap*: boolean
|
||||
|
||||
For complete list of sequeue diagram configurations, see *defaultConfig.js* in the source code.
|
||||
*Soon we plan to publis a complete list all diagram specific configurations updated in the docs*
|
||||
For complete list of sequence diagram configurations, see *defaultConfig.js* in the source code.
|
||||
*Soon we plan to publish a complete list all diagram specific configurations updated in the docs*
|
||||
|
||||
So, `wrap` by default has a value of `false` for sequence diagrams.
|
||||
|
||||
@@ -214,12 +214,12 @@ Let us see an example:
|
||||
sequenceDiagram
|
||||
|
||||
Alice->Bob: Hello Bob, how are you?
|
||||
Bob->Alice: Fine, How did you mother like the book I suggested? And did you catch with the new book about alien invansion?
|
||||
Bob->Alice: Fine, How did you mother like the book I suggested? And did you catch with the new book about alien invasion?
|
||||
Alice->Bob: Good.
|
||||
Bob->Alice: Cool
|
||||
```
|
||||
|
||||
Now let us enble wrap for sequence diagrams.
|
||||
Now let us enable wrap for sequence diagrams.
|
||||
|
||||
The following code snippet changes sequence diagram config for `wrap` to `true`:
|
||||
|
||||
@@ -232,7 +232,7 @@ Using in the diagram above, the wrap will be enabled.
|
||||
%%{init: { "sequence": { "wrap": true, "width":300 } } }%%
|
||||
sequenceDiagram
|
||||
Alice->Bob: Hello Bob, how are you?
|
||||
Bob->Alice: Fine, How did you mother like the book I suggested? And did you catch with the new book about alien invansion?
|
||||
Bob->Alice: Fine, How did you mother like the book I suggested? And did you catch with the new book about alien invasion?
|
||||
Alice->Bob: Good.
|
||||
Bob->Alice: Cool
|
||||
```
|
||||
|
@@ -435,9 +435,9 @@ Examples of tooltip usage below:
|
||||
|
||||
```html
|
||||
<script>
|
||||
var callback = function(){
|
||||
var callback = function () {
|
||||
alert('A callback was triggered');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
@@ -449,9 +449,9 @@ flowchart LR
|
||||
B-->C
|
||||
C-->D
|
||||
click A callback "Tooltip for a callback"
|
||||
click B "http://www.github.com" "This is a tooltip for a link"
|
||||
click B "https://www.github.com" "This is a tooltip for a link"
|
||||
click A call callback() "Tooltip for a callback"
|
||||
click B href "http://www.github.com" "This is a tooltip for a link"
|
||||
click B href "https://www.github.com" "This is a tooltip for a link"
|
||||
```
|
||||
|
||||
> **Success** The tooltip functionality and the ability to link to urls are available from version 0.5.2.
|
||||
@@ -465,10 +465,10 @@ flowchart LR
|
||||
B-->C
|
||||
C-->D
|
||||
D-->E
|
||||
click A "http://www.github.com" _blank
|
||||
click B "http://www.github.com" "Open this in a new tab" _blank
|
||||
click C href "http://www.github.com" _blank
|
||||
click D href "http://www.github.com" "Open this in a new tab" _blank
|
||||
click A "https://www.github.com" _blank
|
||||
click B "https://www.github.com" "Open this in a new tab" _blank
|
||||
click C href "https://www.github.com" _blank
|
||||
click D href "https://www.github.com" "Open this in a new tab" _blank
|
||||
```
|
||||
|
||||
Beginners tip, a full example using interactive links in a html context:
|
||||
@@ -480,25 +480,20 @@ Beginners tip, a full example using interactive links in a html context:
|
||||
B-->C
|
||||
C-->D
|
||||
click A callback "Tooltip"
|
||||
click B "http://www.github.com" "This is a link"
|
||||
click B "https://www.github.com" "This is a link"
|
||||
click C call callback() "Tooltip"
|
||||
click D href "http://www.github.com" "This is a link"
|
||||
click D href "https://www.github.com" "This is a link"
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var callback = function(){
|
||||
var callback = function () {
|
||||
alert('A callback was triggered');
|
||||
}
|
||||
var config = {
|
||||
startOnLoad:true,
|
||||
flowchart:{
|
||||
useMaxWidth:true,
|
||||
htmlLabels:true,
|
||||
curve:'cardinal',
|
||||
},
|
||||
securityLevel:'loose',
|
||||
};
|
||||
|
||||
var config = {
|
||||
startOnLoad: true,
|
||||
flowchart: { useMaxWidth: true, htmlLabels: true, curve: 'cardinal' },
|
||||
securityLevel:'loose'
|
||||
};
|
||||
mermaid.initialize(config);
|
||||
</script>
|
||||
</body>
|
||||
@@ -520,8 +515,8 @@ flowchart LR
|
||||
|
||||
It is possible to style links. For instance you might want to style a link that is going backwards in the flow. As links
|
||||
have no ids in the same way as nodes, some other way of deciding what style the links should be attached to is required.
|
||||
Instead of ids, the order number of when the link was defined in the graph is used. In the example below the style
|
||||
defined in the linkStyle statement will belong to the fourth link in the graph:
|
||||
Instead of ids, the order number of when the link was defined in the graph is used, or use default to apply to all links.
|
||||
In the example below the style defined in the linkStyle statement will belong to the fourth link in the graph:
|
||||
|
||||
```
|
||||
linkStyle 3 stroke:#ff3,stroke-width:4px,color:red;
|
||||
|
@@ -28,6 +28,7 @@ gantt
|
||||
Task in sec :2014-01-12 , 12d
|
||||
another task : 24d
|
||||
```
|
||||
|
||||
## Syntax
|
||||
|
||||
```mermaid-example
|
||||
@@ -64,6 +65,7 @@ gantt
|
||||
```
|
||||
|
||||
It is possible to set multiple dependencies separated by space:
|
||||
|
||||
```mermaid-example
|
||||
gantt
|
||||
apple :a, 2017-07-20, 1w
|
||||
@@ -82,10 +84,9 @@ You can divide the chart into various sections, for example to separate differen
|
||||
|
||||
To do so, start a line with the `section` keyword and give it a name. (Note that unlike with the [title for the entire chart](#title), this name is *required*.
|
||||
|
||||
|
||||
### Milestones
|
||||
|
||||
You can add milestones to the diagrams. Milestones differ from tasks as they represent a single instant in time and are identified by the keyword `milestone`. Below is an example on how to use milestones. As you may notice, the exact location of the milestone is determined by the initial date for the milestone and the "duration" of the task this way: *initial date*+*duration*/2.
|
||||
You can add milestones to the diagrams. Milestones differ from tasks as they represent a single instant in time and are identified by the keyword `milestone`. Below is an example on how to use milestones. As you may notice, the exact location of the milestone is determined by the initial date for the milestone and the "duration" of the task this way: *initial date*+*duration*/2.
|
||||
|
||||
```mermaid-example
|
||||
gantt
|
||||
@@ -102,7 +103,6 @@ Final milestone : milestone, m2, 18:14, 2min
|
||||
|
||||
`dateFormat` defines the format of the date **input** of your gantt elements. How these dates are represented in the rendered chart **output** are defined by `axisFormat`.
|
||||
|
||||
|
||||
### Input date format
|
||||
|
||||
The default input date format is `YYYY-MM-DD`. You can define your custom ``dateFormat``.
|
||||
@@ -136,7 +136,7 @@ SSS 0..999 Thousandths of a second
|
||||
Z ZZ +12:00 Offset from UTC as +-HH:mm, +-HHmm, or Z
|
||||
```
|
||||
|
||||
More info in: http://momentjs.com/docs/#/parsing/string-format/
|
||||
More info in: https://momentjs.com/docs/#/parsing/string-format/
|
||||
|
||||
### Output date format on the axis
|
||||
|
||||
|
146
docs/gitgraph.md
146
docs/gitgraph.md
@@ -182,12 +182,43 @@ After this we made use of the `checkout` keyword to set the current branch as `m
|
||||
After this we merge the `develop` branch onto the current branch `main`, resulting in a merge commit.
|
||||
Since the current branch at this point is still `main`, the last two commits are registered against that.
|
||||
|
||||
### Cherry Pick commit from another branch
|
||||
Similar to how 'git' allows you to cherry pick a commit from **another branch** onto the **current** branch, Mermaid also suports this functionality. You can also cherry pick a commit from another branch using the `cherry-pick` keyword.
|
||||
|
||||
To use the `cherry-pick` keyword, you must specify the id using the `id` attribute, followed by `:` and your desired commit id within `""` quote. For example:
|
||||
|
||||
`cherry-pick id: "your_custom_id"`
|
||||
|
||||
Here, a new commt representing the cherry pick is created on the current branch, and is visually highlighted in the diagram with a **cherry** and a tag depicting the commit id from which it is cherry picked from.
|
||||
|
||||
Few Important rules to note here are:
|
||||
1. You need to provide the `id` for an existing commit to be cherry picked. If given commit id does not exist it will result in an error. For this make use of the `commit id:$value` format of declaring commits. See the examples from above.
|
||||
2. The given commit must not exist on the current branch. Cherry picked commit must always be a different branch than the current branch.
|
||||
3. Current branch must have atleast one commit, before you can cherry pick a commit, otherwise it will case an error is throw.
|
||||
|
||||
Let see an example:
|
||||
```mermaid-example
|
||||
gitGraph
|
||||
commit id: "ZERO"
|
||||
branch develop
|
||||
commit id:"A"
|
||||
checkout main
|
||||
commit id:"ONE"
|
||||
checkout develop
|
||||
commit id:"B"
|
||||
checkout main
|
||||
commit id:"TWO"
|
||||
cherry-pick id:"A"
|
||||
commit id:"THREE"
|
||||
checkout develop
|
||||
commit id:"C"
|
||||
```
|
||||
## Gitgraph specific configuration options
|
||||
In Mermaid, you have the option to configure the gitgraph diagram. You can configure the following options:
|
||||
- `showBranches` : Boolean, default is `true`. If set to `false`, the branches are not shown in the diagram.
|
||||
- `showCommitLabel` : Boolean, default is `true`. If set to `false`, the commit labels are not shown in the diagram.
|
||||
- `mainBranchName` : String, default is `main`. The name of the default/root branch.
|
||||
- `mainBranchOrder` : Position of the main branch in the list of branches. default is `0`, meanig, by default `main` branch is the first in the order.
|
||||
- `mainBranchOrder` : Position of the main branch in the list of branches. default is `0`, meaning, by default `main` branch is the first in the order.
|
||||
|
||||
Let's look at them one by one.
|
||||
## Hiding Branch names and lines
|
||||
@@ -240,6 +271,55 @@ Usage example:
|
||||
merge release
|
||||
```
|
||||
|
||||
## Commit labels Layout: Rotated or Horizontal
|
||||
Mermaid supports two types of commit labels layout. The default layout is **rotated**, which means the labels are placed below the commit circle, rotated at 45 degress for better readability. This is particularly useful for commits with long labels.
|
||||
|
||||
The other option is **horizontal**, which means the labels are placed below the commit circle centred horizontally, and are not rotated. This is particularly useful for commits with short labels.
|
||||
|
||||
You can change the layout of the commit labels by using the `rotateCommitLabel` keyword in the directive. It defaults to `true`, which means the commit labels are rotated.
|
||||
|
||||
Usage example: Rotated commit labels
|
||||
|
||||
```mermaid-example
|
||||
%%{init: { 'logLevel': 'debug', 'theme': 'base', 'gitGraph': {'rotateCommitLabel': true}} }%%
|
||||
gitGraph
|
||||
commit id: "feat(api): ..."
|
||||
commit id: "a"
|
||||
commit id: "b"
|
||||
commit id: "fix(client): .extra long label.."
|
||||
branch c2
|
||||
commit id: "feat(modules): ..."
|
||||
commit id: "test(client): ..."
|
||||
checkout main
|
||||
commit id: "fix(api): ..."
|
||||
commit id: "ci: ..."
|
||||
branch b1
|
||||
commit
|
||||
branch b2
|
||||
commit
|
||||
```
|
||||
|
||||
Usage example: Horizontal commit labels
|
||||
|
||||
```mermaid-example
|
||||
%%{init: { 'logLevel': 'debug', 'theme': 'base', 'gitGraph': {'rotateCommitLabel': false}} }%%
|
||||
gitGraph
|
||||
commit id: "feat(api): ..."
|
||||
commit id: "a"
|
||||
commit id: "b"
|
||||
commit id: "fix(client): .extra long label.."
|
||||
branch c2
|
||||
commit id: "feat(modules): ..."
|
||||
commit id: "test(client): ..."
|
||||
checkout main
|
||||
commit id: "fix(api): ..."
|
||||
commit id: "ci: ..."
|
||||
branch b1
|
||||
commit
|
||||
branch b2
|
||||
commit
|
||||
```
|
||||
|
||||
## Hiding commit labels
|
||||
Sometimes you may want to hide the commit labels from the diagram. You can do this by using the `showCommitLabel` keyword. By default its value is `true`. You can set it to `false` using directives.
|
||||
|
||||
@@ -321,11 +401,11 @@ Usage example:
|
||||
Look at the imaginary railroad map created using Mermaid. Here, we have changed the default main branch name to `MetroLine1`.
|
||||
|
||||
## Customizing branch ordering
|
||||
In Mermaid, by default the branches are shown in the order of their defintion or appearance in the diagram code.
|
||||
In Mermaid, by default the branches are shown in the order of their definition or appearance in the diagram code.
|
||||
|
||||
Sometimes you may want to customize the order of the branches. You can do this by using the `order` keyword next the branch definiton. You can set it to a positive number.
|
||||
Sometimes you may want to customize the order of the branches. You can do this by using the `order` keyword next the branch definition. You can set it to a positive number.
|
||||
|
||||
Mermaid follows the given precendence order of the `order` keyword.
|
||||
Mermaid follows the given precedence order of the `order` keyword.
|
||||
- Main branch is always shown first as it has default order value of `0`. (unless its order is modified and changed from `0` using the `mainBranchOrder` keyword in the config)
|
||||
- Next, All branches without an `order` are shown in the order of their appearance in the diagram code.
|
||||
- Next, All branches with an `order` are shown in the order of their `order` value.
|
||||
@@ -357,11 +437,11 @@ Usage example:
|
||||
|
||||
```
|
||||
Look at the diagram, here, all the branches without a specified order are drawn in their order of definition.
|
||||
Then, `test4` branch is drawn becuase the order of `1`.
|
||||
Then, `main` branch is drawn becuase the order of `2`.
|
||||
And, lastly `test1`is drawn becuase the order of `3`.
|
||||
Then, `test4` branch is drawn because the order of `1`.
|
||||
Then, `main` branch is drawn because the order of `2`.
|
||||
And, lastly `test1`is drawn because the order of `3`.
|
||||
|
||||
NOTE: Becuase we have overriden the `mainBranchOrder` to `2`, the `main` branch is not drawn in the begining, instead follows the ordering.
|
||||
NOTE: Because we have overridden the `mainBranchOrder` to `2`, the `main` branch is not drawn in the beginning, instead follows the ordering.
|
||||
|
||||
|
||||
|
||||
@@ -640,7 +720,9 @@ See how the default theme is used to set the colors for the branches:
|
||||
branch featureA
|
||||
commit
|
||||
```
|
||||
|
||||
> #### IMPORTANT:
|
||||
> Mermaid supports the theme variables to override the default values for **up to 8 branches**, i.e., you can set the color/styling of up to 8 branches using theme variables. After this threshold of 8 branches, the theme variables are reused in the cyclic manner, i.e. 9th branch will use the color/styling of 1st branch, or branch at index position '8' will use the color/styling of branch at index position '0'.
|
||||
*More on this in the next section. See examples on **Customizing branch label colors** below*
|
||||
### Customizing branch colors
|
||||
You can customize the branch colors using the `git0` to `git7` theme variables. Mermaid allows you to set the colors for up-to 8 branches, where `git0` variable will drive the value of the first branch, `git1` will drive the value of the second branch and so on.
|
||||
|
||||
@@ -685,27 +767,32 @@ Now let's override the default values for the `gitBranchLabel0` to `gitBranchLab
|
||||
|
||||
```mermaid-example
|
||||
%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': {
|
||||
'gitBranchLabel0': '#ff0000',
|
||||
'gitBranchLabel1': '#00ff00',
|
||||
'gitBranchLabel2': '#0000ff'
|
||||
} } }%%
|
||||
gitGraph
|
||||
commit
|
||||
branch develop
|
||||
commit tag:"v1.0.0"
|
||||
commit
|
||||
checkout main
|
||||
commit type: HIGHLIGHT
|
||||
commit
|
||||
merge develop
|
||||
commit
|
||||
branch featureA
|
||||
commit
|
||||
|
||||
'gitBranchLabel0': '#ffffff',
|
||||
'gitBranchLabel1': '#ffffff',
|
||||
'gitBranchLabel2': '#ffffff',
|
||||
'gitBranchLabel3': '#ffffff',
|
||||
'gitBranchLabel4': '#ffffff',
|
||||
'gitBranchLabel5': '#ffffff',
|
||||
'gitBranchLabel6': '#ffffff',
|
||||
'gitBranchLabel7': '#ffffff',
|
||||
'gitBranchLabel8': '#ffffff',
|
||||
'gitBranchLabel9': '#ffffff'
|
||||
} } }%%
|
||||
gitGraph
|
||||
checkout main
|
||||
branch branch1
|
||||
branch branch2
|
||||
branch branch3
|
||||
branch branch4
|
||||
branch branch5
|
||||
branch branch6
|
||||
branch branch7
|
||||
branch branch8
|
||||
branch branch9
|
||||
checkout branch1
|
||||
commit
|
||||
```
|
||||
See how the branch label colors are changed to the values specified in the theme variables.
|
||||
|
||||
|
||||
Here, you can see that `branch8` and `branch9` colors and the styles are being picked from branch at index position `0` (`main`) and `1`(`branch1`) respectively, i.e., **branch themeVariables are repeated cyclically**.
|
||||
### Customizing Commit colors
|
||||
You can customize commit using the `commitLabelColor` and `commitLabelBackground` theme variables for changes in the commit label color and background color respectively.
|
||||
|
||||
@@ -787,3 +874,4 @@ See how the highlight commit color on the first branch is changed to the value s
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -17,7 +17,7 @@
|
||||
/>
|
||||
<!-- <link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/vue.css"> -->
|
||||
<link rel="stylesheet" href="theme.css" />
|
||||
<script src="//cdn.jsdelivr.net/npm/mermaid@9.1.1/dist/mermaid.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/mermaid@9.1.2/dist/mermaid.min.js"></script>
|
||||
<!-- <script src="http://localhost:9000/mermaid.js"></script> -->
|
||||
<script>
|
||||
// prettier-ignore
|
||||
@@ -163,5 +163,6 @@
|
||||
<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>
|
||||
|
@@ -17,7 +17,7 @@ They also serve as proof of concept, for the variety of things that can be built
|
||||
- [Joplin](https://joplinapp.org) (**Native support**)
|
||||
- [Notion](https://notion.so) (**Native support**)
|
||||
- [Observable](https://observablehq.com/@observablehq/mermaid) (**Native support**)
|
||||
- [GitBook](http://gitbook.com)
|
||||
- [GitBook](https://gitbook.com)
|
||||
- [Mermaid Plugin](https://github.com/JozoVilcek/gitbook-plugin-mermaid)
|
||||
- [Markdown with Mermaid CLI](https://github.com/miao1007/gitbook-plugin-mermaid-cli)
|
||||
- [Mermaid plugin for GitBook](https://github.com/wwformat/gitbook-plugin-mermaid-pdf)
|
||||
@@ -33,7 +33,7 @@ They also serve as proof of concept, for the variety of things that can be built
|
||||
|
||||
## CRM/ERP/Similar
|
||||
|
||||
- [coreBOS](http://blog.corebos.org/blog/december2019)
|
||||
- [coreBOS](https://blog.corebos.org/blog/december2019)
|
||||
|
||||
## Blogs
|
||||
|
||||
@@ -104,18 +104,18 @@ They also serve as proof of concept, for the variety of things that can be built
|
||||
- [Language Mermaid Syntax Highlighter](https://atom.io/packages/language-mermaid)
|
||||
- [Sublime Text 3](https://sublimetext.com)
|
||||
- [Mermaid Package](https://packagecontrol.io/packages/Mermaid)
|
||||
- [Astah](http://astah.net)
|
||||
- [Astah](https://astah.net)
|
||||
- [Export to Mermaid](https://github.com/Avens666/Astah_Jude_UML_export_to_Markdown-mermaid-Plantuml-)
|
||||
- [Light Table](http://lighttable.com/)
|
||||
- [Mermaid Plugin](https://github.com/cldwalker/Mermaid)
|
||||
- [Draw.io](http://draw.io) - [Plugin](https://github.com/nopeslide/drawio_mermaid_plugin)
|
||||
- [Inkdrop](http://inkdrop.app) - [Plugin](https://github.com/inkdropapp/inkdrop-mermaid)
|
||||
- [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://vim.org)
|
||||
- [Vim Diagram Syntax](https://github.com/zhaozg/vim-diagram)
|
||||
- [GNU Emacs](https://www.gnu.org/software/emacs/)
|
||||
- [Major mode for .mmd files](https://github.com/abrochard/mermaid-mode)
|
||||
- [Org-Mode integration](https://github.com/arnm/ob-mermaid)
|
||||
- [Brackets](http://brackets.io/)
|
||||
- [Brackets](https://brackets.io/)
|
||||
- [Mermaid Preview](https://s3.amazonaws.com/extend.brackets/alanhohn.mermaid-preview/alanhohn.mermaid-preview-1.0.2.zip)
|
||||
- [Iodide](https://github.com/iodide-project/iodide)
|
||||
- [iodide-mermaid-plugin](https://github.com/iodide-project/iodide-mermaid-plugin)
|
||||
|
@@ -15,7 +15,7 @@ They also serve as proof of concept, for the variety of things that can be built
|
||||
- [GitHub](https://github.com)
|
||||
- [GitHub action: Compile mermaid to image](https://github.com/neenjaw/compile-mermaid-markdown-action)
|
||||
- [svg-generator](https://github.com/SimonKenyonShepard/mermaidjs-github-svg-generator)
|
||||
- [GitBook](http://gitbook.com)
|
||||
- [GitBook](https://gitbook.com)
|
||||
- [Mermaid Plugin](https://github.com/JozoVilcek/gitbook-plugin-mermaid)
|
||||
- [Markdown with Mermaid CLI](https://github.com/miao1007/gitbook-plugin-mermaid-cli)
|
||||
- [Mermaid plugin for GitBook](https://github.com/wwformat/gitbook-plugin-mermaid-pdf)
|
||||
@@ -30,7 +30,7 @@ They also serve as proof of concept, for the variety of things that can be built
|
||||
|
||||
## CRM/ERP/Similar
|
||||
|
||||
- [coreBOS](http://blog.corebos.org/blog/december2019)
|
||||
- [coreBOS](https://blog.corebos.org/blog/december2019)
|
||||
|
||||
## Blogs
|
||||
|
||||
@@ -101,18 +101,18 @@ They also serve as proof of concept, for the variety of things that can be built
|
||||
- [Language Mermaid Syntax Highlighter](https://atom.io/packages/language-mermaid)
|
||||
- [Sublime Text 3](https://sublimetext.com)
|
||||
- [Mermaid Package](https://packagecontrol.io/packages/Mermaid)
|
||||
- [Astah](http://astah.net)
|
||||
- [Astah](https://astah.net)
|
||||
- [Export to Mermaid](https://github.com/Avens666/Astah_Jude_UML_export_to_Markdown-mermaid-Plantuml-)
|
||||
- [Light Table](http://lighttable.com/)
|
||||
- [Mermaid Plugin](https://github.com/cldwalker/Mermaid)
|
||||
- [Draw.io](http://draw.io) - [Plugin](https://github.com/nopeslide/drawio_mermaid_plugin)
|
||||
- [Inkdrop](http://inkdrop.app) - [Plugin](https://github.com/inkdropapp/inkdrop-mermaid)
|
||||
- [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://vim.org)
|
||||
- [Vim Diagram Syntax](https://github.com/zhaozg/vim-diagram)
|
||||
- [GNU Emacs](https://www.gnu.org/software/emacs/)
|
||||
- [Major mode for .mmd files](https://github.com/abrochard/mermaid-mode)
|
||||
- [Org-Mode integration](https://github.com/arnm/ob-mermaid)
|
||||
- [Brackets](http://brackets.io/)
|
||||
- [Brackets](https://brackets.io/)
|
||||
- [Mermaid Preview](https://s3.amazonaws.com/extend.brackets/alanhohn.mermaid-preview/alanhohn.mermaid-preview-1.0.2.zip)
|
||||
- [Iodide](https://github.com/iodide-project/iodide)
|
||||
- [iodide-mermaid-plugin](https://github.com/iodide-project/iodide-mermaid-plugin)
|
||||
|
@@ -12,7 +12,7 @@ This section talks about the different ways to deploy Mermaid. Learning the [Syn
|
||||
|
||||
1. Using the Mermaid Live Editor at [mermaid.live](https://mermaid.live).
|
||||
2. Using [mermaid plugins](./integrations.md) with programs you are familiar with.
|
||||
3. Calling the Mermaid Javascript API.
|
||||
3. Calling the Mermaid JavaScript API.
|
||||
4. Deploying Mermaid as a dependency.
|
||||
|
||||
**Note: It is our recommendation that you review all approaches, and choose the one that is best for your project.**
|
||||
@@ -63,7 +63,7 @@ You can generate mermaid diagrams from within popular applications using plug-in
|
||||
|
||||
**This is covered in greater detail in the [Usage section](usage.md)**
|
||||
|
||||
## 3. Calling the Javascript API
|
||||
## 3. Calling the JavaScript API
|
||||
|
||||
This method can be used with any common web server like Apache, IIS, nginx, node express.
|
||||
|
||||
|
@@ -12,7 +12,6 @@ For instance:
|
||||
* the flowchart starts with the keyword graph.
|
||||
* the sequence diagram starts with the keyword sequenceDiagram
|
||||
|
||||
|
||||
#### Store data found during parsing
|
||||
|
||||
There are some jison specific sub steps here where the parser stores the data encountered when parsing the diagram, this data is later used by the renderer. You can during the parsing call a object provided to the parser by the user of the parser. This object can be called during parsing for storing data.
|
||||
@@ -29,7 +28,7 @@ statement
|
||||
In the extract of the grammar above, it is defined that a call to the setTitle method in the data object will be done when parsing and the title keyword is encountered.
|
||||
|
||||
```tip
|
||||
Make sure that the `parseError` function for the parser is defined and calling `mermaidPAI.parseError`. This way a common way of detecting parse errors is provided for the end-user.
|
||||
Make sure that the `parseError` function for the parser is defined and calling `mermaid.parseError`. This way a common way of detecting parse errors is provided for the end-user.
|
||||
```
|
||||
|
||||
For more info look in the example diagram type:
|
||||
@@ -38,7 +37,7 @@ The `yy` object has the following function:
|
||||
|
||||
```javascript
|
||||
exports.parseError = function(err, hash){
|
||||
mermaidAPI.parseError(err, hash)
|
||||
mermaid.parseError(err, hash)
|
||||
};
|
||||
```
|
||||
|
||||
@@ -53,7 +52,7 @@ parser.yy = db
|
||||
|
||||
### Step 2: Rendering
|
||||
|
||||
Write a renderer that given the data found during parsing renders the diagram. To look at an example look at sequendeRenderer.js rather then the flowchart renderer as this is a more generic example.
|
||||
Write a renderer that given the data found during parsing renders the diagram. To look at an example look at sequenceRenderer.js rather then the flowchart renderer as this is a more generic example.
|
||||
|
||||
Place the renderer in the diagram folder.
|
||||
|
||||
@@ -109,4 +108,160 @@ flow.parser.yy = graph
|
||||
|
||||
Look at `graphDb.js` for more details on that object.
|
||||
|
||||
## Layout
|
||||
|
||||
If you are using a dagre based layout, please use flowchart-v2 as a template and by doing that you will be using dagre-wrapper instead of dagreD3 which we are m igrating away from.
|
||||
|
||||
### Common parts of a diagram
|
||||
|
||||
There are a few features that are common between the different types of diagrams. We try to standardize the diagrams that work as similar as possible for the end user. The commonalities are:
|
||||
|
||||
* Directives, a way of modifying the diagram configuration from within the diagram code.
|
||||
* Accessibility, a way for an author to provide additional information like titles and descriptions to people accessing a text with diagrams using a screen reader.
|
||||
* Themes, there is a common way to modify the styling of diagrams in Mermaid.
|
||||
* Comments should follow mermaid standards
|
||||
|
||||
Here some pointers on how to handle these different areas.
|
||||
|
||||
#### [Directives](./directives.md)
|
||||
|
||||
Here is example handling from flowcharts:
|
||||
Jison:
|
||||
```jison
|
||||
|
||||
/* lexial grammar */
|
||||
%lex
|
||||
%x open_directive
|
||||
%x type_directive
|
||||
%x arg_directive
|
||||
%x close_directive
|
||||
|
||||
\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
|
||||
<open_directive>((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
|
||||
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
|
||||
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
|
||||
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
|
||||
|
||||
/* language grammar */
|
||||
|
||||
/* ... */
|
||||
|
||||
directive
|
||||
: openDirective typeDirective closeDirective separator
|
||||
| openDirective typeDirective ':' argDirective closeDirective separator
|
||||
;
|
||||
|
||||
openDirective
|
||||
: open_directive { yy.parseDirective('%%{', 'open_directive'); }
|
||||
;
|
||||
|
||||
typeDirective
|
||||
: type_directive { yy.parseDirective($1, 'type_directive'); }
|
||||
;
|
||||
|
||||
argDirective
|
||||
: arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
|
||||
;
|
||||
|
||||
closeDirective
|
||||
: close_directive { yy.parseDirective('}%%', 'close_directive', 'flowchart'); }
|
||||
;
|
||||
```
|
||||
|
||||
It is probably a good idea to keep the handling similar to this in your new diagram. The parseDirective function is provided by the mermaidAPI.
|
||||
|
||||
## Accessibility
|
||||
|
||||
The syntax for adding title and description looks like this:
|
||||
```
|
||||
accTitle: The title
|
||||
accDescr: The dsscription
|
||||
|
||||
accDescr {
|
||||
Syntax for a description text
|
||||
written on multiple lines.
|
||||
}
|
||||
```
|
||||
|
||||
In a similar way to the directives the jison syntax are quite similar between the diagrams.
|
||||
|
||||
```jison
|
||||
* lexical grammar */
|
||||
%lex
|
||||
%x acc_title
|
||||
%x acc_descr
|
||||
%x acc_descr_multiline
|
||||
|
||||
%%
|
||||
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
|
||||
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
|
||||
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }
|
||||
<acc_descr>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; }
|
||||
accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
|
||||
<acc_descr_multiline>[\}] { this.popState(); }
|
||||
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
|
||||
|
||||
statement
|
||||
: acc_title acc_title_value { $$=$2.trim();yy.setTitle($$); }
|
||||
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
|
||||
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); }
|
||||
```
|
||||
|
||||
The functions for setting title and description are provided by a common module. This is the import from flowDb.js:
|
||||
|
||||
```
|
||||
import {
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
clear as commonClear,
|
||||
} from '../../commonDb';
|
||||
```
|
||||
|
||||
For rendering the accessibility tags you have again an existing function you can use.
|
||||
|
||||
**In the renderer:**
|
||||
```js
|
||||
import addSVGAccessibilityFields from '../../accessibility';
|
||||
|
||||
/* ... */
|
||||
|
||||
// Adds title and description to the flow chart
|
||||
addSVGAccessibilityFields(parser.yy, svg, id);
|
||||
```
|
||||
|
||||
## Theming
|
||||
|
||||
Mermaid supports themes and has an integrated theming engine. You can read more about how the themes can be used [in the docs](./theming.md).
|
||||
|
||||
When adding themes to a diagram it comes down to a few important locations in the code.
|
||||
|
||||
The entry point for the styling engine is in **src/styles.js**. The getStyles function will be called by Mermaid when the styles are being applied to the diagram.
|
||||
|
||||
This function will in turn call a function *your diagram should provide* returning the css for the new diagram. The diagram specific, also which is commonly also called getStyles and located in the folder for your diagram under src/diagrams and should be named styles.js. The getStyles function will be called with the theme options as an argument like in the following example:
|
||||
|
||||
```js
|
||||
const getStyles = (options) =>
|
||||
`
|
||||
.line {
|
||||
stroke-width: 1;
|
||||
stroke: ${options.lineColor};
|
||||
stroke-dasharray: 2;
|
||||
}
|
||||
// ...
|
||||
```
|
||||
|
||||
Note that you need to provide your function to the main getStyles by adding it into the themes object in **src/styles.js** like in the xyzDiagram in the provided example:
|
||||
|
||||
```js
|
||||
const themes = {
|
||||
flowchart,
|
||||
'flowchart-v2': flowchart,
|
||||
sequence,
|
||||
xyzDiagram
|
||||
//...
|
||||
```
|
||||
|
||||
The actual options and values for the colors are defined in **src/theme/theme-[xyz].js**. If you provide the options your diagram needs in the existing theme files then the theming will work smoothly without hiccups.
|
||||
|
||||
|
@@ -1,7 +1,5 @@
|
||||
# Requirement Diagram
|
||||
|
||||
**Edit this Page** [](https://github.com/mermaid-js/mermaid/blob/develop/docs/requirementDiagram.md)
|
||||
|
||||
> A Requirement diagram provides a visualization for requirements and their connections, to each other and other documented elements. The modeling specs follow those defined by SysML v1.6.
|
||||
|
||||
Rendering requirements is straightforward.
|
||||
|
@@ -230,6 +230,70 @@ sequenceDiagram
|
||||
end
|
||||
```
|
||||
|
||||
## Critical Region
|
||||
|
||||
It is possible to show actions that must happen automatically with conditional handling of circumstances.
|
||||
|
||||
This is done by the notation
|
||||
|
||||
```
|
||||
critical [Action that must be performed]
|
||||
... statements ...
|
||||
option [Circumstance A]
|
||||
... statements ...
|
||||
option [Circumstance B]
|
||||
... statements ...
|
||||
end
|
||||
```
|
||||
|
||||
See the example below:
|
||||
|
||||
```mermaid-example
|
||||
sequenceDiagram
|
||||
critical Establish a connection to the DB
|
||||
Service-->DB: connect
|
||||
option Network timeout
|
||||
Service-->Service: Log error
|
||||
option Credentials rejected
|
||||
Service-->Service: Log different error
|
||||
end
|
||||
```
|
||||
|
||||
It is also possible to have no options at all
|
||||
|
||||
```mermaid-example
|
||||
sequenceDiagram
|
||||
critical Establish a connection to the DB
|
||||
Service-->DB: connect
|
||||
end
|
||||
```
|
||||
|
||||
This critical block can also be nested, equivalently to the `par` statement as seen above.
|
||||
|
||||
## Break
|
||||
|
||||
It is possible to indicate a stop of the sequence within the flow (usually used to model exceptions).
|
||||
|
||||
This is done by the notation
|
||||
|
||||
```
|
||||
break [something happened]
|
||||
... statements ...
|
||||
end
|
||||
```
|
||||
|
||||
See the example below:
|
||||
|
||||
```mermaid-example
|
||||
sequenceDiagram
|
||||
Consumer-->API: Book something
|
||||
API-->BookingService: Start booking process
|
||||
break when the booking process fails
|
||||
API-->Consumer: show failure
|
||||
end
|
||||
API-->BillingService: Start billing process
|
||||
```
|
||||
|
||||
## Background Highlighting
|
||||
|
||||
It is possible to highlight flows by providing colored background rects. This is done by the notation
|
||||
|
@@ -9,16 +9,16 @@ The theme can be altered by changing the root level variable `theme` variable in
|
||||
Themes follow and build upon the Levels of Configuration, and employ `directives` to modify and create custom configurations, as they were introduced in Version [8.6.0](./8.6.0_docs.md).
|
||||
|
||||
## Deployable Themes
|
||||
The following are a list of **Deployable themes**, sample `%%init%%` directives and `initialize` calls.
|
||||
1. **Base**- Designed to modified, as the name implies it is supposed to be used as the base for making custom themes.
|
||||
The following are a list of **Deployable themes**, sample `%%init%%` directives and `initialize` calls.
|
||||
1. **base**- Designed to be modified, as the name implies it is supposed to be used as the base for making custom themes.
|
||||
|
||||
2. **Forest**- A theme full of light greens that is easy on the eyes.
|
||||
2. **forest**- A theme full of light greens that is easy on the eyes.
|
||||
|
||||
3. **Dark**- A theme that would go well with other dark colored elements.
|
||||
3. **dark**- A theme that would go well with other dark-colored elements.
|
||||
|
||||
4. **Default**- The default theme for all diagrams.
|
||||
4. **default**- The default theme for all diagrams.
|
||||
|
||||
5. **Neutral**- The theme to be used for black and white printing
|
||||
5. **neutral**- The theme to be used for black and white printing.
|
||||
|
||||
|
||||
## Site-wide Themes
|
||||
@@ -116,15 +116,15 @@ Variables that are unique to some diagrams can be affected by changes in Theme V
|
||||
| -------------------- | ------------------------------ | ---- | -------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| darkMode | false | | Boolean Value that dictates how to calculate colors. "true" will activate darkmode. |
|
||||
| background | #f4f4f4 | | Used to calculate color for items that should either be background colored or contrasting to the background. |
|
||||
| primaryColor | #fff4dd | | Color to be used as background in nodes, other colors will be derived from this |
|
||||
| fontFamily | "trebuchet ms", verdana, arial | | |
|
||||
| fontSize | 16px | | Font Size, in pixels |
|
||||
| secondaryColor | based on primaryColor | * | |
|
||||
| tertiaryColor | based on primaryColor | * | |
|
||||
| primaryColor | #fff4dd | | Color to be used as background in nodes, other colors will be derived from this |
|
||||
| primaryBorderColor | based on primaryColor | * | Color to be used as border in nodes using primaryColor |
|
||||
| primaryTextColor | based on darkMode #ddd/#333 | * | Color to be used as text color in nodes using primaryColor |
|
||||
| primaryTextColor | based on darkMode #ddd/#333 | * | Color to be used as text color in nodes using primaryColor
|
||||
| secondaryColor | based on primaryColor | * | |
|
||||
| secondaryBorderColor | based on secondaryColor | * | Color to be used as border in nodes using secondaryColor |
|
||||
| secondaryTextColor | based on secondaryColor | * | Color to be used as text color in nodes using secondaryColor |
|
||||
| secondaryTextColor | based on secondaryColor | * | Color to be used as text color in nodes using secondaryColor
|
||||
| tertiaryColor | based on primaryColor | * | | | |
|
||||
| tertiaryBorderColor | based on tertiaryColor | * | Color to be used as border in nodes using tertiaryColor |
|
||||
| tertiaryTextColor | based on tertiaryColor | * | Color to be used as text color in nodes using tertiaryColor |
|
||||
| noteBkgColor | #fff5ad | | Color used as background in notes |
|
||||
@@ -160,9 +160,9 @@ Variables that are unique to some diagrams can be affected by changes in Theme V
|
||||
| actorBkg | mainBkg | * | Actor Background Color |
|
||||
| actorTextColor | primaryTextColor | * | Actor Text Color |
|
||||
| actorLineColor | grey | * | Actor Line Color |
|
||||
| labelBoxBkgColor | actorBkg | * | Label Box Background Color |
|
||||
| signalColor | textColor | * | Signal Color |
|
||||
| signalTextColor | textColor | * | Signal Text Color |
|
||||
| labelBoxBkgColor | actorBkg | * | Label Box Background Color |
|
||||
| labelBoxBorderColor | actorBorder | * | Label Box Border Color |
|
||||
| labelTextColor | actorTextColor | * | Label Text Color |
|
||||
| loopTextColor | actorTextColor | * | Loop ext Color |
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
**Edit this Page** [](https://github.com/mermaid-js/mermaid/blob/develop/docs/usage.md)
|
||||
|
||||
Mermaid is a Javascript tool that makes use of a markdown based syntax to render customizable diagrams, charts and visualizations.
|
||||
Mermaid is a JavaScript tool that makes use of a Markdown based syntax to render customizable diagrams, charts and visualizations.
|
||||
|
||||
Diagrams can be re-rendered/modified by modifying their descriptions.
|
||||
|
||||
@@ -114,7 +114,7 @@ Values:
|
||||
- **strict**: (**default**) tags in text are encoded, click functionality is disabled
|
||||
- **loose**: tags in text are allowed, click functionality is enabled
|
||||
- **antiscript**: html tags in text are allowed, (only script element is removed), click functionality is enabled
|
||||
- **sandbox**: With this security level all rendering takes place in a sandboxed iframe. This prevent any javascript running in the context. This may hinder interactive functionality of the diagram like scripts, popups in sequence diagram or links to other tabs/targets etc.
|
||||
- **sandbox**: With this security level all rendering takes place in a sandboxed iframe. This prevent any JavaScript running in the context. This may hinder interactive functionality of the diagram like scripts, popups in sequence diagram or links to other tabs/targets etc.
|
||||
|
||||
|
||||
```note
|
||||
@@ -198,10 +198,10 @@ mermaid fully supports webpack. Here is a [working demo](https://github.com/merm
|
||||
## API usage
|
||||
|
||||
The main idea of the API is to be able to call a render function with the graph definition as a string. The render function
|
||||
will render the graph and call a callback with the resulting svg code. With this approach it is up to the site creator to
|
||||
will render the graph and call a callback with the resulting SVG code. With this approach it is up to the site creator to
|
||||
fetch the graph definition from the site (perhaps from a textarea), render it and place the graph somewhere in the site.
|
||||
|
||||
The example below show an outline of how this could be used. The example just logs the resulting svg to the javascript console.
|
||||
The example below show an outline of how this could be used. The example just logs the resulting SVG to the JavaScript console.
|
||||
|
||||
```html
|
||||
<script src="mermaid.js"></script>
|
||||
@@ -221,7 +221,7 @@ Sometimes the generated graph also has defined interactions like tooltip and cli
|
||||
add those events after the graph has been inserted into the DOM.
|
||||
|
||||
The example code below is an extract of what mermaid does when using the API. The example shows how it is possible to
|
||||
bind events to an svg when using the API for rendering.
|
||||
bind events to an SVG when using the API for rendering.
|
||||
|
||||
```javascript
|
||||
var insertSvg = function(svgCode, bindFunctions) {
|
||||
@@ -240,14 +240,14 @@ mermaidAPI.render(id,txt,insertSvg, element);
|
||||
|
||||
1. The graph is generated using the render call.
|
||||
2. After generation the render function calls the provided callback function, in this case it's called insertSvg.
|
||||
3. The callback function is called with two parameters, the svg code of the generated graph and a function. This function binds events to the svg **after** it is inserted into the DOM.
|
||||
4. Insert the svg code into the DOM for presentation.
|
||||
3. The callback function is called with two parameters, the SVG code of the generated graph and a function. This function binds events to the SVG **after** it is inserted into the DOM.
|
||||
4. Insert the SVG code into the DOM for presentation.
|
||||
5. Call the binding function that binds the events.
|
||||
|
||||
|
||||
## Example of a marked renderer
|
||||
|
||||
This is the renderer used for transforming the documentation from markdown to html with mermaid diagrams in the html.
|
||||
This is the renderer used for transforming the documentation from Markdown to html with mermaid diagrams in the html.
|
||||
|
||||
```javascript
|
||||
var renderer = new marked.Renderer();
|
||||
@@ -261,7 +261,7 @@ renderer.code = function (code, language) {
|
||||
};
|
||||
```
|
||||
|
||||
Another example in coffeescript that also includes the mermaid script tag in the generated markup.
|
||||
Another example in CoffeeScript that also includes the mermaid script tag in the generated markup.
|
||||
|
||||
```coffee
|
||||
marked = require 'marked'
|
||||
|
11
launch.json
11
launch.json
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "attach",
|
||||
"name": "Attach",
|
||||
"port": 9229
|
||||
}
|
||||
]
|
||||
}
|
20
package.json
20
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mermaid",
|
||||
"version": "9.1.1",
|
||||
"version": "9.1.2",
|
||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"main": "dist/mermaid.core.js",
|
||||
"module": "dist/mermaid.esm.min.mjs",
|
||||
@@ -27,13 +27,14 @@
|
||||
"postbuild": "documentation build src/mermaidAPI.js src/config.js src/defaultConfig.js --shallow -f md --markdown-toc false > docs/Setup.md",
|
||||
"build:watch": "yarn build:development --watch",
|
||||
"release": "yarn build",
|
||||
"lint": "eslint ./ --ext js,html",
|
||||
"lint": "eslint ./ --ext .js,.json,.html",
|
||||
"lint:fix": "yarn lint --fix",
|
||||
"e2e:depr": "yarn lint && jest e2e --config e2e/jest.config.js",
|
||||
"cypress": "cypress run",
|
||||
"e2e": "start-server-and-test dev http://localhost:9000/ cypress",
|
||||
"e2e-upd": "yarn lint && jest e2e -u --config e2e/jest.config.js",
|
||||
"dev": "webpack serve --config ./.webpack/webpack.config.e2e.babel.js",
|
||||
"ci": "jest src/.*",
|
||||
"test": "yarn lint && jest src/.*",
|
||||
"test:watch": "jest --watch src",
|
||||
"prepublishOnly": "yarn build && yarn test",
|
||||
@@ -61,7 +62,7 @@
|
||||
"d3": "^7.0.0",
|
||||
"dagre": "^0.8.5",
|
||||
"dagre-d3": "^0.6.4",
|
||||
"dompurify": "2.3.6",
|
||||
"dompurify": "2.3.8",
|
||||
"graphlib": "^2.1.8",
|
||||
"khroma": "^2.0.0",
|
||||
"moment-mini": "^2.24.0",
|
||||
@@ -73,31 +74,32 @@
|
||||
"@babel/eslint-parser": "^7.14.7",
|
||||
"@babel/preset-env": "^7.14.7",
|
||||
"@babel/register": "^7.14.5",
|
||||
"@commitlint/cli": "^16.0.0",
|
||||
"@commitlint/config-conventional": "^16.0.0",
|
||||
"@commitlint/cli": "^17.0.0",
|
||||
"@commitlint/config-conventional": "^17.0.0",
|
||||
"babel-jest": "^28.0.3",
|
||||
"babel-loader": "^8.2.2",
|
||||
"concurrently": "^7.0.0",
|
||||
"coveralls": "^3.0.2",
|
||||
"css-to-string-loader": "^0.1.3",
|
||||
"cypress": "9.6.0",
|
||||
"cypress": "9.7.0",
|
||||
"cypress-image-snapshot": "^4.0.1",
|
||||
"documentation": "13.2.0",
|
||||
"eslint": "^8.2.0",
|
||||
"eslint": "^8.4.1",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-cypress": "^2.12.1",
|
||||
"eslint-plugin-html": "^6.2.0",
|
||||
"eslint-plugin-jest": "^26.0.0",
|
||||
"eslint-plugin-jsdoc": "^39.1.0",
|
||||
"eslint-plugin-json": "^3.1.0",
|
||||
"eslint-plugin-markdown": "^2.2.1",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"husky": "^7.0.1",
|
||||
"husky": "^8.0.0",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^28.0.3",
|
||||
"jest-environment-jsdom": "^28.0.2",
|
||||
"jison": "^0.4.18",
|
||||
"js-base64": "3.7.2",
|
||||
"lint-staged": "^12.1.2",
|
||||
"lint-staged": "^13.0.0",
|
||||
"moment": "^2.23.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"prettier": "^2.3.2",
|
||||
|
675
pie.js
675
pie.js
@@ -1,675 +0,0 @@
|
||||
/* parser generated by jison 0.4.18 */
|
||||
/*
|
||||
Returns a Parser object of the following structure:
|
||||
|
||||
Parser: {
|
||||
yy: {}
|
||||
}
|
||||
|
||||
Parser.prototype: {
|
||||
yy: {},
|
||||
trace: function(),
|
||||
symbols_: {associative list: name ==> number},
|
||||
terminals_: {associative list: number ==> name},
|
||||
productions_: [...],
|
||||
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$),
|
||||
table: [...],
|
||||
defaultActions: {...},
|
||||
parseError: function(str, hash),
|
||||
parse: function(input),
|
||||
|
||||
lexer: {
|
||||
EOF: 1,
|
||||
parseError: function(str, hash),
|
||||
setInput: function(input),
|
||||
input: function(),
|
||||
unput: function(str),
|
||||
more: function(),
|
||||
less: function(n),
|
||||
pastInput: function(),
|
||||
upcomingInput: function(),
|
||||
showPosition: function(),
|
||||
test_match: function(regex_match_array, rule_index),
|
||||
next: function(),
|
||||
lex: function(),
|
||||
begin: function(condition),
|
||||
popState: function(),
|
||||
_currentRules: function(),
|
||||
topState: function(),
|
||||
pushState: function(condition),
|
||||
|
||||
options: {
|
||||
ranges: boolean (optional: true ==> token location info will include a .range[] member)
|
||||
flex: boolean (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match)
|
||||
backtrack_lexer: boolean (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code)
|
||||
},
|
||||
|
||||
performAction: function(yy, yy_, $avoiding_name_collisions, YY_START),
|
||||
rules: [...],
|
||||
conditions: {associative list: name ==> set},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
token location info (@$, _$, etc.): {
|
||||
first_line: n,
|
||||
last_line: n,
|
||||
first_column: n,
|
||||
last_column: n,
|
||||
range: [start_number, end_number] (where the numbers are indexes into the input string, regular zero-based)
|
||||
}
|
||||
|
||||
|
||||
the parseError function receives a 'hash' object with these members for lexer and parser errors: {
|
||||
text: (matched text)
|
||||
token: (the produced terminal token, if any)
|
||||
line: (yylineno)
|
||||
}
|
||||
while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: {
|
||||
loc: (yylloc)
|
||||
expected: (string describing the set of expected tokens)
|
||||
recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error)
|
||||
}
|
||||
*/
|
||||
var pie = (function(){
|
||||
var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[1,4],$V1=[1,5],$V2=[1,6],$V3=[1,7],$V4=[1,9],$V5=[1,11,13,15,22,23,24,25],$V6=[2,5],$V7=[1,6,11,13,15,22,23,24,25],$V8=[22,23,24],$V9=[2,8],$Va=[1,18],$Vb=[1,19],$Vc=[1,20],$Vd=[1,25],$Ve=[6,22,23,24,25];
|
||||
var parser = {trace: function trace () { },
|
||||
yy: {},
|
||||
symbols_: {"error":2,"start":3,"eol":4,"directive":5,"PIE":6,"document":7,"showData":8,"line":9,"statement":10,"txt":11,"value":12,"title":13,"title_value":14,"accDescription":15,"description_value":16,"openDirective":17,"typeDirective":18,"closeDirective":19,":":20,"argDirective":21,"NEWLINE":22,";":23,"EOF":24,"open_directive":25,"type_directive":26,"arg_directive":27,"close_directive":28,"$accept":0,"$end":1},
|
||||
terminals_: {2:"error",6:"PIE",8:"showData",11:"txt",12:"value",13:"title",14:"title_value",15:"accDescription",16:"description_value",20:":",22:"NEWLINE",23:";",24:"EOF",25:"open_directive",26:"type_directive",27:"arg_directive",28:"close_directive"},
|
||||
productions_: [0,[3,2],[3,2],[3,2],[3,3],[7,0],[7,2],[9,2],[10,0],[10,2],[10,2],[10,2],[10,1],[5,3],[5,5],[4,1],[4,1],[4,1],[17,1],[18,1],[21,1],[19,1]],
|
||||
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
|
||||
/* this == yyval */
|
||||
|
||||
var $0 = $$.length - 1;
|
||||
switch (yystate) {
|
||||
case 4:
|
||||
yy.setShowData(true);
|
||||
break;
|
||||
case 7:
|
||||
this.$ = $$[$0-1]
|
||||
break;
|
||||
case 9:
|
||||
yy.addSection($$[$0-1],yy.cleanupValue($$[$0]));
|
||||
break;
|
||||
case 10:
|
||||
this.$=$$[$0].trim();yy.setTitle(this.$);
|
||||
break;
|
||||
case 11:
|
||||
this.$=$$[$0].trim();yy.setAccDescription(this.$);
|
||||
break;
|
||||
case 18:
|
||||
yy.parseDirective('%%{', 'open_directive');
|
||||
break;
|
||||
case 19:
|
||||
yy.parseDirective($$[$0], 'type_directive');
|
||||
break;
|
||||
case 20:
|
||||
$$[$0] = $$[$0].trim().replace(/'/g, '"'); yy.parseDirective($$[$0], 'arg_directive');
|
||||
break;
|
||||
case 21:
|
||||
yy.parseDirective('}%%', 'close_directive', 'pie');
|
||||
break;
|
||||
}
|
||||
},
|
||||
table: [{3:1,4:2,5:3,6:$V0,17:8,22:$V1,23:$V2,24:$V3,25:$V4},{1:[3]},{3:10,4:2,5:3,6:$V0,17:8,22:$V1,23:$V2,24:$V3,25:$V4},{3:11,4:2,5:3,6:$V0,17:8,22:$V1,23:$V2,24:$V3,25:$V4},o($V5,$V6,{7:12,8:[1,13]}),o($V7,[2,15]),o($V7,[2,16]),o($V7,[2,17]),{18:14,26:[1,15]},{26:[2,18]},{1:[2,1]},{1:[2,2]},o($V8,$V9,{17:8,9:16,10:17,5:21,1:[2,3],11:$Va,13:$Vb,15:$Vc,25:$V4}),o($V5,$V6,{7:22}),{19:23,20:[1,24],28:$Vd},o([20,28],[2,19]),o($V5,[2,6]),{4:26,22:$V1,23:$V2,24:$V3},{12:[1,27]},{14:[1,28]},{16:[1,29]},o($V8,[2,12]),o($V8,$V9,{17:8,9:16,10:17,5:21,1:[2,4],11:$Va,13:$Vb,15:$Vc,25:$V4}),o($Ve,[2,13]),{21:30,27:[1,31]},o($Ve,[2,21]),o($V5,[2,7]),o($V8,[2,9]),o($V8,[2,10]),o($V8,[2,11]),{19:32,28:$Vd},{28:[2,20]},o($Ve,[2,14])],
|
||||
defaultActions: {9:[2,18],10:[2,1],11:[2,2],31:[2,20]},
|
||||
parseError: function parseError (str, hash) {
|
||||
if (hash.recoverable) {
|
||||
this.trace(str);
|
||||
} else {
|
||||
var error = new Error(str);
|
||||
error.hash = hash;
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
parse: function parse(input) {
|
||||
var self = this, stack = [0], tstack = [], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
|
||||
var args = lstack.slice.call(arguments, 1);
|
||||
var lexer = Object.create(this.lexer);
|
||||
var sharedState = { yy: {} };
|
||||
for (var k in this.yy) {
|
||||
if (Object.prototype.hasOwnProperty.call(this.yy, k)) {
|
||||
sharedState.yy[k] = this.yy[k];
|
||||
}
|
||||
}
|
||||
lexer.setInput(input, sharedState.yy);
|
||||
sharedState.yy.lexer = lexer;
|
||||
sharedState.yy.parser = this;
|
||||
if (typeof lexer.yylloc == 'undefined') {
|
||||
lexer.yylloc = {};
|
||||
}
|
||||
var yyloc = lexer.yylloc;
|
||||
lstack.push(yyloc);
|
||||
var ranges = lexer.options && lexer.options.ranges;
|
||||
if (typeof sharedState.yy.parseError === 'function') {
|
||||
this.parseError = sharedState.yy.parseError;
|
||||
} else {
|
||||
this.parseError = Object.getPrototypeOf(this).parseError;
|
||||
}
|
||||
function popStack(n) {
|
||||
stack.length = stack.length - 2 * n;
|
||||
vstack.length = vstack.length - n;
|
||||
lstack.length = lstack.length - n;
|
||||
}
|
||||
_token_stack:
|
||||
var lex = function () {
|
||||
var token;
|
||||
token = lexer.lex() || EOF;
|
||||
if (typeof token !== 'number') {
|
||||
token = self.symbols_[token] || token;
|
||||
}
|
||||
return token;
|
||||
};
|
||||
var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
|
||||
while (true) {
|
||||
state = stack[stack.length - 1];
|
||||
if (this.defaultActions[state]) {
|
||||
action = this.defaultActions[state];
|
||||
} else {
|
||||
if (symbol === null || typeof symbol == 'undefined') {
|
||||
symbol = lex();
|
||||
}
|
||||
action = table[state] && table[state][symbol];
|
||||
}
|
||||
if (typeof action === 'undefined' || !action.length || !action[0]) {
|
||||
var errStr = '';
|
||||
expected = [];
|
||||
for (p in table[state]) {
|
||||
if (this.terminals_[p] && p > TERROR) {
|
||||
expected.push('\'' + this.terminals_[p] + '\'');
|
||||
}
|
||||
}
|
||||
if (lexer.showPosition) {
|
||||
errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\'';
|
||||
} else {
|
||||
errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'');
|
||||
}
|
||||
this.parseError(errStr, {
|
||||
text: lexer.match,
|
||||
token: this.terminals_[symbol] || symbol,
|
||||
line: lexer.yylineno,
|
||||
loc: yyloc,
|
||||
expected: expected
|
||||
});
|
||||
}
|
||||
if (action[0] instanceof Array && action.length > 1) {
|
||||
throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
|
||||
}
|
||||
switch (action[0]) {
|
||||
case 1:
|
||||
stack.push(symbol);
|
||||
vstack.push(lexer.yytext);
|
||||
lstack.push(lexer.yylloc);
|
||||
stack.push(action[1]);
|
||||
symbol = null;
|
||||
if (!preErrorSymbol) {
|
||||
yyleng = lexer.yyleng;
|
||||
yytext = lexer.yytext;
|
||||
yylineno = lexer.yylineno;
|
||||
yyloc = lexer.yylloc;
|
||||
if (recovering > 0) {
|
||||
recovering--;
|
||||
}
|
||||
} else {
|
||||
symbol = preErrorSymbol;
|
||||
preErrorSymbol = null;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
len = this.productions_[action[1]][1];
|
||||
yyval.$ = vstack[vstack.length - len];
|
||||
yyval._$ = {
|
||||
first_line: lstack[lstack.length - (len || 1)].first_line,
|
||||
last_line: lstack[lstack.length - 1].last_line,
|
||||
first_column: lstack[lstack.length - (len || 1)].first_column,
|
||||
last_column: lstack[lstack.length - 1].last_column
|
||||
};
|
||||
if (ranges) {
|
||||
yyval._$.range = [
|
||||
lstack[lstack.length - (len || 1)].range[0],
|
||||
lstack[lstack.length - 1].range[1]
|
||||
];
|
||||
}
|
||||
r = this.performAction.apply(yyval, [
|
||||
yytext,
|
||||
yyleng,
|
||||
yylineno,
|
||||
sharedState.yy,
|
||||
action[1],
|
||||
vstack,
|
||||
lstack
|
||||
].concat(args));
|
||||
if (typeof r !== 'undefined') {
|
||||
return r;
|
||||
}
|
||||
if (len) {
|
||||
stack = stack.slice(0, -1 * len * 2);
|
||||
vstack = vstack.slice(0, -1 * len);
|
||||
lstack = lstack.slice(0, -1 * len);
|
||||
}
|
||||
stack.push(this.productions_[action[1]][0]);
|
||||
vstack.push(yyval.$);
|
||||
lstack.push(yyval._$);
|
||||
newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
|
||||
stack.push(newState);
|
||||
break;
|
||||
case 3:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}};
|
||||
|
||||
/* generated by jison-lex 0.3.4 */
|
||||
var lexer = (function(){
|
||||
var lexer = ({
|
||||
|
||||
EOF:1,
|
||||
|
||||
parseError:function parseError(str, hash) {
|
||||
if (this.yy.parser) {
|
||||
this.yy.parser.parseError(str, hash);
|
||||
} else {
|
||||
throw new Error(str);
|
||||
}
|
||||
},
|
||||
|
||||
// resets the lexer, sets new input
|
||||
setInput:function (input, yy) {
|
||||
this.yy = yy || this.yy || {};
|
||||
this._input = input;
|
||||
this._more = this._backtrack = this.done = false;
|
||||
this.yylineno = this.yyleng = 0;
|
||||
this.yytext = this.matched = this.match = '';
|
||||
this.conditionStack = ['INITIAL'];
|
||||
this.yylloc = {
|
||||
first_line: 1,
|
||||
first_column: 0,
|
||||
last_line: 1,
|
||||
last_column: 0
|
||||
};
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range = [0,0];
|
||||
}
|
||||
this.offset = 0;
|
||||
return this;
|
||||
},
|
||||
|
||||
// consumes and returns one char from the input
|
||||
input:function () {
|
||||
var ch = this._input[0];
|
||||
this.yytext += ch;
|
||||
this.yyleng++;
|
||||
this.offset++;
|
||||
this.match += ch;
|
||||
this.matched += ch;
|
||||
var lines = ch.match(/(?:\r\n?|\n).*/g);
|
||||
if (lines) {
|
||||
this.yylineno++;
|
||||
this.yylloc.last_line++;
|
||||
} else {
|
||||
this.yylloc.last_column++;
|
||||
}
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range[1]++;
|
||||
}
|
||||
|
||||
this._input = this._input.slice(1);
|
||||
return ch;
|
||||
},
|
||||
|
||||
// unshifts one char (or a string) into the input
|
||||
unput:function (ch) {
|
||||
var len = ch.length;
|
||||
var lines = ch.split(/(?:\r\n?|\n)/g);
|
||||
|
||||
this._input = ch + this._input;
|
||||
this.yytext = this.yytext.substr(0, this.yytext.length - len);
|
||||
//this.yyleng -= len;
|
||||
this.offset -= len;
|
||||
var oldLines = this.match.split(/(?:\r\n?|\n)/g);
|
||||
this.match = this.match.substr(0, this.match.length - 1);
|
||||
this.matched = this.matched.substr(0, this.matched.length - 1);
|
||||
|
||||
if (lines.length - 1) {
|
||||
this.yylineno -= lines.length - 1;
|
||||
}
|
||||
var r = this.yylloc.range;
|
||||
|
||||
this.yylloc = {
|
||||
first_line: this.yylloc.first_line,
|
||||
last_line: this.yylineno + 1,
|
||||
first_column: this.yylloc.first_column,
|
||||
last_column: lines ?
|
||||
(lines.length === oldLines.length ? this.yylloc.first_column : 0)
|
||||
+ oldLines[oldLines.length - lines.length].length - lines[0].length :
|
||||
this.yylloc.first_column - len
|
||||
};
|
||||
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range = [r[0], r[0] + this.yyleng - len];
|
||||
}
|
||||
this.yyleng = this.yytext.length;
|
||||
return this;
|
||||
},
|
||||
|
||||
// When called from action, caches matched text and appends it on next action
|
||||
more:function () {
|
||||
this._more = true;
|
||||
return this;
|
||||
},
|
||||
|
||||
// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
|
||||
reject:function () {
|
||||
if (this.options.backtrack_lexer) {
|
||||
this._backtrack = true;
|
||||
} else {
|
||||
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), {
|
||||
text: "",
|
||||
token: null,
|
||||
line: this.yylineno
|
||||
});
|
||||
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
// retain first n characters of the match
|
||||
less:function (n) {
|
||||
this.unput(this.match.slice(n));
|
||||
},
|
||||
|
||||
// displays already matched input, i.e. for error messages
|
||||
pastInput:function () {
|
||||
var past = this.matched.substr(0, this.matched.length - this.match.length);
|
||||
return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
|
||||
},
|
||||
|
||||
// displays upcoming input, i.e. for error messages
|
||||
upcomingInput:function () {
|
||||
var next = this.match;
|
||||
if (next.length < 20) {
|
||||
next += this._input.substr(0, 20-next.length);
|
||||
}
|
||||
return (next.substr(0,20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
|
||||
},
|
||||
|
||||
// displays the character position where the lexing error occurred, i.e. for error messages
|
||||
showPosition:function () {
|
||||
var pre = this.pastInput();
|
||||
var c = new Array(pre.length + 1).join("-");
|
||||
return pre + this.upcomingInput() + "\n" + c + "^";
|
||||
},
|
||||
|
||||
// test the lexed token: return FALSE when not a match, otherwise return token
|
||||
test_match:function(match, indexed_rule) {
|
||||
var token,
|
||||
lines,
|
||||
backup;
|
||||
|
||||
if (this.options.backtrack_lexer) {
|
||||
// save context
|
||||
backup = {
|
||||
yylineno: this.yylineno,
|
||||
yylloc: {
|
||||
first_line: this.yylloc.first_line,
|
||||
last_line: this.last_line,
|
||||
first_column: this.yylloc.first_column,
|
||||
last_column: this.yylloc.last_column
|
||||
},
|
||||
yytext: this.yytext,
|
||||
match: this.match,
|
||||
matches: this.matches,
|
||||
matched: this.matched,
|
||||
yyleng: this.yyleng,
|
||||
offset: this.offset,
|
||||
_more: this._more,
|
||||
_input: this._input,
|
||||
yy: this.yy,
|
||||
conditionStack: this.conditionStack.slice(0),
|
||||
done: this.done
|
||||
};
|
||||
if (this.options.ranges) {
|
||||
backup.yylloc.range = this.yylloc.range.slice(0);
|
||||
}
|
||||
}
|
||||
|
||||
lines = match[0].match(/(?:\r\n?|\n).*/g);
|
||||
if (lines) {
|
||||
this.yylineno += lines.length;
|
||||
}
|
||||
this.yylloc = {
|
||||
first_line: this.yylloc.last_line,
|
||||
last_line: this.yylineno + 1,
|
||||
first_column: this.yylloc.last_column,
|
||||
last_column: lines ?
|
||||
lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length :
|
||||
this.yylloc.last_column + match[0].length
|
||||
};
|
||||
this.yytext += match[0];
|
||||
this.match += match[0];
|
||||
this.matches = match;
|
||||
this.yyleng = this.yytext.length;
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range = [this.offset, this.offset += this.yyleng];
|
||||
}
|
||||
this._more = false;
|
||||
this._backtrack = false;
|
||||
this._input = this._input.slice(match[0].length);
|
||||
this.matched += match[0];
|
||||
token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]);
|
||||
if (this.done && this._input) {
|
||||
this.done = false;
|
||||
}
|
||||
if (token) {
|
||||
return token;
|
||||
} else if (this._backtrack) {
|
||||
// recover context
|
||||
for (var k in backup) {
|
||||
this[k] = backup[k];
|
||||
}
|
||||
return false; // rule action called reject() implying the next rule should be tested instead.
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// return next match in input
|
||||
next:function () {
|
||||
if (this.done) {
|
||||
return this.EOF;
|
||||
}
|
||||
if (!this._input) {
|
||||
this.done = true;
|
||||
}
|
||||
|
||||
var token,
|
||||
match,
|
||||
tempMatch,
|
||||
index;
|
||||
if (!this._more) {
|
||||
this.yytext = '';
|
||||
this.match = '';
|
||||
}
|
||||
var rules = this._currentRules();
|
||||
for (var i = 0; i < rules.length; i++) {
|
||||
tempMatch = this._input.match(this.rules[rules[i]]);
|
||||
if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
|
||||
match = tempMatch;
|
||||
index = i;
|
||||
if (this.options.backtrack_lexer) {
|
||||
token = this.test_match(tempMatch, rules[i]);
|
||||
if (token !== false) {
|
||||
return token;
|
||||
} else if (this._backtrack) {
|
||||
match = false;
|
||||
continue; // rule action called reject() implying a rule MISmatch.
|
||||
} else {
|
||||
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
|
||||
return false;
|
||||
}
|
||||
} else if (!this.options.flex) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
token = this.test_match(match, rules[index]);
|
||||
if (token !== false) {
|
||||
return token;
|
||||
}
|
||||
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
|
||||
return false;
|
||||
}
|
||||
if (this._input === "") {
|
||||
return this.EOF;
|
||||
} else {
|
||||
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
|
||||
text: "",
|
||||
token: null,
|
||||
line: this.yylineno
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// return next match that has a token
|
||||
lex:function lex () {
|
||||
var r = this.next();
|
||||
if (r) {
|
||||
return r;
|
||||
} else {
|
||||
return this.lex();
|
||||
}
|
||||
},
|
||||
|
||||
// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
|
||||
begin:function begin (condition) {
|
||||
this.conditionStack.push(condition);
|
||||
},
|
||||
|
||||
// pop the previously active lexer condition state off the condition stack
|
||||
popState:function popState () {
|
||||
var n = this.conditionStack.length - 1;
|
||||
if (n > 0) {
|
||||
return this.conditionStack.pop();
|
||||
} else {
|
||||
return this.conditionStack[0];
|
||||
}
|
||||
},
|
||||
|
||||
// produce the lexer rule set which is active for the currently active lexer condition state
|
||||
_currentRules:function _currentRules () {
|
||||
if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
|
||||
return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
|
||||
} else {
|
||||
return this.conditions["INITIAL"].rules;
|
||||
}
|
||||
},
|
||||
|
||||
// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
|
||||
topState:function topState (n) {
|
||||
n = this.conditionStack.length - 1 - Math.abs(n || 0);
|
||||
if (n >= 0) {
|
||||
return this.conditionStack[n];
|
||||
} else {
|
||||
return "INITIAL";
|
||||
}
|
||||
},
|
||||
|
||||
// alias for begin(condition)
|
||||
pushState:function pushState (condition) {
|
||||
this.begin(condition);
|
||||
},
|
||||
|
||||
// return the number of states currently on the stack
|
||||
stateStackSize:function stateStackSize() {
|
||||
return this.conditionStack.length;
|
||||
},
|
||||
options: {"case-insensitive":true},
|
||||
performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
|
||||
var YYSTATE=YY_START;
|
||||
switch($avoiding_name_collisions) {
|
||||
case 0: this.begin('open_directive'); return 25;
|
||||
break;
|
||||
case 1: this.begin('type_directive'); return 26;
|
||||
break;
|
||||
case 2: this.popState(); this.begin('arg_directive'); return 20;
|
||||
break;
|
||||
case 3: this.popState(); this.popState(); return 28;
|
||||
break;
|
||||
case 4:return 27;
|
||||
break;
|
||||
case 5:/* skip comments */
|
||||
break;
|
||||
case 6:/* skip comments */{ /*console.log('');*/ }
|
||||
break;
|
||||
case 7:return 22;
|
||||
break;
|
||||
case 8:/* do nothing */
|
||||
break;
|
||||
case 9:/* ignore */
|
||||
break;
|
||||
case 10: this.begin("title");return 13;
|
||||
break;
|
||||
case 11: this.popState(); return "title_value";
|
||||
break;
|
||||
case 12: this.begin("acc_title");return 'acc_title';
|
||||
break;
|
||||
case 13: this.popState(); return "acc_title_value";
|
||||
break;
|
||||
case 14: this.begin("acc_descr");return 'acc_descr';
|
||||
break;
|
||||
case 15: this.popState(); return "acc_descr_value";
|
||||
break;
|
||||
case 16: this.begin("acc_descr_multiline");
|
||||
break;
|
||||
case 17: this.popState();
|
||||
break;
|
||||
case 18:return "acc_descr_multiline_value";
|
||||
break;
|
||||
case 19: this.begin("string");
|
||||
break;
|
||||
case 20: this.popState();
|
||||
break;
|
||||
case 21: return "txt";
|
||||
break;
|
||||
case 22:return 6;
|
||||
break;
|
||||
case 23:return 8;
|
||||
break;
|
||||
case 24:return "value";
|
||||
break;
|
||||
case 25:return 24;
|
||||
break;
|
||||
}
|
||||
},
|
||||
rules: [/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n\r]+)/i,/^(?:%%[^\n]*)/i,/^(?:[\s]+)/i,/^(?:title\b)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:[\}])/i,/^(?:[^\}]*)/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:pie\b)/i,/^(?:showData\b)/i,/^(?::[\s]*[\d]+(?:\.[\d]+)?)/i,/^(?:$)/i],
|
||||
conditions: {"acc_descr_multiline":{"rules":[17,18],"inclusive":false},"acc_descr":{"rules":[15],"inclusive":false},"acc_title":{"rules":[13],"inclusive":false},"close_directive":{"rules":[],"inclusive":false},"arg_directive":{"rules":[3,4],"inclusive":false},"type_directive":{"rules":[2,3],"inclusive":false},"open_directive":{"rules":[1],"inclusive":false},"title":{"rules":[11],"inclusive":false},"string":{"rules":[20,21],"inclusive":false},"INITIAL":{"rules":[0,5,6,7,8,9,10,12,14,16,19,22,23,24,25],"inclusive":true}}
|
||||
});
|
||||
return lexer;
|
||||
})();
|
||||
parser.lexer = lexer;
|
||||
function Parser () {
|
||||
this.yy = {};
|
||||
}
|
||||
Parser.prototype = parser;parser.Parser = Parser;
|
||||
return new Parser;
|
||||
})();
|
||||
|
||||
|
||||
if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
|
||||
exports.parser = pie;
|
||||
exports.Parser = pie.Parser;
|
||||
exports.parse = function () { return pie.parse.apply(pie, arguments); };
|
||||
exports.main = function commonjsMain (args) {
|
||||
if (!args[1]) {
|
||||
console.log('Usage: '+args[0]+' FILE');
|
||||
process.exit(1);
|
||||
}
|
||||
var source = require('fs').readFileSync(require('path').normalize(args[1]), "utf8");
|
||||
return exports.parser.parse(source);
|
||||
};
|
||||
if (typeof module !== 'undefined' && require.main === module) {
|
||||
exports.main(process.argv.slice(1));
|
||||
}
|
||||
}
|
@@ -1,3 +1,6 @@
|
||||
import c4Db from './diagrams/c4/c4Db';
|
||||
import c4Renderer from './diagrams/c4/c4Renderer';
|
||||
import c4Parser from './diagrams/c4/parser/c4Diagram';
|
||||
import classDb from './diagrams/class/classDb';
|
||||
import classRenderer from './diagrams/class/classRenderer';
|
||||
import classRendererV2 from './diagrams/class/classRenderer-v2';
|
||||
@@ -49,6 +52,12 @@ class Diagram {
|
||||
this.type = utils.detectType(txt, cnf);
|
||||
log.debug('Type ' + this.type);
|
||||
switch (this.type) {
|
||||
case 'c4':
|
||||
this.parser = c4Parser;
|
||||
this.parser.parser.yy = c4Db;
|
||||
this.db = c4Db;
|
||||
this.renderer = c4Renderer;
|
||||
break;
|
||||
case 'gitGraph':
|
||||
this.parser = gitGraphParser;
|
||||
this.parser.parser.yy = gitGraphAst;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* This method will add a basic title and description element to a chart. The yy parser will need to
|
||||
* respond to getTitle and getAccDescription, where the title is the title element on the chart,
|
||||
* respond to getAccTitle and getAccDescription, where the title is the title element on the chart,
|
||||
* which is generally not displayed and the accDescription is the description element on the chart,
|
||||
* which is never displayed.
|
||||
*
|
||||
@@ -15,7 +15,7 @@ export default function addSVGAccessibilityFields(yy_parser, svg, id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let title_string = yy_parser.getTitle();
|
||||
let title_string = yy_parser.getAccTitle();
|
||||
let description = yy_parser.getAccDescription();
|
||||
svg.attr('role', 'img').attr('aria-labelledby', 'chart-title-' + id + ' chart-desc-' + id);
|
||||
svg
|
||||
|
@@ -1,20 +1,22 @@
|
||||
import { sanitizeText as _sanitizeText } from './diagrams/common/common';
|
||||
import { getConfig } from './config';
|
||||
let title = '';
|
||||
let diagramTitle = '';
|
||||
let description = '';
|
||||
const sanitizeText = (txt) => _sanitizeText(txt, getConfig());
|
||||
|
||||
export const clear = function () {
|
||||
title = '';
|
||||
description = '';
|
||||
diagramTitle = '';
|
||||
};
|
||||
|
||||
export const setTitle = function (txt) {
|
||||
export const setAccTitle = function (txt) {
|
||||
title = sanitizeText(txt).replace(/^\s+/g, '');
|
||||
};
|
||||
|
||||
export const getTitle = function () {
|
||||
return title;
|
||||
export const getAccTitle = function () {
|
||||
return title || diagramTitle;
|
||||
};
|
||||
|
||||
export const setAccDescription = function (txt) {
|
||||
@@ -25,9 +27,19 @@ export const getAccDescription = function () {
|
||||
return description;
|
||||
};
|
||||
|
||||
export const setDiagramTitle = function (txt) {
|
||||
diagramTitle = sanitizeText(txt);
|
||||
};
|
||||
|
||||
export const getDiagramTitle = function () {
|
||||
return diagramTitle;
|
||||
};
|
||||
|
||||
export default {
|
||||
setTitle,
|
||||
getTitle,
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
setDiagramTitle,
|
||||
getDiagramTitle: getDiagramTitle,
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
clear,
|
||||
|
@@ -209,9 +209,9 @@ export const addDirective = (directive) => {
|
||||
*
|
||||
* ## conf
|
||||
*
|
||||
* | Parameter | Description | Type | Required | Values |
|
||||
* | --------- | ------------------------------------------------------------- | ---------- | -------- | -------------------------------------------- |
|
||||
* | conf | base set of values, which currentConfig coul be **reset** to. | Dictionary | Required | Any Values, with respect to the secure Array |
|
||||
* | Parameter | Description | Type | Required | Values |
|
||||
* | --------- | -------------------------------------------------------------- | ---------- | -------- | -------------------------------------------- |
|
||||
* | conf | base set of values, which currentConfig could be **reset** to. | Dictionary | Required | Any Values, with respect to the secure Array |
|
||||
*
|
||||
* **Notes**: (default: current siteConfig ) (optional, default `getSiteConfig()`)
|
||||
*/
|
||||
|
@@ -40,7 +40,7 @@ const rect = (parent, node) => {
|
||||
|
||||
const width = node.width <= bbox.width + padding ? bbox.width + padding : node.width;
|
||||
if (node.width <= bbox.width + padding) {
|
||||
node.diff = (bbox.width - node.width) / 2;
|
||||
node.diff = (bbox.width - node.width) / 2 - node.padding / 2;
|
||||
} else {
|
||||
node.diff = -node.padding / 2;
|
||||
}
|
||||
|
@@ -192,19 +192,6 @@ export const positionEdgeLabel = (edge, paths) => {
|
||||
}
|
||||
};
|
||||
|
||||
// const getRelationType = function(type) {
|
||||
// switch (type) {
|
||||
// case stateDb.relationType.AGGREGATION:
|
||||
// return 'aggregation';
|
||||
// case stateDb.relationType.EXTENSION:
|
||||
// return 'extension';
|
||||
// case stateDb.relationType.COMPOSITION:
|
||||
// return 'composition';
|
||||
// case stateDb.relationType.DEPENDENCY:
|
||||
// return 'dependency';
|
||||
// }
|
||||
// };
|
||||
|
||||
const outsideNode = (node, point) => {
|
||||
// log.warn('Checking bounds ', node, point);
|
||||
const x = node.x;
|
||||
@@ -415,45 +402,7 @@ export const insertEdge = function (elem, e, edge, clusterDb, diagramType, graph
|
||||
if (edge.fromCluster) {
|
||||
log.info('from cluster abc88', clusterDb[edge.fromCluster]);
|
||||
points = cutPathAtIntersect(points.reverse(), clusterDb[edge.fromCluster].node).reverse();
|
||||
// log.warn('edge', edge);
|
||||
// log.warn('from cluster', clusterDb[edge.fromCluster], points);
|
||||
// const updatedPoints = [];
|
||||
// let lastPointOutside = edge.points[edge.points.length - 1];
|
||||
// let isInside = false;
|
||||
// for (let i = points.length - 1; i >= 0; i--) {
|
||||
// const point = points[i];
|
||||
// const node = clusterDb[edge.fromCluster].node;
|
||||
// log.warn('checking to', edge.fromCluster, point, node);
|
||||
|
||||
// if (!outsideNode(node, point) && !isInside) {
|
||||
// log.warn('inside', edge.fromCluster, point, node);
|
||||
|
||||
// // First point inside the rect
|
||||
// const inter = intersection(node, lastPointOutside, point);
|
||||
// log.warn('intersect', intersection(node, lastPointOutside, point));
|
||||
// let pointPresent = false;
|
||||
// points.forEach(p => {
|
||||
// pointPresent = pointPresent || (p.x === inter.x && p.y === inter.y);
|
||||
// });
|
||||
// // if (!pointPresent) {
|
||||
// if (!points.find(e => e.x === inter.x && e.y === inter.y)) {
|
||||
// updatedPoints.unshift(inter);
|
||||
// log.warn('Adding point -updated = ', updatedPoints);
|
||||
// } else {
|
||||
// log.warn('no intersect', inter, points);
|
||||
// }
|
||||
// // points.push(insterection);
|
||||
// isInside = true;
|
||||
// } else {
|
||||
// // at the outside
|
||||
// // if (!isInside) updatedPoints.unshift(point);
|
||||
// updatedPoints.unshift(point);
|
||||
// log.warn('Outside point', point, updatedPoints);
|
||||
// }
|
||||
// lastPointOutside = point;
|
||||
// }
|
||||
// points = updatedPoints;
|
||||
// points = edge.points;
|
||||
pointsHasChanged = true;
|
||||
}
|
||||
|
||||
|
@@ -5,7 +5,7 @@
|
||||
* @param point
|
||||
*/
|
||||
function intersectEllipse(node, rx, ry, point) {
|
||||
// Formulae from: http://mathworld.wolfram.com/Ellipse-LineIntersection.html
|
||||
// Formulae from: https://mathworld.wolfram.com/Ellipse-LineIntersection.html
|
||||
|
||||
var cx = node.x;
|
||||
var cy = node.y;
|
||||
|
@@ -3,7 +3,7 @@ const intersectRect = (node, point) => {
|
||||
var y = node.y;
|
||||
|
||||
// Rectangle intersection algorithm from:
|
||||
// http://math.stackexchange.com/questions/108113/find-edge-between-two-boxes
|
||||
// https://math.stackexchange.com/questions/108113/find-edge-between-two-boxes
|
||||
var dx = point.x - x;
|
||||
var dy = point.y - y;
|
||||
var w = node.width / 2;
|
||||
|
@@ -277,7 +277,33 @@ export const adjustClustersAndEdges = (graph, depth) => {
|
||||
' --- ',
|
||||
clusterDb[e.w]
|
||||
);
|
||||
if (clusterDb[e.v] || clusterDb[e.w]) {
|
||||
if (clusterDb[e.v] && clusterDb[e.w] && clusterDb[e.v] === clusterDb[e.w]) {
|
||||
log.warn('Fixing and trixing link to self - removing XXX', e.v, e.w, e.name);
|
||||
log.warn('Fixing and trixing - removing XXX', e.v, e.w, e.name);
|
||||
v = getAnchorId(e.v);
|
||||
w = getAnchorId(e.w);
|
||||
graph.removeEdge(e.v, e.w, e.name);
|
||||
const specialId = e.w + '---' + e.v;
|
||||
graph.setNode(specialId, {
|
||||
domId: specialId,
|
||||
id: specialId,
|
||||
labelStyle: '',
|
||||
labelText: edge.label,
|
||||
padding: 0,
|
||||
shape: 'labelRect',
|
||||
style: '',
|
||||
});
|
||||
const edge1 = JSON.parse(JSON.stringify(edge));
|
||||
const edge2 = JSON.parse(JSON.stringify(edge));
|
||||
edge1.label = '';
|
||||
edge1.arrowTypeEnd = 'none';
|
||||
edge2.label = '';
|
||||
edge1.fromCluster = e.v;
|
||||
edge2.toCluster = e.v;
|
||||
|
||||
graph.setEdge(v, specialId, edge1, e.name + '-cyclic-special');
|
||||
graph.setEdge(specialId, w, edge2, e.name + '-cyclic-special');
|
||||
} else if (clusterDb[e.v] || clusterDb[e.w]) {
|
||||
log.warn('Fixing and trixing - removing XXX', e.v, e.w, e.name);
|
||||
v = getAnchorId(e.v);
|
||||
w = getAnchorId(e.w);
|
||||
|
@@ -347,6 +347,39 @@ const rect = (parent, node) => {
|
||||
return shapeSvg;
|
||||
};
|
||||
|
||||
const labelRect = (parent, node) => {
|
||||
const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, 'label', true);
|
||||
|
||||
log.trace('Classes = ', node.classes);
|
||||
// add the rect
|
||||
const rect = shapeSvg.insert('rect', ':first-child');
|
||||
|
||||
// Hide the rect we are only after the label
|
||||
const totalWidth = 0;
|
||||
const totalHeight = 0;
|
||||
rect.attr('width', totalWidth).attr('height', totalHeight);
|
||||
shapeSvg.attr('class', 'label edgeLabel');
|
||||
|
||||
if (node.props) {
|
||||
const propKeys = new Set(Object.keys(node.props));
|
||||
if (node.props.borders) {
|
||||
applyNodePropertyBorders(rect, node.props.borders, totalWidth, totalHeight);
|
||||
propKeys.delete('borders');
|
||||
}
|
||||
propKeys.forEach((propKey) => {
|
||||
log.warn(`Unknown node property ${propKey}`);
|
||||
});
|
||||
}
|
||||
|
||||
updateNodeBounds(node, rect);
|
||||
|
||||
node.intersect = function (point) {
|
||||
return intersect.rect(node, point);
|
||||
};
|
||||
|
||||
return shapeSvg;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param rect
|
||||
* @param borders
|
||||
@@ -976,6 +1009,7 @@ const class_box = (parent, node) => {
|
||||
const shapes = {
|
||||
question,
|
||||
rect,
|
||||
labelRect,
|
||||
rectWithTitle,
|
||||
choice,
|
||||
circle,
|
||||
|
@@ -942,13 +942,13 @@ const config = {
|
||||
minEntityHeight: 75,
|
||||
|
||||
/**
|
||||
* | Parameter | Description | Type | Required | Values |
|
||||
* | ------------- | ----------------------------------------------------------- | ------- | -------- | ------------------ |
|
||||
* | entityPadding | Minimum internal padding betweentext in box and box borders | Integer | 4 | Any Positive Value |
|
||||
* | Parameter | Description | Type | Required | Values |
|
||||
* | ------------- | ------------------------------------------------------------ | ------- | -------- | ------------------ |
|
||||
* | entityPadding | Minimum internal padding between text in box and box borders | Integer | 4 | Any Positive Value |
|
||||
*
|
||||
* **Notes:**
|
||||
*
|
||||
* The minimum internal padding betweentext in an entity box and the enclosing box borders,
|
||||
* The minimum internal padding between text in an entity box and the enclosing box borders,
|
||||
* expressed in pixels.
|
||||
*
|
||||
* Default value: 15
|
||||
@@ -1058,6 +1058,406 @@ const config = {
|
||||
mainBranchOrder: 0,
|
||||
showCommitLabel: true,
|
||||
showBranches: true,
|
||||
rotateCommitLabel: true,
|
||||
},
|
||||
|
||||
/** The object containing configurations specific for c4 diagrams */
|
||||
c4: {
|
||||
useWidth: undefined,
|
||||
|
||||
/**
|
||||
* | Parameter | Description | Type | Required | Values |
|
||||
* | -------------- | ---------------------------------------------- | ------- | -------- | ------------------ |
|
||||
* | diagramMarginX | Margin to the right and left of the c4 diagram | Integer | Required | Any Positive Value |
|
||||
*
|
||||
* **Notes:** Default value: 50
|
||||
*/
|
||||
diagramMarginX: 50,
|
||||
|
||||
/**
|
||||
* | Parameter | Description | Type | Required | Values |
|
||||
* | -------------- | ------------------------------------------- | ------- | -------- | ------------------ |
|
||||
* | diagramMarginY | Margin to the over and under the c4 diagram | Integer | Required | Any Positive Value |
|
||||
*
|
||||
* **Notes:** Default value: 10
|
||||
*/
|
||||
diagramMarginY: 10,
|
||||
|
||||
/**
|
||||
* | Parameter | Description | Type | Required | Values |
|
||||
* | ----------- | --------------------- | ------- | -------- | ------------------ |
|
||||
* | shapeMargin | Margin between shapes | Integer | Required | Any Positive Value |
|
||||
*
|
||||
* **Notes:** Default value: 50
|
||||
*/
|
||||
c4ShapeMargin: 50,
|
||||
|
||||
c4ShapePadding: 20,
|
||||
|
||||
/**
|
||||
* | Parameter | Description | Type | Required | Values |
|
||||
* | --------- | --------------------- | ------- | -------- | ------------------ |
|
||||
* | width | Width of person boxes | Integer | Required | Any Positive Value |
|
||||
*
|
||||
* **Notes:** Default value: 216
|
||||
*/
|
||||
width: 216,
|
||||
|
||||
/**
|
||||
* | Parameter | Description | Type | Required | Values |
|
||||
* | --------- | ---------------------- | ------- | -------- | ------------------ |
|
||||
* | height | Height of person boxes | Integer | Required | Any Positive Value |
|
||||
*
|
||||
* **Notes:** Default value: 60
|
||||
*/
|
||||
height: 60,
|
||||
|
||||
/**
|
||||
* | Parameter | Description | Type | Required | Values |
|
||||
* | --------- | ------------------------ | ------- | -------- | ------------------ |
|
||||
* | boxMargin | Margin around loop boxes | Integer | Required | Any Positive Value |
|
||||
*
|
||||
* **Notes:** Default value: 10
|
||||
*/
|
||||
boxMargin: 10,
|
||||
|
||||
/**
|
||||
* | Parameter | Description | Type | Required | Values |
|
||||
* | ----------- | ----------- | ------- | -------- | ----------- |
|
||||
* | useMaxWidth | See Notes | boolean | Required | true, false |
|
||||
*
|
||||
* **Notes:** When this flag is set to true, the height and width is set to 100% and is then
|
||||
* scaling with the available space. If set to false, the absolute space required is used.
|
||||
*
|
||||
* Default value: true
|
||||
*/
|
||||
useMaxWidth: true,
|
||||
|
||||
c4ShapeInRow: 4,
|
||||
nextLinePaddingX: 0,
|
||||
|
||||
c4BoundaryInRow: 2,
|
||||
|
||||
personFontSize: 14,
|
||||
personFontFamily: '"Open Sans", sans-serif',
|
||||
personFontWeight: 'normal',
|
||||
|
||||
external_personFontSize: 14,
|
||||
external_personFontFamily: '"Open Sans", sans-serif',
|
||||
external_personFontWeight: 'normal',
|
||||
|
||||
systemFontSize: 14,
|
||||
systemFontFamily: '"Open Sans", sans-serif',
|
||||
systemFontWeight: 'normal',
|
||||
|
||||
external_systemFontSize: 14,
|
||||
external_systemFontFamily: '"Open Sans", sans-serif',
|
||||
external_systemFontWeight: 'normal',
|
||||
|
||||
system_dbFontSize: 14,
|
||||
system_dbFontFamily: '"Open Sans", sans-serif',
|
||||
system_dbFontWeight: 'normal',
|
||||
|
||||
external_system_dbFontSize: 14,
|
||||
external_system_dbFontFamily: '"Open Sans", sans-serif',
|
||||
external_system_dbFontWeight: 'normal',
|
||||
|
||||
system_queueFontSize: 14,
|
||||
system_queueFontFamily: '"Open Sans", sans-serif',
|
||||
system_queueFontWeight: 'normal',
|
||||
|
||||
external_system_queueFontSize: 14,
|
||||
external_system_queueFontFamily: '"Open Sans", sans-serif',
|
||||
external_system_queueFontWeight: 'normal',
|
||||
|
||||
boundaryFontSize: 14,
|
||||
boundaryFontFamily: '"Open Sans", sans-serif',
|
||||
boundaryFontWeight: 'normal',
|
||||
|
||||
messageFontSize: 12,
|
||||
messageFontFamily: '"Open Sans", sans-serif',
|
||||
messageFontWeight: 'normal',
|
||||
|
||||
containerFontSize: 14,
|
||||
containerFontFamily: '"Open Sans", sans-serif',
|
||||
containerFontWeight: 'normal',
|
||||
|
||||
external_containerFontSize: 14,
|
||||
external_containerFontFamily: '"Open Sans", sans-serif',
|
||||
external_containerFontWeight: 'normal',
|
||||
|
||||
container_dbFontSize: 14,
|
||||
container_dbFontFamily: '"Open Sans", sans-serif',
|
||||
container_dbFontWeight: 'normal',
|
||||
|
||||
external_container_dbFontSize: 14,
|
||||
external_container_dbFontFamily: '"Open Sans", sans-serif',
|
||||
external_container_dbFontWeight: 'normal',
|
||||
|
||||
container_queueFontSize: 14,
|
||||
container_queueFontFamily: '"Open Sans", sans-serif',
|
||||
container_queueFontWeight: 'normal',
|
||||
|
||||
external_container_queueFontSize: 14,
|
||||
external_container_queueFontFamily: '"Open Sans", sans-serif',
|
||||
external_container_queueFontWeight: 'normal',
|
||||
|
||||
componentFontSize: 14,
|
||||
componentFontFamily: '"Open Sans", sans-serif',
|
||||
componentFontWeight: 'normal',
|
||||
|
||||
external_componentFontSize: 14,
|
||||
external_componentFontFamily: '"Open Sans", sans-serif',
|
||||
external_componentFontWeight: 'normal',
|
||||
|
||||
component_dbFontSize: 14,
|
||||
component_dbFontFamily: '"Open Sans", sans-serif',
|
||||
component_dbFontWeight: 'normal',
|
||||
|
||||
external_component_dbFontSize: 14,
|
||||
external_component_dbFontFamily: '"Open Sans", sans-serif',
|
||||
external_component_dbFontWeight: 'normal',
|
||||
|
||||
component_queueFontSize: 14,
|
||||
component_queueFontFamily: '"Open Sans", sans-serif',
|
||||
component_queueFontWeight: 'normal',
|
||||
|
||||
external_component_queueFontSize: 14,
|
||||
external_component_queueFontFamily: '"Open Sans", sans-serif',
|
||||
external_component_queueFontWeight: 'normal',
|
||||
|
||||
/**
|
||||
* This sets the auto-wrap state for the diagram
|
||||
*
|
||||
* **Notes:** Default value: true.
|
||||
*/
|
||||
wrap: true,
|
||||
|
||||
/**
|
||||
* This sets the auto-wrap padding for the diagram (sides only)
|
||||
*
|
||||
* **Notes:** Default value: 0.
|
||||
*/
|
||||
wrapPadding: 10,
|
||||
|
||||
personFont: function () {
|
||||
return {
|
||||
fontFamily: this.personFontFamily,
|
||||
fontSize: this.personFontSize,
|
||||
fontWeight: this.personFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
external_personFont: function () {
|
||||
return {
|
||||
fontFamily: this.external_personFontFamily,
|
||||
fontSize: this.external_personFontSize,
|
||||
fontWeight: this.external_personFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
systemFont: function () {
|
||||
return {
|
||||
fontFamily: this.systemFontFamily,
|
||||
fontSize: this.systemFontSize,
|
||||
fontWeight: this.systemFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
external_systemFont: function () {
|
||||
return {
|
||||
fontFamily: this.external_systemFontFamily,
|
||||
fontSize: this.external_systemFontSize,
|
||||
fontWeight: this.external_systemFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
system_dbFont: function () {
|
||||
return {
|
||||
fontFamily: this.system_dbFontFamily,
|
||||
fontSize: this.system_dbFontSize,
|
||||
fontWeight: this.system_dbFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
external_system_dbFont: function () {
|
||||
return {
|
||||
fontFamily: this.external_system_dbFontFamily,
|
||||
fontSize: this.external_system_dbFontSize,
|
||||
fontWeight: this.external_system_dbFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
system_queueFont: function () {
|
||||
return {
|
||||
fontFamily: this.system_queueFontFamily,
|
||||
fontSize: this.system_queueFontSize,
|
||||
fontWeight: this.system_queueFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
external_system_queueFont: function () {
|
||||
return {
|
||||
fontFamily: this.external_system_queueFontFamily,
|
||||
fontSize: this.external_system_queueFontSize,
|
||||
fontWeight: this.external_system_queueFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
containerFont: function () {
|
||||
return {
|
||||
fontFamily: this.containerFontFamily,
|
||||
fontSize: this.containerFontSize,
|
||||
fontWeight: this.containerFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
external_containerFont: function () {
|
||||
return {
|
||||
fontFamily: this.external_containerFontFamily,
|
||||
fontSize: this.external_containerFontSize,
|
||||
fontWeight: this.external_containerFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
container_dbFont: function () {
|
||||
return {
|
||||
fontFamily: this.container_dbFontFamily,
|
||||
fontSize: this.container_dbFontSize,
|
||||
fontWeight: this.container_dbFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
external_container_dbFont: function () {
|
||||
return {
|
||||
fontFamily: this.external_container_dbFontFamily,
|
||||
fontSize: this.external_container_dbFontSize,
|
||||
fontWeight: this.external_container_dbFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
container_queueFont: function () {
|
||||
return {
|
||||
fontFamily: this.container_queueFontFamily,
|
||||
fontSize: this.container_queueFontSize,
|
||||
fontWeight: this.container_queueFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
external_container_queueFont: function () {
|
||||
return {
|
||||
fontFamily: this.external_container_queueFontFamily,
|
||||
fontSize: this.external_container_queueFontSize,
|
||||
fontWeight: this.external_container_queueFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
componentFont: function () {
|
||||
return {
|
||||
fontFamily: this.componentFontFamily,
|
||||
fontSize: this.componentFontSize,
|
||||
fontWeight: this.componentFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
external_componentFont: function () {
|
||||
return {
|
||||
fontFamily: this.external_componentFontFamily,
|
||||
fontSize: this.external_componentFontSize,
|
||||
fontWeight: this.external_componentFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
component_dbFont: function () {
|
||||
return {
|
||||
fontFamily: this.component_dbFontFamily,
|
||||
fontSize: this.component_dbFontSize,
|
||||
fontWeight: this.component_dbFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
external_component_dbFont: function () {
|
||||
return {
|
||||
fontFamily: this.external_component_dbFontFamily,
|
||||
fontSize: this.external_component_dbFontSize,
|
||||
fontWeight: this.external_component_dbFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
component_queueFont: function () {
|
||||
return {
|
||||
fontFamily: this.component_queueFontFamily,
|
||||
fontSize: this.component_queueFontSize,
|
||||
fontWeight: this.component_queueFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
external_component_queueFont: function () {
|
||||
return {
|
||||
fontFamily: this.external_component_queueFontFamily,
|
||||
fontSize: this.external_component_queueFontSize,
|
||||
fontWeight: this.external_component_queueFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
boundaryFont: function () {
|
||||
return {
|
||||
fontFamily: this.boundaryFontFamily,
|
||||
fontSize: this.boundaryFontSize,
|
||||
fontWeight: this.boundaryFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
messageFont: function () {
|
||||
return {
|
||||
fontFamily: this.messageFontFamily,
|
||||
fontSize: this.messageFontSize,
|
||||
fontWeight: this.messageFontWeight,
|
||||
};
|
||||
},
|
||||
|
||||
// ' Colors
|
||||
// ' ##################################
|
||||
person_bg_color: '#08427B',
|
||||
person_border_color: '#073B6F',
|
||||
external_person_bg_color: '#686868',
|
||||
external_person_border_color: '#8A8A8A',
|
||||
system_bg_color: '#1168BD',
|
||||
system_border_color: '#3C7FC0',
|
||||
system_db_bg_color: '#1168BD',
|
||||
system_db_border_color: '#3C7FC0',
|
||||
system_queue_bg_color: '#1168BD',
|
||||
system_queue_border_color: '#3C7FC0',
|
||||
external_system_bg_color: '#999999',
|
||||
external_system_border_color: '#8A8A8A',
|
||||
external_system_db_bg_color: '#999999',
|
||||
external_system_db_border_color: '#8A8A8A',
|
||||
external_system_queue_bg_color: '#999999',
|
||||
external_system_queue_border_color: '#8A8A8A',
|
||||
container_bg_color: '#438DD5',
|
||||
container_border_color: '#3C7FC0',
|
||||
container_db_bg_color: '#438DD5',
|
||||
container_db_border_color: '#3C7FC0',
|
||||
container_queue_bg_color: '#438DD5',
|
||||
container_queue_border_color: '#3C7FC0',
|
||||
external_container_bg_color: '#B3B3B3',
|
||||
external_container_border_color: '#A6A6A6',
|
||||
external_container_db_bg_color: '#B3B3B3',
|
||||
external_container_db_border_color: '#A6A6A6',
|
||||
external_container_queue_bg_color: '#B3B3B3',
|
||||
external_container_queue_border_color: '#A6A6A6',
|
||||
component_bg_color: '#85BBF0',
|
||||
component_border_color: '#78A8D8',
|
||||
component_db_bg_color: '#85BBF0',
|
||||
component_db_border_color: '#78A8D8',
|
||||
component_queue_bg_color: '#85BBF0',
|
||||
component_queue_border_color: '#78A8D8',
|
||||
external_component_bg_color: '#CCCCCC',
|
||||
external_component_border_color: '#BFBFBF',
|
||||
external_component_db_bg_color: '#CCCCCC',
|
||||
external_component_db_border_color: '#BFBFBF',
|
||||
external_component_queue_bg_color: '#CCCCCC',
|
||||
external_component_queue_border_color: '#BFBFBF',
|
||||
},
|
||||
};
|
||||
|
||||
|
480
src/diagrams/c4/c4Db.js
Normal file
480
src/diagrams/c4/c4Db.js
Normal file
@@ -0,0 +1,480 @@
|
||||
import mermaidAPI from '../../mermaidAPI';
|
||||
import * as configApi from '../../config';
|
||||
import { log } from '../../logger';
|
||||
import { sanitizeText } from '../common/common';
|
||||
import { setAccTitle, getAccTitle, getAccDescription, setAccDescription } from '../../commonDb';
|
||||
|
||||
let c4ShapeArray = [];
|
||||
let boundaryParseStack = [''];
|
||||
let currentBoundaryParse = 'global';
|
||||
let parentBoundaryParse = '';
|
||||
let boundarys = [
|
||||
{
|
||||
alias: 'global',
|
||||
label: { text: 'global' },
|
||||
type: { text: 'global' },
|
||||
tags: null,
|
||||
link: null,
|
||||
parentBoundary: '',
|
||||
},
|
||||
];
|
||||
let rels = [];
|
||||
let title = '';
|
||||
let wrapEnabled = false;
|
||||
let description = '';
|
||||
var c4Type;
|
||||
|
||||
export const getC4Type = function () {
|
||||
return c4Type;
|
||||
};
|
||||
|
||||
export const setC4Type = function (c4TypeParam) {
|
||||
let sanitizedText = sanitizeText(c4TypeParam, configApi.getConfig());
|
||||
c4Type = sanitizedText;
|
||||
};
|
||||
|
||||
export const parseDirective = function (statement, context, type) {
|
||||
mermaidAPI.parseDirective(this, statement, context, type);
|
||||
};
|
||||
|
||||
//type, from, to, label, ?techn, ?descr, ?sprite, ?tags, $link
|
||||
export const addRel = function (type, from, to, label, techn, descr, sprite, tags, link) {
|
||||
// Don't allow label nulling
|
||||
if (
|
||||
type === undefined ||
|
||||
type === null ||
|
||||
from === undefined ||
|
||||
from === null ||
|
||||
to === undefined ||
|
||||
to === null ||
|
||||
label === undefined ||
|
||||
label === null
|
||||
)
|
||||
return;
|
||||
|
||||
let rel = {};
|
||||
const old = rels.find((rel) => rel.from === from && rel.to === to);
|
||||
if (old) {
|
||||
rel = old;
|
||||
} else {
|
||||
rels.push(rel);
|
||||
}
|
||||
|
||||
rel.type = type;
|
||||
rel.from = from;
|
||||
rel.to = to;
|
||||
rel.label = { text: label };
|
||||
|
||||
if (descr === undefined || descr === null) {
|
||||
rel.descr = { text: '' };
|
||||
} else {
|
||||
rel.descr = { text: descr };
|
||||
}
|
||||
|
||||
if (techn === undefined || techn === null) {
|
||||
rel.techn = { text: '' };
|
||||
} else {
|
||||
rel.techn = { text: techn };
|
||||
}
|
||||
|
||||
// rel.techn = techn;
|
||||
rel.sprite = sprite;
|
||||
rel.tags = tags;
|
||||
rel.link = link;
|
||||
rel.wrap = autoWrap();
|
||||
};
|
||||
|
||||
//type, alias, label, ?descr, ?sprite, ?tags, $link
|
||||
export const addPersonOrSystem = function (typeC4Shape, alias, label, descr, sprite, tags, link) {
|
||||
// Don't allow label nulling
|
||||
if (alias === null || label === null) return;
|
||||
|
||||
let personOrSystem = {};
|
||||
const old = c4ShapeArray.find((personOrSystem) => personOrSystem.alias === alias);
|
||||
if (old && alias === old.alias) {
|
||||
personOrSystem = old;
|
||||
} else {
|
||||
personOrSystem.alias = alias;
|
||||
c4ShapeArray.push(personOrSystem);
|
||||
}
|
||||
|
||||
// Don't allow null labels, either
|
||||
if (label === undefined || label === null) {
|
||||
personOrSystem.label = { text: '' };
|
||||
} else {
|
||||
personOrSystem.label = { text: label };
|
||||
}
|
||||
|
||||
if (descr === undefined || descr === null) {
|
||||
personOrSystem.descr = { text: '' };
|
||||
} else {
|
||||
personOrSystem.descr = { text: descr };
|
||||
}
|
||||
|
||||
personOrSystem.wrap = autoWrap();
|
||||
personOrSystem.sprite = sprite;
|
||||
personOrSystem.tags = tags;
|
||||
personOrSystem.link = link;
|
||||
personOrSystem.typeC4Shape = { text: typeC4Shape };
|
||||
personOrSystem.parentBoundary = currentBoundaryParse;
|
||||
};
|
||||
|
||||
//type, alias, label, ?techn, ?descr ?sprite, ?tags, $link
|
||||
export const addContainer = function (typeC4Shape, alias, label, techn, descr, sprite, tags, link) {
|
||||
// Don't allow label nulling
|
||||
if (alias === null || label === null) return;
|
||||
|
||||
let container = {};
|
||||
const old = c4ShapeArray.find((container) => container.alias === alias);
|
||||
if (old && alias === old.alias) {
|
||||
container = old;
|
||||
} else {
|
||||
container.alias = alias;
|
||||
c4ShapeArray.push(container);
|
||||
}
|
||||
|
||||
// Don't allow null labels, either
|
||||
if (label === undefined || label === null) {
|
||||
container.label = { text: '' };
|
||||
} else {
|
||||
container.label = { text: label };
|
||||
}
|
||||
|
||||
if (techn === undefined || techn === null) {
|
||||
container.techn = { text: '' };
|
||||
} else {
|
||||
container.techn = { text: techn };
|
||||
}
|
||||
|
||||
if (descr === undefined || descr === null) {
|
||||
container.descr = { text: '' };
|
||||
} else {
|
||||
container.descr = { text: descr };
|
||||
}
|
||||
|
||||
container.sprite = sprite;
|
||||
container.tags = tags;
|
||||
container.link = link;
|
||||
container.wrap = autoWrap();
|
||||
container.typeC4Shape = { text: typeC4Shape };
|
||||
container.parentBoundary = currentBoundaryParse;
|
||||
};
|
||||
|
||||
//type, alias, label, ?techn, ?descr ?sprite, ?tags, $link
|
||||
export const addComponent = function (typeC4Shape, alias, label, techn, descr, sprite, tags, link) {
|
||||
// Don't allow label nulling
|
||||
if (alias === null || label === null) return;
|
||||
|
||||
let component = {};
|
||||
const old = c4ShapeArray.find((component) => component.alias === alias);
|
||||
if (old && alias === old.alias) {
|
||||
component = old;
|
||||
} else {
|
||||
component.alias = alias;
|
||||
c4ShapeArray.push(component);
|
||||
}
|
||||
|
||||
// Don't allow null labels, either
|
||||
if (label === undefined || label === null) {
|
||||
component.label = { text: '' };
|
||||
} else {
|
||||
component.label = { text: label };
|
||||
}
|
||||
|
||||
if (techn === undefined || techn === null) {
|
||||
component.techn = { text: '' };
|
||||
} else {
|
||||
component.techn = { text: techn };
|
||||
}
|
||||
|
||||
if (descr === undefined || descr === null) {
|
||||
component.descr = { text: '' };
|
||||
} else {
|
||||
component.descr = { text: descr };
|
||||
}
|
||||
|
||||
component.sprite = sprite;
|
||||
component.tags = tags;
|
||||
component.link = link;
|
||||
component.wrap = autoWrap();
|
||||
component.typeC4Shape = { text: typeC4Shape };
|
||||
component.parentBoundary = currentBoundaryParse;
|
||||
};
|
||||
|
||||
//alias, label, ?type, ?tags, $link
|
||||
export const addPersonOrSystemBoundary = function (alias, label, type, tags, link) {
|
||||
// if (parentBoundary === null) return;
|
||||
|
||||
// Don't allow label nulling
|
||||
if (alias === null || label === null) return;
|
||||
|
||||
let boundary = {};
|
||||
const old = boundarys.find((boundary) => boundary.alias === alias);
|
||||
if (old && alias === old.alias) {
|
||||
boundary = old;
|
||||
} else {
|
||||
boundary.alias = alias;
|
||||
boundarys.push(boundary);
|
||||
}
|
||||
|
||||
// Don't allow null labels, either
|
||||
if (label === undefined || label === null) {
|
||||
boundary.label = { text: '' };
|
||||
} else {
|
||||
boundary.label = { text: label };
|
||||
}
|
||||
|
||||
if (type === undefined || type === null) {
|
||||
boundary.type = { text: 'system' };
|
||||
} else {
|
||||
boundary.type = { text: type };
|
||||
}
|
||||
|
||||
boundary.tags = tags;
|
||||
boundary.link = link;
|
||||
boundary.parentBoundary = currentBoundaryParse;
|
||||
boundary.wrap = autoWrap();
|
||||
|
||||
parentBoundaryParse = currentBoundaryParse;
|
||||
currentBoundaryParse = alias;
|
||||
boundaryParseStack.push(parentBoundaryParse);
|
||||
};
|
||||
|
||||
//alias, label, ?type, ?tags, $link
|
||||
export const addContainerBoundary = function (alias, label, type, tags, link) {
|
||||
// if (parentBoundary === null) return;
|
||||
|
||||
// Don't allow label nulling
|
||||
if (alias === null || label === null) return;
|
||||
|
||||
let boundary = {};
|
||||
const old = boundarys.find((boundary) => boundary.alias === alias);
|
||||
if (old && alias === old.alias) {
|
||||
boundary = old;
|
||||
} else {
|
||||
boundary.alias = alias;
|
||||
boundarys.push(boundary);
|
||||
}
|
||||
|
||||
// Don't allow null labels, either
|
||||
if (label === undefined || label === null) {
|
||||
boundary.label = { text: '' };
|
||||
} else {
|
||||
boundary.label = { text: label };
|
||||
}
|
||||
|
||||
if (type === undefined || type === null) {
|
||||
boundary.type = { text: 'container' };
|
||||
} else {
|
||||
boundary.type = { text: type };
|
||||
}
|
||||
|
||||
boundary.tags = tags;
|
||||
boundary.link = link;
|
||||
boundary.parentBoundary = currentBoundaryParse;
|
||||
boundary.wrap = autoWrap();
|
||||
|
||||
parentBoundaryParse = currentBoundaryParse;
|
||||
currentBoundaryParse = alias;
|
||||
boundaryParseStack.push(parentBoundaryParse);
|
||||
};
|
||||
|
||||
//alias, label, ?type, ?descr, ?sprite, ?tags, $link
|
||||
export const addDeploymentNode = function (
|
||||
nodeType,
|
||||
alias,
|
||||
label,
|
||||
type,
|
||||
descr,
|
||||
sprite,
|
||||
tags,
|
||||
link
|
||||
) {
|
||||
// if (parentBoundary === null) return;
|
||||
|
||||
// Don't allow label nulling
|
||||
if (alias === null || label === null) return;
|
||||
|
||||
let boundary = {};
|
||||
const old = boundarys.find((boundary) => boundary.alias === alias);
|
||||
if (old && alias === old.alias) {
|
||||
boundary = old;
|
||||
} else {
|
||||
boundary.alias = alias;
|
||||
boundarys.push(boundary);
|
||||
}
|
||||
|
||||
// Don't allow null labels, either
|
||||
if (label === undefined || label === null) {
|
||||
boundary.label = { text: '' };
|
||||
} else {
|
||||
boundary.label = { text: label };
|
||||
}
|
||||
|
||||
if (type === undefined || type === null) {
|
||||
boundary.type = { text: 'node' };
|
||||
} else {
|
||||
boundary.type = { text: type };
|
||||
}
|
||||
|
||||
if (descr === undefined || descr === null) {
|
||||
boundary.descr = { text: '' };
|
||||
} else {
|
||||
boundary.descr = { text: type };
|
||||
}
|
||||
|
||||
boundary.tags = tags;
|
||||
boundary.link = link;
|
||||
boundary.nodeType = nodeType;
|
||||
boundary.parentBoundary = currentBoundaryParse;
|
||||
boundary.wrap = autoWrap();
|
||||
|
||||
parentBoundaryParse = currentBoundaryParse;
|
||||
currentBoundaryParse = alias;
|
||||
boundaryParseStack.push(parentBoundaryParse);
|
||||
};
|
||||
|
||||
export const popBoundaryParseStack = function () {
|
||||
currentBoundaryParse = parentBoundaryParse;
|
||||
boundaryParseStack.pop();
|
||||
parentBoundaryParse = boundaryParseStack.pop();
|
||||
boundaryParseStack.push(parentBoundaryParse);
|
||||
};
|
||||
|
||||
export const getCurrentBoundaryParse = function () {
|
||||
return currentBoundaryParse;
|
||||
};
|
||||
|
||||
export const getParentBoundaryParse = function () {
|
||||
return parentBoundaryParse;
|
||||
};
|
||||
|
||||
export const getC4ShapeArray = function (parentBoundary) {
|
||||
if (parentBoundary === undefined || parentBoundary === null) return c4ShapeArray;
|
||||
else
|
||||
return c4ShapeArray.filter((personOrSystem) => {
|
||||
return personOrSystem.parentBoundary === parentBoundary;
|
||||
});
|
||||
};
|
||||
export const getC4Shape = function (alias) {
|
||||
return c4ShapeArray.find((personOrSystem) => personOrSystem.alias === alias);
|
||||
};
|
||||
export const getC4ShapeKeys = function (parentBoundary) {
|
||||
return Object.keys(getC4ShapeArray(parentBoundary));
|
||||
};
|
||||
|
||||
export const getBoundarys = function (parentBoundary) {
|
||||
if (parentBoundary === undefined || parentBoundary === null) return boundarys;
|
||||
else return boundarys.filter((boundary) => boundary.parentBoundary === parentBoundary);
|
||||
};
|
||||
|
||||
export const getRels = function () {
|
||||
return rels;
|
||||
};
|
||||
|
||||
export const getTitle = function () {
|
||||
return title;
|
||||
};
|
||||
|
||||
export const setWrap = function (wrapSetting) {
|
||||
wrapEnabled = wrapSetting;
|
||||
};
|
||||
|
||||
export const autoWrap = function () {
|
||||
return wrapEnabled;
|
||||
};
|
||||
|
||||
export const clear = function () {
|
||||
c4ShapeArray = [];
|
||||
boundarys = [
|
||||
{
|
||||
alias: 'global',
|
||||
label: { text: 'global' },
|
||||
type: { text: 'global' },
|
||||
tags: null,
|
||||
link: null,
|
||||
parentBoundary: '',
|
||||
},
|
||||
];
|
||||
parentBoundaryParse = '';
|
||||
currentBoundaryParse = 'global';
|
||||
boundaryParseStack = [''];
|
||||
rels = [];
|
||||
};
|
||||
|
||||
export const LINETYPE = {
|
||||
SOLID: 0,
|
||||
DOTTED: 1,
|
||||
NOTE: 2,
|
||||
SOLID_CROSS: 3,
|
||||
DOTTED_CROSS: 4,
|
||||
SOLID_OPEN: 5,
|
||||
DOTTED_OPEN: 6,
|
||||
LOOP_START: 10,
|
||||
LOOP_END: 11,
|
||||
ALT_START: 12,
|
||||
ALT_ELSE: 13,
|
||||
ALT_END: 14,
|
||||
OPT_START: 15,
|
||||
OPT_END: 16,
|
||||
ACTIVE_START: 17,
|
||||
ACTIVE_END: 18,
|
||||
PAR_START: 19,
|
||||
PAR_AND: 20,
|
||||
PAR_END: 21,
|
||||
RECT_START: 22,
|
||||
RECT_END: 23,
|
||||
SOLID_POINT: 24,
|
||||
DOTTED_POINT: 25,
|
||||
};
|
||||
|
||||
export const ARROWTYPE = {
|
||||
FILLED: 0,
|
||||
OPEN: 1,
|
||||
};
|
||||
|
||||
export const PLACEMENT = {
|
||||
LEFTOF: 0,
|
||||
RIGHTOF: 1,
|
||||
OVER: 2,
|
||||
};
|
||||
|
||||
export const setTitle = function (txt) {
|
||||
let sanitizedText = sanitizeText(txt, configApi.getConfig());
|
||||
title = sanitizedText;
|
||||
};
|
||||
|
||||
export default {
|
||||
addPersonOrSystem,
|
||||
addPersonOrSystemBoundary,
|
||||
addContainer,
|
||||
addContainerBoundary,
|
||||
addComponent,
|
||||
addDeploymentNode,
|
||||
popBoundaryParseStack,
|
||||
addRel,
|
||||
autoWrap,
|
||||
setWrap,
|
||||
getC4ShapeArray,
|
||||
getC4Shape,
|
||||
getC4ShapeKeys,
|
||||
getBoundarys,
|
||||
getCurrentBoundaryParse,
|
||||
getParentBoundaryParse,
|
||||
getRels,
|
||||
getTitle,
|
||||
getC4Type,
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
parseDirective,
|
||||
getConfig: () => configApi.getConfig().c4,
|
||||
clear,
|
||||
LINETYPE,
|
||||
ARROWTYPE,
|
||||
PLACEMENT,
|
||||
setTitle,
|
||||
setC4Type,
|
||||
// apply,
|
||||
};
|
670
src/diagrams/c4/c4Renderer.js
Normal file
670
src/diagrams/c4/c4Renderer.js
Normal file
@@ -0,0 +1,670 @@
|
||||
import { select } from 'd3';
|
||||
import svgDraw, { drawText, fixLifeLineHeights } from './svgDraw';
|
||||
import { log } from '../../logger';
|
||||
import { parser } from './parser/c4Diagram';
|
||||
import common from '../common/common';
|
||||
import c4Db from './c4Db';
|
||||
import * as configApi from '../../config';
|
||||
import utils, {
|
||||
wrapLabel,
|
||||
calculateTextWidth,
|
||||
calculateTextHeight,
|
||||
assignWithDepth,
|
||||
configureSvgSize,
|
||||
} from '../../utils';
|
||||
import addSVGAccessibilityFields from '../../accessibility';
|
||||
|
||||
let globalBoundaryMaxX = 0,
|
||||
globalBoundaryMaxY = 0;
|
||||
|
||||
parser.yy = c4Db;
|
||||
|
||||
let conf = {};
|
||||
|
||||
class Bounds {
|
||||
constructor() {
|
||||
this.name = '';
|
||||
this.data = {};
|
||||
this.data.startx = undefined;
|
||||
this.data.stopx = undefined;
|
||||
this.data.starty = undefined;
|
||||
this.data.stopy = undefined;
|
||||
this.data.widthLimit = undefined;
|
||||
|
||||
this.nextData = {};
|
||||
this.nextData.startx = undefined;
|
||||
this.nextData.stopx = undefined;
|
||||
this.nextData.starty = undefined;
|
||||
this.nextData.stopy = undefined;
|
||||
this.nextData.cnt = 0;
|
||||
|
||||
setConf(parser.yy.getConfig());
|
||||
}
|
||||
|
||||
setData(startx, stopx, starty, stopy) {
|
||||
this.nextData.startx = this.data.startx = startx;
|
||||
this.nextData.stopx = this.data.stopx = stopx;
|
||||
this.nextData.starty = this.data.starty = starty;
|
||||
this.nextData.stopy = this.data.stopy = stopy;
|
||||
}
|
||||
|
||||
updateVal(obj, key, val, fun) {
|
||||
if (typeof obj[key] === 'undefined') {
|
||||
obj[key] = val;
|
||||
} else {
|
||||
obj[key] = fun(val, obj[key]);
|
||||
}
|
||||
}
|
||||
|
||||
insert(c4Shape) {
|
||||
this.nextData.cnt = this.nextData.cnt + 1;
|
||||
let _startx =
|
||||
this.nextData.startx === this.nextData.stopx
|
||||
? this.nextData.stopx + c4Shape.margin
|
||||
: this.nextData.stopx + c4Shape.margin * 2;
|
||||
let _stopx = _startx + c4Shape.width;
|
||||
let _starty = this.nextData.starty + c4Shape.margin * 2;
|
||||
let _stopy = _starty + c4Shape.height;
|
||||
if (
|
||||
_startx >= this.data.widthLimit ||
|
||||
_stopx >= this.data.widthLimit ||
|
||||
this.nextData.cnt > conf.c4ShapeInRow
|
||||
) {
|
||||
_startx = this.nextData.startx + c4Shape.margin + conf.nextLinePaddingX;
|
||||
_starty = this.nextData.stopy + c4Shape.margin * 2;
|
||||
|
||||
this.nextData.stopx = _stopx = _startx + c4Shape.width;
|
||||
this.nextData.starty = this.nextData.stopy;
|
||||
this.nextData.stopy = _stopy = _starty + c4Shape.height;
|
||||
this.nextData.cnt = 1;
|
||||
}
|
||||
|
||||
c4Shape.x = _startx;
|
||||
c4Shape.y = _starty;
|
||||
|
||||
this.updateVal(this.data, 'startx', _startx, Math.min);
|
||||
this.updateVal(this.data, 'starty', _starty, Math.min);
|
||||
this.updateVal(this.data, 'stopx', _stopx, Math.max);
|
||||
this.updateVal(this.data, 'stopy', _stopy, Math.max);
|
||||
|
||||
this.updateVal(this.nextData, 'startx', _startx, Math.min);
|
||||
this.updateVal(this.nextData, 'starty', _starty, Math.min);
|
||||
this.updateVal(this.nextData, 'stopx', _stopx, Math.max);
|
||||
this.updateVal(this.nextData, 'stopy', _stopy, Math.max);
|
||||
}
|
||||
|
||||
init() {
|
||||
this.name = '';
|
||||
this.data = {
|
||||
startx: undefined,
|
||||
stopx: undefined,
|
||||
starty: undefined,
|
||||
stopy: undefined,
|
||||
widthLimit: undefined,
|
||||
};
|
||||
this.nextData = {
|
||||
startx: undefined,
|
||||
stopx: undefined,
|
||||
starty: undefined,
|
||||
stopy: undefined,
|
||||
cnt: 0,
|
||||
};
|
||||
setConf(parser.yy.getConfig());
|
||||
}
|
||||
|
||||
bumpLastMargin(margin) {
|
||||
this.data.stopx += margin;
|
||||
this.data.stopy += margin;
|
||||
}
|
||||
}
|
||||
|
||||
export const setConf = function (cnf) {
|
||||
assignWithDepth(conf, cnf);
|
||||
|
||||
if (cnf.fontFamily) {
|
||||
conf.personFontFamily = conf.systemFontFamily = conf.messageFontFamily = cnf.fontFamily;
|
||||
}
|
||||
if (cnf.fontSize) {
|
||||
conf.personFontSize = conf.systemFontSize = conf.messageFontSize = cnf.fontSize;
|
||||
}
|
||||
if (cnf.fontWeight) {
|
||||
conf.personFontWeight = conf.systemFontWeight = conf.messageFontWeight = cnf.fontWeight;
|
||||
}
|
||||
};
|
||||
|
||||
const c4ShapeFont = (cnf, typeC4Shape) => {
|
||||
return {
|
||||
fontFamily: cnf[typeC4Shape + 'FontFamily'],
|
||||
fontSize: cnf[typeC4Shape + 'FontSize'],
|
||||
fontWeight: cnf[typeC4Shape + 'FontWeight'],
|
||||
};
|
||||
};
|
||||
|
||||
const boundaryFont = (cnf) => {
|
||||
return {
|
||||
fontFamily: cnf.boundaryFontFamily,
|
||||
fontSize: cnf.boundaryFontSize,
|
||||
fontWeight: cnf.boundaryFontWeight,
|
||||
};
|
||||
};
|
||||
|
||||
const messageFont = (cnf) => {
|
||||
return {
|
||||
fontFamily: cnf.messageFontFamily,
|
||||
fontSize: cnf.messageFontSize,
|
||||
fontWeight: cnf.messageFontWeight,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param textType
|
||||
* @param c4Shape
|
||||
* @param c4ShapeTextWrap
|
||||
* @param textConf
|
||||
* @param textLimitWidth
|
||||
*/
|
||||
function calcC4ShapeTextWH(textType, c4Shape, c4ShapeTextWrap, textConf, textLimitWidth) {
|
||||
if (!c4Shape[textType].width) {
|
||||
if (c4ShapeTextWrap) {
|
||||
c4Shape[textType].text = wrapLabel(c4Shape[textType].text, textLimitWidth, textConf);
|
||||
c4Shape[textType].textLines = c4Shape[textType].text.split(common.lineBreakRegex).length;
|
||||
// c4Shape[textType].width = calculateTextWidth(c4Shape[textType].text, textConf);
|
||||
c4Shape[textType].width = textLimitWidth;
|
||||
// c4Shape[textType].height = c4Shape[textType].textLines * textConf.fontSize;
|
||||
c4Shape[textType].height = calculateTextHeight(c4Shape[textType].text, textConf);
|
||||
} else {
|
||||
let lines = c4Shape[textType].text.split(common.lineBreakRegex);
|
||||
c4Shape[textType].textLines = lines.length;
|
||||
let lineHeight = 0;
|
||||
c4Shape[textType].height = 0;
|
||||
c4Shape[textType].width = 0;
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
c4Shape[textType].width = Math.max(
|
||||
calculateTextWidth(lines[i], textConf),
|
||||
c4Shape[textType].width
|
||||
);
|
||||
lineHeight = calculateTextHeight(lines[i], textConf);
|
||||
c4Shape[textType].height = c4Shape[textType].height + lineHeight;
|
||||
}
|
||||
// c4Shapes[textType].height = c4Shapes[textType].textLines * textConf.fontSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const drawBoundary = function (diagram, boundary, bounds) {
|
||||
boundary.x = bounds.data.startx;
|
||||
boundary.y = bounds.data.starty;
|
||||
boundary.width = bounds.data.stopx - bounds.data.startx;
|
||||
boundary.height = bounds.data.stopy - bounds.data.starty;
|
||||
|
||||
boundary.label.y = conf.c4ShapeMargin - 35;
|
||||
|
||||
let boundaryTextWrap = boundary.wrap && conf.wrap;
|
||||
let boundaryLabelConf = boundaryFont(conf);
|
||||
boundaryLabelConf.fontSize = boundaryLabelConf.fontSize + 2;
|
||||
boundaryLabelConf.fontWeight = 'bold';
|
||||
let textLimitWidth = calculateTextWidth(boundary.label.text, boundaryLabelConf);
|
||||
calcC4ShapeTextWH('label', boundary, boundaryTextWrap, boundaryLabelConf, textLimitWidth);
|
||||
|
||||
svgDraw.drawBoundary(diagram, boundary, conf);
|
||||
};
|
||||
|
||||
export const drawC4ShapeArray = function (currentBounds, diagram, c4ShapeArray, c4ShapeKeys) {
|
||||
// Upper Y is relative point
|
||||
let Y = 0;
|
||||
// Draw the c4ShapeArray
|
||||
for (let i = 0; i < c4ShapeKeys.length; i++) {
|
||||
Y = 0;
|
||||
const c4Shape = c4ShapeArray[c4ShapeKeys[i]];
|
||||
|
||||
// calc c4 shape type width and height
|
||||
|
||||
let c4ShapeTypeConf = c4ShapeFont(conf, c4Shape.typeC4Shape.text);
|
||||
c4ShapeTypeConf.fontSize = c4ShapeTypeConf.fontSize - 2;
|
||||
c4Shape.typeC4Shape.width = calculateTextWidth(
|
||||
'<<' + c4Shape.typeC4Shape.text + '>>',
|
||||
c4ShapeTypeConf
|
||||
);
|
||||
c4Shape.typeC4Shape.height = c4ShapeTypeConf.fontSize + 2;
|
||||
c4Shape.typeC4Shape.Y = conf.c4ShapePadding;
|
||||
Y = c4Shape.typeC4Shape.Y + c4Shape.typeC4Shape.height - 4;
|
||||
|
||||
// set image width and height c4Shape.x + c4Shape.width / 2 - 24, c4Shape.y + 28
|
||||
// let imageWidth = 0,
|
||||
// imageHeight = 0,
|
||||
// imageY = 0;
|
||||
//
|
||||
c4Shape.image = { width: 0, height: 0, Y: 0 };
|
||||
switch (c4Shape.typeC4Shape.text) {
|
||||
case 'person':
|
||||
case 'external_person':
|
||||
c4Shape.image.width = 48;
|
||||
c4Shape.image.height = 48;
|
||||
c4Shape.image.Y = Y;
|
||||
Y = c4Shape.image.Y + c4Shape.image.height;
|
||||
break;
|
||||
}
|
||||
if (c4Shape.sprite) {
|
||||
c4Shape.image.width = 48;
|
||||
c4Shape.image.height = 48;
|
||||
c4Shape.image.Y = Y;
|
||||
Y = c4Shape.image.Y + c4Shape.image.height;
|
||||
}
|
||||
|
||||
// Y = conf.c4ShapePadding + c4Shape.image.height;
|
||||
|
||||
let c4ShapeTextWrap = c4Shape.wrap && conf.wrap;
|
||||
let textLimitWidth = conf.width - conf.c4ShapePadding * 2;
|
||||
|
||||
let c4ShapeLabelConf = c4ShapeFont(conf, c4Shape.typeC4Shape.text);
|
||||
c4ShapeLabelConf.fontSize = c4ShapeLabelConf.fontSize + 2;
|
||||
c4ShapeLabelConf.fontWeight = 'bold';
|
||||
calcC4ShapeTextWH('label', c4Shape, c4ShapeTextWrap, c4ShapeLabelConf, textLimitWidth);
|
||||
c4Shape['label'].Y = Y + 8;
|
||||
Y = c4Shape['label'].Y + c4Shape['label'].height;
|
||||
|
||||
if (c4Shape.type && c4Shape.type.text !== '') {
|
||||
c4Shape.type.text = '[' + c4Shape.type.text + ']';
|
||||
let c4ShapeTypeConf = c4ShapeFont(conf, c4Shape.typeC4Shape.text);
|
||||
calcC4ShapeTextWH('type', c4Shape, c4ShapeTextWrap, c4ShapeTypeConf, textLimitWidth);
|
||||
c4Shape['type'].Y = Y + 5;
|
||||
Y = c4Shape['type'].Y + c4Shape['type'].height;
|
||||
} else if (c4Shape.techn && c4Shape.techn.text !== '') {
|
||||
c4Shape.techn.text = '[' + c4Shape.techn.text + ']';
|
||||
let c4ShapeTechnConf = c4ShapeFont(conf, c4Shape.techn.text);
|
||||
calcC4ShapeTextWH('techn', c4Shape, c4ShapeTextWrap, c4ShapeTechnConf, textLimitWidth);
|
||||
c4Shape['techn'].Y = Y + 5;
|
||||
Y = c4Shape['techn'].Y + c4Shape['techn'].height;
|
||||
}
|
||||
|
||||
let rectHeight = Y;
|
||||
let rectWidth = c4Shape.label.width;
|
||||
|
||||
if (c4Shape.descr && c4Shape.descr.text !== '') {
|
||||
let c4ShapeDescrConf = c4ShapeFont(conf, c4Shape.typeC4Shape.text);
|
||||
calcC4ShapeTextWH('descr', c4Shape, c4ShapeTextWrap, c4ShapeDescrConf, textLimitWidth);
|
||||
c4Shape['descr'].Y = Y + 20;
|
||||
Y = c4Shape['descr'].Y + c4Shape['descr'].height;
|
||||
|
||||
rectWidth = Math.max(c4Shape.label.width, c4Shape.descr.width);
|
||||
rectHeight = Y - c4Shape['descr'].textLines * 5;
|
||||
}
|
||||
|
||||
rectWidth = rectWidth + conf.c4ShapePadding;
|
||||
// let rectHeight =
|
||||
|
||||
c4Shape.width = Math.max(c4Shape.width || conf.width, rectWidth, conf.width);
|
||||
c4Shape.height = Math.max(c4Shape.height || conf.height, rectHeight, conf.height);
|
||||
c4Shape.margin = c4Shape.margin || conf.c4ShapeMargin;
|
||||
|
||||
currentBounds.insert(c4Shape);
|
||||
|
||||
const height = svgDraw.drawC4Shape(diagram, c4Shape, conf);
|
||||
}
|
||||
|
||||
currentBounds.bumpLastMargin(conf.c4ShapeMargin);
|
||||
};
|
||||
|
||||
class Point {
|
||||
constructor(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
/* * *
|
||||
* Get the intersection of the line between the center point of a rectangle and a point outside the rectangle.
|
||||
* Algorithm idea.
|
||||
* Using a point outside the rectangle as the coordinate origin, the graph is divided into four quadrants, and each quadrant is divided into two cases, with separate treatment on the coordinate axes
|
||||
* 1. The case of coordinate axes.
|
||||
* 1. The case of the negative x-axis
|
||||
* 2. The case of the positive x-axis
|
||||
* 3. The case of the positive y-axis
|
||||
* 4. The negative y-axis case
|
||||
* 2. Quadrant cases.
|
||||
* 2.1. first quadrant: the case where the line intersects the left side of the rectangle; the case where it intersects the lower side of the rectangle
|
||||
* 2.2. second quadrant: the case where the line intersects the right side of the rectangle; the case where it intersects the lower edge of the rectangle
|
||||
* 2.3. third quadrant: the case where the line intersects the right side of the rectangle; the case where it intersects the upper edge of the rectangle
|
||||
* 2.4. fourth quadrant: the case where the line intersects the left side of the rectangle; the case where it intersects the upper side of the rectangle
|
||||
*
|
||||
*/
|
||||
let getIntersectPoint = function (fromNode, endPoint) {
|
||||
let x1 = fromNode.x;
|
||||
|
||||
let y1 = fromNode.y;
|
||||
|
||||
let x2 = endPoint.x;
|
||||
|
||||
let y2 = endPoint.y;
|
||||
|
||||
let fromCenterX = x1 + fromNode.width / 2;
|
||||
|
||||
let fromCenterY = y1 + fromNode.height / 2;
|
||||
|
||||
let dx = Math.abs(x1 - x2);
|
||||
|
||||
let dy = Math.abs(y1 - y2);
|
||||
|
||||
let tanDYX = dy / dx;
|
||||
|
||||
let fromDYX = fromNode.height / fromNode.width;
|
||||
|
||||
let returnPoint = null;
|
||||
|
||||
if (y1 == y2 && x1 < x2) {
|
||||
returnPoint = new Point(x1 + fromNode.width, fromCenterY);
|
||||
} else if (y1 == y2 && x1 > x2) {
|
||||
returnPoint = new Point(x1, fromCenterY);
|
||||
} else if (x1 == x2 && y1 < y2) {
|
||||
returnPoint = new Point(fromCenterX, y1 + fromNode.height);
|
||||
} else if (x1 == x2 && y1 > y2) {
|
||||
returnPoint = new Point(fromCenterX, y1);
|
||||
}
|
||||
|
||||
if (x1 > x2 && y1 < y2) {
|
||||
if (fromDYX >= tanDYX) {
|
||||
returnPoint = new Point(x1, fromCenterY + (tanDYX * fromNode.width) / 2);
|
||||
} else {
|
||||
returnPoint = new Point(
|
||||
fromCenterX - ((dx / dy) * fromNode.height) / 2,
|
||||
y1 + fromNode.height
|
||||
);
|
||||
}
|
||||
} else if (x1 < x2 && y1 < y2) {
|
||||
//
|
||||
if (fromDYX >= tanDYX) {
|
||||
returnPoint = new Point(x1 + fromNode.width, fromCenterY + (tanDYX * fromNode.width) / 2);
|
||||
} else {
|
||||
returnPoint = new Point(
|
||||
fromCenterX + ((dx / dy) * fromNode.height) / 2,
|
||||
y1 + fromNode.height
|
||||
);
|
||||
}
|
||||
} else if (x1 < x2 && y1 > y2) {
|
||||
if (fromDYX >= tanDYX) {
|
||||
returnPoint = new Point(x1 + fromNode.width, fromCenterY - (tanDYX * fromNode.width) / 2);
|
||||
} else {
|
||||
returnPoint = new Point(fromCenterX + ((fromNode.height / 2) * dx) / dy, y1);
|
||||
}
|
||||
} else if (x1 > x2 && y1 > y2) {
|
||||
if (fromDYX >= tanDYX) {
|
||||
returnPoint = new Point(x1, fromCenterY - (fromNode.width / 2) * tanDYX);
|
||||
} else {
|
||||
returnPoint = new Point(fromCenterX - ((fromNode.height / 2) * dx) / dy, y1);
|
||||
}
|
||||
}
|
||||
return returnPoint;
|
||||
};
|
||||
|
||||
let getIntersectPoints = function (fromNode, endNode) {
|
||||
let endIntersectPoint = { x: 0, y: 0 };
|
||||
endIntersectPoint.x = endNode.x + endNode.width / 2;
|
||||
endIntersectPoint.y = endNode.y + endNode.height / 2;
|
||||
let startPoint = getIntersectPoint(fromNode, endIntersectPoint);
|
||||
|
||||
endIntersectPoint.x = fromNode.x + fromNode.width / 2;
|
||||
endIntersectPoint.y = fromNode.y + fromNode.height / 2;
|
||||
let endPoint = getIntersectPoint(endNode, endIntersectPoint);
|
||||
return { startPoint: startPoint, endPoint: endPoint };
|
||||
};
|
||||
|
||||
export const drawRels = function (diagram, rels, getC4ShapeObj) {
|
||||
let i = 0;
|
||||
for (let rel of rels) {
|
||||
i = i + 1;
|
||||
let relTextWrap = rel.wrap && conf.wrap;
|
||||
let relConf = messageFont(conf);
|
||||
let diagramType = parser.yy.getC4Type();
|
||||
if (diagramType === 'C4Dynamic') rel.label.text = i + ': ' + rel.label.text;
|
||||
let textLimitWidth = calculateTextWidth(rel.label.text, relConf);
|
||||
calcC4ShapeTextWH('label', rel, relTextWrap, relConf, textLimitWidth);
|
||||
|
||||
if (rel.techn && rel.techn.text !== '') {
|
||||
textLimitWidth = calculateTextWidth(rel.techn.text, relConf);
|
||||
calcC4ShapeTextWH('techn', rel, relTextWrap, relConf, textLimitWidth);
|
||||
}
|
||||
|
||||
if (rel.descr && rel.descr.text !== '') {
|
||||
textLimitWidth = calculateTextWidth(rel.descr.text, relConf);
|
||||
calcC4ShapeTextWH('descr', rel, relTextWrap, relConf, textLimitWidth);
|
||||
}
|
||||
|
||||
let fromNode = getC4ShapeObj(rel.from);
|
||||
let endNode = getC4ShapeObj(rel.to);
|
||||
let points = getIntersectPoints(fromNode, endNode);
|
||||
rel.startPoint = points.startPoint;
|
||||
rel.endPoint = points.endPoint;
|
||||
}
|
||||
svgDraw.drawRels(diagram, rels, conf);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param diagram
|
||||
* @param parentBoundaryAlias
|
||||
* @param parentBounds
|
||||
* @param currentBoundarys
|
||||
*/
|
||||
function drawInsideBoundary(diagram, parentBoundaryAlias, parentBounds, currentBoundarys) {
|
||||
let currentBounds = new Bounds();
|
||||
// Calculate the width limit of the boundar. label/type 的长度,
|
||||
currentBounds.data.widthLimit =
|
||||
parentBounds.data.widthLimit / Math.min(conf.c4BoundaryInRow, currentBoundarys.length);
|
||||
// Math.min(
|
||||
// conf.width * conf.c4ShapeInRow + conf.c4ShapeMargin * conf.c4ShapeInRow * 2,
|
||||
// parentBounds.data.widthLimit / Math.min(conf.c4BoundaryInRow, currentBoundarys.length)
|
||||
// );
|
||||
for (let i = 0; i < currentBoundarys.length; i++) {
|
||||
let currentBoundary = currentBoundarys[i];
|
||||
let Y = 0;
|
||||
currentBoundary.image = { width: 0, height: 0, Y: 0 };
|
||||
if (currentBoundary.sprite) {
|
||||
currentBoundary.image.width = 48;
|
||||
currentBoundary.image.height = 48;
|
||||
currentBoundary.image.Y = Y;
|
||||
Y = currentBoundary.image.Y + currentBoundary.image.height;
|
||||
}
|
||||
|
||||
let currentBoundaryTextWrap = currentBoundary.wrap && conf.wrap;
|
||||
|
||||
let currentBoundaryLabelConf = boundaryFont(conf);
|
||||
currentBoundaryLabelConf.fontSize = currentBoundaryLabelConf.fontSize + 2;
|
||||
currentBoundaryLabelConf.fontWeight = 'bold';
|
||||
calcC4ShapeTextWH(
|
||||
'label',
|
||||
currentBoundary,
|
||||
currentBoundaryTextWrap,
|
||||
currentBoundaryLabelConf,
|
||||
currentBounds.data.widthLimit
|
||||
);
|
||||
currentBoundary['label'].Y = Y + 8;
|
||||
Y = currentBoundary['label'].Y + currentBoundary['label'].height;
|
||||
|
||||
if (currentBoundary.type && currentBoundary.type.text !== '') {
|
||||
currentBoundary.type.text = '[' + currentBoundary.type.text + ']';
|
||||
let currentBoundaryTypeConf = boundaryFont(conf);
|
||||
calcC4ShapeTextWH(
|
||||
'type',
|
||||
currentBoundary,
|
||||
currentBoundaryTextWrap,
|
||||
currentBoundaryTypeConf,
|
||||
currentBounds.data.widthLimit
|
||||
);
|
||||
currentBoundary['type'].Y = Y + 5;
|
||||
Y = currentBoundary['type'].Y + currentBoundary['type'].height;
|
||||
}
|
||||
|
||||
if (currentBoundary.descr && currentBoundary.descr.text !== '') {
|
||||
let currentBoundaryDescrConf = boundaryFont(conf);
|
||||
currentBoundaryDescrConf.fontSize = currentBoundaryDescrConf.fontSize - 2;
|
||||
calcC4ShapeTextWH(
|
||||
'descr',
|
||||
currentBoundary,
|
||||
currentBoundaryTextWrap,
|
||||
currentBoundaryDescrConf,
|
||||
currentBounds.data.widthLimit
|
||||
);
|
||||
currentBoundary['descr'].Y = Y + 20;
|
||||
Y = currentBoundary['descr'].Y + currentBoundary['descr'].height;
|
||||
}
|
||||
|
||||
if (i == 0 || i % conf.c4BoundaryInRow === 0) {
|
||||
// Calculate the drawing start point of the currentBoundarys.
|
||||
let _x = parentBounds.data.startx + conf.diagramMarginX;
|
||||
let _y = parentBounds.data.stopy + conf.diagramMarginY + Y;
|
||||
|
||||
currentBounds.setData(_x, _x, _y, _y);
|
||||
} else {
|
||||
// Calculate the drawing start point of the currentBoundarys.
|
||||
let _x =
|
||||
currentBounds.data.stopx !== currentBounds.data.startx
|
||||
? currentBounds.data.stopx + conf.diagramMarginX
|
||||
: currentBounds.data.startx;
|
||||
let _y = currentBounds.data.starty;
|
||||
|
||||
currentBounds.setData(_x, _x, _y, _y);
|
||||
}
|
||||
currentBounds.name = currentBoundary.alias;
|
||||
let currentPersonOrSystemArray = parser.yy.getC4ShapeArray(currentBoundary.alias);
|
||||
let currentPersonOrSystemKeys = parser.yy.getC4ShapeKeys(currentBoundary.alias);
|
||||
|
||||
if (currentPersonOrSystemKeys.length > 0) {
|
||||
drawC4ShapeArray(
|
||||
currentBounds,
|
||||
diagram,
|
||||
currentPersonOrSystemArray,
|
||||
currentPersonOrSystemKeys
|
||||
);
|
||||
}
|
||||
parentBoundaryAlias = currentBoundary.alias;
|
||||
let nextCurrentBoundarys = parser.yy.getBoundarys(parentBoundaryAlias);
|
||||
|
||||
if (nextCurrentBoundarys.length > 0) {
|
||||
// draw boundary inside currentBoundary
|
||||
// bounds.init();
|
||||
// parentBoundaryWidthLimit = bounds.data.stopx - bounds.startx;
|
||||
drawInsideBoundary(diagram, parentBoundaryAlias, currentBounds, nextCurrentBoundarys);
|
||||
}
|
||||
// draw boundary
|
||||
if (currentBoundary.alias !== 'global') drawBoundary(diagram, currentBoundary, currentBounds);
|
||||
parentBounds.data.stopy = Math.max(
|
||||
currentBounds.data.stopy + conf.c4ShapeMargin,
|
||||
parentBounds.data.stopy
|
||||
);
|
||||
parentBounds.data.stopx = Math.max(
|
||||
currentBounds.data.stopx + conf.c4ShapeMargin,
|
||||
parentBounds.data.stopx
|
||||
);
|
||||
globalBoundaryMaxX = Math.max(globalBoundaryMaxX, parentBounds.data.stopx);
|
||||
globalBoundaryMaxY = Math.max(globalBoundaryMaxY, parentBounds.data.stopy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a sequenceDiagram in the tag with id: id based on the graph definition in text.
|
||||
*
|
||||
* @param {any} text
|
||||
* @param {any} id
|
||||
*/
|
||||
export const draw = function (text, id) {
|
||||
conf = configApi.getConfig().c4;
|
||||
const securityLevel = configApi.getConfig().securityLevel;
|
||||
// Handle root and ocument for when rendering in sanbox mode
|
||||
let sandboxElement;
|
||||
if (securityLevel === 'sandbox') {
|
||||
sandboxElement = select('#i' + id);
|
||||
}
|
||||
const root =
|
||||
securityLevel === 'sandbox'
|
||||
? select(sandboxElement.nodes()[0].contentDocument.body)
|
||||
: select('body');
|
||||
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
|
||||
|
||||
parser.yy.clear();
|
||||
parser.yy.setWrap(conf.wrap);
|
||||
parser.parse(text + '\n');
|
||||
|
||||
log.debug(`C:${JSON.stringify(conf, null, 2)}`);
|
||||
|
||||
const diagram =
|
||||
securityLevel === 'sandbox' ? root.select(`[id="${id}"]`) : select(`[id="${id}"]`);
|
||||
|
||||
svgDraw.insertComputerIcon(diagram);
|
||||
svgDraw.insertDatabaseIcon(diagram);
|
||||
svgDraw.insertClockIcon(diagram);
|
||||
|
||||
let screenBounds = new Bounds();
|
||||
screenBounds.setData(
|
||||
conf.diagramMarginX,
|
||||
conf.diagramMarginX,
|
||||
conf.diagramMarginY,
|
||||
conf.diagramMarginY
|
||||
);
|
||||
|
||||
screenBounds.data.widthLimit = screen.availWidth;
|
||||
globalBoundaryMaxX = conf.diagramMarginX;
|
||||
globalBoundaryMaxY = conf.diagramMarginY;
|
||||
|
||||
const title = parser.yy.getTitle();
|
||||
const c4type = parser.yy.getC4Type();
|
||||
let currentBoundarys = parser.yy.getBoundarys('');
|
||||
// switch (c4type) {
|
||||
// case 'C4Context':
|
||||
drawInsideBoundary(diagram, '', screenBounds, currentBoundarys);
|
||||
// break;
|
||||
// }
|
||||
|
||||
// The arrow head definition is attached to the svg once
|
||||
svgDraw.insertArrowHead(diagram);
|
||||
svgDraw.insertArrowEnd(diagram);
|
||||
svgDraw.insertArrowCrossHead(diagram);
|
||||
svgDraw.insertArrowFilledHead(diagram);
|
||||
|
||||
drawRels(diagram, parser.yy.getRels(), parser.yy.getC4Shape);
|
||||
|
||||
screenBounds.data.stopx = globalBoundaryMaxX;
|
||||
screenBounds.data.stopy = globalBoundaryMaxY;
|
||||
|
||||
const box = screenBounds.data;
|
||||
|
||||
// Make sure the height of the diagram supports long menus.
|
||||
let boxHeight = box.stopy - box.starty;
|
||||
|
||||
let height = boxHeight + 2 * conf.diagramMarginY;
|
||||
|
||||
// Make sure the width of the diagram supports wide menus.
|
||||
let boxWidth = box.stopx - box.startx;
|
||||
const width = boxWidth + 2 * conf.diagramMarginX;
|
||||
|
||||
if (title) {
|
||||
diagram
|
||||
.append('text')
|
||||
.text(title)
|
||||
.attr('x', (box.stopx - box.startx) / 2 - 4 * conf.diagramMarginX)
|
||||
.attr('y', box.starty + conf.diagramMarginY);
|
||||
}
|
||||
|
||||
configureSvgSize(diagram, height, width, conf.useMaxWidth);
|
||||
|
||||
const extraVertForTitle = title ? 60 : 0;
|
||||
diagram.attr(
|
||||
'viewBox',
|
||||
box.startx -
|
||||
conf.diagramMarginX +
|
||||
' -' +
|
||||
(conf.diagramMarginY + extraVertForTitle) +
|
||||
' ' +
|
||||
width +
|
||||
' ' +
|
||||
(height + extraVertForTitle)
|
||||
);
|
||||
|
||||
addSVGAccessibilityFields(parser.yy, diagram, id);
|
||||
log.debug(`models:`, box);
|
||||
};
|
||||
|
||||
export default {
|
||||
drawPersonOrSystemArray: drawC4ShapeArray,
|
||||
drawBoundary,
|
||||
setConf,
|
||||
draw,
|
||||
};
|
312
src/diagrams/c4/parser/c4Diagram.jison
Normal file
312
src/diagrams/c4/parser/c4Diagram.jison
Normal file
@@ -0,0 +1,312 @@
|
||||
/** mermaid
|
||||
* https://mermaidjs.github.io/
|
||||
* (c) 2022 mzhx.meng@gmail.com
|
||||
* MIT license.
|
||||
*/
|
||||
|
||||
/* lexical grammar */
|
||||
%lex
|
||||
|
||||
/* context */
|
||||
%x person
|
||||
%x person_ext
|
||||
%x system
|
||||
%x system_db
|
||||
%x system_queue
|
||||
%x system_ext
|
||||
%x system_ext_db
|
||||
%x system_ext_queue
|
||||
%x boundary
|
||||
%x enterprise_boundary
|
||||
%x system_boundary
|
||||
%x rel
|
||||
%x birel
|
||||
%x rel_u
|
||||
%x rel_d
|
||||
%x rel_l
|
||||
%x rel_r
|
||||
|
||||
/* container */
|
||||
%x container
|
||||
%x container_db
|
||||
%x container_queue
|
||||
%x container_ext
|
||||
%x container_ext_db
|
||||
%x container_ext_queue
|
||||
%x container_boundary
|
||||
|
||||
/* component */
|
||||
%x component
|
||||
%x component_db
|
||||
%x component_queue
|
||||
%x component_ext
|
||||
%x component_ext_db
|
||||
%x component_ext_queue
|
||||
|
||||
/* Dynamic diagram */
|
||||
%x rel_index
|
||||
%x index
|
||||
|
||||
/* Deployment diagram */
|
||||
%x node
|
||||
%x node_l
|
||||
%x node_r
|
||||
|
||||
/* Relationship Types */
|
||||
%x rel
|
||||
%x rel_bi
|
||||
%x rel_u
|
||||
%x rel_d
|
||||
%x rel_l
|
||||
%x rel_r
|
||||
%x rel_b
|
||||
|
||||
%x attribute
|
||||
%x string
|
||||
|
||||
%x open_directive
|
||||
%x type_directive
|
||||
%x arg_directive
|
||||
|
||||
%%
|
||||
\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
|
||||
.*direction\s+TB[^\n]* return 'direction_tb';
|
||||
.*direction\s+BT[^\n]* return 'direction_bt';
|
||||
.*direction\s+RL[^\n]* return 'direction_rl';
|
||||
.*direction\s+LR[^\n]* return 'direction_lr';
|
||||
<open_directive>((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
|
||||
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
|
||||
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
|
||||
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
|
||||
\%\%(?!\{)*[^\n]*(\r?\n?)+ /* skip comments */
|
||||
\%\%[^\n]*(\r?\n)* c /* skip comments */
|
||||
|
||||
"title"\s[^#\n;]+ return 'title';
|
||||
"accDescription"\s[^#\n;]+ return 'accDescription';
|
||||
|
||||
\s*(\r?\n)+ return 'NEWLINE';
|
||||
\s+ /* skip whitespace */
|
||||
"C4Context" return 'C4_CONTEXT';
|
||||
"C4Container" return 'C4_CONTAINER';
|
||||
"C4Component" return 'C4_COMPONENT';
|
||||
"C4Dynamic" return 'C4_DYNAMIC';
|
||||
"C4Deployment" return 'C4_DEPLOYMENT';
|
||||
|
||||
"Person_Ext" { this.begin("person_ext"); console.log('begin person_ext'); return 'PERSON_EXT';}
|
||||
"Person" { this.begin("person"); console.log('begin person'); return 'PERSON';}
|
||||
"SystemQueue_Ext" { this.begin("system_ext_queue"); console.log('begin system_ext_queue'); return 'SYSTEM_EXT_QUEUE';}
|
||||
"SystemDb_Ext" { this.begin("system_ext_db"); console.log('begin system_ext_db'); return 'SYSTEM_EXT_DB';}
|
||||
"System_Ext" { this.begin("system_ext"); console.log('begin system_ext'); return 'SYSTEM_EXT';}
|
||||
"SystemQueue" { this.begin("system_queue"); console.log('begin system_queue'); return 'SYSTEM_QUEUE';}
|
||||
"SystemDb" { this.begin("system_db"); console.log('begin system_db'); return 'SYSTEM_DB';}
|
||||
"System" { this.begin("system"); console.log('begin system'); return 'SYSTEM';}
|
||||
|
||||
"Boundary" { this.begin("boundary"); console.log('begin boundary'); return 'BOUNDARY';}
|
||||
"Enterprise_Boundary" { this.begin("enterprise_boundary"); console.log('begin enterprise_boundary'); return 'ENTERPRISE_BOUNDARY';}
|
||||
"System_Boundary" { this.begin("system_boundary"); console.log('begin system_boundary'); return 'SYSTEM_BOUNDARY';}
|
||||
|
||||
"ContainerQueue_Ext" { this.begin("container_ext_queue"); console.log('begin container_ext_queue'); return 'CONTAINER_EXT_QUEUE';}
|
||||
"ContainerDb_Ext" { this.begin("container_ext_db"); console.log('begin container_ext_db'); return 'CONTAINER_EXT_DB';}
|
||||
"Container_Ext" { this.begin("container_ext"); console.log('begin container_ext'); return 'CONTAINER_EXT';}
|
||||
"ContainerQueue" { this.begin("container_queue"); console.log('begin container_queue'); return 'CONTAINER_QUEUE';}
|
||||
"ContainerDb" { this.begin("container_db"); console.log('begin container_db'); return 'CONTAINER_DB';}
|
||||
"Container" { this.begin("container"); console.log('begin container'); return 'CONTAINER';}
|
||||
|
||||
"Container_Boundary" { this.begin("container_boundary"); console.log('begin container_boundary'); return 'CONTAINER_BOUNDARY';}
|
||||
|
||||
"ComponentQueue_Ext" { this.begin("component_ext_queue"); console.log('begin component_ext_queue'); return 'COMPONENT_EXT_QUEUE';}
|
||||
"ComponentDb_Ext" { this.begin("component_ext_db"); console.log('begin component_ext_db'); return 'COMPONENT_EXT_DB';}
|
||||
"Component_Ext" { this.begin("component_ext"); console.log('begin component_ext'); return 'COMPONENT_EXT';}
|
||||
"ComponentQueue" { this.begin("component_queue"); console.log('begin component_queue'); return 'COMPONENT_QUEUE';}
|
||||
"ComponentDb" { this.begin("component_db"); console.log('begin component_db'); return 'COMPONENT_DB';}
|
||||
"Component" { this.begin("component"); console.log('begin component'); return 'COMPONENT';}
|
||||
|
||||
"Deployment_Node" { this.begin("node"); console.log('begin node'); return 'NODE';}
|
||||
"Node" { this.begin("node"); console.log('begin node'); return 'NODE';}
|
||||
"Node_L" { this.begin("node_l"); console.log('begin node_l'); return 'NODE_L';}
|
||||
"Node_R" { this.begin("node_r"); console.log('begin node_r'); return 'NODE_R';}
|
||||
|
||||
|
||||
"Rel" { this.begin("rel"); console.log('begin rel'); return 'REL';}
|
||||
"BiRel" { this.begin("birel"); console.log('begin birel'); return 'BIREL';}
|
||||
"Rel_Up" { this.begin("rel_u"); console.log('begin rel_u'); return 'REL_U';}
|
||||
"Rel_U" { this.begin("rel_u"); console.log('begin rel_u'); return 'REL_U';}
|
||||
"Rel_Down" { this.begin("rel_d"); console.log('begin rel_d'); return 'REL_D';}
|
||||
"Rel_D" { this.begin("rel_d"); console.log('begin rel_d'); return 'REL_D';}
|
||||
"Rel_Left" { this.begin("rel_l"); console.log('begin rel_l'); return 'REL_L';}
|
||||
"Rel_L" { this.begin("rel_l"); console.log('begin rel_l'); return 'REL_L';}
|
||||
"Rel_Right" { this.begin("rel_r"); console.log('begin rel_r'); return 'REL_R';}
|
||||
"Rel_R" { this.begin("rel_r"); console.log('begin rel_r'); return 'REL_R';}
|
||||
"Rel_Back" { this.begin("rel_b"); console.log('begin rel_b'); return 'REL_B';}
|
||||
"RelIndex" { this.begin("rel_index"); console.log('begin rel_index'); return 'REL_INDEX';}
|
||||
|
||||
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index><<EOF>> return "EOF_IN_STRUCT";
|
||||
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index>[(][ ]*[,] { console.log('begin attribute with ATTRIBUTE_EMPTY'); this.begin("attribute"); return "ATTRIBUTE_EMPTY";}
|
||||
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index>[(] { console.log('begin attribute'); this.begin("attribute"); }
|
||||
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,attribute>[)] { console.log('STOP attribute'); this.popState();console.log('STOP diagram'); this.popState();}
|
||||
|
||||
<attribute>",," { console.log(',,'); return 'ATTRIBUTE_EMPTY';}
|
||||
<attribute>"," { console.log(','); }
|
||||
<attribute>[ ]*["]["] { console.log('ATTRIBUTE_EMPTY'); return 'ATTRIBUTE_EMPTY';}
|
||||
<attribute>[ ]*["] { console.log('begin string'); this.begin("string");}
|
||||
<string>["] { console.log('STOP string'); this.popState(); }
|
||||
<string>[^"]* { console.log('STR'); return "STR";}
|
||||
<attribute>[^,]+ { console.log('not STR'); return "STR";}
|
||||
|
||||
'{' { /* this.begin("lbrace"); */ console.log('begin boundary block'); return "LBRACE";}
|
||||
'}' { /* this.popState(); */ console.log('STOP boundary block'); return "RBRACE";}
|
||||
|
||||
[\s]+ return 'SPACE';
|
||||
[\n\r]+ return 'EOL';
|
||||
<<EOF>> return 'EOF';
|
||||
|
||||
/lex
|
||||
|
||||
/* operator associations and precedence */
|
||||
|
||||
%left '^'
|
||||
|
||||
%start start
|
||||
|
||||
%% /* language grammar */
|
||||
|
||||
start
|
||||
: mermaidDoc
|
||||
| direction
|
||||
| directive start
|
||||
;
|
||||
|
||||
direction
|
||||
: direction_tb
|
||||
{ yy.setDirection('TB');}
|
||||
| direction_bt
|
||||
{ yy.setDirection('BT');}
|
||||
| direction_rl
|
||||
{ yy.setDirection('RL');}
|
||||
| direction_lr
|
||||
{ yy.setDirection('LR');}
|
||||
;
|
||||
|
||||
mermaidDoc
|
||||
: graphConfig
|
||||
;
|
||||
|
||||
directive
|
||||
: openDirective typeDirective closeDirective NEWLINE
|
||||
| openDirective typeDirective ':' argDirective closeDirective NEWLINE
|
||||
;
|
||||
|
||||
openDirective
|
||||
: open_directive { console.log("open_directive: ", $1); yy.parseDirective('%%{', 'open_directive'); }
|
||||
;
|
||||
|
||||
typeDirective
|
||||
: type_directive { }
|
||||
;
|
||||
|
||||
argDirective
|
||||
: arg_directive { $1 = $1.trim().replace(/'/g, '"'); console.log("arg_directive: ", $1); yy.parseDirective($1, 'arg_directive'); }
|
||||
;
|
||||
|
||||
closeDirective
|
||||
: close_directive { console.log("close_directive: ", $1); yy.parseDirective('}%%', 'close_directive', 'c4Context'); }
|
||||
;
|
||||
|
||||
graphConfig
|
||||
: C4_CONTEXT NEWLINE statements EOF {yy.setC4Type($1)}
|
||||
| C4_CONTAINER NEWLINE statements EOF {yy.setC4Type($1)}
|
||||
| C4_COMPONENT NEWLINE statements EOF {yy.setC4Type($1)}
|
||||
| C4_DYNAMIC NEWLINE statements EOF {yy.setC4Type($1)}
|
||||
| C4_DEPLOYMENT NEWLINE statements EOF {yy.setC4Type($1)}
|
||||
;
|
||||
|
||||
statements
|
||||
: otherStatements
|
||||
| diagramStatements
|
||||
| otherStatements diagramStatements
|
||||
;
|
||||
|
||||
otherStatements
|
||||
: otherStatement
|
||||
| otherStatement NEWLINE
|
||||
| otherStatement NEWLINE otherStatements
|
||||
;
|
||||
|
||||
otherStatement
|
||||
: title {yy.setTitle($1.substring(6));$$=$1.substring(6);}
|
||||
| accDescription {yy.setAccDescription($1.substring(15));$$=$1.substring(15);}
|
||||
;
|
||||
|
||||
boundaryStatement
|
||||
: boundaryStartStatement diagramStatements boundaryStopStatement
|
||||
;
|
||||
|
||||
boundaryStartStatement
|
||||
: boundaryStart LBRACE NEWLINE
|
||||
| boundaryStart NEWLINE LBRACE
|
||||
| boundaryStart NEWLINE LBRACE NEWLINE
|
||||
;
|
||||
|
||||
boundaryStart
|
||||
: ENTERPRISE_BOUNDARY attributes {console.log($1,JSON.stringify($2)); $2.splice(2, 0, 'ENTERPRISE'); yy.addPersonOrSystemBoundary(...$2); $$=$2;}
|
||||
| SYSTEM_BOUNDARY attributes {console.log($1,JSON.stringify($2)); $2.splice(2, 0, 'ENTERPRISE'); yy.addPersonOrSystemBoundary(...$2); $$=$2;}
|
||||
| BOUNDARY attributes {console.log($1,JSON.stringify($2)); yy.addPersonOrSystemBoundary(...$2); $$=$2;}
|
||||
| CONTAINER_BOUNDARY attributes {console.log($1,JSON.stringify($2)); $2.splice(2, 0, 'CONTAINER'); yy.addContainerBoundary(...$2); $$=$2;}
|
||||
| NODE attributes {console.log($1,JSON.stringify($2)); yy.addDeploymentNode('node', ...$2); $$=$2;}
|
||||
| NODE_L attributes {console.log($1,JSON.stringify($2)); yy.addDeploymentNode('nodeL', ...$2); $$=$2;}
|
||||
| NODE_R attributes {console.log($1,JSON.stringify($2)); yy.addDeploymentNode('nodeR', ...$2); $$=$2;}
|
||||
;
|
||||
|
||||
boundaryStopStatement
|
||||
: RBRACE { yy.popBoundaryParseStack() }
|
||||
;
|
||||
|
||||
diagramStatements
|
||||
: diagramStatement
|
||||
| diagramStatement NEWLINE
|
||||
| diagramStatement NEWLINE statements
|
||||
;
|
||||
|
||||
diagramStatement
|
||||
: PERSON attributes {console.log($1,JSON.stringify($2)); yy.addPersonOrSystem('person', ...$2); $$=$2;}
|
||||
| PERSON_EXT attributes {console.log($1,JSON.stringify($2)); yy.addPersonOrSystem('external_person', ...$2); $$=$2;}
|
||||
| SYSTEM attributes {console.log($1,JSON.stringify($2)); yy.addPersonOrSystem('system', ...$2); $$=$2;}
|
||||
| SYSTEM_DB attributes {console.log($1,JSON.stringify($2)); yy.addPersonOrSystem('system_db', ...$2); $$=$2;}
|
||||
| SYSTEM_QUEUE attributes {console.log($1,JSON.stringify($2)); yy.addPersonOrSystem('system_queue', ...$2); $$=$2;}
|
||||
| SYSTEM_EXT attributes {console.log($1,JSON.stringify($2)); yy.addPersonOrSystem('external_system', ...$2); $$=$2;}
|
||||
| SYSTEM_EXT_DB attributes {console.log($1,JSON.stringify($2)); yy.addPersonOrSystem('external_system_db', ...$2); $$=$2;}
|
||||
| SYSTEM_EXT_QUEUE attributes {console.log($1,JSON.stringify($2)); yy.addPersonOrSystem('external_system_queue', ...$2); $$=$2;}
|
||||
| CONTAINER attributes {console.log($1,JSON.stringify($2)); yy.addContainer('container', ...$2); $$=$2;}
|
||||
| CONTAINER_DB attributes {console.log($1,JSON.stringify($2)); yy.addContainer('container_db', ...$2); $$=$2;}
|
||||
| CONTAINER_QUEUE attributes {console.log($1,JSON.stringify($2)); yy.addContainer('container_queue', ...$2); $$=$2;}
|
||||
| CONTAINER_EXT attributes {console.log($1,JSON.stringify($2)); yy.addContainer('external_container', ...$2); $$=$2;}
|
||||
| CONTAINER_EXT_DB attributes {console.log($1,JSON.stringify($2)); yy.addContainer('external_container_db', ...$2); $$=$2;}
|
||||
| CONTAINER_EXT_QUEUE attributes {console.log($1,JSON.stringify($2)); yy.addContainer('external_container_queue', ...$2); $$=$2;}
|
||||
| COMPONENT attributes {console.log($1,JSON.stringify($2)); yy.addComponent('component', ...$2); $$=$2;}
|
||||
| COMPONENT_DB attributes {console.log($1,JSON.stringify($2)); yy.addComponent('component_db', ...$2); $$=$2;}
|
||||
| COMPONENT_QUEUE attributes {console.log($1,JSON.stringify($2)); yy.addComponent('component_queue', ...$2); $$=$2;}
|
||||
| COMPONENT_EXT attributes {console.log($1,JSON.stringify($2)); yy.addComponent('external_component', ...$2); $$=$2;}
|
||||
| COMPONENT_EXT_DB attributes {console.log($1,JSON.stringify($2)); yy.addComponent('external_component_db', ...$2); $$=$2;}
|
||||
| COMPONENT_EXT_QUEUE attributes {console.log($1,JSON.stringify($2)); yy.addComponent('external_component_queue', ...$2); $$=$2;}
|
||||
| boundaryStatement
|
||||
| REL attributes {console.log($1,JSON.stringify($2)); yy.addRel('rel', ...$2); $$=$2;}
|
||||
| BIREL attributes {console.log($1,JSON.stringify($2)); yy.addRel('birel', ...$2); $$=$2;}
|
||||
| REL_U attributes {console.log($1,JSON.stringify($2)); yy.addRel('rel_u', ...$2); $$=$2;}
|
||||
| REL_D attributes {console.log($1,JSON.stringify($2)); yy.addRel('rel_d', ...$2); $$=$2;}
|
||||
| REL_L attributes {console.log($1,JSON.stringify($2)); yy.addRel('rel_l', ...$2); $$=$2;}
|
||||
| REL_R attributes {console.log($1,JSON.stringify($2)); yy.addRel('rel_r', ...$2); $$=$2;}
|
||||
| REL_B attributes {console.log($1,JSON.stringify($2)); yy.addRel('rel_b', ...$2); $$=$2;}
|
||||
| REL_INDEX attributes {console.log($1,JSON.stringify($2)); $2.splice(0, 1); yy.addRel('rel', ...$2); $$=$2;}
|
||||
;
|
||||
|
||||
attributes
|
||||
: attribute { console.log('PUSH ATTRIBUTE: ', $1); $$ = [$1]; }
|
||||
| attribute attributes { console.log('PUSH ATTRIBUTE: ', $1); $2.unshift($1); $$=$2;}
|
||||
;
|
||||
|
||||
attribute
|
||||
: STR { $$ = $1.trim(); }
|
||||
| ATTRIBUTE { $$ = $1.trim(); }
|
||||
| ATTRIBUTE_EMPTY { $$ = ""; }
|
||||
;
|
||||
|
8
src/diagrams/c4/styles.js
Normal file
8
src/diagrams/c4/styles.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const getStyles = (options) =>
|
||||
`.person {
|
||||
stroke: ${options.personBorder};
|
||||
fill: ${options.personBkg};
|
||||
}
|
||||
`;
|
||||
|
||||
export default getStyles;
|
884
src/diagrams/c4/svgDraw.js
Normal file
884
src/diagrams/c4/svgDraw.js
Normal file
File diff suppressed because one or more lines are too long
@@ -5,8 +5,8 @@ import common from '../common/common';
|
||||
import utils from '../../utils';
|
||||
import mermaidAPI from '../../mermaidAPI';
|
||||
import {
|
||||
setTitle,
|
||||
getTitle,
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
clear as commonClear,
|
||||
@@ -142,7 +142,6 @@ export const addAnnotation = function (className, annotation) {
|
||||
* @public
|
||||
*/
|
||||
export const addMember = function (className, member) {
|
||||
console.log(className, member);
|
||||
const validatedClassName = splitClassNameAndType(className).className;
|
||||
const theClass = classes[validatedClassName];
|
||||
|
||||
@@ -356,8 +355,8 @@ const setDirection = (dir) => {
|
||||
|
||||
export default {
|
||||
parseDirective,
|
||||
setTitle,
|
||||
getTitle,
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
getConfig: () => configApi.getConfig().class,
|
||||
|
@@ -110,14 +110,13 @@ describe('class diagram, ', function () {
|
||||
' flightNumber : Integer\n' +
|
||||
' departureTime : Date\n' +
|
||||
'}';
|
||||
let testPased = false;
|
||||
let testPassed = false;
|
||||
try {
|
||||
parser.parse(str);
|
||||
} catch (error) {
|
||||
console.log(error.name);
|
||||
testPased = true;
|
||||
testPassed = true;
|
||||
}
|
||||
expect(testPased).toBe(true);
|
||||
expect(testPassed).toBe(true);
|
||||
});
|
||||
|
||||
it('should break when EOF is encountered before closing the first `{` while defining generic class with brackets', function () {
|
||||
@@ -129,14 +128,13 @@ describe('class diagram, ', function () {
|
||||
'}\n' +
|
||||
'\n' +
|
||||
'class Dummy_Class {\n';
|
||||
let testPased = false;
|
||||
let testPassed = false;
|
||||
try {
|
||||
parser.parse(str);
|
||||
} catch (error) {
|
||||
console.log(error.name);
|
||||
testPased = true;
|
||||
testPassed = true;
|
||||
}
|
||||
expect(testPased).toBe(true);
|
||||
expect(testPassed).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle generic class with brackets', function () {
|
||||
@@ -552,7 +550,7 @@ foo()
|
||||
`;
|
||||
|
||||
parser.parse(str);
|
||||
expect(parser.yy.getTitle()).toBe('My Title');
|
||||
expect(parser.yy.getAccTitle()).toBe('My Title');
|
||||
expect(parser.yy.getAccDescription()).toBe('My Description');
|
||||
});
|
||||
it('should handle accTitle and multiline accDescr', function () {
|
||||
@@ -567,7 +565,7 @@ foo()
|
||||
`;
|
||||
|
||||
parser.parse(str);
|
||||
expect(parser.yy.getTitle()).toBe('My Title');
|
||||
expect(parser.yy.getAccTitle()).toBe('My Title');
|
||||
expect(parser.yy.getAccDescription()).toBe('This is mu multi\nline description');
|
||||
});
|
||||
|
||||
|
@@ -9,7 +9,7 @@ import { getConfig } from '../../config';
|
||||
import { render } from '../../dagre-wrapper/index.js';
|
||||
// import addHtmlLabel from 'dagre-d3/lib/label/add-html-label.js';
|
||||
import { curveLinear } from 'd3';
|
||||
import { interpolateToCurve, getStylesFromArray, configureSvgSize } from '../../utils';
|
||||
import { interpolateToCurve, getStylesFromArray, setupGraphViewbox } from '../../utils';
|
||||
import common from '../common/common';
|
||||
import addSVGAccessibilityFields from '../../accessibility';
|
||||
|
||||
@@ -322,16 +322,6 @@ export const draw = function (text, id) {
|
||||
const relations = classDb.getRelations();
|
||||
|
||||
log.info(relations);
|
||||
// let i = 0;
|
||||
// for (i = subGraphs.length - 1; i >= 0; i--) {
|
||||
// subG = subGraphs[i];
|
||||
|
||||
// selectAll('cluster').append('text');
|
||||
|
||||
// for (let j = 0; j < subG.nodes.length; j++) {
|
||||
// g.setParent(subG.nodes[j], subG.id);
|
||||
// }
|
||||
// }
|
||||
addClasses(classes, g, id);
|
||||
addRelations(relations, g);
|
||||
|
||||
@@ -354,28 +344,7 @@ export const draw = function (text, id) {
|
||||
const element = root.select('#' + id + ' g');
|
||||
render(element, g, ['aggregation', 'extension', 'composition', 'dependency'], 'classDiagram', id);
|
||||
|
||||
// element.selectAll('g.node').attr('title', function() {
|
||||
// return flowDb.getTooltip(this.id);
|
||||
// });
|
||||
|
||||
const padding = 8;
|
||||
const svgBounds = svg.node().getBBox();
|
||||
const width = svgBounds.width + padding * 2;
|
||||
const height = svgBounds.height + padding * 2;
|
||||
log.debug(
|
||||
`new ViewBox 0 0 ${width} ${height}`,
|
||||
`translate(${padding - g._label.marginx}, ${padding - g._label.marginy})`
|
||||
);
|
||||
|
||||
configureSvgSize(svg, height, width, conf.useMaxWidth);
|
||||
|
||||
svg.attr('viewBox', `0 0 ${width} ${height}`);
|
||||
svg
|
||||
.select('g')
|
||||
.attr('transform', `translate(${padding - g._label.marginx}, ${padding - svgBounds.y})`);
|
||||
|
||||
// Index nodes
|
||||
// flowDb.indexNodes('subGraph' + i);
|
||||
setupGraphViewbox(g, svg, conf.diagramPadding, conf.useMaxWidth);
|
||||
|
||||
// Add label rects for non html labels
|
||||
if (!conf.htmlLabels) {
|
||||
|
@@ -263,7 +263,7 @@ statement
|
||||
| cssClassStatement
|
||||
| directive
|
||||
| direction
|
||||
| acc_title acc_title_value { $$=$2.trim();yy.setTitle($$); }
|
||||
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
|
||||
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
|
||||
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); }
|
||||
;
|
||||
|
@@ -100,7 +100,6 @@ describe('Sanitize text', function () {
|
||||
securityLevel: 'strict',
|
||||
flowchart: { htmlLabels: true },
|
||||
});
|
||||
console.log('result', result);
|
||||
expect(result).not.toContain('javascript:alert(1)');
|
||||
});
|
||||
});
|
||||
|
@@ -3,8 +3,8 @@ import mermaidAPI from '../../mermaidAPI';
|
||||
import * as configApi from '../../config';
|
||||
import common from '../common/common';
|
||||
import {
|
||||
setTitle,
|
||||
getTitle,
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
clear as commonClear,
|
||||
@@ -93,8 +93,8 @@ export default {
|
||||
addRelationship,
|
||||
getRelationships,
|
||||
clear,
|
||||
setTitle,
|
||||
getTitle,
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
setAccDescription,
|
||||
getAccDescription,
|
||||
};
|
||||
|
@@ -94,14 +94,15 @@ statement
|
||||
}
|
||||
| entityName BLOCK_START BLOCK_STOP { yy.addEntity($1); }
|
||||
| entityName { yy.addEntity($1); }
|
||||
| title title_value { $$=$2.trim();yy.setTitle($$); }
|
||||
| acc_title acc_title_value { $$=$2.trim();yy.setTitle($$); }
|
||||
| title title_value { $$=$2.trim();yy.setAccTitle($$); }
|
||||
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
|
||||
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
|
||||
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); }
|
||||
;
|
||||
|
||||
entityName
|
||||
: 'ALPHANUM' { $$ = $1; /*console.log('Entity: ' + $1);*/ }
|
||||
| 'ALPHANUM' '.' entityName { $$ = $1 + $2 + $3; }
|
||||
;
|
||||
|
||||
attributes
|
||||
|
@@ -191,7 +191,7 @@ describe('when parsing ER diagram it...', function () {
|
||||
accDescr: this graph is about stuff
|
||||
${line1}`
|
||||
);
|
||||
expect(erDb.getTitle()).toBe('graph title');
|
||||
expect(erDb.getAccTitle()).toBe('graph title');
|
||||
expect(erDb.getAccDescription()).toBe('this graph is about stuff');
|
||||
});
|
||||
|
||||
@@ -207,7 +207,7 @@ describe('when parsing ER diagram it...', function () {
|
||||
}\n
|
||||
${line1}`
|
||||
);
|
||||
expect(erDb.getTitle()).toBe('graph title');
|
||||
expect(erDb.getAccTitle()).toBe('graph title');
|
||||
expect(erDb.getAccDescription()).toBe('this graph is about stuff');
|
||||
});
|
||||
|
||||
@@ -426,4 +426,10 @@ describe('when parsing ER diagram it...', function () {
|
||||
const rels = erDb.getRelationships();
|
||||
expect(rels[0].roleA).toBe('places');
|
||||
});
|
||||
|
||||
it('should allow an entity name with a dot', function () {
|
||||
erDiagram.parser.parse('erDiagram\nCUSTOMER.PROP ||--|{ ORDER : places');
|
||||
const rels = erDb.getRelationships();
|
||||
expect(rels[0].entityA).toBe('CUSTOMER.PROP');
|
||||
});
|
||||
});
|
||||
|
@@ -5,8 +5,8 @@ import common from '../common/common';
|
||||
import mermaidAPI from '../../mermaidAPI';
|
||||
import { log } from '../../logger';
|
||||
import {
|
||||
setTitle,
|
||||
getTitle,
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
clear as commonClear,
|
||||
@@ -746,8 +746,8 @@ const makeUniq = (sg, allSubgraphs) => {
|
||||
export default {
|
||||
parseDirective,
|
||||
defaultConfig: () => configApi.defaultConfig.flowchart,
|
||||
setTitle,
|
||||
getTitle,
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
addVertex,
|
||||
|
@@ -9,7 +9,7 @@ import { render } from '../../dagre-wrapper/index.js';
|
||||
import addHtmlLabel from 'dagre-d3/lib/label/add-html-label.js';
|
||||
import { log } from '../../logger';
|
||||
import common, { evaluate } from '../common/common';
|
||||
import { interpolateToCurve, getStylesFromArray, configureSvgSize } from '../../utils';
|
||||
import { interpolateToCurve, getStylesFromArray, setupGraphViewbox } from '../../utils';
|
||||
import addSVGAccessibilityFields from '../../accessibility';
|
||||
|
||||
const conf = {};
|
||||
@@ -452,21 +452,7 @@ export const draw = function (text, id) {
|
||||
const element = root.select('#' + id + ' g');
|
||||
render(element, g, ['point', 'circle', 'cross'], 'flowchart', id);
|
||||
|
||||
const padding = conf.diagramPadding;
|
||||
const svgBounds = svg.node().getBBox();
|
||||
const width = svgBounds.width + padding * 2;
|
||||
const height = svgBounds.height + padding * 2;
|
||||
log.debug(
|
||||
`new ViewBox 0 0 ${width} ${height}`,
|
||||
`translate(${padding - g._label.marginx}, ${padding - g._label.marginy})`
|
||||
);
|
||||
|
||||
configureSvgSize(svg, height, width, conf.useMaxWidth);
|
||||
|
||||
svg.attr('viewBox', `0 0 ${width} ${height}`);
|
||||
svg
|
||||
.select('g')
|
||||
.attr('transform', `translate(${padding - g._label.marginx}, ${padding - svgBounds.y})`);
|
||||
setupGraphViewbox(g, svg, conf.diagramPadding, conf.useMaxWidth);
|
||||
|
||||
// Index nodes
|
||||
flowDb.indexNodes('subGraph' + i);
|
||||
|
@@ -9,7 +9,7 @@ import dagreD3 from 'dagre-d3';
|
||||
import addHtmlLabel from 'dagre-d3/lib/label/add-html-label.js';
|
||||
import { log } from '../../logger';
|
||||
import common, { evaluate } from '../common/common';
|
||||
import { interpolateToCurve, getStylesFromArray, configureSvgSize } from '../../utils';
|
||||
import { interpolateToCurve, getStylesFromArray, setupGraphViewbox } from '../../utils';
|
||||
import flowChartShapes from './flowChartShapes';
|
||||
import addSVGAccessibilityFields from '../../accessibility';
|
||||
|
||||
@@ -427,8 +427,6 @@ export const draw = function (text, id) {
|
||||
const svg = root.select(`[id="${id}"]`);
|
||||
svg.attr('xmlns:xlink', 'http://www.w3.org/1999/xlink');
|
||||
|
||||
log.warn(g);
|
||||
|
||||
// Adds title and description to the flow chart
|
||||
addSVGAccessibilityFields(parser.yy, svg, id);
|
||||
|
||||
@@ -440,18 +438,6 @@ export const draw = function (text, id) {
|
||||
return flowDb.getTooltip(this.id);
|
||||
});
|
||||
|
||||
const padding = conf.diagramPadding;
|
||||
const svgBounds = svg.node().getBBox();
|
||||
const width = svgBounds.width + padding * 2;
|
||||
const height = svgBounds.height + padding * 2;
|
||||
|
||||
configureSvgSize(svg, height, width, conf.useMaxWidth);
|
||||
|
||||
// Ensure the viewBox includes the whole svgBounds area with extra space for padding
|
||||
const vBox = `${svgBounds.x - padding} ${svgBounds.y - padding} ${width} ${height}`;
|
||||
log.debug(`viewBox ${vBox}`);
|
||||
svg.attr('viewBox', vBox);
|
||||
|
||||
// Index nodes
|
||||
flowDb.indexNodes('subGraph' + i);
|
||||
|
||||
@@ -468,10 +454,10 @@ export const draw = function (text, id) {
|
||||
|
||||
const xPos = clusterRects[0].x.baseVal.value;
|
||||
const yPos = clusterRects[0].y.baseVal.value;
|
||||
const width = clusterRects[0].width.baseVal.value;
|
||||
const _width = clusterRects[0].width.baseVal.value;
|
||||
const cluster = select(clusterEl[0]);
|
||||
const te = cluster.select('.label');
|
||||
te.attr('transform', `translate(${xPos + width / 2}, ${yPos + 14})`);
|
||||
te.attr('transform', `translate(${xPos + _width / 2}, ${yPos + 14})`);
|
||||
te.attr('id', id + 'Text');
|
||||
|
||||
for (let j = 0; j < subG.classes.length; j++) {
|
||||
@@ -499,6 +485,7 @@ export const draw = function (text, id) {
|
||||
label.insertBefore(rect, label.firstChild);
|
||||
}
|
||||
}
|
||||
setupGraphViewbox(g, svg, conf.diagramPadding, conf.useMaxWidth);
|
||||
|
||||
// If node has a link, wrap it in an anchor SVG object.
|
||||
const keys = Object.keys(vert);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user