diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c92f8d573..9ed0a7edd 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -6,6 +6,14 @@ So you want to help? That's great!
Here are a few things to know to get you started on the right path.
+## Development Installation
+```bash
+git clone git@github.com:mermaid-js/mermaid.git
+cd mermaid
+yarn
+yarn test
+```
+
## Committing code
We make all changes via pull requests. As we have many pull requests from developers new to mermaid, the current approach is to have *knsv, Knut Sveidqvist* as a main reviewer of changes and merging pull requests. More precisely like this:
diff --git a/README.md b/README.md
index 4d86b6a43..0e49ea4da 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ English | [简体中文](./README.zh-CN.md)
## About
-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.
@@ -219,6 +219,77 @@ pie
Sit down: 3: Me
```
+### C4 diagram [docs]
+
+```
+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,
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")
+```
+```mermaid
+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,
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")
+```
+
## Release
For those who have the permission to do so:
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 91297c323..3042304d9 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -202,6 +202,77 @@ pie
Sit down: 3: Me
```
+### C4 图 [文档]
+
+```
+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,
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")
+```
+```mermaid
+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,
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")
+```
+
## 发布
对于有权限的同学来说,你可以通过以下步骤来完成发布操作:
diff --git a/cypress/e2e/other/configuration.spec.js b/cypress/e2e/other/configuration.spec.js
index a92aae52b..a67758d9c 100644
--- a/cypress/e2e/other/configuration.spec.js
+++ b/cypress/e2e/other/configuration.spec.js
@@ -41,7 +41,7 @@ describe('Configuration', () => {
.should('exist')
.and('include', 'url(#');
});
- it('should handle arrowMarkerAbsolute excplicitly set to false', () => {
+ it('should handle arrowMarkerAbsolute explicitly set to false', () => {
renderGraph(
`graph TD
A[Christmas] -->|Get money| B(Go shopping)
@@ -63,7 +63,7 @@ describe('Configuration', () => {
.should('exist')
.and('include', 'url(#');
});
- it('should handle arrowMarkerAbsolute excplicitly set to "false" as false', () => {
+ it('should handle arrowMarkerAbsolute explicitly set to "false" as false', () => {
renderGraph(
`graph TD
A[Christmas] -->|Get money| B(Go shopping)
diff --git a/cypress/e2e/other/ghsa.spec.js b/cypress/e2e/other/ghsa.spec.js
new file mode 100644
index 000000000..5b168a8a8
--- /dev/null
+++ b/cypress/e2e/other/ghsa.spec.js
@@ -0,0 +1,10 @@
+import { urlSnapshotTest } from '../../helpers/util';
+
+describe('CSS injections', () => {
+ it('should not allow CSS injections outside of the diagram', () => {
+ urlSnapshotTest('http://localhost:9000/ghsa1.html', {
+ logLevel: 1,
+ flowchart: { htmlLabels: false },
+ });
+ });
+});
diff --git a/cypress/e2e/other/rerender.spec.js b/cypress/e2e/other/rerender.spec.js
index 8e8c68694..f160a2e27 100644
--- a/cypress/e2e/other/rerender.spec.js
+++ b/cypress/e2e/other/rerender.spec.js
@@ -1,5 +1,5 @@
describe('Rerendering', () => {
- it('should be able to render after an error has occured', () => {
+ it('should be able to render after an error has occurred', () => {
const url = 'http://localhost:9000/render-after-error.html';
cy.viewport(1440, 1024);
cy.visit(url);
diff --git a/cypress/e2e/other/xss.spec.js b/cypress/e2e/other/xss.spec.js
index 6226feaeb..912354f7d 100644
--- a/cypress/e2e/other/xss.spec.js
+++ b/cypress/e2e/other/xss.spec.js
@@ -60,52 +60,52 @@ describe('XSS', () => {
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
- it('should not allow maniplulating htmlLabels into a false positive', () => {
+ it('should not allow manipulating htmlLabels into a false positive', () => {
cy.visit('http://localhost:9000/xss4.html');
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
- it('should not allow maniplulating antiscript to run javascript', () => {
+ it('should not allow manipulating antiscript to run javascript', () => {
cy.visit('http://localhost:9000/xss5.html');
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
- it('should not allow maniplulating antiscript to run javascript using onerror', () => {
+ it('should not allow manipulating antiscript to run javascript using onerror', () => {
cy.visit('http://localhost:9000/xss6.html');
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
- it('should not allow maniplulating antiscript to run javascript using onerror in state diagrams with dagre wrapper', () => {
+ it('should not allow manipulating antiscript to run javascript using onerror in state diagrams with dagre wrapper', () => {
cy.visit('http://localhost:9000/xss8.html');
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
- it('should not allow maniplulating antiscript to run javascript using onerror in state diagrams with dagre d3', () => {
+ it('should not allow manipulating antiscript to run javascript using onerror in state diagrams with dagre d3', () => {
cy.visit('http://localhost:9000/xss9.html');
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
- it('should not allow maniplulating antiscript to run javascript using onerror in state diagrams with dagre d3', () => {
+ it('should not allow manipulating antiscript to run javascript using onerror in state diagrams with dagre d3', () => {
cy.visit('http://localhost:9000/xss10.html');
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
- it('should not allow maniplulating antiscript to run javascript using onerror in state diagrams with dagre d3', () => {
+ it('should not allow manipulating antiscript to run javascript using onerror in state diagrams with dagre d3', () => {
cy.visit('http://localhost:9000/xss11.html');
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
- it('should not allow maniplulating antiscript to run javascript using onerror in state diagrams with dagre d3', () => {
+ it('should not allow manipulating antiscript to run javascript using onerror in state diagrams with dagre d3', () => {
cy.visit('http://localhost:9000/xss12.html');
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
- it('should not allow maniplulating antiscript to run javascript using onerror in state diagrams with dagre d3', () => {
+ it('should not allow manipulating antiscript to run javascript using onerror in state diagrams with dagre d3', () => {
cy.visit('http://localhost:9000/xss13.html');
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
- it('should not allow maniplulating antiscript to run javascript iframes in class diagrams', () => {
+ it('should not allow manipulating antiscript to run javascript iframes in class diagrams', () => {
cy.visit('http://localhost:9000/xss14.html');
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
diff --git a/cypress/e2e/rendering/classDiagram-v2.spec.js b/cypress/e2e/rendering/classDiagram-v2.spec.js
index fd373da73..d285a9237 100644
--- a/cypress/e2e/rendering/classDiagram-v2.spec.js
+++ b/cypress/e2e/rendering/classDiagram-v2.spec.js
@@ -381,7 +381,7 @@ describe('Class diagram V2', () => {
cy.get('svg');
});
- it('16b: should handle the direction statemnent with TB', () => {
+ it('16b: should handle the direction statement with TB', () => {
imgSnapshotTest(
`
classDiagram
@@ -406,7 +406,7 @@ describe('Class diagram V2', () => {
cy.get('svg');
});
- it('18: should handle the direction statemnent with LR', () => {
+ it('18: should handle the direction statement with LR', () => {
imgSnapshotTest(
`
classDiagram
@@ -430,7 +430,7 @@ describe('Class diagram V2', () => {
);
cy.get('svg');
});
- it('17a: should handle the direction statemnent with BT', () => {
+ it('17a: should handle the direction statement with BT', () => {
imgSnapshotTest(
`
classDiagram
@@ -454,7 +454,7 @@ describe('Class diagram V2', () => {
);
cy.get('svg');
});
- it('17b: should handle the direction statemment with RL', () => {
+ it('17b: should handle the direction statement with RL', () => {
imgSnapshotTest(
`
classDiagram
diff --git a/cypress/e2e/rendering/conf-and-directives.spec.js b/cypress/e2e/rendering/conf-and-directives.spec.js
index 76de5871d..3fc0f7f02 100644
--- a/cypress/e2e/rendering/conf-and-directives.spec.js
+++ b/cypress/e2e/rendering/conf-and-directives.spec.js
@@ -16,7 +16,7 @@ describe('Configuration and directives - nodes should be light blue', () => {
);
cy.get('svg');
});
- it('Settigns from intitialize - nodes should be green', () => {
+ it('Settings from initialize - nodes should be green', () => {
imgSnapshotTest(
`
graph TD
@@ -30,7 +30,7 @@ graph TD
);
cy.get('svg');
});
- it('Settings from initialize overriding themeVariable - nodes shold be red', () => {
+ it('Settings from initialize overriding themeVariable - nodes should be red', () => {
imgSnapshotTest(
`
diff --git a/cypress/e2e/rendering/sequencediagram.spec.js b/cypress/e2e/rendering/sequencediagram.spec.js
index 1122a3009..c110f05ad 100644
--- a/cypress/e2e/rendering/sequencediagram.spec.js
+++ b/cypress/e2e/rendering/sequencediagram.spec.js
@@ -76,7 +76,7 @@ context('Sequence diagram', () => {
imgSnapshotTest(
`
sequenceDiagram
- Alice->>Bob: Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
+ Alice->>Bob: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
loop Loopy
Bob->>Alice: Pasten
end `,
@@ -143,7 +143,7 @@ context('Sequence diagram', () => {
imgSnapshotTest(
`
sequenceDiagram
- participant A as Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
+ participant A as Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
A->>Bob: Hola
Bob-->A: Pasten !
`,
@@ -154,7 +154,7 @@ context('Sequence diagram', () => {
imgSnapshotTest(
`
sequenceDiagram
- participant A as wrap:Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
+ participant A as wrap:Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
A->>Bob: Hola
Bob-->A: Pasten !
`,
@@ -166,7 +166,7 @@ context('Sequence diagram', () => {
`
%%{init: {'config': {'wrap': true }}}%%
sequenceDiagram
- participant A as Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
+ participant A as Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
A->>Bob: Hola
Bob-->A: Pasten !
`,
@@ -190,7 +190,7 @@ context('Sequence diagram', () => {
`
sequenceDiagram
Alice->>Bob: Hola
- Note left of Alice: Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
+ Note left of Alice: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
`,
{}
@@ -201,7 +201,7 @@ context('Sequence diagram', () => {
`
sequenceDiagram
Alice->>Bob: Hola
- Note left of Alice:wrap: Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
+ Note left of Alice:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
`,
{}
@@ -212,7 +212,7 @@ context('Sequence diagram', () => {
`
sequenceDiagram
Alice->>Bob: Hola
- Note right of Alice: Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
+ Note right of Alice: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
`,
{}
@@ -223,7 +223,7 @@ context('Sequence diagram', () => {
`
sequenceDiagram
Alice->>Bob: Hola
- Note right of Alice:wrap: Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
+ Note right of Alice:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
`,
{}
@@ -234,7 +234,7 @@ context('Sequence diagram', () => {
`
sequenceDiagram
Alice->>Bob: Hola
- Note over Alice: Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
+ Note over Alice: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
`,
{}
@@ -245,7 +245,7 @@ context('Sequence diagram', () => {
`
sequenceDiagram
Alice->>Bob: Hola
- Note over Alice:wrap: Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
+ Note over Alice:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
`,
{}
@@ -255,7 +255,7 @@ context('Sequence diagram', () => {
imgSnapshotTest(
`
sequenceDiagram
- Alice->>Bob: Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
+ Alice->>Bob: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
`,
{}
@@ -265,7 +265,7 @@ context('Sequence diagram', () => {
imgSnapshotTest(
`
sequenceDiagram
- Alice->>Bob:wrap:Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
+ Alice->>Bob:wrap:Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
`,
{}
@@ -276,7 +276,7 @@ context('Sequence diagram', () => {
`
sequenceDiagram
Alice->>Bob: I'm short
- Bob->>Alice: Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
+ Bob->>Alice: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
`,
{}
);
@@ -286,7 +286,7 @@ context('Sequence diagram', () => {
`
sequenceDiagram
Alice->>Bob: I'm short
- Bob->>Alice:wrap: Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
+ Bob->>Alice:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
`,
{}
);
diff --git a/cypress/helpers/util.js b/cypress/helpers/util.js
index bef409936..dd3fdd2c9 100644
--- a/cypress/helpers/util.js
+++ b/cypress/helpers/util.js
@@ -70,6 +70,56 @@ export const imgSnapshotTest = (graphStr, _options, api = false, validation) =>
}
};
+export const urlSnapshotTest = (url, _options, api = false, validation) => {
+ cy.log(_options);
+ const options = Object.assign(_options);
+ if (!options.fontFamily) {
+ options.fontFamily = 'courier';
+ }
+ if (!options.sequence) {
+ options.sequence = {};
+ }
+ if (!options.sequence || (options.sequence && !options.sequence.actorFontFamily)) {
+ options.sequence.actorFontFamily = 'courier';
+ }
+ if (options.sequence && !options.sequence.noteFontFamily) {
+ options.sequence.noteFontFamily = 'courier';
+ }
+ options.sequence.actorFontFamily = 'courier';
+ options.sequence.noteFontFamily = 'courier';
+ options.sequence.messageFontFamily = 'courier';
+ if (options.sequence && !options.sequence.actorFontFamily) {
+ options.sequence.actorFontFamily = 'courier';
+ }
+ if (!options.fontSize) {
+ options.fontSize = '16px';
+ }
+ const useAppli = Cypress.env('useAppli');
+ const branch = Cypress.env('codeBranch');
+ cy.log('Hello ' + useAppli ? 'Appli' : 'image-snapshot');
+ const name = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
+
+ if (useAppli) {
+ cy.eyesOpen({
+ appName: 'Mermaid-' + branch,
+ testName: name,
+ batchName: branch,
+ });
+ }
+
+ cy.visit(url);
+ if (validation) cy.get('svg').should(validation);
+ cy.get('body');
+ // Default name to test title
+
+ if (useAppli) {
+ cy.eyesCheckWindow('Click!');
+ cy.eyesClose();
+ } else {
+ cy.matchImageSnapshot(name);
+ }
+};
+
export const renderGraph = (graphStr, options, api) => {
const url = mermaidUrl(graphStr, options, api);
diff --git a/cypress/platform/current2.html b/cypress/platform/current2.html
index 16ddb4a99..6f9c2184a 100644
--- a/cypress/platform/current2.html
+++ b/cypress/platform/current2.html
@@ -35,7 +35,7 @@ flowchart BT