Merge remote-tracking branch 'origin/master' into flow/trapezoid
|
@ -7,3 +7,4 @@ dist/*.js
|
|||
dist/*.map
|
||||
|
||||
yarn-error.log
|
||||
.npmrc
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"typescript.format.enable": false,
|
||||
"typescript.reportStyleChecksAsWarnings": false,
|
||||
"typescript.validate.enable": false,
|
||||
"javascript.validate.enable": false,
|
||||
"editor.formatOnSave": false
|
||||
}
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Adding weekend ignore do Gantt [\$314] (https://github.com/knsv/mermaid/issues/314)
|
||||
|
||||
- Adding init argument to the global API [\#137](https://github.com/knsv/mermaid/pull/137) ([bollwyvl](https://github.com/bollwyvl))
|
||||
|
||||
- Add description of manual calling of init [\#136](https://github.com/knsv/mermaid/pull/136) ([bollwyvl](https://github.com/bollwyvl))
|
||||
|
|
22
README.md
|
@ -12,6 +12,23 @@ Ever wanted to simplify documentation and avoid heavy tools like Visio when expl
|
|||
|
||||
This is why mermaid was born, a simple markdown-like script language for generating charts from text via javascript.
|
||||
|
||||
**Mermaid was nomiated and won the JS Open Source Awards (2019) in the catory The most existing use of technology!!! Thanks to all involved, people committing pull requests, people answering questions and special thanks to Tyler Long who is helping me maintin the project.**
|
||||
|
||||
### Are you someone who wants to take an active role in improving mermaid?
|
||||
|
||||
Look at the list of areas we need help with:
|
||||
|
||||
* Development - help solving issues
|
||||
* Development - work with the build environment, with JS we keep updating the tools we use
|
||||
* Development - new diagram types
|
||||
* Development - Handling Pull Requests
|
||||
* Test - testing in connection with realeases, regression testing
|
||||
* Test - verification of fixed issues
|
||||
* Test - test of pull requests and verification testing
|
||||
* Release management - more of a PL role, make roadmap for the project, coordinating the work
|
||||
* Release management - classification and monitoring of incoming issues
|
||||
|
||||
If you think lending a hand to one or more of these areas would be fun, please send an email tp knsv@sveido.com!
|
||||
|
||||
### Flowchart
|
||||
|
||||
|
@ -35,7 +52,7 @@ sequenceDiagram
|
|||
loop Healthcheck
|
||||
John->>John: Fight against hypochondria
|
||||
end
|
||||
Note right of John: Rational thoughts <br/>prevail...
|
||||
Note right of John: Rational thoughts <br/>prevail!
|
||||
John-->>Alice: Great!
|
||||
John->>Bob: How about you?
|
||||
Bob-->>John: Jolly good!
|
||||
|
@ -49,6 +66,7 @@ sequenceDiagram
|
|||
gantt
|
||||
dateFormat YYYY-MM-DD
|
||||
title Adding GANTT diagram to mermaid
|
||||
excludes weekdays 2014-01-10
|
||||
|
||||
section A section
|
||||
Completed task :done, des1, 2014-01-06,2014-01-08
|
||||
|
@ -147,7 +165,7 @@ As part of this team you would get write access to the repository and would
|
|||
represent the project when answering questions and issues.
|
||||
|
||||
Together we could continue the work with things like:
|
||||
* adding more typers of diagrams like mindmaps, ert digrams etc
|
||||
* adding more types of diagrams like mindmaps, ert diagrams etc
|
||||
* improving existing diagrams
|
||||
|
||||
Don't hesitate to contact me if you want to get involved.
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
export const curveBasis = 'basis'
|
||||
export const curveLinear = 'linear'
|
||||
export const curveCardinal = 'cardinal'
|
|
@ -0,0 +1,12 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
targets: {
|
||||
node: 'current'
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
|
@ -207,6 +207,35 @@ graph TB
|
|||
a1-->a2
|
||||
end
|
||||
</div>
|
||||
<div class="mermaid">
|
||||
graph TB
|
||||
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
|
||||
style bar fill:#999,stroke-width:10px,stroke:#0F0
|
||||
</div>
|
||||
<div class="mermaid">
|
||||
graph LR
|
||||
456ac9b0d15a8b7f1e71073221059886[1051 AAA fa:fa-check]
|
||||
|
@ -239,6 +268,9 @@ class A someclass;
|
|||
|
||||
<div class="mermaid">
|
||||
sequenceDiagram
|
||||
participant Alice
|
||||
participant Bob
|
||||
participant John as John<br/>Second Line
|
||||
Alice ->> Bob: Hello Bob, how are you?
|
||||
Bob-->>John: How about you John?
|
||||
Bob--x Alice: I am good thanks!
|
||||
|
@ -266,6 +298,7 @@ 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
|
||||
|
@ -286,6 +319,13 @@ 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
|
||||
|
||||
click cl1 href "https://mermaidjs.github.io/"
|
||||
click cl2 call ganttTestClick("test", test, test)
|
||||
|
||||
section Last section
|
||||
Describe gantt syntax :after doc1, 3d
|
||||
Add gantt diagram to demo page : 20h
|
||||
|
@ -318,11 +358,11 @@ merge newbranch
|
|||
<div class="mermaid">
|
||||
classDiagram
|
||||
Class01 <|-- AveryLongClass : Cool
|
||||
Class03 *-- Class04
|
||||
Class05 o-- Class06
|
||||
Class03 "0" *-- "0..n" Class04
|
||||
Class05 "1" o-- "many" Class06
|
||||
Class07 .. Class08
|
||||
Class09 --> C2 : Where am i?
|
||||
Class09 --* C3
|
||||
Class09 "many" --> "1" C2 : Where am i?
|
||||
Class09 "0" --* "1..n" C3
|
||||
Class09 --|> Class07
|
||||
Class07 : equals()
|
||||
Class07 : Object[] elementData
|
||||
|
@ -344,6 +384,11 @@ Class08 <--> C2: Cool label
|
|||
});
|
||||
</script>
|
||||
<script>
|
||||
function ganttTestClick(a, b, c){
|
||||
console.log("a:", a)
|
||||
console.log("b:", b)
|
||||
console.log("c:", c)
|
||||
}
|
||||
function testClick(nodeId) {
|
||||
console.log("clicked", nodeId)
|
||||
var originalBgColor = document.querySelector('body').style.backgroundColor
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Mermaid Quick Test Page</title>
|
||||
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=">
|
||||
</head>
|
||||
<body>
|
||||
<div class="mermaid">info
|
||||
showInfo
|
||||
</div>
|
||||
<script src="./mermaid.js"></script>
|
||||
<script>
|
||||
mermaid.initialize({
|
||||
theme: 'forest',
|
||||
// themeCSS: '.node rect { fill: red; }',
|
||||
logLevel: 1,
|
||||
flowchart: { curve: 'linear' },
|
||||
gantt: { axisFormat: '%m/%d/%Y' },
|
||||
sequence: { actorMargin: 50 },
|
||||
// sequenceDiagram: { actorMargin: 300 } // deprecated
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
function ganttTestClick(a, b, c){
|
||||
console.log("a:", a)
|
||||
console.log("b:", b)
|
||||
console.log("c:", c)
|
||||
}
|
||||
function testClick(nodeId) {
|
||||
console.log("clicked", nodeId)
|
||||
var originalBgColor = document.querySelector('body').style.backgroundColor
|
||||
document.querySelector('body').style.backgroundColor = 'yellow'
|
||||
setTimeout(function() {
|
||||
document.querySelector('body').style.backgroundColor = originalBgColor
|
||||
}, 100)
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,9 @@
|
|||
# End to end tests
|
||||
|
||||
These tests are end to end tests in the sense that they actually render a full diagram in the browser. The purpose of these tests is to simplify handling of merge requests and releases by highlighting possible unexpected side-effects.
|
||||
|
||||
Apart from beeing rendered in a browser the tests perform image snapshots of the diagrams. The tests is handled in the same way as regular jest snapshots tests with the difference that an image comparison is performed instead of a comparison of the generated code.
|
||||
|
||||
## To run the tests
|
||||
1. Start the dev server by running **yarn dev**
|
||||
2. Run yarn e2e to run the tests
|
|
@ -0,0 +1,26 @@
|
|||
/* eslint-env jest */
|
||||
import { Base64 } from 'js-base64'
|
||||
|
||||
export const mermaidUrl = (graphStr, options) => {
|
||||
const obj = {
|
||||
code: graphStr,
|
||||
mermaid: options
|
||||
}
|
||||
const objStr = JSON.stringify(obj)
|
||||
// console.log(Base64)
|
||||
return 'http://localhost:9000/e2e.html?graph=' + Base64.encodeURI(objStr)
|
||||
}
|
||||
|
||||
export const imgSnapshotTest = async (page, graphStr, options) => {
|
||||
return new Promise(async resolve => {
|
||||
const url = mermaidUrl(graphStr, options)
|
||||
|
||||
await page.goto(url)
|
||||
|
||||
const image = await page.screenshot()
|
||||
|
||||
expect(image).toMatchImageSnapshot()
|
||||
resolve()
|
||||
})
|
||||
// page.close()
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
// jest.config.js
|
||||
module.exports = {
|
||||
// verbose: true,
|
||||
transform: {
|
||||
'^.+\\.jsx?$': '../transformer.js'
|
||||
},
|
||||
preset: 'jest-puppeteer',
|
||||
'globalSetup': 'jest-environment-puppeteer/setup',
|
||||
'globalTeardown': 'jest-environment-puppeteer/teardown',
|
||||
'testEnvironment': 'jest-environment-puppeteer'
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import mermaid from '../../dist/mermaid.core'
|
||||
|
||||
mermaid.initialize({
|
||||
theme: 'forest',
|
||||
gantt: { axisFormatter: [
|
||||
['%Y-%m-%d', (d) => {
|
||||
return d.getDay() === 1
|
||||
}]
|
||||
] }
|
||||
})
|
|
@ -0,0 +1,19 @@
|
|||
<html>
|
||||
<head>
|
||||
<script src="/e2e.js"></script>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css?family=Montserrat&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<style></style>
|
||||
</head>
|
||||
<body>
|
||||
<script src="./mermaid.js"></script>
|
||||
<script>
|
||||
mermaid.initialize({
|
||||
startOnLoad: false,
|
||||
useMaxWidth: true,
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,37 @@
|
|||
import { Base64 } from 'js-base64'
|
||||
|
||||
/**
|
||||
* ##contentLoaded
|
||||
* Callback function that is called when page is loaded. This functions fetches configuration for mermaid rendering and
|
||||
* calls init for rendering the mermaid diagrams on the page.
|
||||
*/
|
||||
const contentLoaded = function () {
|
||||
let pos = document.location.href.indexOf('?graph=')
|
||||
if (pos > 0) {
|
||||
pos = pos + 7
|
||||
const graphBase64 = document.location.href.substr(pos)
|
||||
const graphObj = JSON.parse(Base64.decode(graphBase64))
|
||||
// const graph = 'hello'
|
||||
console.log(graphObj)
|
||||
const div = document.createElement('div')
|
||||
div.id = 'block'
|
||||
div.className = 'mermaid'
|
||||
div.innerHTML = graphObj.code
|
||||
document.getElementsByTagName('body')[0].appendChild(div)
|
||||
global.mermaid.initialize(graphObj.mermaid)
|
||||
global.mermaid.init()
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof document !== 'undefined') {
|
||||
/*!
|
||||
* Wait for document loaded before starting the execution
|
||||
*/
|
||||
window.addEventListener(
|
||||
'load',
|
||||
function () {
|
||||
contentLoaded()
|
||||
},
|
||||
false
|
||||
)
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<body>
|
||||
<div class="mermaid">
|
||||
graph LR
|
||||
A-->B
|
||||
</div>
|
||||
<div class="mermaid">
|
||||
gantt
|
||||
title A Gantt Diagram
|
||||
dateFormat YYYY-MM-DD
|
||||
section Section
|
||||
A task :a1, 2014-01-01, 30d
|
||||
Another task :after a1 , 20d
|
||||
section Another
|
||||
Task in sec :2014-01-12 , 12d
|
||||
another task : 24d
|
||||
</div>
|
||||
<script src="./bundle-test.js" charset="utf-8"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 15 KiB |
|
@ -0,0 +1,27 @@
|
|||
/* eslint-env jest */
|
||||
import { imgSnapshotTest } from '../helpers/util.js'
|
||||
const { toMatchImageSnapshot } = require('jest-image-snapshot')
|
||||
|
||||
expect.extend({ toMatchImageSnapshot })
|
||||
|
||||
describe('Sequencediagram', () => {
|
||||
it('should render a simple class diagrams', async () => {
|
||||
await imgSnapshotTest(page, `
|
||||
classDiagram
|
||||
Class01 <|-- AveryLongClass : Cool
|
||||
Class03 *-- Class04
|
||||
Class05 o-- Class06
|
||||
Class07 .. Class08
|
||||
Class09 --> C2 : Where am i?
|
||||
Class09 --* C3
|
||||
Class09 --|> Class07
|
||||
Class07 : equals()
|
||||
Class07 : Object[] elementData
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class08 <--> C2: Cool label
|
||||
`,
|
||||
{})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,267 @@
|
|||
/* eslint-env jest */
|
||||
import { imgSnapshotTest } from '../helpers/util.js'
|
||||
const { toMatchImageSnapshot } = require('jest-image-snapshot')
|
||||
|
||||
expect.extend({ toMatchImageSnapshot })
|
||||
|
||||
describe('Flowcart', () => {
|
||||
it('should render a simple flowchart', async () => {
|
||||
await imgSnapshotTest(page, `graph TD
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
B --> C{Let me think}
|
||||
C -->|One| D[Laptop]
|
||||
C -->|Two| E[iPhone]
|
||||
C -->|Three| F[fa:fa-car Car]
|
||||
`,
|
||||
{})
|
||||
})
|
||||
it('should render a simple flowchart with line breaks', async () => {
|
||||
await imgSnapshotTest(page, `
|
||||
graph TD
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
B --> C{Let me thinksssss<br/>ssssssssssssssssssssss<br/>sssssssssssssssssssssssssss}
|
||||
C -->|One| D[Laptop]
|
||||
C -->|Two| E[iPhone]
|
||||
C -->|Three| F[Car]
|
||||
`,
|
||||
{})
|
||||
})
|
||||
|
||||
it('should render a flowchart full of circles', async () => {
|
||||
await imgSnapshotTest(page, `
|
||||
graph LR
|
||||
47(SAM.CommonFA.FMESummary)-->48(SAM.CommonFA.CommonFAFinanceBudget)
|
||||
37(SAM.CommonFA.BudgetSubserviceLineVolume)-->48(SAM.CommonFA.CommonFAFinanceBudget)
|
||||
35(SAM.CommonFA.PopulationFME)-->47(SAM.CommonFA.FMESummary)
|
||||
41(SAM.CommonFA.MetricCost)-->47(SAM.CommonFA.FMESummary)
|
||||
44(SAM.CommonFA.MetricOutliers)-->47(SAM.CommonFA.FMESummary)
|
||||
46(SAM.CommonFA.MetricOpportunity)-->47(SAM.CommonFA.FMESummary)
|
||||
40(SAM.CommonFA.OPVisits)-->47(SAM.CommonFA.FMESummary)
|
||||
38(SAM.CommonFA.CommonFAFinanceRefund)-->47(SAM.CommonFA.FMESummary)
|
||||
43(SAM.CommonFA.CommonFAFinancePicuDays)-->47(SAM.CommonFA.FMESummary)
|
||||
42(SAM.CommonFA.CommonFAFinanceNurseryDays)-->47(SAM.CommonFA.FMESummary)
|
||||
45(SAM.CommonFA.MetricPreOpportunity)-->46(SAM.CommonFA.MetricOpportunity)
|
||||
35(SAM.CommonFA.PopulationFME)-->45(SAM.CommonFA.MetricPreOpportunity)
|
||||
41(SAM.CommonFA.MetricCost)-->45(SAM.CommonFA.MetricPreOpportunity)
|
||||
41(SAM.CommonFA.MetricCost)-->44(SAM.CommonFA.MetricOutliers)
|
||||
39(SAM.CommonFA.ChargeDetails)-->43(SAM.CommonFA.CommonFAFinancePicuDays)
|
||||
39(SAM.CommonFA.ChargeDetails)-->42(SAM.CommonFA.CommonFAFinanceNurseryDays)
|
||||
39(SAM.CommonFA.ChargeDetails)-->41(SAM.CommonFA.MetricCost)
|
||||
39(SAM.CommonFA.ChargeDetails)-->40(SAM.CommonFA.OPVisits)
|
||||
35(SAM.CommonFA.PopulationFME)-->39(SAM.CommonFA.ChargeDetails)
|
||||
36(SAM.CommonFA.PremetricCost)-->39(SAM.CommonFA.ChargeDetails)
|
||||
`,
|
||||
{})
|
||||
})
|
||||
it('should render a flowchart full of icons', async () => {
|
||||
await imgSnapshotTest(page, `
|
||||
graph TD
|
||||
9e122290_1ec3_e711_8c5a_005056ad0002("fa:fa-creative-commons My System | Test Environment")
|
||||
82072290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs Shared Business Logic Server:Service 1")
|
||||
db052290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs Shared Business Logic Server:Service 2")
|
||||
4e112290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs Shared Report Server:Service 1")
|
||||
30122290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs Shared Report Server:Service 2")
|
||||
5e112290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs Dedicated Test Business Logic Server:Service 1")
|
||||
c1112290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs Dedicated Test Business Logic Server:Service 2")
|
||||
b7042290_1ec3_e711_8c5a_005056ad0002("fa:fa-circle [DBServer\\SharedDbInstance].[SupportDb]")
|
||||
8f102290_1ec3_e711_8c5a_005056ad0002("fa:fa-circle [DBServer\\SharedDbInstance].[DevelopmentDb]")
|
||||
0e102290_1ec3_e711_8c5a_005056ad0002("fa:fa-circle [DBServer\\SharedDbInstance].[TestDb]")
|
||||
07132290_1ec3_e711_8c5a_005056ad0002("fa:fa-circle [DBServer\\SharedDbInstance].[SharedReportingDb]")
|
||||
c7072290_1ec3_e711_8c5a_005056ad0002("fa:fa-server Shared Business Logic Server")
|
||||
ca122290_1ec3_e711_8c5a_005056ad0002("fa:fa-server Shared Report Server")
|
||||
68102290_1ec3_e711_8c5a_005056ad0002("fa:fa-server Dedicated Test Business Logic Server")
|
||||
f4112290_1ec3_e711_8c5a_005056ad0002("fa:fa-database [DBServer\\SharedDbInstance]")
|
||||
d6072290_1ec3_e711_8c5a_005056ad0002("fa:fa-server DBServer")
|
||||
71082290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs DBServer\\:MSSQLSERVER")
|
||||
c0102290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs DBServer\\:SQLAgent")
|
||||
9a072290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs DBServer\\:SQLBrowser")
|
||||
1d0a2290_1ec3_e711_8c5a_005056ad0002("fa:fa-server VmHost1")
|
||||
200a2290_1ec3_e711_8c5a_005056ad0002("fa:fa-server VmHost2")
|
||||
1c0a2290_1ec3_e711_8c5a_005056ad0002("fa:fa-server VmHost3")
|
||||
9e122290_1ec3_e711_8c5a_005056ad0002-->82072290_1ec3_e711_8c5a_005056ad0002
|
||||
9e122290_1ec3_e711_8c5a_005056ad0002-->db052290_1ec3_e711_8c5a_005056ad0002
|
||||
9e122290_1ec3_e711_8c5a_005056ad0002-->4e112290_1ec3_e711_8c5a_005056ad0002
|
||||
9e122290_1ec3_e711_8c5a_005056ad0002-->30122290_1ec3_e711_8c5a_005056ad0002
|
||||
9e122290_1ec3_e711_8c5a_005056ad0002-->5e112290_1ec3_e711_8c5a_005056ad0002
|
||||
9e122290_1ec3_e711_8c5a_005056ad0002-->c1112290_1ec3_e711_8c5a_005056ad0002
|
||||
82072290_1ec3_e711_8c5a_005056ad0002-->b7042290_1ec3_e711_8c5a_005056ad0002
|
||||
82072290_1ec3_e711_8c5a_005056ad0002-->8f102290_1ec3_e711_8c5a_005056ad0002
|
||||
82072290_1ec3_e711_8c5a_005056ad0002-->0e102290_1ec3_e711_8c5a_005056ad0002
|
||||
82072290_1ec3_e711_8c5a_005056ad0002-->c7072290_1ec3_e711_8c5a_005056ad0002
|
||||
db052290_1ec3_e711_8c5a_005056ad0002-->c7072290_1ec3_e711_8c5a_005056ad0002
|
||||
db052290_1ec3_e711_8c5a_005056ad0002-->82072290_1ec3_e711_8c5a_005056ad0002
|
||||
4e112290_1ec3_e711_8c5a_005056ad0002-->b7042290_1ec3_e711_8c5a_005056ad0002
|
||||
4e112290_1ec3_e711_8c5a_005056ad0002-->8f102290_1ec3_e711_8c5a_005056ad0002
|
||||
4e112290_1ec3_e711_8c5a_005056ad0002-->0e102290_1ec3_e711_8c5a_005056ad0002
|
||||
4e112290_1ec3_e711_8c5a_005056ad0002-->07132290_1ec3_e711_8c5a_005056ad0002
|
||||
4e112290_1ec3_e711_8c5a_005056ad0002-->ca122290_1ec3_e711_8c5a_005056ad0002
|
||||
30122290_1ec3_e711_8c5a_005056ad0002-->ca122290_1ec3_e711_8c5a_005056ad0002
|
||||
30122290_1ec3_e711_8c5a_005056ad0002-->4e112290_1ec3_e711_8c5a_005056ad0002
|
||||
5e112290_1ec3_e711_8c5a_005056ad0002-->8f102290_1ec3_e711_8c5a_005056ad0002
|
||||
5e112290_1ec3_e711_8c5a_005056ad0002-->68102290_1ec3_e711_8c5a_005056ad0002
|
||||
c1112290_1ec3_e711_8c5a_005056ad0002-->68102290_1ec3_e711_8c5a_005056ad0002
|
||||
c1112290_1ec3_e711_8c5a_005056ad0002-->5e112290_1ec3_e711_8c5a_005056ad0002
|
||||
b7042290_1ec3_e711_8c5a_005056ad0002-->f4112290_1ec3_e711_8c5a_005056ad0002
|
||||
8f102290_1ec3_e711_8c5a_005056ad0002-->f4112290_1ec3_e711_8c5a_005056ad0002
|
||||
0e102290_1ec3_e711_8c5a_005056ad0002-->f4112290_1ec3_e711_8c5a_005056ad0002
|
||||
07132290_1ec3_e711_8c5a_005056ad0002-->f4112290_1ec3_e711_8c5a_005056ad0002
|
||||
c7072290_1ec3_e711_8c5a_005056ad0002-->1d0a2290_1ec3_e711_8c5a_005056ad0002
|
||||
ca122290_1ec3_e711_8c5a_005056ad0002-->200a2290_1ec3_e711_8c5a_005056ad0002
|
||||
68102290_1ec3_e711_8c5a_005056ad0002-->1c0a2290_1ec3_e711_8c5a_005056ad0002
|
||||
f4112290_1ec3_e711_8c5a_005056ad0002-->d6072290_1ec3_e711_8c5a_005056ad0002
|
||||
f4112290_1ec3_e711_8c5a_005056ad0002-->71082290_1ec3_e711_8c5a_005056ad0002
|
||||
f4112290_1ec3_e711_8c5a_005056ad0002-->c0102290_1ec3_e711_8c5a_005056ad0002
|
||||
f4112290_1ec3_e711_8c5a_005056ad0002-->9a072290_1ec3_e711_8c5a_005056ad0002
|
||||
d6072290_1ec3_e711_8c5a_005056ad0002-->1c0a2290_1ec3_e711_8c5a_005056ad0002
|
||||
71082290_1ec3_e711_8c5a_005056ad0002-->d6072290_1ec3_e711_8c5a_005056ad0002
|
||||
c0102290_1ec3_e711_8c5a_005056ad0002-->d6072290_1ec3_e711_8c5a_005056ad0002
|
||||
c0102290_1ec3_e711_8c5a_005056ad0002-->71082290_1ec3_e711_8c5a_005056ad0002
|
||||
9a072290_1ec3_e711_8c5a_005056ad0002-->d6072290_1ec3_e711_8c5a_005056ad0002
|
||||
9a072290_1ec3_e711_8c5a_005056ad0002-->71082290_1ec3_e711_8c5a_005056ad0002
|
||||
`,
|
||||
{})
|
||||
})
|
||||
|
||||
it('should render subgraphs', async () => {
|
||||
await imgSnapshotTest(page, `
|
||||
graph TB
|
||||
subgraph One
|
||||
a1-->a2
|
||||
end
|
||||
`,
|
||||
{})
|
||||
})
|
||||
|
||||
it('should render styled subgraphs', async () => {
|
||||
await imgSnapshotTest(page, `
|
||||
graph TB
|
||||
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
|
||||
style bar fill:#999,stroke-width:10px,stroke:#0F0
|
||||
`,
|
||||
{})
|
||||
})
|
||||
|
||||
it('should render a flowchart with ling sames and class definitoins', async () => {
|
||||
await imgSnapshotTest(page, `graph LR
|
||||
sid-B3655226-6C29-4D00-B685-3D5C734DC7E1["
|
||||
|
||||
提交申请
|
||||
熊大
|
||||
"];
|
||||
class sid-B3655226-6C29-4D00-B685-3D5C734DC7E1 node-executed;
|
||||
sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A["
|
||||
负责人审批
|
||||
强子
|
||||
"];
|
||||
class sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A node-executed;
|
||||
sid-E27C0367-E6D6-497F-9736-3CDC21FDE221["
|
||||
DBA审批
|
||||
强子
|
||||
"];
|
||||
class sid-E27C0367-E6D6-497F-9736-3CDC21FDE221 node-executed;
|
||||
sid-BED98281-9585-4D1B-934E-BD1AC6AC0EFD["
|
||||
SA审批
|
||||
阿美
|
||||
"];
|
||||
class sid-BED98281-9585-4D1B-934E-BD1AC6AC0EFD node-executed;
|
||||
sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7["
|
||||
主管审批
|
||||
光头强
|
||||
"];
|
||||
class sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7 node-executed;
|
||||
sid-A1B3CD96-7697-4D7C-BEAA-73D187B1BE89["
|
||||
DBA确认
|
||||
强子
|
||||
"];
|
||||
class sid-A1B3CD96-7697-4D7C-BEAA-73D187B1BE89 node-executed;
|
||||
sid-3E35A7FF-A2F4-4E07-9247-DBF884C81937["
|
||||
SA确认
|
||||
阿美
|
||||
"];
|
||||
class sid-3E35A7FF-A2F4-4E07-9247-DBF884C81937 node-executed;
|
||||
sid-4FC27B48-A6F9-460A-A675-021F5854FE22["
|
||||
结束
|
||||
"];
|
||||
class sid-4FC27B48-A6F9-460A-A675-021F5854FE22 node-executed;
|
||||
sid-19DD9E9F-98C1-44EE-B604-842AFEE76F1E["
|
||||
SA执行1
|
||||
强子
|
||||
"];
|
||||
class sid-19DD9E9F-98C1-44EE-B604-842AFEE76F1E node-executed;
|
||||
sid-6C2120F3-D940-4958-A067-0903DCE879C4["
|
||||
SA执行2
|
||||
强子
|
||||
"];
|
||||
class sid-6C2120F3-D940-4958-A067-0903DCE879C4 node-executed;
|
||||
sid-9180E2A0-5C4B-435F-B42F-0D152470A338["
|
||||
DBA执行1
|
||||
强子
|
||||
"];
|
||||
class sid-9180E2A0-5C4B-435F-B42F-0D152470A338 node-executed;
|
||||
sid-03A2C3AC-5337-48A5-B154-BB3FD0EC8DAD["
|
||||
DBA执行3
|
||||
强子
|
||||
"];
|
||||
class sid-03A2C3AC-5337-48A5-B154-BB3FD0EC8DAD node-executed;
|
||||
sid-D5E1F2F4-306C-47A2-BF74-F66E3D769756["
|
||||
DBA执行2
|
||||
强子
|
||||
"];
|
||||
class sid-D5E1F2F4-306C-47A2-BF74-F66E3D769756 node-executed;
|
||||
sid-8C3F2F1D-F014-4F99-B966-095DC1A2BD93["
|
||||
DBA执行4
|
||||
强子
|
||||
"];
|
||||
class sid-8C3F2F1D-F014-4F99-B966-095DC1A2BD93 node-executed;
|
||||
sid-1897B30A-9C5C-4D5B-B80B-76A038785070["
|
||||
负责人确认
|
||||
梁静茹
|
||||
"];
|
||||
class sid-1897B30A-9C5C-4D5B-B80B-76A038785070 node-executed;
|
||||
sid-B3655226-6C29-4D00-B685-3D5C734DC7E1-->sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7;
|
||||
sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A-->sid-1897B30A-9C5C-4D5B-B80B-76A038785070;
|
||||
sid-E27C0367-E6D6-497F-9736-3CDC21FDE221-->sid-A1B3CD96-7697-4D7C-BEAA-73D187B1BE89;
|
||||
sid-BED98281-9585-4D1B-934E-BD1AC6AC0EFD-->sid-3E35A7FF-A2F4-4E07-9247-DBF884C81937;
|
||||
sid-19DD9E9F-98C1-44EE-B604-842AFEE76F1E-->sid-6C2120F3-D940-4958-A067-0903DCE879C4;
|
||||
sid-9180E2A0-5C4B-435F-B42F-0D152470A338-->sid-D5E1F2F4-306C-47A2-BF74-F66E3D769756;
|
||||
sid-03A2C3AC-5337-48A5-B154-BB3FD0EC8DAD-->sid-8C3F2F1D-F014-4F99-B966-095DC1A2BD93;
|
||||
sid-6C2120F3-D940-4958-A067-0903DCE879C4-->sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A;
|
||||
sid-1897B30A-9C5C-4D5B-B80B-76A038785070-->sid-4FC27B48-A6F9-460A-A675-021F5854FE22;
|
||||
sid-3E35A7FF-A2F4-4E07-9247-DBF884C81937-->sid-19DD9E9F-98C1-44EE-B604-842AFEE76F1E;
|
||||
sid-A1B3CD96-7697-4D7C-BEAA-73D187B1BE89-->sid-9180E2A0-5C4B-435F-B42F-0D152470A338;
|
||||
sid-A1B3CD96-7697-4D7C-BEAA-73D187B1BE89-->sid-03A2C3AC-5337-48A5-B154-BB3FD0EC8DAD;
|
||||
sid-D5E1F2F4-306C-47A2-BF74-F66E3D769756-->sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A;
|
||||
sid-8C3F2F1D-F014-4F99-B966-095DC1A2BD93-->sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A;
|
||||
sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7-->sid-BED98281-9585-4D1B-934E-BD1AC6AC0EFD;
|
||||
sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7-->sid-E27C0367-E6D6-497F-9736-3CDC21FDE221;
|
||||
sid-3E35A7FF-A2F4-4E07-9247-DBF884C81937-->sid-6C2120F3-D940-4958-A067-0903DCE879C4;
|
||||
sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7-->sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A;
|
||||
sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7-->sid-4FC27B48-A6F9-460A-A675-021F5854FE22;
|
||||
`,
|
||||
{})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,42 @@
|
|||
/* eslint-env jest */
|
||||
import { imgSnapshotTest } from '../helpers/util.js'
|
||||
const { toMatchImageSnapshot } = require('jest-image-snapshot')
|
||||
|
||||
expect.extend({ toMatchImageSnapshot })
|
||||
|
||||
describe('Sequencediagram', () => {
|
||||
it('should render a gantt chart', async () => {
|
||||
await imgSnapshotTest(page, `
|
||||
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 Last section
|
||||
Describe gantt syntax :after doc1, 3d
|
||||
Add gantt diagram to demo page : 20h
|
||||
Add another diagram to demo page : 48h
|
||||
`,
|
||||
{})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,29 @@
|
|||
/* eslint-env jest */
|
||||
import { imgSnapshotTest } from '../helpers/util.js'
|
||||
const { toMatchImageSnapshot } = require('jest-image-snapshot')
|
||||
|
||||
expect.extend({ toMatchImageSnapshot })
|
||||
|
||||
describe('Sequencediagram', () => {
|
||||
it('should render a simple git graph', async () => {
|
||||
await imgSnapshotTest(page, `
|
||||
gitGraph:
|
||||
options
|
||||
{
|
||||
"nodeSpacing": 150,
|
||||
"nodeRadius": 10
|
||||
}
|
||||
end
|
||||
commit
|
||||
branch newbranch
|
||||
checkout newbranch
|
||||
commit
|
||||
commit
|
||||
checkout master
|
||||
commit
|
||||
commit
|
||||
merge newbranch
|
||||
`,
|
||||
{})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,15 @@
|
|||
/* eslint-env jest */
|
||||
import { imgSnapshotTest } from '../helpers/util.js'
|
||||
const { toMatchImageSnapshot } = require('jest-image-snapshot')
|
||||
|
||||
expect.extend({ toMatchImageSnapshot })
|
||||
|
||||
describe('Sequencediagram', () => {
|
||||
it('should render a simple info diagrams', async () => {
|
||||
await imgSnapshotTest(page, `
|
||||
info
|
||||
showInfo
|
||||
`,
|
||||
{})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,35 @@
|
|||
/* eslint-env jest */
|
||||
import { imgSnapshotTest } from '../helpers/util.js'
|
||||
const { toMatchImageSnapshot } = require('jest-image-snapshot')
|
||||
|
||||
expect.extend({ toMatchImageSnapshot })
|
||||
|
||||
describe('Sequencediagram', () => {
|
||||
it('should render a simple sequence diagrams', async () => {
|
||||
await imgSnapshotTest(page, `
|
||||
sequenceDiagram
|
||||
participant Alice
|
||||
participant Bob
|
||||
participant John as John<br/>Second Line
|
||||
Alice ->> Bob: Hello Bob, how are you?
|
||||
Bob-->>John: How about you John?
|
||||
Bob--x Alice: I am good thanks!
|
||||
Bob-x John: I am good thanks!
|
||||
Note right of John: Bob thinks a long<br/>long time, so long<br/>that the text does<br/>not fit on a row.
|
||||
Bob-->Alice: Checking with John...
|
||||
alt either this
|
||||
Alice->>John: Yes
|
||||
else or this
|
||||
Alice->>John: No
|
||||
else or this will happen
|
||||
Alice->John: Maybe
|
||||
end
|
||||
par this happens in parallel
|
||||
Alice -->> Bob: Parallel message 1
|
||||
and
|
||||
Alice -->> John: Parallel message 2
|
||||
end
|
||||
`,
|
||||
{})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,16 @@
|
|||
/* eslint-env jest */
|
||||
const { toMatchImageSnapshot } = require('jest-image-snapshot')
|
||||
|
||||
expect.extend({ toMatchImageSnapshot })
|
||||
|
||||
describe('Sequencediagram', () => {
|
||||
it('should render a simple sequence diagrams', async () => {
|
||||
const url = 'http://localhost:9000/webpackUsage.html'
|
||||
|
||||
await page.goto(url)
|
||||
|
||||
const image = await page.screenshot()
|
||||
|
||||
expect(image).toMatchImageSnapshot()
|
||||
})
|
||||
})
|
|
@ -0,0 +1,9 @@
|
|||
module.exports = {
|
||||
transform: {
|
||||
'^.+\\.jsx?$': './transformer.js'
|
||||
},
|
||||
transformIgnorePatterns: ['/node_modules/(?!dagre-d3-renderer/lib).*\\.js'],
|
||||
moduleNameMapper: {
|
||||
'\\.(css|scss)$': 'identity-obj-proxy'
|
||||
}
|
||||
}
|
78
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "mermaid",
|
||||
"version": "8.0.0-rc.8",
|
||||
"version": "8.1.0",
|
||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"main": "dist/mermaid.core.js",
|
||||
"keywords": [
|
||||
|
@ -15,12 +15,14 @@
|
|||
"scripts": {
|
||||
"build": "webpack --progress --colors",
|
||||
"build:watch": "yarn build --watch",
|
||||
"minify": "minify ./dist/mermaid.js > ./dist/mermaid.min.js",
|
||||
"release": "yarn build -p --config webpack.config.prod.babel.js",
|
||||
"upgrade": "yarn-upgrade-all",
|
||||
"lint": "standard",
|
||||
"test": "yarn lint && jest",
|
||||
"test:watch": "jest --watch",
|
||||
"jison": "node -r babel-register node_modules/.bin/gulp jison",
|
||||
"e2e": "yarn lint && jest e2e --config e2e/jest.config.js",
|
||||
"dev": "yarn lint && webpack-dev-server --config webpack.config.e2e.js",
|
||||
"test": "yarn lint && jest src",
|
||||
"test:watch": "jest --watch src",
|
||||
"jison": "node -r @babel/register node_modules/.bin/gulp jison",
|
||||
"prepublishOnly": "yarn build && yarn release && yarn test",
|
||||
"prepush": "yarn test"
|
||||
},
|
||||
|
@ -34,47 +36,59 @@
|
|||
"ignore": [
|
||||
"**/parser/*.js",
|
||||
"dist/**/*.js"
|
||||
],
|
||||
"globals": [
|
||||
"page"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"d3": "^4.13.0",
|
||||
"d3": "^5.7.0",
|
||||
"dagre-d3-renderer": "^0.5.8",
|
||||
"dagre-layout": "^0.8.8",
|
||||
"graphlibrary": "^2.2.0",
|
||||
"he": "^1.1.1",
|
||||
"lodash": "^4.17.5",
|
||||
"moment": "^2.21.0",
|
||||
"scope-css": "^1.0.5"
|
||||
"he": "^1.2.0",
|
||||
"moment-mini": "^2.22.1",
|
||||
"lodash": "^4.17.11",
|
||||
"minify": "^4.1.1",
|
||||
"scope-css": "^1.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-loader": "^7.1.4",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"coveralls": "^3.0.0",
|
||||
"css-loader": "^0.28.11",
|
||||
"@babel/core": "^7.2.2",
|
||||
"@babel/preset-env": "^7.2.0",
|
||||
"@babel/register": "^7.0.0",
|
||||
"babel-core": "7.0.0-bridge.0",
|
||||
"babel-jest": "^23.6.0",
|
||||
"babel-loader": "^8.0.4",
|
||||
"coveralls": "^3.0.2",
|
||||
"css-loader": "^2.0.1",
|
||||
"css-to-string-loader": "^0.1.3",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp": "^4.0.0",
|
||||
"gulp-filelog": "^0.4.1",
|
||||
"gulp-jison": "^1.2.0",
|
||||
"husky": "^0.14.3",
|
||||
"husky": "^1.2.1",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^22.4.2",
|
||||
"jest": "^23.6.0",
|
||||
"jest-environment-puppeteer": "^4.2.0",
|
||||
"jest-image-snapshot": "^2.8.2",
|
||||
"jest-puppeteer": "^4.2.0",
|
||||
"jison": "^0.4.18",
|
||||
"node-sass": "^4.7.2",
|
||||
"sass-loader": "^6.0.7",
|
||||
"standard": "^11.0.1",
|
||||
"webpack": "^4.1.1",
|
||||
"webpack-cli": "^2.0.12",
|
||||
"webpack-node-externals": "^1.6.0",
|
||||
"yarn-upgrade-all": "^0.3.0"
|
||||
"moment": "^2.23.0",
|
||||
"node-sass": "^4.11.0",
|
||||
"puppeteer": "^1.17.0",
|
||||
"sass-loader": "^7.1.0",
|
||||
"standard": "^12.0.1",
|
||||
"webpack": "^4.27.1",
|
||||
"webpack-cli": "^3.1.2",
|
||||
"webpack-dev-server": "^3.4.1",
|
||||
"webpack-node-externals": "^1.7.2",
|
||||
"yarn-upgrade-all": "^0.5.0"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"src"
|
||||
"dist"
|
||||
],
|
||||
"jest": {
|
||||
"moduleNameMapper": {
|
||||
"\\.(css|scss)$": "identity-obj-proxy"
|
||||
}
|
||||
"yarn-upgrade-all": {
|
||||
"ignore": [
|
||||
"babel-core"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -44,17 +44,23 @@ export const addRelation = function (relation) {
|
|||
relations.push(relation)
|
||||
}
|
||||
|
||||
export const addMembers = function (className, MembersArr) {
|
||||
export const addMember = function (className, member) {
|
||||
const theClass = classes[className]
|
||||
if (typeof MembersArr === 'string') {
|
||||
if (MembersArr.substr(-1) === ')') {
|
||||
theClass.methods.push(MembersArr)
|
||||
if (typeof member === 'string') {
|
||||
if (member.substr(-1) === ')') {
|
||||
theClass.methods.push(member)
|
||||
} else {
|
||||
theClass.members.push(MembersArr)
|
||||
theClass.members.push(member)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const addMembers = function (className, MembersArr) {
|
||||
if (Array.isArray(MembersArr)) {
|
||||
MembersArr.forEach(member => addMember(className, member))
|
||||
}
|
||||
}
|
||||
|
||||
export const cleanupLabel = function (label) {
|
||||
if (label.substring(0, 1) === ':') {
|
||||
return label.substr(2).trim()
|
||||
|
@ -82,6 +88,7 @@ export default {
|
|||
getClasses,
|
||||
getRelations,
|
||||
addRelation,
|
||||
addMember,
|
||||
addMembers,
|
||||
cleanupLabel,
|
||||
lineType,
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import * as d3 from 'd3'
|
||||
import dagre from 'dagre-layout'
|
||||
import graphlib from 'graphlibrary'
|
||||
import * as d3 from 'd3'
|
||||
|
||||
import classDb from './classDb'
|
||||
import { logger } from '../../logger'
|
||||
import classDb from './classDb'
|
||||
import { parser } from './parser/classDiagram'
|
||||
|
||||
parser.yy = classDb
|
||||
|
@ -34,7 +33,9 @@ const getGraphId = function (label) {
|
|||
* Setup arrow head and define the marker. The result is appended to the svg.
|
||||
*/
|
||||
const insertMarkers = function (elem) {
|
||||
elem.append('defs').append('marker')
|
||||
elem
|
||||
.append('defs')
|
||||
.append('marker')
|
||||
.attr('id', 'extensionStart')
|
||||
.attr('class', 'extension')
|
||||
.attr('refX', 0)
|
||||
|
@ -45,7 +46,9 @@ const insertMarkers = function (elem) {
|
|||
.append('path')
|
||||
.attr('d', 'M 1,7 L18,13 V 1 Z')
|
||||
|
||||
elem.append('defs').append('marker')
|
||||
elem
|
||||
.append('defs')
|
||||
.append('marker')
|
||||
.attr('id', 'extensionEnd')
|
||||
.attr('refX', 19)
|
||||
.attr('refY', 7)
|
||||
|
@ -55,7 +58,9 @@ const insertMarkers = function (elem) {
|
|||
.append('path')
|
||||
.attr('d', 'M 1,1 V 13 L18,7 Z') // this is actual shape for arrowhead
|
||||
|
||||
elem.append('defs').append('marker')
|
||||
elem
|
||||
.append('defs')
|
||||
.append('marker')
|
||||
.attr('id', 'compositionStart')
|
||||
.attr('class', 'extension')
|
||||
.attr('refX', 0)
|
||||
|
@ -66,7 +71,9 @@ const insertMarkers = function (elem) {
|
|||
.append('path')
|
||||
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z')
|
||||
|
||||
elem.append('defs').append('marker')
|
||||
elem
|
||||
.append('defs')
|
||||
.append('marker')
|
||||
.attr('id', 'compositionEnd')
|
||||
.attr('refX', 19)
|
||||
.attr('refY', 7)
|
||||
|
@ -76,7 +83,9 @@ const insertMarkers = function (elem) {
|
|||
.append('path')
|
||||
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z')
|
||||
|
||||
elem.append('defs').append('marker')
|
||||
elem
|
||||
.append('defs')
|
||||
.append('marker')
|
||||
.attr('id', 'aggregationStart')
|
||||
.attr('class', 'extension')
|
||||
.attr('refX', 0)
|
||||
|
@ -87,7 +96,9 @@ const insertMarkers = function (elem) {
|
|||
.append('path')
|
||||
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z')
|
||||
|
||||
elem.append('defs').append('marker')
|
||||
elem
|
||||
.append('defs')
|
||||
.append('marker')
|
||||
.attr('id', 'aggregationEnd')
|
||||
.attr('refX', 19)
|
||||
.attr('refY', 7)
|
||||
|
@ -97,7 +108,9 @@ const insertMarkers = function (elem) {
|
|||
.append('path')
|
||||
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z')
|
||||
|
||||
elem.append('defs').append('marker')
|
||||
elem
|
||||
.append('defs')
|
||||
.append('marker')
|
||||
.attr('id', 'dependencyStart')
|
||||
.attr('class', 'extension')
|
||||
.attr('refX', 0)
|
||||
|
@ -108,7 +121,9 @@ const insertMarkers = function (elem) {
|
|||
.append('path')
|
||||
.attr('d', 'M 5,7 L9,13 L1,7 L9,1 Z')
|
||||
|
||||
elem.append('defs').append('marker')
|
||||
elem
|
||||
.append('defs')
|
||||
.append('marker')
|
||||
.attr('id', 'dependencyEnd')
|
||||
.attr('refX', 19)
|
||||
.attr('refY', 7)
|
||||
|
@ -120,6 +135,7 @@ const insertMarkers = function (elem) {
|
|||
}
|
||||
|
||||
let edgeCount = 0
|
||||
let total = 0
|
||||
const drawEdge = function (elem, path, relation) {
|
||||
const getRelationType = function (type) {
|
||||
switch (type) {
|
||||
|
@ -134,11 +150,14 @@ const drawEdge = function (elem, path, relation) {
|
|||
}
|
||||
}
|
||||
|
||||
path.points = path.points.filter(p => !Number.isNaN(p.y))
|
||||
|
||||
// The data for our line
|
||||
const lineData = path.points
|
||||
|
||||
// This is the accessor function we talked about above
|
||||
const lineFunction = d3.line()
|
||||
const lineFunction = d3
|
||||
.line()
|
||||
.x(function (d) {
|
||||
return d.x
|
||||
})
|
||||
|
@ -147,27 +166,49 @@ const drawEdge = function (elem, path, relation) {
|
|||
})
|
||||
.curve(d3.curveBasis)
|
||||
|
||||
const svgPath = elem.append('path')
|
||||
const svgPath = elem
|
||||
.append('path')
|
||||
.attr('d', lineFunction(lineData))
|
||||
.attr('id', 'edge' + edgeCount)
|
||||
.attr('class', 'relation')
|
||||
let url = ''
|
||||
if (conf.arrowMarkerAbsolute) {
|
||||
url = window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search
|
||||
url =
|
||||
window.location.protocol +
|
||||
'//' +
|
||||
window.location.host +
|
||||
window.location.pathname +
|
||||
window.location.search
|
||||
url = url.replace(/\(/g, '\\(')
|
||||
url = url.replace(/\)/g, '\\)')
|
||||
}
|
||||
|
||||
if (relation.relation.type1 !== 'none') {
|
||||
svgPath.attr('marker-start', 'url(' + url + '#' + getRelationType(relation.relation.type1) + 'Start' + ')')
|
||||
svgPath.attr(
|
||||
'marker-start',
|
||||
'url(' +
|
||||
url +
|
||||
'#' +
|
||||
getRelationType(relation.relation.type1) +
|
||||
'Start' +
|
||||
')'
|
||||
)
|
||||
}
|
||||
if (relation.relation.type2 !== 'none') {
|
||||
svgPath.attr('marker-end', 'url(' + url + '#' + getRelationType(relation.relation.type2) + 'End' + ')')
|
||||
svgPath.attr(
|
||||
'marker-end',
|
||||
'url(' +
|
||||
url +
|
||||
'#' +
|
||||
getRelationType(relation.relation.type2) +
|
||||
'End' +
|
||||
')'
|
||||
)
|
||||
}
|
||||
|
||||
let x, y
|
||||
const l = path.points.length
|
||||
if ((l % 2) !== 0) {
|
||||
if (l % 2 !== 0 && l > 1) {
|
||||
const p1 = path.points[Math.floor(l / 2)]
|
||||
const p2 = path.points[Math.ceil(l / 2)]
|
||||
x = (p1.x + p2.x) / 2
|
||||
|
@ -179,9 +220,9 @@ const drawEdge = function (elem, path, relation) {
|
|||
}
|
||||
|
||||
if (typeof relation.title !== 'undefined') {
|
||||
const g = elem.append('g')
|
||||
.attr('class', 'classLabel')
|
||||
const label = g.append('text')
|
||||
const g = elem.append('g').attr('class', 'classLabel')
|
||||
const label = g
|
||||
.append('text')
|
||||
.attr('class', 'label')
|
||||
.attr('x', x)
|
||||
.attr('y', y)
|
||||
|
@ -207,7 +248,8 @@ const drawClass = function (elem, classDef) {
|
|||
logger.info('Rendering class ' + classDef)
|
||||
|
||||
const addTspan = function (textEl, txt, isFirst) {
|
||||
const tSpan = textEl.append('tspan')
|
||||
const tSpan = textEl
|
||||
.append('tspan')
|
||||
.attr('x', conf.padding)
|
||||
.text(txt)
|
||||
if (!isFirst) {
|
||||
|
@ -215,7 +257,7 @@ const drawClass = function (elem, classDef) {
|
|||
}
|
||||
}
|
||||
|
||||
const id = 'classId' + classCnt
|
||||
const id = 'classId' + (classCnt % total)
|
||||
const classInfo = {
|
||||
id: id,
|
||||
label: classDef.id,
|
||||
|
@ -223,24 +265,28 @@ const drawClass = function (elem, classDef) {
|
|||
height: 0
|
||||
}
|
||||
|
||||
const g = elem.append('g')
|
||||
const g = elem
|
||||
.append('g')
|
||||
.attr('id', id)
|
||||
.attr('class', 'classGroup')
|
||||
const title = g.append('text')
|
||||
const title = g
|
||||
.append('text')
|
||||
.attr('x', conf.padding)
|
||||
.attr('y', conf.textHeight + conf.padding)
|
||||
.text(classDef.id)
|
||||
|
||||
const titleHeight = title.node().getBBox().height
|
||||
|
||||
const membersLine = g.append('line') // text label for the x axis
|
||||
const membersLine = g
|
||||
.append('line') // text label for the x axis
|
||||
.attr('x1', 0)
|
||||
.attr('y1', conf.padding + titleHeight + conf.dividerMargin / 2)
|
||||
.attr('y2', conf.padding + titleHeight + conf.dividerMargin / 2)
|
||||
|
||||
const members = g.append('text') // text label for the x axis
|
||||
const members = g
|
||||
.append('text') // text label for the x axis
|
||||
.attr('x', conf.padding)
|
||||
.attr('y', titleHeight + (conf.dividerMargin) + conf.textHeight)
|
||||
.attr('y', titleHeight + conf.dividerMargin + conf.textHeight)
|
||||
.attr('fill', 'white')
|
||||
.attr('class', 'classText')
|
||||
|
||||
|
@ -252,14 +298,25 @@ const drawClass = function (elem, classDef) {
|
|||
|
||||
const membersBox = members.node().getBBox()
|
||||
|
||||
const methodsLine = g.append('line') // text label for the x axis
|
||||
const methodsLine = g
|
||||
.append('line') // text label for the x axis
|
||||
.attr('x1', 0)
|
||||
.attr('y1', conf.padding + titleHeight + conf.dividerMargin + membersBox.height)
|
||||
.attr('y2', conf.padding + titleHeight + conf.dividerMargin + membersBox.height)
|
||||
.attr(
|
||||
'y1',
|
||||
conf.padding + titleHeight + conf.dividerMargin + membersBox.height
|
||||
)
|
||||
.attr(
|
||||
'y2',
|
||||
conf.padding + titleHeight + conf.dividerMargin + membersBox.height
|
||||
)
|
||||
|
||||
const methods = g.append('text') // text label for the x axis
|
||||
const methods = g
|
||||
.append('text') // text label for the x axis
|
||||
.attr('x', conf.padding)
|
||||
.attr('y', titleHeight + 2 * conf.dividerMargin + membersBox.height + conf.textHeight)
|
||||
.attr(
|
||||
'y',
|
||||
titleHeight + 2 * conf.dividerMargin + membersBox.height + conf.textHeight
|
||||
)
|
||||
.attr('fill', 'white')
|
||||
.attr('class', 'classText')
|
||||
|
||||
|
@ -307,7 +364,7 @@ export const draw = function (text, id) {
|
|||
logger.info('Rendering diagram ' + text)
|
||||
|
||||
/// / Fetch the default direction, use TD if none was found
|
||||
const diagram = d3.select(`[id="${id}"]`)
|
||||
const diagram = d3.select(`[id='${id}']`)
|
||||
insertMarkers(diagram)
|
||||
|
||||
// Layout graph, Create a new directed graph
|
||||
|
@ -327,6 +384,7 @@ export const draw = function (text, id) {
|
|||
|
||||
const classes = classDb.getClasses()
|
||||
const keys = Object.keys(classes)
|
||||
total = keys.length
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const classDef = classes[keys[i]]
|
||||
const node = drawClass(diagram, classDef)
|
||||
|
@ -339,24 +397,45 @@ export const draw = function (text, id) {
|
|||
|
||||
const relations = classDb.getRelations()
|
||||
relations.forEach(function (relation) {
|
||||
logger.info('tjoho' + getGraphId(relation.id1) + getGraphId(relation.id2) + JSON.stringify(relation))
|
||||
g.setEdge(getGraphId(relation.id1), getGraphId(relation.id2), { relation: relation })
|
||||
logger.info(
|
||||
'tjoho' +
|
||||
getGraphId(relation.id1) +
|
||||
getGraphId(relation.id2) +
|
||||
JSON.stringify(relation)
|
||||
)
|
||||
g.setEdge(getGraphId(relation.id1), getGraphId(relation.id2), {
|
||||
relation: relation
|
||||
})
|
||||
})
|
||||
dagre.layout(g)
|
||||
g.nodes().forEach(function (v) {
|
||||
if (typeof v !== 'undefined') {
|
||||
if (typeof v !== 'undefined' && typeof g.node(v) !== 'undefined') {
|
||||
logger.debug('Node ' + v + ': ' + JSON.stringify(g.node(v)))
|
||||
d3.select('#' + v).attr('transform', 'translate(' + (g.node(v).x - (g.node(v).width / 2)) + ',' + (g.node(v).y - (g.node(v).height / 2)) + ' )')
|
||||
d3.select('#' + v).attr(
|
||||
'transform',
|
||||
'translate(' +
|
||||
(g.node(v).x - g.node(v).width / 2) +
|
||||
',' +
|
||||
(g.node(v).y - g.node(v).height / 2) +
|
||||
' )'
|
||||
)
|
||||
}
|
||||
})
|
||||
g.edges().forEach(function (e) {
|
||||
logger.debug('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(g.edge(e)))
|
||||
drawEdge(diagram, g.edge(e), g.edge(e).relation)
|
||||
if (typeof e !== 'undefined' && typeof g.edge(e) !== 'undefined') {
|
||||
logger.debug(
|
||||
'Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(g.edge(e))
|
||||
)
|
||||
drawEdge(diagram, g.edge(e), g.edge(e).relation)
|
||||
}
|
||||
})
|
||||
|
||||
diagram.attr('height', '100%')
|
||||
diagram.attr('width', '100%')
|
||||
diagram.attr('viewBox', '0 0 ' + (g.graph().width + 20) + ' ' + (g.graph().height + 20))
|
||||
diagram.attr(
|
||||
'viewBox',
|
||||
'0 0 ' + (g.graph().width + 20) + ' ' + (g.graph().height + 20)
|
||||
)
|
||||
}
|
||||
|
||||
export default {
|
||||
|
|
|
@ -127,6 +127,7 @@ graphConfig
|
|||
|
||||
statements
|
||||
: statement
|
||||
| statement NEWLINE
|
||||
| statement NEWLINE statements
|
||||
;
|
||||
|
||||
|
@ -144,8 +145,8 @@ statement
|
|||
;
|
||||
|
||||
classStatement
|
||||
: CLASS className
|
||||
| CLASS className STRUCT_START members STRUCT_STOP {/*console.log($2,JSON.stringify($4));*/yy.addMembers($2,$4);}
|
||||
: CLASS className {yy.addClass($2);}
|
||||
| CLASS className STRUCT_START members STRUCT_STOP {/*console.log($2,JSON.stringify($4));*/yy.addClass($2);yy.addMembers($2,$4);}
|
||||
;
|
||||
|
||||
members
|
||||
|
@ -155,7 +156,7 @@ members
|
|||
|
||||
methodStatement
|
||||
: className {/*console.log('Rel found',$1);*/}
|
||||
| className LABEL {yy.addMembers($1,yy.cleanupLabel($2));}
|
||||
| className LABEL {yy.addMember($1,yy.cleanupLabel($2));}
|
||||
| MEMBER {console.warn('Member',$1);}
|
||||
| SEPARATOR {/*console.log('sep found',$1);*/}
|
||||
;
|
||||
|
|
|
@ -77,91 +77,94 @@ var parser = {trace: function trace() { },
|
|||
yy: {},
|
||||
symbols_: {"error":2,"mermaidDoc":3,"graphConfig":4,"CLASS_DIAGRAM":5,"NEWLINE":6,"statements":7,"EOF":8,"statement":9,"className":10,"alphaNumToken":11,"relationStatement":12,"LABEL":13,"classStatement":14,"methodStatement":15,"CLASS":16,"STRUCT_START":17,"members":18,"STRUCT_STOP":19,"MEMBER":20,"SEPARATOR":21,"relation":22,"STR":23,"relationType":24,"lineType":25,"AGGREGATION":26,"EXTENSION":27,"COMPOSITION":28,"DEPENDENCY":29,"LINE":30,"DOTTED_LINE":31,"commentToken":32,"textToken":33,"graphCodeTokens":34,"textNoTagsToken":35,"TAGSTART":36,"TAGEND":37,"==":38,"--":39,"PCT":40,"DEFAULT":41,"SPACE":42,"MINUS":43,"keywords":44,"UNICODE_TEXT":45,"NUM":46,"ALPHA":47,"$accept":0,"$end":1},
|
||||
terminals_: {2:"error",5:"CLASS_DIAGRAM",6:"NEWLINE",8:"EOF",13:"LABEL",16:"CLASS",17:"STRUCT_START",19:"STRUCT_STOP",20:"MEMBER",21:"SEPARATOR",23:"STR",26:"AGGREGATION",27:"EXTENSION",28:"COMPOSITION",29:"DEPENDENCY",30:"LINE",31:"DOTTED_LINE",34:"graphCodeTokens",36:"TAGSTART",37:"TAGEND",38:"==",39:"--",40:"PCT",41:"DEFAULT",42:"SPACE",43:"MINUS",44:"keywords",45:"UNICODE_TEXT",46:"NUM",47:"ALPHA"},
|
||||
productions_: [0,[3,1],[4,4],[7,1],[7,3],[10,2],[10,1],[9,1],[9,2],[9,1],[9,1],[14,2],[14,5],[18,1],[18,2],[15,1],[15,2],[15,1],[15,1],[12,3],[12,4],[12,4],[12,5],[22,3],[22,2],[22,2],[22,1],[24,1],[24,1],[24,1],[24,1],[25,1],[25,1],[32,1],[32,1],[33,1],[33,1],[33,1],[33,1],[33,1],[33,1],[33,1],[35,1],[35,1],[35,1],[35,1],[11,1],[11,1],[11,1]],
|
||||
productions_: [0,[3,1],[4,4],[7,1],[7,2],[7,3],[10,2],[10,1],[9,1],[9,2],[9,1],[9,1],[14,2],[14,5],[18,1],[18,2],[15,1],[15,2],[15,1],[15,1],[12,3],[12,4],[12,4],[12,5],[22,3],[22,2],[22,2],[22,1],[24,1],[24,1],[24,1],[24,1],[25,1],[25,1],[32,1],[32,1],[33,1],[33,1],[33,1],[33,1],[33,1],[33,1],[33,1],[35,1],[35,1],[35,1],[35,1],[11,1],[11,1],[11,1]],
|
||||
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
|
||||
/* this == yyval */
|
||||
|
||||
var $0 = $$.length - 1;
|
||||
switch (yystate) {
|
||||
case 5:
|
||||
case 6:
|
||||
this.$=$$[$0-1]+$$[$0];
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
this.$=$$[$0];
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
yy.addRelation($$[$0]);
|
||||
break;
|
||||
case 8:
|
||||
case 9:
|
||||
$$[$0-1].title = yy.cleanupLabel($$[$0]); yy.addRelation($$[$0-1]);
|
||||
break;
|
||||
case 12:
|
||||
/*console.log($$[$0-3],JSON.stringify($$[$0-1]));*/yy.addMembers($$[$0-3],$$[$0-1]);
|
||||
yy.addClass($$[$0]);
|
||||
break;
|
||||
case 13:
|
||||
this.$ = [$$[$0]];
|
||||
/*console.log($$[$0-3],JSON.stringify($$[$0-1]));*/yy.addClass($$[$0-3]);yy.addMembers($$[$0-3],$$[$0-1]);
|
||||
break;
|
||||
case 14:
|
||||
$$[$0].push($$[$0-1]);this.$=$$[$0];
|
||||
this.$ = [$$[$0]];
|
||||
break;
|
||||
case 15:
|
||||
/*console.log('Rel found',$$[$0]);*/
|
||||
$$[$0].push($$[$0-1]);this.$=$$[$0];
|
||||
break;
|
||||
case 16:
|
||||
yy.addMembers($$[$0-1],yy.cleanupLabel($$[$0]));
|
||||
/*console.log('Rel found',$$[$0]);*/
|
||||
break;
|
||||
case 17:
|
||||
console.warn('Member',$$[$0]);
|
||||
yy.addMember($$[$0-1],yy.cleanupLabel($$[$0]));
|
||||
break;
|
||||
case 18:
|
||||
/*console.log('sep found',$$[$0]);*/
|
||||
console.warn('Member',$$[$0]);
|
||||
break;
|
||||
case 19:
|
||||
this.$ = {'id1':$$[$0-2],'id2':$$[$0], relation:$$[$0-1], relationTitle1:'none', relationTitle2:'none'};
|
||||
/*console.log('sep found',$$[$0]);*/
|
||||
break;
|
||||
case 20:
|
||||
this.$ = {id1:$$[$0-3], id2:$$[$0], relation:$$[$0-1], relationTitle1:$$[$0-2], relationTitle2:'none'}
|
||||
this.$ = {'id1':$$[$0-2],'id2':$$[$0], relation:$$[$0-1], relationTitle1:'none', relationTitle2:'none'};
|
||||
break;
|
||||
case 21:
|
||||
this.$ = {id1:$$[$0-3], id2:$$[$0], relation:$$[$0-2], relationTitle1:'none', relationTitle2:$$[$0-1]};
|
||||
this.$ = {id1:$$[$0-3], id2:$$[$0], relation:$$[$0-1], relationTitle1:$$[$0-2], relationTitle2:'none'}
|
||||
break;
|
||||
case 22:
|
||||
this.$ = {id1:$$[$0-4], id2:$$[$0], relation:$$[$0-2], relationTitle1:$$[$0-3], relationTitle2:$$[$0-1]}
|
||||
this.$ = {id1:$$[$0-3], id2:$$[$0], relation:$$[$0-2], relationTitle1:'none', relationTitle2:$$[$0-1]};
|
||||
break;
|
||||
case 23:
|
||||
this.$={type1:$$[$0-2],type2:$$[$0],lineType:$$[$0-1]};
|
||||
this.$ = {id1:$$[$0-4], id2:$$[$0], relation:$$[$0-2], relationTitle1:$$[$0-3], relationTitle2:$$[$0-1]}
|
||||
break;
|
||||
case 24:
|
||||
this.$={type1:'none',type2:$$[$0],lineType:$$[$0-1]};
|
||||
this.$={type1:$$[$0-2],type2:$$[$0],lineType:$$[$0-1]};
|
||||
break;
|
||||
case 25:
|
||||
this.$={type1:$$[$0-1],type2:'none',lineType:$$[$0]};
|
||||
this.$={type1:'none',type2:$$[$0],lineType:$$[$0-1]};
|
||||
break;
|
||||
case 26:
|
||||
this.$={type1:'none',type2:'none',lineType:$$[$0]};
|
||||
this.$={type1:$$[$0-1],type2:'none',lineType:$$[$0]};
|
||||
break;
|
||||
case 27:
|
||||
this.$=yy.relationType.AGGREGATION;
|
||||
this.$={type1:'none',type2:'none',lineType:$$[$0]};
|
||||
break;
|
||||
case 28:
|
||||
this.$=yy.relationType.EXTENSION;
|
||||
this.$=yy.relationType.AGGREGATION;
|
||||
break;
|
||||
case 29:
|
||||
this.$=yy.relationType.COMPOSITION;
|
||||
this.$=yy.relationType.EXTENSION;
|
||||
break;
|
||||
case 30:
|
||||
this.$=yy.relationType.DEPENDENCY;
|
||||
this.$=yy.relationType.COMPOSITION;
|
||||
break;
|
||||
case 31:
|
||||
this.$=yy.lineType.LINE;
|
||||
this.$=yy.relationType.DEPENDENCY;
|
||||
break;
|
||||
case 32:
|
||||
this.$=yy.lineType.LINE;
|
||||
break;
|
||||
case 33:
|
||||
this.$=yy.lineType.DOTTED_LINE;
|
||||
break;
|
||||
}
|
||||
},
|
||||
table: [{3:1,4:2,5:[1,3]},{1:[3]},{1:[2,1]},{6:[1,4]},{7:5,9:6,10:10,11:14,12:7,14:8,15:9,16:$V0,20:$V1,21:$V2,45:$V3,46:$V4,47:$V5},{8:[1,18]},{6:[1,19],8:[2,3]},o($V6,[2,7],{13:[1,20]}),o($V6,[2,9]),o($V6,[2,10]),o($V6,[2,15],{22:21,24:24,25:25,13:[1,23],23:[1,22],26:$V7,27:$V8,28:$V9,29:$Va,30:$Vb,31:$Vc}),{10:32,11:14,45:$V3,46:$V4,47:$V5},o($V6,[2,17]),o($V6,[2,18]),o($Vd,[2,6],{11:14,10:33,45:$V3,46:$V4,47:$V5}),o($Ve,[2,46]),o($Ve,[2,47]),o($Ve,[2,48]),{1:[2,2]},{7:34,9:6,10:10,11:14,12:7,14:8,15:9,16:$V0,20:$V1,21:$V2,45:$V3,46:$V4,47:$V5},o($V6,[2,8]),{10:35,11:14,23:[1,36],45:$V3,46:$V4,47:$V5},{22:37,24:24,25:25,26:$V7,27:$V8,28:$V9,29:$Va,30:$Vb,31:$Vc},o($V6,[2,16]),{25:38,30:$Vb,31:$Vc},o($Vf,[2,26],{24:39,26:$V7,27:$V8,28:$V9,29:$Va}),o($Vg,[2,27]),o($Vg,[2,28]),o($Vg,[2,29]),o($Vg,[2,30]),o($Vh,[2,31]),o($Vh,[2,32]),o($V6,[2,11],{17:[1,40]}),o($Vd,[2,5]),{8:[2,4]},o($Vi,[2,19]),{10:41,11:14,45:$V3,46:$V4,47:$V5},{10:42,11:14,23:[1,43],45:$V3,46:$V4,47:$V5},o($Vf,[2,25],{24:44,26:$V7,27:$V8,28:$V9,29:$Va}),o($Vf,[2,24]),{18:45,20:$Vj},o($Vi,[2,21]),o($Vi,[2,20]),{10:47,11:14,45:$V3,46:$V4,47:$V5},o($Vf,[2,23]),{19:[1,48]},{18:49,19:[2,13],20:$Vj},o($Vi,[2,22]),o($V6,[2,12]),{19:[2,14]}],
|
||||
defaultActions: {2:[2,1],18:[2,2],34:[2,4],49:[2,14]},
|
||||
table: [{3:1,4:2,5:[1,3]},{1:[3]},{1:[2,1]},{6:[1,4]},{7:5,9:6,10:10,11:14,12:7,14:8,15:9,16:$V0,20:$V1,21:$V2,45:$V3,46:$V4,47:$V5},{8:[1,18]},{6:[1,19],8:[2,3]},o($V6,[2,8],{13:[1,20]}),o($V6,[2,10]),o($V6,[2,11]),o($V6,[2,16],{22:21,24:24,25:25,13:[1,23],23:[1,22],26:$V7,27:$V8,28:$V9,29:$Va,30:$Vb,31:$Vc}),{10:32,11:14,45:$V3,46:$V4,47:$V5},o($V6,[2,18]),o($V6,[2,19]),o($Vd,[2,7],{11:14,10:33,45:$V3,46:$V4,47:$V5}),o($Ve,[2,47]),o($Ve,[2,48]),o($Ve,[2,49]),{1:[2,2]},{7:34,8:[2,4],9:6,10:10,11:14,12:7,14:8,15:9,16:$V0,20:$V1,21:$V2,45:$V3,46:$V4,47:$V5},o($V6,[2,9]),{10:35,11:14,23:[1,36],45:$V3,46:$V4,47:$V5},{22:37,24:24,25:25,26:$V7,27:$V8,28:$V9,29:$Va,30:$Vb,31:$Vc},o($V6,[2,17]),{25:38,30:$Vb,31:$Vc},o($Vf,[2,27],{24:39,26:$V7,27:$V8,28:$V9,29:$Va}),o($Vg,[2,28]),o($Vg,[2,29]),o($Vg,[2,30]),o($Vg,[2,31]),o($Vh,[2,32]),o($Vh,[2,33]),o($V6,[2,12],{17:[1,40]}),o($Vd,[2,6]),{8:[2,5]},o($Vi,[2,20]),{10:41,11:14,45:$V3,46:$V4,47:$V5},{10:42,11:14,23:[1,43],45:$V3,46:$V4,47:$V5},o($Vf,[2,26],{24:44,26:$V7,27:$V8,28:$V9,29:$Va}),o($Vf,[2,25]),{18:45,20:$Vj},o($Vi,[2,22]),o($Vi,[2,21]),{10:47,11:14,45:$V3,46:$V4,47:$V5},o($Vf,[2,24]),{19:[1,48]},{18:49,19:[2,14],20:$Vj},o($Vi,[2,23]),o($V6,[2,13]),{19:[2,15]}],
|
||||
defaultActions: {2:[2,1],18:[2,2],34:[2,5],49:[2,15]},
|
||||
parseError: function parseError(str, hash) {
|
||||
if (hash.recoverable) {
|
||||
this.trace(str);
|
||||
|
|
|
@ -7,6 +7,7 @@ let vertices = {}
|
|||
let edges = []
|
||||
let classes = []
|
||||
let subGraphs = []
|
||||
let subGraphLookup = {}
|
||||
let tooltips = {}
|
||||
let subCount = 0
|
||||
let direction
|
||||
|
@ -18,8 +19,9 @@ let funs = []
|
|||
* @param text
|
||||
* @param type
|
||||
* @param style
|
||||
* @param classes
|
||||
*/
|
||||
export const addVertex = function (id, text, type, style) {
|
||||
export const addVertex = function (id, text, type, style, classes) {
|
||||
let txt
|
||||
|
||||
if (typeof id === 'undefined') {
|
||||
|
@ -45,9 +47,6 @@ export const addVertex = function (id, text, type, style) {
|
|||
if (typeof type !== 'undefined') {
|
||||
vertices[id].type = type
|
||||
}
|
||||
if (typeof type !== 'undefined') {
|
||||
vertices[id].type = type
|
||||
}
|
||||
if (typeof style !== 'undefined') {
|
||||
if (style !== null) {
|
||||
style.forEach(function (s) {
|
||||
|
@ -55,6 +54,13 @@ export const addVertex = function (id, text, type, style) {
|
|||
})
|
||||
}
|
||||
}
|
||||
if (typeof classes !== 'undefined') {
|
||||
if (classes !== null) {
|
||||
classes.forEach(function (s) {
|
||||
vertices[id].classes.push(s)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,12 +96,14 @@ export const addLink = function (start, end, type, linktext) {
|
|||
* @param pos
|
||||
* @param interpolate
|
||||
*/
|
||||
export const updateLinkInterpolate = function (pos, interp) {
|
||||
if (pos === 'default') {
|
||||
edges.defaultInterpolate = interp
|
||||
} else {
|
||||
edges[pos].interpolate = interp
|
||||
}
|
||||
export const updateLinkInterpolate = function (positions, interp) {
|
||||
positions.forEach(function (pos) {
|
||||
if (pos === 'default') {
|
||||
edges.defaultInterpolate = interp
|
||||
} else {
|
||||
edges[pos].interpolate = interp
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,15 +111,17 @@ export const updateLinkInterpolate = function (pos, interp) {
|
|||
* @param pos
|
||||
* @param style
|
||||
*/
|
||||
export const updateLink = function (pos, style) {
|
||||
if (pos === 'default') {
|
||||
edges.defaultStyle = style
|
||||
} else {
|
||||
if (utils.isSubstringInArray('fill', style) === -1) {
|
||||
style.push('fill:none')
|
||||
export const updateLink = function (positions, style) {
|
||||
positions.forEach(function (pos) {
|
||||
if (pos === 'default') {
|
||||
edges.defaultStyle = style
|
||||
} else {
|
||||
if (utils.isSubstringInArray('fill', style) === -1) {
|
||||
style.push('fill:none')
|
||||
}
|
||||
edges[pos].style = style
|
||||
}
|
||||
edges[pos].style = style
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const addClass = function (id, style) {
|
||||
|
@ -137,27 +147,28 @@ export const setDirection = function (dir) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Called by parser when a graph definition is found, stores the direction of the chart.
|
||||
* @param dir
|
||||
* Called by parser when a special node is found, e.g. a clickable element.
|
||||
* @param ids Comma separated list of ids
|
||||
* @param className Class to add
|
||||
*/
|
||||
export const setClass = function (id, className) {
|
||||
if (id.indexOf(',') > 0) {
|
||||
id.split(',').forEach(function (id2) {
|
||||
if (typeof vertices[id2] !== 'undefined') {
|
||||
vertices[id2].classes.push(className)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
export const setClass = function (ids, className) {
|
||||
ids.split(',').forEach(function (id) {
|
||||
if (typeof vertices[id] !== 'undefined') {
|
||||
vertices[id].classes.push(className)
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof subGraphLookup[id] !== 'undefined') {
|
||||
subGraphLookup[id].classes.push(className)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const setTooltip = function (id, tooltip) {
|
||||
if (typeof tooltip !== 'undefined') {
|
||||
tooltips[id] = tooltip
|
||||
}
|
||||
const setTooltip = function (ids, tooltip) {
|
||||
ids.split(',').forEach(function (id) {
|
||||
if (typeof tooltip !== 'undefined') {
|
||||
tooltips[id] = tooltip
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const setClickFun = function (id, functionName) {
|
||||
|
@ -176,43 +187,35 @@ const setClickFun = function (id, functionName) {
|
|||
}
|
||||
}
|
||||
|
||||
const setLink = function (id, linkStr) {
|
||||
if (typeof linkStr === 'undefined') {
|
||||
return
|
||||
}
|
||||
if (typeof vertices[id] !== 'undefined') {
|
||||
funs.push(function (element) {
|
||||
const elem = d3.select(element).select(`[id="${id}"]`)
|
||||
if (elem !== null) {
|
||||
elem.on('click', function () {
|
||||
window.open(linkStr, 'newTab')
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
/**
|
||||
* Called by parser when a link is found. Adds the URL to the vertex data.
|
||||
* @param ids Comma separated list of ids
|
||||
* @param linkStr URL to create a link for
|
||||
* @param tooltip Tooltip for the clickable element
|
||||
*/
|
||||
export const setLink = function (ids, linkStr, tooltip) {
|
||||
ids.split(',').forEach(function (id) {
|
||||
if (typeof vertices[id] !== 'undefined') {
|
||||
vertices[id].link = linkStr
|
||||
}
|
||||
})
|
||||
setTooltip(ids, tooltip)
|
||||
setClass(ids, 'clickable')
|
||||
}
|
||||
export const getTooltip = function (id) {
|
||||
return tooltips[id]
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by parser when a graph definition is found, stores the direction of the chart.
|
||||
* @param dir
|
||||
* Called by parser when a click definition is found. Registers an event handler.
|
||||
* @param ids Comma separated list of ids
|
||||
* @param functionName Function to be called on click
|
||||
* @param tooltip Tooltip for the clickable element
|
||||
*/
|
||||
export const setClickEvent = function (id, functionName, link, tooltip) {
|
||||
if (id.indexOf(',') > 0) {
|
||||
id.split(',').forEach(function (id2) {
|
||||
setTooltip(id2, tooltip)
|
||||
setClickFun(id2, functionName)
|
||||
setLink(id2, link)
|
||||
setClass(id, 'clickable')
|
||||
})
|
||||
} else {
|
||||
setTooltip(id, tooltip)
|
||||
setClickFun(id, functionName)
|
||||
setLink(id, link)
|
||||
setClass(id, 'clickable')
|
||||
}
|
||||
export const setClickEvent = function (ids, functionName, tooltip) {
|
||||
ids.split(',').forEach(function (id) { setClickFun(id, functionName) })
|
||||
setTooltip(ids, tooltip)
|
||||
setClass(ids, 'clickable')
|
||||
}
|
||||
|
||||
export const bindFunctions = function (element) {
|
||||
|
@ -297,6 +300,7 @@ export const clear = function () {
|
|||
funs = []
|
||||
funs.push(setupToolTips)
|
||||
subGraphs = []
|
||||
subGraphLookup = {}
|
||||
subCount = 0
|
||||
tooltips = []
|
||||
}
|
||||
|
@ -311,7 +315,7 @@ export const defaultStyle = function () {
|
|||
/**
|
||||
* Clears the internal graph db so that a new graph can be parsed.
|
||||
*/
|
||||
export const addSubGraph = function (list, title) {
|
||||
export const addSubGraph = function (id, list, title) {
|
||||
function uniq (a) {
|
||||
const prims = { 'boolean': {}, 'number': {}, 'string': {} }
|
||||
const objs = []
|
||||
|
@ -329,10 +333,13 @@ export const addSubGraph = function (list, title) {
|
|||
|
||||
nodeList = uniq(nodeList.concat.apply(nodeList, list))
|
||||
|
||||
const subGraph = { id: 'subGraph' + subCount, nodes: nodeList, title: title.trim() }
|
||||
subGraphs.push(subGraph)
|
||||
id = id || ('subGraph' + subCount)
|
||||
title = title || ''
|
||||
subCount = subCount + 1
|
||||
return subGraph.id
|
||||
const subGraph = { id: id, nodes: nodeList, title: title.trim(), classes: [] }
|
||||
subGraphs.push(subGraph)
|
||||
subGraphLookup[id] = subGraph
|
||||
return id
|
||||
}
|
||||
|
||||
const getPosForId = function (id) {
|
||||
|
@ -409,6 +416,7 @@ export default {
|
|||
setClass,
|
||||
getTooltip,
|
||||
setClickEvent,
|
||||
setLink,
|
||||
bindFunctions,
|
||||
getDirection,
|
||||
getVertices,
|
||||
|
|
|
@ -4,6 +4,7 @@ import * as d3 from 'd3'
|
|||
import flowDb from './flowDb'
|
||||
import flow from './parser/flow'
|
||||
import dagreD3 from 'dagre-d3-renderer'
|
||||
import addHtmlLabel from 'dagre-d3-renderer/lib/label/add-html-label.js'
|
||||
import { logger } from '../../logger'
|
||||
import { interpolateToCurve } from '../../utils'
|
||||
|
||||
|
@ -21,7 +22,8 @@ export const setConf = function (cnf) {
|
|||
* @param vert Object containing the vertices.
|
||||
* @param g The graph that is to be drawn.
|
||||
*/
|
||||
export const addVertices = function (vert, g) {
|
||||
export const addVertices = function (vert, g, svgId) {
|
||||
const svg = d3.select(`[id="${svgId}"]`)
|
||||
const keys = Object.keys(vert)
|
||||
|
||||
const styleFromStyleArr = function (styleStr, arr) {
|
||||
|
@ -35,45 +37,41 @@ export const addVertices = function (vert, g) {
|
|||
return styleStr
|
||||
}
|
||||
|
||||
// Iterate through each item in the vertice object (containing all the vertices found) in the graph definition
|
||||
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
|
||||
keys.forEach(function (id) {
|
||||
const vertice = vert[id]
|
||||
let verticeText
|
||||
const vertex = vert[id]
|
||||
|
||||
/**
|
||||
* Variable for storing the classes for the vertice
|
||||
* Variable for storing the classes for the vertex
|
||||
* @type {string}
|
||||
*/
|
||||
let classStr = ''
|
||||
if (vertice.classes.length > 0) {
|
||||
classStr = vertice.classes.join(' ')
|
||||
if (vertex.classes.length > 0) {
|
||||
classStr = vertex.classes.join(' ')
|
||||
}
|
||||
|
||||
/**
|
||||
* Variable for storing the extracted style for the vertice
|
||||
* Variable for storing the extracted style for the vertex
|
||||
* @type {string}
|
||||
*/
|
||||
let style = ''
|
||||
// Create a compound style definition from the style definitions found for the node in the graph definition
|
||||
style = styleFromStyleArr(style, vertice.styles)
|
||||
style = styleFromStyleArr(style, vertex.styles)
|
||||
|
||||
// Use vertice id as text in the box if no text is provided by the graph definition
|
||||
if (typeof vertice.text === 'undefined') {
|
||||
verticeText = vertice.id
|
||||
} else {
|
||||
verticeText = vertice.text
|
||||
}
|
||||
// Use vertex id as text in the box if no text is provided by the graph definition
|
||||
let vertexText = vertex.text !== undefined ? vertex.text : vertex.id
|
||||
|
||||
let labelTypeStr = ''
|
||||
// We create a SVG label, either by delegating to addHtmlLabel or manually
|
||||
let vertexNode
|
||||
if (conf.htmlLabels) {
|
||||
labelTypeStr = 'html'
|
||||
verticeText = verticeText.replace(/fa:fa[\w-]+/g, function (s) {
|
||||
return '<i class="fa ' + s.substring(3) + '"></i>'
|
||||
})
|
||||
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
||||
const node = { label: vertexText.replace(/fa[lrsb]?:fa-[\w-]+/g, s => `<i class='${s.replace(':', ' ')}'></i>`) }
|
||||
vertexNode = addHtmlLabel(svg, node).node()
|
||||
vertexNode.parentNode.removeChild(vertexNode)
|
||||
} else {
|
||||
const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text')
|
||||
|
||||
const rows = verticeText.split(/<br>/)
|
||||
const rows = vertexText.split(/<br[/]{0,1}>/)
|
||||
|
||||
for (let j = 0; j < rows.length; j++) {
|
||||
const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan')
|
||||
|
@ -83,15 +81,22 @@ export const addVertices = function (vert, g) {
|
|||
tspan.textContent = rows[j]
|
||||
svgLabel.appendChild(tspan)
|
||||
}
|
||||
vertexNode = svgLabel
|
||||
}
|
||||
|
||||
labelTypeStr = 'svg'
|
||||
verticeText = svgLabel
|
||||
// If the node has a link, we wrap it in a SVG link
|
||||
if (vertex.link) {
|
||||
const link = document.createElementNS('http://www.w3.org/2000/svg', 'a')
|
||||
link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link)
|
||||
link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener')
|
||||
link.appendChild(vertexNode)
|
||||
vertexNode = link
|
||||
}
|
||||
|
||||
let radious = 0
|
||||
let _shape = ''
|
||||
// Set the shape based parameters
|
||||
switch (vertice.type) {
|
||||
switch (vertex.type) {
|
||||
case 'round':
|
||||
radious = 5
|
||||
_shape = 'rect'
|
||||
|
@ -122,14 +127,12 @@ export const addVertices = function (vert, g) {
|
|||
break
|
||||
case 'group':
|
||||
_shape = 'rect'
|
||||
// Need to create a text node if using svg labels, see #367
|
||||
verticeText = conf.htmlLabels ? '' : document.createElementNS('http://www.w3.org/2000/svg', 'text')
|
||||
break
|
||||
default:
|
||||
_shape = 'rect'
|
||||
}
|
||||
// Add the node
|
||||
g.setNode(vertice.id, { labelType: labelTypeStr, shape: _shape, label: verticeText, rx: radious, ry: radious, 'class': classStr, style: style, id: vertice.id })
|
||||
g.setNode(vertex.id, { labelType: 'svg', shape: _shape, label: vertexNode, rx: radious, ry: radious, 'class': classStr, style: style, id: vertex.id })
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -201,7 +204,7 @@ export const addEdges = function (edges, g) {
|
|||
edgeData.label = '<span class="edgeLabel">' + edge.text + '</span>'
|
||||
} else {
|
||||
edgeData.labelType = 'text'
|
||||
edgeData.style = 'stroke: #333; stroke-width: 1.5px;fill:none'
|
||||
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none'
|
||||
edgeData.label = edge.text.replace(/<br>/g, '\n')
|
||||
}
|
||||
} else {
|
||||
|
@ -270,7 +273,7 @@ export const draw = function (text, id) {
|
|||
const subGraphs = flowDb.getSubGraphs()
|
||||
for (let i = subGraphs.length - 1; i >= 0; i--) {
|
||||
subG = subGraphs[i]
|
||||
flowDb.addVertex(subG.id, subG.title, 'group', undefined)
|
||||
flowDb.addVertex(subG.id, subG.title, 'group', undefined, subG.classes)
|
||||
}
|
||||
|
||||
// Fetch the verices/nodes and edges/links from the parsed graph definition
|
||||
|
@ -288,7 +291,7 @@ export const draw = function (text, id) {
|
|||
g.setParent(subG.nodes[j], subG.id)
|
||||
}
|
||||
}
|
||||
addVertices(vert, g)
|
||||
addVertices(vert, g, id)
|
||||
addEdges(edges, g)
|
||||
|
||||
// Create the renderer
|
||||
|
@ -464,6 +467,7 @@ export const draw = function (text, id) {
|
|||
// Index nodes
|
||||
flowDb.indexNodes('subGraph' + i)
|
||||
|
||||
// reposition labels
|
||||
for (i = 0; i < subGraphs.length; i++) {
|
||||
subG = subGraphs[i]
|
||||
|
||||
|
@ -475,19 +479,9 @@ export const draw = function (text, id) {
|
|||
const yPos = clusterRects[0].y.baseVal.value
|
||||
const width = clusterRects[0].width.baseVal.value
|
||||
const cluster = d3.select(clusterEl[0])
|
||||
const te = cluster.append('text')
|
||||
te.attr('x', xPos + width / 2)
|
||||
te.attr('y', yPos + 14)
|
||||
te.attr('fill', 'black')
|
||||
te.attr('stroke', 'none')
|
||||
const te = cluster.select('.label')
|
||||
te.attr('transform', `translate(${xPos + width / 2}, ${yPos + 14})`)
|
||||
te.attr('id', id + 'Text')
|
||||
te.style('text-anchor', 'middle')
|
||||
|
||||
if (typeof subG.title === 'undefined') {
|
||||
te.text('Undef')
|
||||
} else {
|
||||
te.text(subG.title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -227,10 +227,14 @@ statement
|
|||
{$$=[];}
|
||||
| clickStatement separator
|
||||
{$$=[];}
|
||||
| subgraph text separator document end
|
||||
{$$=yy.addSubGraph($4,$2);}
|
||||
| subgraph SPACE alphaNum SQS text SQE separator document end
|
||||
{$$=yy.addSubGraph($3,$8,$5);}
|
||||
| subgraph SPACE STR separator document end
|
||||
{$$=yy.addSubGraph(undefined,$5,$3);}
|
||||
| subgraph SPACE alphaNum separator document end
|
||||
{$$=yy.addSubGraph($3,$5,$3);}
|
||||
| subgraph separator document end
|
||||
{$$=yy.addSubGraph($3,undefined);}
|
||||
{$$=yy.addSubGraph(undefined,$3,undefined);}
|
||||
;
|
||||
|
||||
separator: NEWLINE | SEMI | EOF ;
|
||||
|
@ -406,10 +410,10 @@ classStatement:CLASS SPACE alphaNum SPACE alphaNum
|
|||
;
|
||||
|
||||
clickStatement
|
||||
: CLICK SPACE alphaNum SPACE alphaNum {$$ = $1;yy.setClickEvent($3, $5, undefined, undefined);}
|
||||
| CLICK SPACE alphaNum SPACE alphaNum SPACE STR {$$ = $1;yy.setClickEvent($3, $5, undefined, $7) ;}
|
||||
| CLICK SPACE alphaNum SPACE STR {$$ = $1;yy.setClickEvent($3, undefined, $5, undefined);}
|
||||
| CLICK SPACE alphaNum SPACE STR SPACE STR {$$ = $1;yy.setClickEvent($3, undefined, $5, $7 );}
|
||||
: CLICK SPACE alphaNum SPACE alphaNum {$$ = $1;yy.setClickEvent($3, $5, undefined);}
|
||||
| CLICK SPACE alphaNum SPACE alphaNum SPACE STR {$$ = $1;yy.setClickEvent($3, $5, $7) ;}
|
||||
| CLICK SPACE alphaNum SPACE STR {$$ = $1;yy.setLink($3, $5, undefined);}
|
||||
| CLICK SPACE alphaNum SPACE STR SPACE STR {$$ = $1;yy.setLink($3, $5, $7 );}
|
||||
;
|
||||
|
||||
styleStatement:STYLE SPACE alphaNum SPACE stylesOpt
|
||||
|
@ -420,21 +424,27 @@ styleStatement:STYLE SPACE alphaNum SPACE stylesOpt
|
|||
|
||||
linkStyleStatement
|
||||
: LINKSTYLE SPACE DEFAULT SPACE stylesOpt
|
||||
{$$ = $1;yy.updateLink($3,$5);}
|
||||
| LINKSTYLE SPACE NUM SPACE stylesOpt
|
||||
{$$ = $1;yy.updateLink([$3],$5);}
|
||||
| LINKSTYLE SPACE numList SPACE stylesOpt
|
||||
{$$ = $1;yy.updateLink($3,$5);}
|
||||
| LINKSTYLE SPACE DEFAULT SPACE INTERPOLATE SPACE alphaNum SPACE stylesOpt
|
||||
{$$ = $1;yy.updateLinkInterpolate($3,$7);yy.updateLink($3,$9);}
|
||||
| LINKSTYLE SPACE NUM SPACE INTERPOLATE SPACE alphaNum SPACE stylesOpt
|
||||
{$$ = $1;yy.updateLinkInterpolate([$3],$7);yy.updateLink([$3],$9);}
|
||||
| LINKSTYLE SPACE numList SPACE INTERPOLATE SPACE alphaNum SPACE stylesOpt
|
||||
{$$ = $1;yy.updateLinkInterpolate($3,$7);yy.updateLink($3,$9);}
|
||||
| LINKSTYLE SPACE DEFAULT SPACE INTERPOLATE SPACE alphaNum
|
||||
{$$ = $1;yy.updateLinkInterpolate($3,$7);}
|
||||
| LINKSTYLE SPACE NUM SPACE INTERPOLATE SPACE alphaNum
|
||||
{$$ = $1;yy.updateLinkInterpolate([$3],$7);}
|
||||
| LINKSTYLE SPACE numList SPACE INTERPOLATE SPACE alphaNum
|
||||
{$$ = $1;yy.updateLinkInterpolate($3,$7);}
|
||||
;
|
||||
|
||||
commentStatement: PCT PCT commentText;
|
||||
|
||||
numList: NUM
|
||||
{$$ = [$1]}
|
||||
| numList COMMA NUM
|
||||
{$1.push($3);$$ = $1;}
|
||||
;
|
||||
|
||||
stylesOpt: style
|
||||
{$$ = [$1]}
|
||||
| stylesOpt COMMA style
|
||||
|
|
|
@ -31,9 +31,34 @@ describe('when parsing ', function () {
|
|||
expect(subgraph.nodes[0]).toBe('a1')
|
||||
expect(subgraph.nodes[1]).toBe('a2')
|
||||
expect(subgraph.title).toBe('One')
|
||||
expect(subgraph.id).toBe('One')
|
||||
})
|
||||
|
||||
it('should handle angle bracket ' > ' as direction LR', function () {
|
||||
it('should handle subgraph with multiple words in title', function () {
|
||||
const res = flow.parser.parse('graph TB\nsubgraph "Some Title"\n\ta1-->a2\nend')
|
||||
const subgraphs = flow.parser.yy.getSubGraphs()
|
||||
expect(subgraphs.length).toBe(1)
|
||||
const subgraph = subgraphs[0]
|
||||
expect(subgraph.nodes.length).toBe(2)
|
||||
expect(subgraph.nodes[0]).toBe('a1')
|
||||
expect(subgraph.nodes[1]).toBe('a2')
|
||||
expect(subgraph.title).toBe('Some Title')
|
||||
expect(subgraph.id).toBe('subGraph0')
|
||||
});
|
||||
|
||||
it('should handle subgraph with id and title notation', function () {
|
||||
const res = flow.parser.parse('graph TB\nsubgraph some-id[Some Title]\n\ta1-->a2\nend')
|
||||
const subgraphs = flow.parser.yy.getSubGraphs()
|
||||
expect(subgraphs.length).toBe(1)
|
||||
const subgraph = subgraphs[0]
|
||||
expect(subgraph.nodes.length).toBe(2)
|
||||
expect(subgraph.nodes[0]).toBe('a1')
|
||||
expect(subgraph.nodes[1]).toBe('a2')
|
||||
expect(subgraph.title).toBe('Some Title')
|
||||
expect(subgraph.id).toBe('some-id')
|
||||
});
|
||||
|
||||
it("should handle angle bracket ' > ' as direction LR", function () {
|
||||
const res = flow.parser.parse('graph >;A-->B;')
|
||||
|
||||
const vert = flow.parser.yy.getVertices()
|
||||
|
@ -51,7 +76,7 @@ describe('when parsing ', function () {
|
|||
expect(edges[0].text).toBe('')
|
||||
})
|
||||
|
||||
it('should handle angle bracket ' < ' as direction RL', function () {
|
||||
it("should handle angle bracket ' < ' as direction RL", function () {
|
||||
const res = flow.parser.parse('graph <;A-->B;')
|
||||
|
||||
const vert = flow.parser.yy.getVertices()
|
||||
|
@ -69,7 +94,7 @@ describe('when parsing ', function () {
|
|||
expect(edges[0].text).toBe('')
|
||||
})
|
||||
|
||||
it('should handle caret ' ^ ' as direction BT', function () {
|
||||
it("should handle caret ' ^ ' as direction BT", function () {
|
||||
const res = flow.parser.parse('graph ^;A-->B;')
|
||||
|
||||
const vert = flow.parser.yy.getVertices()
|
||||
|
@ -428,6 +453,28 @@ describe('when parsing ', function () {
|
|||
expect(edges[0].type).toBe('arrow')
|
||||
})
|
||||
|
||||
it('should handle multi-numbered style definitons with more then 1 digit in a row', function () {
|
||||
const res = flow.parser.parse('graph TD\n' +
|
||||
'A-->B1\n' +
|
||||
'A-->B2\n' +
|
||||
'A-->B3\n' +
|
||||
'A-->B4\n' +
|
||||
'A-->B5\n' +
|
||||
'A-->B6\n' +
|
||||
'A-->B7\n' +
|
||||
'A-->B8\n' +
|
||||
'A-->B9\n' +
|
||||
'A-->B10\n' +
|
||||
'A-->B11\n' +
|
||||
'A-->B12\n' +
|
||||
'linkStyle 10,11 stroke-width:1px;')
|
||||
|
||||
const vert = flow.parser.yy.getVertices()
|
||||
const edges = flow.parser.yy.getEdges()
|
||||
|
||||
expect(edges[0].type).toBe('arrow')
|
||||
})
|
||||
|
||||
it('should handle line interpolation default definitions', function () {
|
||||
const res = flow.parser.parse('graph TD\n' +
|
||||
'A-->B\n' +
|
||||
|
@ -453,6 +500,19 @@ describe('when parsing ', function () {
|
|||
expect(edges[1].interpolate).toBe('cardinal')
|
||||
})
|
||||
|
||||
it('should handle line interpolation multi-numbered definitions', function () {
|
||||
const res = flow.parser.parse('graph TD\n' +
|
||||
'A-->B\n' +
|
||||
'A-->C\n' +
|
||||
'linkStyle 0,1 interpolate basis')
|
||||
|
||||
const vert = flow.parser.yy.getVertices()
|
||||
const edges = flow.parser.yy.getEdges()
|
||||
|
||||
expect(edges[0].interpolate).toBe('basis')
|
||||
expect(edges[1].interpolate).toBe('basis')
|
||||
})
|
||||
|
||||
it('should handle line interpolation default with style', function () {
|
||||
const res = flow.parser.parse('graph TD\n' +
|
||||
'A-->B\n' +
|
||||
|
@ -478,6 +538,19 @@ describe('when parsing ', function () {
|
|||
expect(edges[1].interpolate).toBe('cardinal')
|
||||
})
|
||||
|
||||
it('should handle line interpolation multi-numbered with style', function () {
|
||||
const res = flow.parser.parse('graph TD\n' +
|
||||
'A-->B\n' +
|
||||
'A-->C\n' +
|
||||
'linkStyle 0,1 interpolate basis stroke-width:1px;')
|
||||
|
||||
const vert = flow.parser.yy.getVertices()
|
||||
const edges = flow.parser.yy.getEdges()
|
||||
|
||||
expect(edges[0].interpolate).toBe('basis')
|
||||
expect(edges[1].interpolate).toBe('basis')
|
||||
})
|
||||
|
||||
describe('it should handle interaction, ', function () {
|
||||
it('it should be possible to use click to a callback', function () {
|
||||
spyOn(flowDb, 'setClickEvent')
|
||||
|
@ -486,7 +559,7 @@ describe('when parsing ', function () {
|
|||
const vert = flow.parser.yy.getVertices()
|
||||
const edges = flow.parser.yy.getEdges()
|
||||
|
||||
expect(flowDb.setClickEvent).toHaveBeenCalledWith('A', 'callback', undefined, undefined)
|
||||
expect(flowDb.setClickEvent).toHaveBeenCalledWith('A', 'callback', undefined)
|
||||
})
|
||||
|
||||
it('it should be possible to use click to a callback with toolip', function () {
|
||||
|
@ -496,26 +569,26 @@ describe('when parsing ', function () {
|
|||
const vert = flow.parser.yy.getVertices()
|
||||
const edges = flow.parser.yy.getEdges()
|
||||
|
||||
expect(flowDb.setClickEvent).toHaveBeenCalledWith('A', 'callback', undefined, 'tooltip')
|
||||
expect(flowDb.setClickEvent).toHaveBeenCalledWith('A', 'callback', 'tooltip')
|
||||
})
|
||||
|
||||
it('should handle interaction - click to a link', function () {
|
||||
spyOn(flowDb, 'setClickEvent')
|
||||
spyOn(flowDb, 'setLink')
|
||||
const res = flow.parser.parse('graph TD\nA-->B\nclick A "click.html"')
|
||||
|
||||
const vert = flow.parser.yy.getVertices()
|
||||
const edges = flow.parser.yy.getEdges()
|
||||
|
||||
expect(flowDb.setClickEvent).toHaveBeenCalledWith('A', undefined, 'click.html', undefined)
|
||||
expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html', undefined)
|
||||
})
|
||||
it('should handle interaction - click to a link with tooltip', function () {
|
||||
spyOn(flowDb, 'setClickEvent')
|
||||
spyOn(flowDb, 'setLink')
|
||||
const res = flow.parser.parse('graph TD\nA-->B\nclick A "click.html" "tooltip"')
|
||||
|
||||
const vert = flow.parser.yy.getVertices()
|
||||
const edges = flow.parser.yy.getEdges()
|
||||
|
||||
expect(flowDb.setClickEvent).toHaveBeenCalledWith('A', undefined, 'click.html', 'tooltip')
|
||||
expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html', 'tooltip')
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
/* eslint-env jasmine */
|
||||
import { parser } from './parser/gantt'
|
||||
import ganttDb from './ganttDb'
|
||||
|
||||
describe('when parsing a gantt diagram it', function () {
|
||||
beforeEach(function () {
|
||||
parser.yy = ganttDb
|
||||
})
|
||||
|
||||
it('should handle a dateFormat definition', function () {
|
||||
const str = 'gantt\ndateFormat yyyy-mm-dd'
|
||||
|
||||
parser.parse(str)
|
||||
})
|
||||
it('should handle a title definition', function () {
|
||||
const str = 'gantt\ndateFormat yyyy-mm-dd\ntitle Adding gantt diagram functionality to mermaid'
|
||||
|
||||
parser.parse(str)
|
||||
})
|
||||
it('should handle a section definition', function () {
|
||||
const str = 'gantt\n' +
|
||||
'dateFormat yyyy-mm-dd\n' +
|
||||
'title Adding gantt diagram functionality to mermaid\n' +
|
||||
'section Documentation'
|
||||
|
||||
parser.parse(str)
|
||||
})
|
||||
/**
|
||||
* Beslutsflöde inligt nedan. Obs bla bla bla
|
||||
* ```
|
||||
* graph TD
|
||||
* A[Hard pledge] -- text on link -->B(Round edge)
|
||||
* B --> C{to do or not to do}
|
||||
* C -->|Too| D[Result one]
|
||||
* C -->|Doo| E[Result two]
|
||||
```
|
||||
* params bapa - a unique bapap
|
||||
*/
|
||||
it('should handle a task definition', function () {
|
||||
const str = 'gantt\n' +
|
||||
'dateFormat yyyy-mm-dd\n' +
|
||||
'title Adding gantt diagram functionality to mermaid\n' +
|
||||
'section Documentation\n' +
|
||||
'Design jison grammar:des1, 2014-01-01, 2014-01-04'
|
||||
|
||||
parser.parse(str)
|
||||
})
|
||||
})
|
|
@ -1,17 +1,22 @@
|
|||
import moment from 'moment'
|
||||
import moment from 'moment-mini'
|
||||
import { logger } from '../../logger'
|
||||
import * as d3 from 'd3'
|
||||
|
||||
let dateFormat = ''
|
||||
let axisFormat = ''
|
||||
let excludes = []
|
||||
let title = ''
|
||||
let sections = []
|
||||
let tasks = []
|
||||
let currentSection = ''
|
||||
const tags = ['active', 'done', 'crit', 'milestone']
|
||||
let funs = []
|
||||
|
||||
export const clear = function () {
|
||||
sections = []
|
||||
tasks = []
|
||||
currentSection = ''
|
||||
funs = []
|
||||
title = ''
|
||||
taskCnt = 0
|
||||
lastTask = undefined
|
||||
|
@ -31,6 +36,10 @@ export const setDateFormat = function (txt) {
|
|||
dateFormat = txt
|
||||
}
|
||||
|
||||
export const setExcludes = function (txt) {
|
||||
excludes = txt.toLowerCase().split(/[\s,]+/)
|
||||
}
|
||||
|
||||
export const setTitle = function (txt) {
|
||||
title = txt
|
||||
}
|
||||
|
@ -58,6 +67,42 @@ export const getTasks = function () {
|
|||
return tasks
|
||||
}
|
||||
|
||||
const isInvalidDate = function (date, dateFormat, excludes) {
|
||||
if (date.isoWeekday() >= 6 && excludes.indexOf('weekends') >= 0) {
|
||||
return true
|
||||
}
|
||||
if (excludes.indexOf(date.format('dddd').toLowerCase()) >= 0) {
|
||||
return true
|
||||
}
|
||||
return excludes.indexOf(date.format(dateFormat.trim())) >= 0
|
||||
}
|
||||
|
||||
const checkTaskDates = function (task, dateFormat, excludes) {
|
||||
if (!excludes.length || task.manualEndTime) return
|
||||
let startTime = moment(task.startTime, dateFormat, true)
|
||||
startTime.add(1, 'd')
|
||||
let endTime = moment(task.endTime, dateFormat, true)
|
||||
let renderEndTime = fixTaskDates(startTime, endTime, dateFormat, excludes)
|
||||
task.endTime = endTime.toDate()
|
||||
task.renderEndTime = renderEndTime
|
||||
}
|
||||
|
||||
const fixTaskDates = function (startTime, endTime, dateFormat, excludes) {
|
||||
let invalid = false
|
||||
let renderEndTime = null
|
||||
while (startTime.date() <= endTime.date()) {
|
||||
if (!invalid) {
|
||||
renderEndTime = endTime.toDate()
|
||||
}
|
||||
invalid = isInvalidDate(startTime, dateFormat, excludes)
|
||||
if (invalid) {
|
||||
endTime.add(1, 'd')
|
||||
}
|
||||
startTime.add(1, 'd')
|
||||
}
|
||||
return renderEndTime
|
||||
}
|
||||
|
||||
const getStartDate = function (prevTime, dateFormat, str) {
|
||||
str = str.trim()
|
||||
|
||||
|
@ -77,8 +122,9 @@ const getStartDate = function (prevTime, dateFormat, str) {
|
|||
}
|
||||
|
||||
// Check for actual date set
|
||||
if (moment(str, dateFormat.trim(), true).isValid()) {
|
||||
return moment(str, dateFormat.trim(), true).toDate()
|
||||
let mDate = moment(str, dateFormat.trim(), true)
|
||||
if (mDate.isValid()) {
|
||||
return mDate.toDate()
|
||||
} else {
|
||||
logger.debug('Invalid date:' + str)
|
||||
logger.debug('With date format:' + dateFormat.trim())
|
||||
|
@ -92,8 +138,9 @@ const getEndDate = function (prevTime, dateFormat, str) {
|
|||
str = str.trim()
|
||||
|
||||
// Check for actual date
|
||||
if (moment(str, dateFormat.trim(), true).isValid()) {
|
||||
return moment(str, dateFormat.trim()).toDate()
|
||||
let mDate = moment(str, dateFormat.trim(), true)
|
||||
if (mDate.isValid()) {
|
||||
return mDate.toDate()
|
||||
}
|
||||
|
||||
const d = moment(prevTime)
|
||||
|
@ -119,7 +166,6 @@ const getEndDate = function (prevTime, dateFormat, str) {
|
|||
d.add(durationStatement[1], 'weeks')
|
||||
break
|
||||
}
|
||||
return d.toDate()
|
||||
}
|
||||
// Default date - now
|
||||
return d.toDate()
|
||||
|
@ -157,49 +203,39 @@ const compileData = function (prevTask, dataStr) {
|
|||
|
||||
const task = {}
|
||||
|
||||
// Get tags like active, done cand crit
|
||||
let matchFound = true
|
||||
while (matchFound) {
|
||||
matchFound = false
|
||||
if (data[0].match(/^\s*active\s*$/)) {
|
||||
task.active = true
|
||||
data.shift(1)
|
||||
matchFound = true
|
||||
}
|
||||
if (data[0].match(/^\s*done\s*$/)) {
|
||||
task.done = true
|
||||
data.shift(1)
|
||||
matchFound = true
|
||||
}
|
||||
if (data[0].match(/^\s*crit\s*$/)) {
|
||||
task.crit = true
|
||||
data.shift(1)
|
||||
matchFound = true
|
||||
}
|
||||
}
|
||||
// Get tags like active, done, crit and milestone
|
||||
getTaskTags(data, task, tags)
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
data[i] = data[i].trim()
|
||||
}
|
||||
|
||||
let endTimeData = ''
|
||||
switch (data.length) {
|
||||
case 1:
|
||||
task.id = parseId()
|
||||
task.startTime = prevTask.endTime
|
||||
task.endTime = getEndDate(task.startTime, dateFormat, data[0])
|
||||
endTimeData = data[0]
|
||||
break
|
||||
case 2:
|
||||
task.id = parseId()
|
||||
task.startTime = getStartDate(undefined, dateFormat, data[0])
|
||||
task.endTime = getEndDate(task.startTime, dateFormat, data[1])
|
||||
endTimeData = data[1]
|
||||
break
|
||||
case 3:
|
||||
task.id = parseId(data[0])
|
||||
task.startTime = getStartDate(undefined, dateFormat, data[1])
|
||||
task.endTime = getEndDate(task.startTime, dateFormat, data[2])
|
||||
endTimeData = data[2]
|
||||
break
|
||||
default:
|
||||
}
|
||||
|
||||
if (endTimeData) {
|
||||
task.endTime = getEndDate(task.startTime, dateFormat, endTimeData)
|
||||
task.manualEndTime = endTimeData === moment(task.endTime).format(dateFormat.trim())
|
||||
checkTaskDates(task, dateFormat, excludes)
|
||||
}
|
||||
|
||||
return task
|
||||
}
|
||||
|
||||
|
@ -215,26 +251,9 @@ const parseData = function (prevTaskId, dataStr) {
|
|||
|
||||
const task = {}
|
||||
|
||||
// Get tags like active, done cand crit
|
||||
let matchFound = true
|
||||
while (matchFound) {
|
||||
matchFound = false
|
||||
if (data[0].match(/^\s*active\s*$/)) {
|
||||
task.active = true
|
||||
data.shift(1)
|
||||
matchFound = true
|
||||
}
|
||||
if (data[0].match(/^\s*done\s*$/)) {
|
||||
task.done = true
|
||||
data.shift(1)
|
||||
matchFound = true
|
||||
}
|
||||
if (data[0].match(/^\s*crit\s*$/)) {
|
||||
task.crit = true
|
||||
data.shift(1)
|
||||
matchFound = true
|
||||
}
|
||||
}
|
||||
// Get tags like active, done, crit and milestone
|
||||
getTaskTags(data, task, tags)
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
data[i] = data[i].trim()
|
||||
}
|
||||
|
@ -242,18 +261,33 @@ const parseData = function (prevTaskId, dataStr) {
|
|||
switch (data.length) {
|
||||
case 1:
|
||||
task.id = parseId()
|
||||
task.startTime = { type: 'prevTaskEnd', id: prevTaskId }
|
||||
task.endTime = { data: data[0] }
|
||||
task.startTime = {
|
||||
type: 'prevTaskEnd',
|
||||
id: prevTaskId
|
||||
}
|
||||
task.endTime = {
|
||||
data: data[0]
|
||||
}
|
||||
break
|
||||
case 2:
|
||||
task.id = parseId()
|
||||
task.startTime = { type: 'getStartDate', startData: data[0] }
|
||||
task.endTime = { data: data[1] }
|
||||
task.startTime = {
|
||||
type: 'getStartDate',
|
||||
startData: data[0]
|
||||
}
|
||||
task.endTime = {
|
||||
data: data[1]
|
||||
}
|
||||
break
|
||||
case 3:
|
||||
task.id = parseId(data[0])
|
||||
task.startTime = { type: 'getStartDate', startData: data[1] }
|
||||
task.endTime = { data: data[2] }
|
||||
task.startTime = {
|
||||
type: 'getStartDate',
|
||||
startData: data[1]
|
||||
}
|
||||
task.endTime = {
|
||||
data: data[2]
|
||||
}
|
||||
break
|
||||
default:
|
||||
}
|
||||
|
@ -270,8 +304,11 @@ export const addTask = function (descr, data) {
|
|||
section: currentSection,
|
||||
type: currentSection,
|
||||
processed: false,
|
||||
manualEndTime: false,
|
||||
renderEndTime: null,
|
||||
raw: { data: data },
|
||||
task: descr
|
||||
task: descr,
|
||||
classes: []
|
||||
}
|
||||
const taskInfo = parseData(lastTaskID, data)
|
||||
rawTask.raw.startTime = taskInfo.startTime
|
||||
|
@ -281,6 +318,7 @@ export const addTask = function (descr, data) {
|
|||
rawTask.active = taskInfo.active
|
||||
rawTask.done = taskInfo.done
|
||||
rawTask.crit = taskInfo.crit
|
||||
rawTask.milestone = taskInfo.milestone
|
||||
|
||||
const pos = rawTasks.push(rawTask)
|
||||
|
||||
|
@ -299,7 +337,8 @@ export const addTaskOrg = function (descr, data) {
|
|||
section: currentSection,
|
||||
type: currentSection,
|
||||
description: descr,
|
||||
task: descr
|
||||
task: descr,
|
||||
classes: []
|
||||
}
|
||||
const taskInfo = compileData(lastTask, data)
|
||||
newTask.startTime = taskInfo.startTime
|
||||
|
@ -308,6 +347,7 @@ export const addTaskOrg = function (descr, data) {
|
|||
newTask.active = taskInfo.active
|
||||
newTask.done = taskInfo.done
|
||||
newTask.crit = taskInfo.crit
|
||||
newTask.milestone = taskInfo.milestone
|
||||
lastTask = newTask
|
||||
tasks.push(newTask)
|
||||
}
|
||||
|
@ -333,6 +373,8 @@ const compileTasks = function () {
|
|||
rawTasks[pos].endTime = getEndDate(rawTasks[pos].startTime, dateFormat, rawTasks[pos].raw.endTime.data)
|
||||
if (rawTasks[pos].endTime) {
|
||||
rawTasks[pos].processed = true
|
||||
rawTasks[pos].manualEndTime = rawTasks[pos].raw.endTime.data === moment(rawTasks[pos].endTime).format(dateFormat.trim())
|
||||
checkTaskDates(rawTasks[pos], dateFormat, excludes)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -348,6 +390,108 @@ const compileTasks = function () {
|
|||
return allProcessed
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by parser when a link is found. Adds the URL to the vertex data.
|
||||
* @param ids Comma separated list of ids
|
||||
* @param linkStr URL to create a link for
|
||||
*/
|
||||
export const setLink = function (ids, linkStr) {
|
||||
ids.split(',').forEach(function (id) {
|
||||
let rawTask = findTaskById(id)
|
||||
if (typeof rawTask !== 'undefined') {
|
||||
pushFun(id, () => { window.open(linkStr, '_self') })
|
||||
}
|
||||
})
|
||||
setClass(ids, 'clickable')
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by parser when a special node is found, e.g. a clickable element.
|
||||
* @param ids Comma separated list of ids
|
||||
* @param className Class to add
|
||||
*/
|
||||
export const setClass = function (ids, className) {
|
||||
ids.split(',').forEach(function (id) {
|
||||
let rawTask = findTaskById(id)
|
||||
if (typeof rawTask !== 'undefined') {
|
||||
rawTask.classes.push(className)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const setClickFun = function (id, functionName, functionArgs) {
|
||||
if (typeof functionName === 'undefined') {
|
||||
return
|
||||
}
|
||||
|
||||
let argList = []
|
||||
if (typeof functionArgs === 'string') {
|
||||
/* Splits functionArgs by ',', ignoring all ',' in double quoted strings */
|
||||
argList = functionArgs.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/)
|
||||
for (let i = 0; i < argList.length; i++) {
|
||||
let item = argList[i].trim()
|
||||
/* Removes all double quotes at the start and end of an argument */
|
||||
/* This preserves all starting and ending whitespace inside */
|
||||
if (item.charAt(0) === '"' && item.charAt(item.length - 1) === '"') {
|
||||
item = item.substr(1, item.length - 2)
|
||||
}
|
||||
argList[i] = item
|
||||
}
|
||||
}
|
||||
|
||||
let rawTask = findTaskById(id)
|
||||
if (typeof rawTask !== 'undefined') {
|
||||
pushFun(id, () => { window[functionName](...argList) })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The callbackFunction is executed in a click event bound to the task with the specified id or the task's assigned text
|
||||
* @param id The task's id
|
||||
* @param callbackFunction A function to be executed when clicked on the task or the task's text
|
||||
*/
|
||||
const pushFun = function (id, callbackFunction) {
|
||||
funs.push(function (element) {
|
||||
const elem = d3.select(element).select(`[id="${id}"]`)
|
||||
if (elem !== null) {
|
||||
elem.on('click', function () {
|
||||
callbackFunction()
|
||||
})
|
||||
}
|
||||
})
|
||||
funs.push(function (element) {
|
||||
const elem = d3.select(element).select(`[id="${id}-text"]`)
|
||||
if (elem !== null) {
|
||||
elem.on('click', function () {
|
||||
callbackFunction()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by parser when a click definition is found. Registers an event handler.
|
||||
* @param ids Comma separated list of ids
|
||||
* @param functionName Function to be called on click
|
||||
* @param functionArgs Function args the function should be called with
|
||||
*/
|
||||
export const setClickEvent = function (ids, functionName, functionArgs) {
|
||||
ids.split(',').forEach(function (id) {
|
||||
setClickFun(id, functionName, functionArgs)
|
||||
})
|
||||
setClass(ids, 'clickable')
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds all functions previously added to fun (specified through click) to the element
|
||||
* @param element
|
||||
*/
|
||||
export const bindFunctions = function (element) {
|
||||
funs.forEach(function (fun) {
|
||||
fun(element)
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
clear,
|
||||
setDateFormat,
|
||||
|
@ -359,5 +503,25 @@ export default {
|
|||
getTasks,
|
||||
addTask,
|
||||
findTaskById,
|
||||
addTaskOrg
|
||||
addTaskOrg,
|
||||
setExcludes,
|
||||
setClickEvent,
|
||||
setLink,
|
||||
bindFunctions
|
||||
}
|
||||
|
||||
function getTaskTags (data, task, tags) {
|
||||
let matchFound = true
|
||||
while (matchFound) {
|
||||
matchFound = false
|
||||
tags.forEach(function (t) {
|
||||
const pattern = '^\\s*' + t + '\\s*$'
|
||||
const regex = new RegExp(pattern)
|
||||
if (data[0].match(regex)) {
|
||||
task[t] = true
|
||||
data.shift(1)
|
||||
matchFound = true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jasmine */
|
||||
import moment from 'moment'
|
||||
import moment from 'moment-mini'
|
||||
import ganttDb from './ganttDb'
|
||||
|
||||
describe('when using the ganttDb', function () {
|
||||
|
@ -7,151 +7,48 @@ describe('when using the ganttDb', function () {
|
|||
ganttDb.clear()
|
||||
})
|
||||
|
||||
it('should handle an fixed dates', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2013-01-12')
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].endTime).toEqual(moment('2013-01-12', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].id).toEqual('id1')
|
||||
expect(tasks[0].task).toEqual('test1')
|
||||
})
|
||||
it('should handle duration (days) instead of fixed date to determine end date', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2d')
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].endTime).toEqual(moment('2013-01-03', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].id).toEqual('id1')
|
||||
expect(tasks[0].task).toEqual('test1')
|
||||
})
|
||||
it('should handle duration (hours) instead of fixed date to determine end date', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2h')
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].endTime).toEqual(moment('2013-01-01 2:00', 'YYYY-MM-DD hh:mm').toDate())
|
||||
expect(tasks[0].id).toEqual('id1')
|
||||
expect(tasks[0].task).toEqual('test1')
|
||||
})
|
||||
it('should handle duration (minutes) instead of fixed date to determine end date', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2m')
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].endTime).toEqual(moment('2013-01-01 00:02', 'YYYY-MM-DD hh:mm').toDate())
|
||||
expect(tasks[0].id).toEqual('id1')
|
||||
expect(tasks[0].task).toEqual('test1')
|
||||
})
|
||||
it('should handle duration (seconds) instead of fixed date to determine end date', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2s')
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].endTime).toEqual(moment('2013-01-01 00:00:02', 'YYYY-MM-DD hh:mm:ss').toDate())
|
||||
expect(tasks[0].id).toEqual('id1')
|
||||
expect(tasks[0].task).toEqual('test1')
|
||||
})
|
||||
it('should handle duration (weeks) instead of fixed date to determine end date', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2w')
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].endTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].id).toEqual('id1')
|
||||
expect(tasks[0].task).toEqual('test1')
|
||||
})
|
||||
it.each`
|
||||
testName | section | taskName | taskData | expStartDate | expEndDate | expId | expTask
|
||||
${'should handle fixed dates'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2013-01-12'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 12)} | ${'id1'} | ${'test1'}
|
||||
${'should handle duration (days) instead of fixed date to determine end date'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2d'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 3)} | ${'id1'} | ${'test1'}
|
||||
${'should handle duration (hours) instead of fixed date to determine end date'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2h'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 1, 2)} | ${'id1'} | ${'test1'}
|
||||
${'should handle duration (minutes) instead of fixed date to determine end date'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2m'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 1, 0, 2)} | ${'id1'} | ${'test1'}
|
||||
${'should handle duration (seconds) instead of fixed date to determine end date'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2s'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 1, 0, 0, 2)} | ${'id1'} | ${'test1'}
|
||||
${'should handle duration (weeks) instead of fixed date to determine end date'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2w'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 15)} | ${'id1'} | ${'test1'}
|
||||
${'should handle fixed dates without id'} | ${'testa1'} | ${'test1'} | ${'2013-01-01,2013-01-12'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 12)} | ${'task1'} | ${'test1'}
|
||||
${'should handle duration instead of a fixed date to determine end date without id'} | ${'testa1'} | ${'test1'} | ${'2013-01-01,4d'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 5)} | ${'task1'} | ${'test1'}
|
||||
`('$testName', ({ section, taskName, taskData, expStartDate, expEndDate, expId, expTask }) => {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection(section)
|
||||
ganttDb.addTask(taskName, taskData)
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[0].startTime).toEqual(expStartDate)
|
||||
expect(tasks[0].endTime).toEqual(expEndDate)
|
||||
expect(tasks[0].id).toEqual(expId)
|
||||
expect(tasks[0].task).toEqual(expTask)
|
||||
})
|
||||
|
||||
it('should handle relative start date based on id', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2w')
|
||||
ganttDb.addTask('test2', 'id2,after id1,1d')
|
||||
it.each`
|
||||
section | taskName1 | taskName2 | taskData1 | taskData2 | expStartDate2 | expEndDate2 | expId2 | expTask2
|
||||
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'id2,after id1,1d'} | ${new Date(2013, 0, 15)} | ${undefined} | ${'id2'} | ${'test2'}
|
||||
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'id2,after id3,1d'} | ${new Date((new Date()).setHours(0, 0, 0, 0))} | ${undefined} | ${'id2'} | ${'test2'}
|
||||
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'after id1,1d'} | ${new Date(2013, 0, 15)} | ${undefined} | ${'task1'} | ${'test2'}
|
||||
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'2013-01-26'} | ${new Date(2013, 0, 15)} | ${new Date(2013, 0, 26)} | ${'task1'} | ${'test2'}
|
||||
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'2d'} | ${new Date(2013, 0, 15)} | ${new Date(2013, 0, 17)} | ${'task1'} | ${'test2'}
|
||||
`('$testName', ({ section, taskName1, taskName2, taskData1, taskData2, expStartDate2, expEndDate2, expId2, expTask2 }) => {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection(section)
|
||||
ganttDb.addTask(taskName1, taskData1)
|
||||
ganttDb.addTask(taskName2, taskData2)
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[1].startTime).toEqual(expStartDate2)
|
||||
if (!expEndDate2 === undefined) {
|
||||
expect(tasks[1].endTime).toEqual(expEndDate2)
|
||||
}
|
||||
expect(tasks[1].id).toEqual(expId2)
|
||||
expect(tasks[1].task).toEqual(expTask2)
|
||||
})
|
||||
|
||||
const tasks = ganttDb.getTasks()
|
||||
|
||||
expect(tasks[1].startTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].id).toEqual('id2')
|
||||
expect(tasks[1].task).toEqual('test2')
|
||||
})
|
||||
|
||||
it('should handle relative start date based on id when id is invalid', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2w')
|
||||
ganttDb.addTask('test2', 'id2,after id3,1d')
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[1].startTime).toEqual(new Date((new Date()).setHours(0, 0, 0, 0)))
|
||||
expect(tasks[1].id).toEqual('id2')
|
||||
expect(tasks[1].task).toEqual('test2')
|
||||
})
|
||||
|
||||
it('should handle fixed dates without id', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', '2013-01-01,2013-01-12')
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].endTime).toEqual(moment('2013-01-12', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].id).toEqual('task1')
|
||||
expect(tasks[0].task).toEqual('test1')
|
||||
})
|
||||
|
||||
it('should handle duration instead of a fixed date to determine end date without id', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', '2013-01-01,4d')
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].endTime).toEqual(moment('2013-01-05', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].id).toEqual('task1')
|
||||
expect(tasks[0].task).toEqual('test1')
|
||||
})
|
||||
|
||||
it('should handle relative start date of a fixed date to determine end date without id', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2w')
|
||||
ganttDb.addTask('test2', 'after id1,1d')
|
||||
|
||||
const tasks = ganttDb.getTasks()
|
||||
|
||||
expect(tasks[1].startTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].id).toEqual('task1')
|
||||
expect(tasks[1].task).toEqual('test2')
|
||||
})
|
||||
it('should handle a new task with only an end date as definition', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2w')
|
||||
ganttDb.addTask('test2', '2013-01-26')
|
||||
|
||||
const tasks = ganttDb.getTasks()
|
||||
|
||||
expect(tasks[1].startTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].endTime).toEqual(moment('2013-01-26', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].id).toEqual('task1')
|
||||
expect(tasks[1].task).toEqual('test2')
|
||||
})
|
||||
it('should handle a new task with only an end date as definition', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2w')
|
||||
ganttDb.addTask('test2', '2d')
|
||||
|
||||
const tasks = ganttDb.getTasks()
|
||||
|
||||
expect(tasks[1].startTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].endTime).toEqual(moment('2013-01-17', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].id).toEqual('task1')
|
||||
expect(tasks[1].task).toEqual('test2')
|
||||
})
|
||||
it('should handle relative start date based on id regardless of sections', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
|
@ -162,14 +59,70 @@ describe('when using the ganttDb', function () {
|
|||
|
||||
const tasks = ganttDb.getTasks()
|
||||
|
||||
expect(tasks[1].startTime).toEqual(moment('2013-01-17', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].endTime).toEqual(moment('2013-01-18', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].startTime).toEqual(new Date(2013, 0, 17))
|
||||
expect(tasks[1].endTime).toEqual(new Date(2013, 0, 18))
|
||||
expect(tasks[1].id).toEqual('id2')
|
||||
expect(tasks[1].task).toEqual('test2')
|
||||
|
||||
expect(tasks[2].id).toEqual('id3')
|
||||
expect(tasks[2].task).toEqual('test3')
|
||||
expect(tasks[2].startTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[2].endTime).toEqual(moment('2013-01-17', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[2].startTime).toEqual(new Date(2013, 0, 15))
|
||||
expect(tasks[2].endTime).toEqual(new Date(2013, 0, 17))
|
||||
})
|
||||
it('should ignore weekends', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.setExcludes('weekends 2019-02-06,friday')
|
||||
ganttDb.addSection('weekends skip test')
|
||||
ganttDb.addTask('test1', 'id1,2019-02-01,1d')
|
||||
ganttDb.addTask('test2', 'id2,after id1,2d')
|
||||
ganttDb.addTask('test3', 'id3,after id2,7d')
|
||||
ganttDb.addTask('test4', 'id4,2019-02-01,2019-02-20') // Fixed endTime
|
||||
ganttDb.addTask('test5', 'id5,after id4,1d')
|
||||
ganttDb.addSection('full ending taks on last day')
|
||||
ganttDb.addTask('test6', 'id6,2019-02-13,2d')
|
||||
ganttDb.addTask('test7', 'id7,after id6,1d')
|
||||
|
||||
const tasks = ganttDb.getTasks()
|
||||
|
||||
expect(tasks[0].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].endTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].renderEndTime).toEqual(moment('2019-02-02', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].id).toEqual('id1')
|
||||
expect(tasks[0].task).toEqual('test1')
|
||||
|
||||
expect(tasks[1].startTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].endTime).toEqual(moment('2019-02-07', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].renderEndTime).toEqual(moment('2019-02-06', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].id).toEqual('id2')
|
||||
expect(tasks[1].task).toEqual('test2')
|
||||
|
||||
expect(tasks[2].startTime).toEqual(moment('2019-02-07', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[2].endTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[2].renderEndTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[2].id).toEqual('id3')
|
||||
expect(tasks[2].task).toEqual('test3')
|
||||
|
||||
expect(tasks[3].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[3].endTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[3].renderEndTime).toBeNull() // Fixed end
|
||||
expect(tasks[3].id).toEqual('id4')
|
||||
expect(tasks[3].task).toEqual('test4')
|
||||
|
||||
expect(tasks[4].startTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[4].endTime).toEqual(moment('2019-02-21', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[4].renderEndTime).toEqual(moment('2019-02-21', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[4].id).toEqual('id5')
|
||||
expect(tasks[4].task).toEqual('test5')
|
||||
|
||||
expect(tasks[5].startTime).toEqual(moment('2019-02-13', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[5].endTime).toEqual(moment('2019-02-18', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[5].renderEndTime).toEqual(moment('2019-02-15', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[5].id).toEqual('id6')
|
||||
expect(tasks[5].task).toEqual('test6')
|
||||
|
||||
expect(tasks[6].startTime).toEqual(moment('2019-02-18', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[6].endTime).toEqual(moment('2019-02-19', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[6].id).toEqual('id7')
|
||||
expect(tasks[6].task).toEqual('test7')
|
||||
})
|
||||
})
|
||||
|
|
|
@ -98,6 +98,7 @@ export const draw = function (text, id) {
|
|||
}
|
||||
|
||||
function drawRects (theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w, h) {
|
||||
// Draw background rects covering the entire width of the graph, these form the section rows.
|
||||
svg.append('g')
|
||||
.selectAll('rect')
|
||||
.data(theArray)
|
||||
|
@ -120,26 +121,42 @@ export const draw = function (text, id) {
|
|||
return 'section section0'
|
||||
})
|
||||
|
||||
// Draw the rects representing the tasks
|
||||
const rectangles = svg.append('g')
|
||||
.selectAll('rect')
|
||||
.data(theArray)
|
||||
.enter()
|
||||
|
||||
rectangles.append('rect')
|
||||
.attr('id', function (d) { return d.id })
|
||||
.attr('rx', 3)
|
||||
.attr('ry', 3)
|
||||
.attr('x', function (d) {
|
||||
if (d.milestone) {
|
||||
return timeScale(d.startTime) + theSidePad + (0.5 * (timeScale(d.endTime) - timeScale(d.startTime))) - (0.5 * theBarHeight)
|
||||
}
|
||||
return timeScale(d.startTime) + theSidePad
|
||||
})
|
||||
.attr('y', function (d, i) {
|
||||
return i * theGap + theTopPad
|
||||
})
|
||||
.attr('width', function (d) {
|
||||
return (timeScale(d.endTime) - timeScale(d.startTime))
|
||||
if (d.milestone) {
|
||||
return theBarHeight
|
||||
}
|
||||
return (timeScale(d.renderEndTime || d.endTime) - timeScale(d.startTime))
|
||||
})
|
||||
.attr('height', theBarHeight)
|
||||
.attr('transform-origin', function (d, i) {
|
||||
return (timeScale(d.startTime) + theSidePad + 0.5 * (timeScale(d.endTime) - timeScale(d.startTime))).toString() + 'px ' + (i * theGap + theTopPad + 0.5 * theBarHeight).toString() + 'px'
|
||||
})
|
||||
.attr('class', function (d) {
|
||||
const res = 'task '
|
||||
const res = 'task'
|
||||
|
||||
let classStr = ''
|
||||
if (d.classes.length > 0) {
|
||||
classStr = d.classes.join(' ')
|
||||
}
|
||||
|
||||
let secNum = 0
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
|
@ -148,37 +165,55 @@ export const draw = function (text, id) {
|
|||
}
|
||||
}
|
||||
|
||||
let taskClass = ''
|
||||
if (d.active) {
|
||||
if (d.crit) {
|
||||
return res + ' activeCrit' + secNum
|
||||
taskClass += ' activeCrit'
|
||||
} else {
|
||||
return res + ' active' + secNum
|
||||
taskClass = ' active'
|
||||
}
|
||||
}
|
||||
|
||||
if (d.done) {
|
||||
} else if (d.done) {
|
||||
if (d.crit) {
|
||||
return res + ' doneCrit' + secNum
|
||||
taskClass = ' doneCrit'
|
||||
} else {
|
||||
return res + ' done' + secNum
|
||||
taskClass = ' done'
|
||||
}
|
||||
} else {
|
||||
if (d.crit) {
|
||||
taskClass += ' crit'
|
||||
}
|
||||
}
|
||||
|
||||
if (d.crit) {
|
||||
return res + ' crit' + secNum
|
||||
if (taskClass.length === 0) {
|
||||
taskClass = ' task'
|
||||
}
|
||||
|
||||
return res + ' task' + secNum
|
||||
if (d.milestone) {
|
||||
taskClass = ' milestone ' + taskClass
|
||||
}
|
||||
|
||||
taskClass += secNum
|
||||
|
||||
taskClass += ' ' + classStr
|
||||
|
||||
return res + taskClass
|
||||
})
|
||||
|
||||
// Append task labels
|
||||
rectangles.append('text')
|
||||
.text(function (d) {
|
||||
return d.task
|
||||
})
|
||||
.attr('font-size', conf.fontSize)
|
||||
.attr('x', function (d) {
|
||||
const startX = timeScale(d.startTime)
|
||||
const endX = timeScale(d.endTime)
|
||||
let startX = timeScale(d.startTime)
|
||||
let endX = timeScale(d.renderEndTime || d.endTime)
|
||||
if (d.milestone) {
|
||||
startX += (0.5 * (timeScale(d.endTime) - timeScale(d.startTime))) - (0.5 * theBarHeight)
|
||||
}
|
||||
if (d.milestone) {
|
||||
endX = startX + theBarHeight
|
||||
}
|
||||
const textWidth = this.getBBox().width
|
||||
|
||||
// Check id text width > width of rectangle
|
||||
|
@ -198,8 +233,17 @@ export const draw = function (text, id) {
|
|||
.attr('text-height', theBarHeight)
|
||||
.attr('class', function (d) {
|
||||
const startX = timeScale(d.startTime)
|
||||
const endX = timeScale(d.endTime)
|
||||
let endX = timeScale(d.endTime)
|
||||
if (d.milestone) {
|
||||
endX = startX + theBarHeight
|
||||
}
|
||||
const textWidth = this.getBBox().width
|
||||
|
||||
let classStr = ''
|
||||
if (d.classes.length > 0) {
|
||||
classStr = d.classes.join(' ')
|
||||
}
|
||||
|
||||
let secNum = 0
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
if (d.type === categories[i]) {
|
||||
|
@ -228,15 +272,19 @@ export const draw = function (text, id) {
|
|||
}
|
||||
}
|
||||
|
||||
if (d.milestone) {
|
||||
taskType += ' milestoneText'
|
||||
}
|
||||
|
||||
// Check id text width > width of rectangle
|
||||
if (textWidth > (endX - startX)) {
|
||||
if (endX + textWidth + 1.5 * conf.leftPadding > w) {
|
||||
return 'taskTextOutsideLeft taskTextOutside' + secNum + ' ' + taskType
|
||||
return classStr + ' taskTextOutsideLeft taskTextOutside' + secNum + ' ' + taskType
|
||||
} else {
|
||||
return 'taskTextOutsideRight taskTextOutside' + secNum + ' ' + taskType
|
||||
return classStr + ' taskTextOutsideRight taskTextOutside' + secNum + ' ' + taskType
|
||||
}
|
||||
} else {
|
||||
return 'taskText taskText' + secNum + ' ' + taskType
|
||||
return classStr + ' taskText taskText' + secNum + ' ' + taskType
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,27 +7,64 @@
|
|||
|
||||
%options case-insensitive
|
||||
|
||||
%{
|
||||
// Pre-lexer code can go here
|
||||
%}
|
||||
|
||||
%x click
|
||||
%x href
|
||||
%x callbackname
|
||||
%x callbackargs
|
||||
%%
|
||||
|
||||
[\n]+ return 'NL';
|
||||
\s+ /* skip whitespace */
|
||||
\#[^\n]* /* skip comments */
|
||||
\%%[^\n]* /* skip comments */
|
||||
"gantt" return 'gantt';
|
||||
|
||||
/*
|
||||
---interactivity command---
|
||||
'href' adds a link to the specified task. 'href' can only be specified when the
|
||||
line was introduced with 'click'.
|
||||
'href "<link>"' attaches the specified link to the task that was specified by 'click'.
|
||||
*/
|
||||
"href"[\s]+["] this.begin("href");
|
||||
<href>["] this.popState();
|
||||
<href>[^"]* return 'href';
|
||||
|
||||
/*
|
||||
---interactivity command---
|
||||
'call' adds a callback to the specified task. 'call' can only be specified when
|
||||
the line was introdcued with 'click'.
|
||||
'call <callbackname>(<args>)' attaches the function 'callbackname' with the specified
|
||||
arguments to the task that was specified by 'click'.
|
||||
Function arguments are optional: 'call <callbackname>()' simply executes 'callbackname' without any arguments.
|
||||
*/
|
||||
"call"[\s]+ this.begin("callbackname");
|
||||
<callbackname>\([\s]*\) this.popState();
|
||||
<callbackname>\( this.popState(); this.begin("callbackargs");
|
||||
<callbackname>[^(]* return 'callbackname';
|
||||
<callbackargs>\) this.popState();
|
||||
<callbackargs>[^)]* return 'callbackargs';
|
||||
|
||||
/*
|
||||
'click' is the keyword to introduce a line that contains interactivity commands.
|
||||
'click' must be followed by an existing task-id. All commands are attached to
|
||||
that id.
|
||||
'click <id>' can be followed by href or call commands in any desired order
|
||||
*/
|
||||
"click"[\s]+ this.begin("click");
|
||||
<click>[\s\n] this.popState();
|
||||
<click>[^\s\n]* return 'click';
|
||||
|
||||
"gantt" return 'gantt';
|
||||
"dateFormat"\s[^#\n;]+ return 'dateFormat';
|
||||
"axisFormat"\s[^#\n;]+ return 'axisFormat';
|
||||
"excludes"\s[^#\n;]+ return 'excludes';
|
||||
\d\d\d\d"-"\d\d"-"\d\d return 'date';
|
||||
"title"\s[^#\n;]+ return 'title';
|
||||
"section"\s[^#:\n;]+ return 'section';
|
||||
[^#:\n;]+ return 'taskTxt';
|
||||
[^#:\n;]+ return 'taskTxt';
|
||||
":"[^#\n;]+ return 'taskData';
|
||||
":" return ':';
|
||||
<<EOF>> return 'EOF';
|
||||
. return 'INVALID';
|
||||
":" return ':';
|
||||
<<EOF>> return 'EOF';
|
||||
. return 'INVALID';
|
||||
|
||||
/lex
|
||||
|
||||
|
@ -54,11 +91,40 @@ line
|
|||
;
|
||||
|
||||
statement
|
||||
: 'dateFormat' {yy.setDateFormat($1.substr(11));$$=$1.substr(11);}
|
||||
: 'dateFormat' {yy.setDateFormat($1.substr(11));$$=$1.substr(11);}
|
||||
| 'axisFormat' {yy.setAxisFormat($1.substr(11));$$=$1.substr(11);}
|
||||
| title {yy.setTitle($1.substr(6));$$=$1.substr(6);}
|
||||
| section {yy.addSection($1.substr(8));$$=$1.substr(8);}
|
||||
| taskTxt taskData {yy.addTask($1,$2);$$='task';}
|
||||
;
|
||||
| 'excludes' {yy.setExcludes($1.substr(9));$$=$1.substr(9);}
|
||||
| title {yy.setTitle($1.substr(6));$$=$1.substr(6);}
|
||||
| section {yy.addSection($1.substr(8));$$=$1.substr(8);}
|
||||
| clickStatement
|
||||
| taskTxt taskData {yy.addTask($1,$2);$$='task';}
|
||||
;
|
||||
|
||||
%%
|
||||
/*
|
||||
click allows any combination of href and call.
|
||||
*/
|
||||
clickStatement
|
||||
: click callbackname {$$ = $1;yy.setClickEvent($1, $2, null);}
|
||||
| click callbackname callbackargs {$$ = $1;yy.setClickEvent($1, $2, $3);}
|
||||
|
||||
| click callbackname href {$$ = $1;yy.setClickEvent($1, $2, null);yy.setLink($1,$3);}
|
||||
| click callbackname callbackargs href {$$ = $1;yy.setClickEvent($1, $2, $3);yy.setLink($1,$4);}
|
||||
|
||||
| click href callbackname {$$ = $1;yy.setClickEvent($1, $3, null);yy.setLink($1,$2);}
|
||||
| click href callbackname callbackargs {$$ = $1;yy.setClickEvent($1, $3, $4);yy.setLink($1,$2);}
|
||||
|
||||
| click href {$$ = $1;yy.setLink($1, $2);}
|
||||
;
|
||||
|
||||
clickStatementDebug
|
||||
: click callbackname {$$=$1 + ' ' + $2;}
|
||||
| click callbackname href {$$=$1 + ' ' + $2 + ' ' + $3;}
|
||||
|
||||
| click callbackname callbackargs {$$=$1 + ' ' + $2 + ' ' + $3;}
|
||||
| click callbackname callbackargs href {$$=$1 + ' ' + $2 + ' ' + $3 + ' ' + $4;}
|
||||
|
||||
| click href callbackname {$$=$1 + ' ' + $2 + ' ' + $3;}
|
||||
| click href callbackname callbackargs {$$=$1 + ' ' + $2 + ' ' + $3 + ' ' + $4;}
|
||||
|
||||
| click href {$$=$1 + ' ' + $2;}
|
||||
;%%
|
||||
|
|
|
@ -72,12 +72,12 @@
|
|||
}
|
||||
*/
|
||||
var parser = (function(){
|
||||
var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[6,8,10,11,12,13,14,15],$V1=[1,9],$V2=[1,10],$V3=[1,11],$V4=[1,12],$V5=[1,13];
|
||||
var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[6,8,10,11,12,13,14,15,17,19],$V1=[1,9],$V2=[1,10],$V3=[1,11],$V4=[1,12],$V5=[1,13],$V6=[1,15],$V7=[1,16];
|
||||
var parser = {trace: function trace() { },
|
||||
yy: {},
|
||||
symbols_: {"error":2,"start":3,"gantt":4,"document":5,"EOF":6,"line":7,"SPACE":8,"statement":9,"NL":10,"dateFormat":11,"axisFormat":12,"title":13,"section":14,"taskTxt":15,"taskData":16,"$accept":0,"$end":1},
|
||||
terminals_: {2:"error",4:"gantt",6:"EOF",8:"SPACE",10:"NL",11:"dateFormat",12:"axisFormat",13:"title",14:"section",15:"taskTxt",16:"taskData"},
|
||||
productions_: [0,[3,3],[5,0],[5,2],[7,2],[7,1],[7,1],[7,1],[9,1],[9,1],[9,1],[9,1],[9,2]],
|
||||
symbols_: {"error":2,"start":3,"gantt":4,"document":5,"EOF":6,"line":7,"SPACE":8,"statement":9,"NL":10,"dateFormat":11,"axisFormat":12,"excludes":13,"title":14,"section":15,"clickStatement":16,"taskTxt":17,"taskData":18,"click":19,"callbackname":20,"callbackargs":21,"href":22,"clickStatementDebug":23,"$accept":0,"$end":1},
|
||||
terminals_: {2:"error",4:"gantt",6:"EOF",8:"SPACE",10:"NL",11:"dateFormat",12:"axisFormat",13:"excludes",14:"title",15:"section",17:"taskTxt",18:"taskData",19:"click",20:"callbackname",21:"callbackargs",22:"href"},
|
||||
productions_: [0,[3,3],[5,0],[5,2],[7,2],[7,1],[7,1],[7,1],[9,1],[9,1],[9,1],[9,1],[9,1],[9,1],[9,2],[16,2],[16,3],[16,3],[16,4],[16,3],[16,4],[16,2],[23,2],[23,3],[23,3],[23,4],[23,3],[23,4],[23,2]],
|
||||
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
|
||||
/* this == yyval */
|
||||
|
||||
|
@ -105,17 +105,50 @@ case 9:
|
|||
yy.setAxisFormat($$[$0].substr(11));this.$=$$[$0].substr(11);
|
||||
break;
|
||||
case 10:
|
||||
yy.setTitle($$[$0].substr(6));this.$=$$[$0].substr(6);
|
||||
yy.setExcludes($$[$0].substr(9));this.$=$$[$0].substr(9);
|
||||
break;
|
||||
case 11:
|
||||
yy.addSection($$[$0].substr(8));this.$=$$[$0].substr(8);
|
||||
yy.setTitle($$[$0].substr(6));this.$=$$[$0].substr(6);
|
||||
break;
|
||||
case 12:
|
||||
yy.addSection($$[$0].substr(8));this.$=$$[$0].substr(8);
|
||||
break;
|
||||
case 14:
|
||||
yy.addTask($$[$0-1],$$[$0]);this.$='task';
|
||||
break;
|
||||
case 15:
|
||||
this.$ = $$[$0-1];yy.setClickEvent($$[$0-1], $$[$0], null);
|
||||
break;
|
||||
case 16:
|
||||
this.$ = $$[$0-2];yy.setClickEvent($$[$0-2], $$[$0-1], $$[$0]);
|
||||
break;
|
||||
case 17:
|
||||
this.$ = $$[$0-2];yy.setClickEvent($$[$0-2], $$[$0-1], null);yy.setLink($$[$0-2],$$[$0]);
|
||||
break;
|
||||
case 18:
|
||||
this.$ = $$[$0-3];yy.setClickEvent($$[$0-3], $$[$0-2], $$[$0-1]);yy.setLink($$[$0-3],$$[$0]);
|
||||
break;
|
||||
case 19:
|
||||
this.$ = $$[$0-2];yy.setClickEvent($$[$0-2], $$[$0], null);yy.setLink($$[$0-2],$$[$0-1]);
|
||||
break;
|
||||
case 20:
|
||||
this.$ = $$[$0-3];yy.setClickEvent($$[$0-3], $$[$0-1], $$[$0]);yy.setLink($$[$0-3],$$[$0-2]);
|
||||
break;
|
||||
case 21:
|
||||
this.$ = $$[$0-1];yy.setLink($$[$0-1], $$[$0]);
|
||||
break;
|
||||
case 22: case 28:
|
||||
this.$=$$[$0-1] + ' ' + $$[$0];
|
||||
break;
|
||||
case 23: case 24: case 26:
|
||||
this.$=$$[$0-2] + ' ' + $$[$0-1] + ' ' + $$[$0];
|
||||
break;
|
||||
case 25: case 27:
|
||||
this.$=$$[$0-3] + ' ' + $$[$0-2] + ' ' + $$[$0-1] + ' ' + $$[$0];
|
||||
break;
|
||||
}
|
||||
},
|
||||
table: [{3:1,4:[1,2]},{1:[3]},o($V0,[2,2],{5:3}),{6:[1,4],7:5,8:[1,6],9:7,10:[1,8],11:$V1,12:$V2,13:$V3,14:$V4,15:$V5},o($V0,[2,7],{1:[2,1]}),o($V0,[2,3]),{9:14,11:$V1,12:$V2,13:$V3,14:$V4,15:$V5},o($V0,[2,5]),o($V0,[2,6]),o($V0,[2,8]),o($V0,[2,9]),o($V0,[2,10]),o($V0,[2,11]),{16:[1,15]},o($V0,[2,4]),o($V0,[2,12])],
|
||||
table: [{3:1,4:[1,2]},{1:[3]},o($V0,[2,2],{5:3}),{6:[1,4],7:5,8:[1,6],9:7,10:[1,8],11:$V1,12:$V2,13:$V3,14:$V4,15:$V5,16:14,17:$V6,19:$V7},o($V0,[2,7],{1:[2,1]}),o($V0,[2,3]),{9:17,11:$V1,12:$V2,13:$V3,14:$V4,15:$V5,16:14,17:$V6,19:$V7},o($V0,[2,5]),o($V0,[2,6]),o($V0,[2,8]),o($V0,[2,9]),o($V0,[2,10]),o($V0,[2,11]),o($V0,[2,12]),o($V0,[2,13]),{18:[1,18]},{20:[1,19],22:[1,20]},o($V0,[2,4]),o($V0,[2,14]),o($V0,[2,15],{21:[1,21],22:[1,22]}),o($V0,[2,21],{20:[1,23]}),o($V0,[2,16],{22:[1,24]}),o($V0,[2,17]),o($V0,[2,19],{21:[1,25]}),o($V0,[2,18]),o($V0,[2,20])],
|
||||
defaultActions: {},
|
||||
parseError: function parseError(str, hash) {
|
||||
if (hash.recoverable) {
|
||||
|
@ -593,8 +626,6 @@ stateStackSize:function stateStackSize() {
|
|||
},
|
||||
options: {"case-insensitive":true},
|
||||
performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
|
||||
// Pre-lexer code can go here
|
||||
|
||||
var YYSTATE=YY_START;
|
||||
switch($avoiding_name_collisions) {
|
||||
case 0:return 10;
|
||||
|
@ -605,32 +636,58 @@ case 2:/* skip comments */
|
|||
break;
|
||||
case 3:/* skip comments */
|
||||
break;
|
||||
case 4:return 4;
|
||||
case 4:this.begin("href");
|
||||
break;
|
||||
case 5:return 11;
|
||||
case 5:this.popState();
|
||||
break;
|
||||
case 6:return 12;
|
||||
case 6:return 22;
|
||||
break;
|
||||
case 7:return 'date';
|
||||
case 7:this.begin("callbackname");
|
||||
break;
|
||||
case 8:return 13;
|
||||
case 8:this.popState();
|
||||
break;
|
||||
case 9:return 14;
|
||||
case 9:this.popState(); this.begin("callbackargs");
|
||||
break;
|
||||
case 10:return 15;
|
||||
case 10:return 20;
|
||||
break;
|
||||
case 11:return 16;
|
||||
case 11:this.popState();
|
||||
break;
|
||||
case 12:return ':';
|
||||
case 12:return 21;
|
||||
break;
|
||||
case 13:return 6;
|
||||
case 13:this.begin("click");
|
||||
break;
|
||||
case 14:return 'INVALID';
|
||||
case 14:this.popState();
|
||||
break;
|
||||
case 15:return 19;
|
||||
break;
|
||||
case 16:return 4;
|
||||
break;
|
||||
case 17:return 11;
|
||||
break;
|
||||
case 18:return 12;
|
||||
break;
|
||||
case 19:return 13;
|
||||
break;
|
||||
case 20:return 'date';
|
||||
break;
|
||||
case 21:return 14;
|
||||
break;
|
||||
case 22:return 15;
|
||||
break;
|
||||
case 23:return 17;
|
||||
break;
|
||||
case 24:return 18;
|
||||
break;
|
||||
case 25:return ':';
|
||||
break;
|
||||
case 26:return 6;
|
||||
break;
|
||||
case 27:return 'INVALID';
|
||||
break;
|
||||
}
|
||||
},
|
||||
rules: [/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:gantt\b)/i,/^(?:dateFormat\s[^#\n;]+)/i,/^(?:axisFormat\s[^#\n;]+)/i,/^(?:\d\d\d\d-\d\d-\d\d\b)/i,/^(?:title\s[^#\n;]+)/i,/^(?:section\s[^#:\n;]+)/i,/^(?:[^#:\n;]+)/i,/^(?::[^#\n;]+)/i,/^(?::)/i,/^(?:$)/i,/^(?:.)/i],
|
||||
conditions: {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14],"inclusive":true}}
|
||||
rules: [/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:href[\s]+["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:call[\s]+)/i,/^(?:\([\s]*\))/i,/^(?:\()/i,/^(?:[^(]*)/i,/^(?:\))/i,/^(?:[^)]*)/i,/^(?:click[\s]+)/i,/^(?:[\s\n])/i,/^(?:[^\s\n]*)/i,/^(?:gantt\b)/i,/^(?:dateFormat\s[^#\n;]+)/i,/^(?:axisFormat\s[^#\n;]+)/i,/^(?:excludes\s[^#\n;]+)/i,/^(?:\d\d\d\d-\d\d-\d\d\b)/i,/^(?:title\s[^#\n;]+)/i,/^(?:section\s[^#:\n;]+)/i,/^(?:[^#:\n;]+)/i,/^(?::[^#\n;]+)/i,/^(?::)/i,/^(?:$)/i,/^(?:.)/i],
|
||||
conditions: {"callbackargs":{"rules":[11,12],"inclusive":false},"callbackname":{"rules":[8,9,10],"inclusive":false},"href":{"rules":[5,6],"inclusive":false},"click":{"rules":[14,15],"inclusive":false},"INITIAL":{"rules":[0,1,2,3,4,7,13,16,17,18,19,20,21,22,23,24,25,26,27],"inclusive":true}}
|
||||
});
|
||||
return lexer;
|
||||
})();
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/* eslint-env jasmine */
|
||||
/* eslint-disable no-eval */
|
||||
import { parser } from './gantt'
|
||||
import ganttDb from '../ganttDb'
|
||||
|
||||
describe('when parsing a gantt diagram it', function () {
|
||||
beforeEach(function () {
|
||||
parser.yy = ganttDb
|
||||
parser.yy.clear()
|
||||
})
|
||||
|
||||
it('should handle a dateFormat definition', function () {
|
||||
const str = 'gantt\ndateFormat yyyy-mm-dd'
|
||||
|
||||
parser.parse(str)
|
||||
})
|
||||
it('should handle a title definition', function () {
|
||||
const str = 'gantt\ndateFormat yyyy-mm-dd\ntitle Adding gantt diagram functionality to mermaid'
|
||||
|
||||
parser.parse(str)
|
||||
})
|
||||
it('should handle an excludes definition', function () {
|
||||
const str = 'gantt\ndateFormat yyyy-mm-dd\ntitle Adding gantt diagram functionality to mermaid\nexcludes weekdays 2019-02-01'
|
||||
|
||||
parser.parse(str)
|
||||
})
|
||||
it('should handle a section definition', function () {
|
||||
const str = 'gantt\n' +
|
||||
'dateFormat yyyy-mm-dd\n' +
|
||||
'title Adding gantt diagram functionality to mermaid\n' +
|
||||
'excludes weekdays 2019-02-01\n' +
|
||||
'section Documentation'
|
||||
|
||||
parser.parse(str)
|
||||
})
|
||||
/**
|
||||
* Beslutsflöde inligt nedan. Obs bla bla bla
|
||||
* ```
|
||||
* graph TD
|
||||
* A[Hard pledge] -- text on link -->B(Round edge)
|
||||
* B --> C{to do or not to do}
|
||||
* C -->|Too| D[Result one]
|
||||
* C -->|Doo| E[Result two]
|
||||
```
|
||||
* params bapa - a unique bapap
|
||||
*/
|
||||
it('should handle a task definition', function () {
|
||||
const str = 'gantt\n' +
|
||||
'dateFormat YYYY-MM-DD\n' +
|
||||
'title Adding gantt diagram functionality to mermaid\n' +
|
||||
'section Documentation\n' +
|
||||
'Design jison grammar:des1, 2014-01-01, 2014-01-04'
|
||||
|
||||
parser.parse(str)
|
||||
|
||||
const tasks = parser.yy.getTasks()
|
||||
|
||||
expect(tasks[0].startTime).toEqual(new Date(2014, 0, 1))
|
||||
expect(tasks[0].endTime).toEqual(new Date(2014, 0, 4))
|
||||
expect(tasks[0].id).toEqual('des1')
|
||||
expect(tasks[0].task).toEqual('Design jison grammar')
|
||||
})
|
||||
it.each`
|
||||
tags | milestone | done | crit | active
|
||||
${'milestone'} | ${true} | ${false} | ${false} | ${false}
|
||||
${'done'} | ${false} | ${true} | ${false} | ${false}
|
||||
${'crit'} | ${false} | ${false} | ${true} | ${false}
|
||||
${'active'} | ${false} | ${false} | ${false} | ${true}
|
||||
${'crit,milestone,done'} | ${true} | ${true} | ${true} | ${false}
|
||||
`('should handle a task with tags $tags', ({ tags, milestone, done, crit, active }) => {
|
||||
const str = 'gantt\n' +
|
||||
'dateFormat YYYY-MM-DD\n' +
|
||||
'title Adding gantt diagram functionality to mermaid\n' +
|
||||
'section Documentation\n' +
|
||||
'test task:' + tags + ', 2014-01-01, 2014-01-04'
|
||||
|
||||
const allowedTags = ['active', 'done', 'crit', 'milestone']
|
||||
|
||||
parser.parse(str)
|
||||
|
||||
const tasks = parser.yy.getTasks()
|
||||
|
||||
allowedTags.forEach(function (t) {
|
||||
if (eval(t)) {
|
||||
expect(tasks[0][t]).toBeTruthy()
|
||||
} else {
|
||||
expect(tasks[0][t]).toBeFalsy()
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
|
@ -155,9 +155,9 @@ function prettyPrintCommitHistory (commitArr) {
|
|||
}
|
||||
})
|
||||
const label = [line, commit.id, commit.seq]
|
||||
_.each(branches, function (value, key) {
|
||||
if (value === commit.id) label.push(key)
|
||||
})
|
||||
for (let branch in branches) {
|
||||
if (branches[branch] === commit.id) label.push(branch)
|
||||
}
|
||||
logger.debug(label.join(' '))
|
||||
if (Array.isArray(commit.parent)) {
|
||||
const newCommit = commits[commit.parent[0]]
|
||||
|
@ -188,9 +188,10 @@ export const clear = function () {
|
|||
}
|
||||
|
||||
export const getBranchesAsObjArray = function () {
|
||||
const branchArr = _.map(branches, function (value, key) {
|
||||
return { 'name': key, 'commit': commits[value] }
|
||||
})
|
||||
const branchArr = []
|
||||
for (let branch in branches) {
|
||||
branchArr.push({ name: branch, commit: commits[branches[branch]] })
|
||||
}
|
||||
return branchArr
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import _ from 'lodash'
|
||||
import * as d3 from 'd3'
|
||||
import _ from 'lodash'
|
||||
|
||||
import db from './gitGraphAst'
|
||||
import gitGraphParser from './parser/gitGraph'
|
||||
|
@ -160,7 +160,7 @@ function cloneNode (svg, selector) {
|
|||
function renderCommitHistory (svg, commitid, branches, direction) {
|
||||
let commit
|
||||
const numCommits = Object.keys(allCommitsDict).length
|
||||
if (_.isString(commitid)) {
|
||||
if (typeof commitid === 'string') {
|
||||
do {
|
||||
commit = allCommitsDict[commitid]
|
||||
logger.debug('in renderCommitHistory', commit.id, commit.seq)
|
||||
|
@ -189,7 +189,13 @@ function renderCommitHistory (svg, commitid, branches, direction) {
|
|||
.attr('stroke', config.nodeStrokeColor)
|
||||
.attr('stroke-width', config.nodeStrokeWidth)
|
||||
|
||||
const branch = _.find(branches, ['commit', commit])
|
||||
let branch
|
||||
for (let branchName in branches) {
|
||||
if (branches[branchName].commit === commit) {
|
||||
branch = branches[branchName]
|
||||
break
|
||||
}
|
||||
}
|
||||
if (branch) {
|
||||
logger.debug('found branch ', branch.name)
|
||||
svg.select('#node-' + commit.id + ' p')
|
||||
|
@ -211,7 +217,7 @@ function renderCommitHistory (svg, commitid, branches, direction) {
|
|||
} while (commitid && allCommitsDict[commitid])
|
||||
}
|
||||
|
||||
if (_.isArray(commitid)) {
|
||||
if (Array.isArray(commitid)) {
|
||||
logger.debug('found merge commmit', commitid)
|
||||
renderCommitHistory(svg, commitid[0], branches, direction)
|
||||
branchNum++
|
||||
|
@ -223,11 +229,11 @@ function renderCommitHistory (svg, commitid, branches, direction) {
|
|||
function renderLines (svg, commit, direction, branchColor) {
|
||||
branchColor = branchColor || 0
|
||||
while (commit.seq > 0 && !commit.lineDrawn) {
|
||||
if (_.isString(commit.parent)) {
|
||||
if (typeof commit.parent === 'string') {
|
||||
svgDrawLineForCommits(svg, commit.id, commit.parent, direction, branchColor)
|
||||
commit.lineDrawn = true
|
||||
commit = allCommitsDict[commit.parent]
|
||||
} else if (_.isArray(commit.parent)) {
|
||||
} else if (Array.isArray(commit.parent)) {
|
||||
svgDrawLineForCommits(svg, commit.id, commit.parent[0], direction, branchColor)
|
||||
svgDrawLineForCommits(svg, commit.id, commit.parent[1], direction, branchColor + 1)
|
||||
renderLines(svg, allCommitsDict[commit.parent[1]], direction, branchColor + 1)
|
||||
|
@ -246,7 +252,7 @@ export const draw = function (txt, id, ver) {
|
|||
// Parse the graph definition
|
||||
parser.parse(txt + '\n')
|
||||
|
||||
config = _.extend(config, apiConfig, db.getOptions())
|
||||
config = _.assign(config, apiConfig, db.getOptions())
|
||||
logger.debug('effective options', config)
|
||||
const direction = db.getDirection()
|
||||
allCommitsDict = db.getCommits()
|
||||
|
@ -259,11 +265,12 @@ export const draw = function (txt, id, ver) {
|
|||
const svg = d3.select(`[id="${id}"]`)
|
||||
svgCreateDefs(svg)
|
||||
branchNum = 1
|
||||
_.each(branches, function (v) {
|
||||
for (let branch in branches) {
|
||||
const v = branches[branch]
|
||||
renderCommitHistory(svg, v.commit.id, branches, direction)
|
||||
renderLines(svg, v.commit, direction)
|
||||
branchNum++
|
||||
})
|
||||
}
|
||||
svg.attr('height', function () {
|
||||
if (direction === 'BT') return Object.keys(allCommitsDict).length * config.nodeSpacing
|
||||
return (branches.length + 1) * config.branchOffset
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/* eslint-env jasmine */
|
||||
describe('when parsing an info graph it', function () {
|
||||
var ex
|
||||
beforeEach(function () {
|
||||
ex = require('./parser/info').parser
|
||||
ex.yy = require('./infoDb')
|
||||
})
|
||||
|
||||
it('should handle an info definition', function () {
|
||||
var str = `info
|
||||
showInfo`
|
||||
|
||||
ex.parse(str)
|
||||
})
|
||||
})
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* Created by knut on 15-01-14.
|
||||
*/
|
||||
import { logger } from '../../logger'
|
||||
|
||||
var message = ''
|
||||
var info = false
|
||||
|
||||
export const setMessage = txt => {
|
||||
logger.debug('Setting message to: ' + txt)
|
||||
message = txt
|
||||
}
|
||||
|
||||
export const getMessage = () => {
|
||||
return message
|
||||
}
|
||||
|
||||
export const setInfo = inf => {
|
||||
info = inf
|
||||
}
|
||||
|
||||
export const getInfo = () => {
|
||||
return info
|
||||
}
|
||||
|
||||
// export const parseError = (err, hash) => {
|
||||
// global.mermaidAPI.parseError(err, hash)
|
||||
// }
|
||||
|
||||
export default {
|
||||
setMessage,
|
||||
getMessage,
|
||||
setInfo,
|
||||
getInfo
|
||||
// parseError
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* Created by knut on 14-12-11.
|
||||
*/
|
||||
import * as d3 from 'd3'
|
||||
import db from './infoDb'
|
||||
import infoParser from './parser/info.js'
|
||||
import { logger } from '../../logger'
|
||||
|
||||
const conf = {
|
||||
}
|
||||
export const setConf = function (cnf) {
|
||||
const keys = Object.keys(cnf)
|
||||
|
||||
keys.forEach(function (key) {
|
||||
conf[key] = cnf[key]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a an info picture in the tag with id: id based on the graph definition in text.
|
||||
* @param text
|
||||
* @param id
|
||||
*/
|
||||
export const draw = (txt, id, ver) => {
|
||||
try {
|
||||
const parser = infoParser.parser
|
||||
parser.yy = db
|
||||
logger.debug('Renering info diagram\n' + txt)
|
||||
// Parse the graph definition
|
||||
parser.parse(txt)
|
||||
logger.debug('Parsed info diagram')
|
||||
// Fetch the default direction, use TD if none was found
|
||||
const svg = d3.select('#' + id)
|
||||
|
||||
const g = svg.append('g')
|
||||
|
||||
g.append('text') // text label for the x axis
|
||||
.attr('x', 100)
|
||||
.attr('y', 40)
|
||||
.attr('class', 'version')
|
||||
.attr('font-size', '32px')
|
||||
.style('text-anchor', 'middle')
|
||||
.text('v ' + ver)
|
||||
|
||||
svg.attr('height', 100)
|
||||
svg.attr('width', 400)
|
||||
// svg.attr('viewBox', '0 0 300 150');
|
||||
} catch (e) {
|
||||
logger.error('Error while rendering info diagram')
|
||||
logger.error(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
setConf,
|
||||
draw
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/** mermaid
|
||||
* http://knsv.github.io/mermaid/
|
||||
* (c) 2015 Knut Sveidqvist
|
||||
* MIT license.
|
||||
*/
|
||||
%lex
|
||||
|
||||
%options case-insensitive
|
||||
|
||||
%{
|
||||
// Pre-lexer code can go here
|
||||
%}
|
||||
|
||||
%%
|
||||
|
||||
"info" return 'info' ;
|
||||
[\s\n\r]+ return 'NL' ;
|
||||
[\s]+ return 'space';
|
||||
"showInfo" return 'showInfo';
|
||||
<<EOF>> return 'EOF' ;
|
||||
. return 'TXT' ;
|
||||
|
||||
/lex
|
||||
|
||||
%start start
|
||||
|
||||
%% /* language grammar */
|
||||
|
||||
start
|
||||
// %{ : info document 'EOF' { return yy; } }
|
||||
: info document 'EOF' { return yy; }
|
||||
;
|
||||
|
||||
document
|
||||
: /* empty */
|
||||
| document line
|
||||
;
|
||||
|
||||
line
|
||||
: statement { }
|
||||
| 'NL'
|
||||
;
|
||||
|
||||
statement
|
||||
: showInfo { yy.setInfo(true); }
|
||||
;
|
||||
|
||||
%%
|
|
@ -0,0 +1,621 @@
|
|||
/* 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 parser = (function(){
|
||||
var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[6,9,10];
|
||||
var parser = {trace: function trace() { },
|
||||
yy: {},
|
||||
symbols_: {"error":2,"start":3,"info":4,"document":5,"EOF":6,"line":7,"statement":8,"NL":9,"showInfo":10,"$accept":0,"$end":1},
|
||||
terminals_: {2:"error",4:"info",6:"EOF",9:"NL",10:"showInfo"},
|
||||
productions_: [0,[3,3],[5,0],[5,2],[7,1],[7,1],[8,1]],
|
||||
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
|
||||
/* this == yyval */
|
||||
|
||||
var $0 = $$.length - 1;
|
||||
switch (yystate) {
|
||||
case 1:
|
||||
return yy;
|
||||
break;
|
||||
case 4:
|
||||
|
||||
break;
|
||||
case 6:
|
||||
yy.setInfo(true);
|
||||
break;
|
||||
}
|
||||
},
|
||||
table: [{3:1,4:[1,2]},{1:[3]},o($V0,[2,2],{5:3}),{6:[1,4],7:5,8:6,9:[1,7],10:[1,8]},{1:[2,1]},o($V0,[2,3]),o($V0,[2,4]),o($V0,[2,5]),o($V0,[2,6])],
|
||||
defaultActions: {4:[2,1]},
|
||||
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;
|
||||
}
|
||||
function lex() {
|
||||
var token;
|
||||
token = tstack.pop() || lexer.lex() || EOF;
|
||||
if (typeof token !== 'number') {
|
||||
if (token instanceof Array) {
|
||||
tstack = token;
|
||||
token = tstack.pop();
|
||||
}
|
||||
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) {
|
||||
// Pre-lexer code can go here
|
||||
|
||||
var YYSTATE=YY_START;
|
||||
switch($avoiding_name_collisions) {
|
||||
case 0:return 4 ;
|
||||
break;
|
||||
case 1:return 9 ;
|
||||
break;
|
||||
case 2:return 'space';
|
||||
break;
|
||||
case 3:return 10;
|
||||
break;
|
||||
case 4:return 6 ;
|
||||
break;
|
||||
case 5:return 'TXT' ;
|
||||
break;
|
||||
}
|
||||
},
|
||||
rules: [/^(?:info\b)/i,/^(?:[\s\n\r]+)/i,/^(?:[\s]+)/i,/^(?:showInfo\b)/i,/^(?:$)/i,/^(?:.)/i],
|
||||
conditions: {"INITIAL":{"rules":[0,1,2,3,4,5],"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 = parser;
|
||||
exports.Parser = parser.Parser;
|
||||
exports.parse = function () { return parser.parse.apply(parser, 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));
|
||||
}
|
||||
}
|
|
@ -17,6 +17,8 @@ const conf = {
|
|||
width: 150,
|
||||
// Height of actor boxes
|
||||
height: 65,
|
||||
actorFontSize: 14,
|
||||
actorFontFamily: '"Open-Sans", "sans-serif"',
|
||||
// Margin around loop boxes
|
||||
boxMargin: 10,
|
||||
boxTextMargin: 5,
|
||||
|
@ -218,9 +220,13 @@ const drawMessage = function (elem, startx, stopx, verticalPos, msg) {
|
|||
|
||||
let line
|
||||
if (startx === stopx) {
|
||||
line = g.append('path')
|
||||
.attr('d', 'M ' + startx + ',' + verticalPos + ' C ' + (startx + 60) + ',' + (verticalPos - 10) + ' ' + (startx + 60) + ',' +
|
||||
(verticalPos + 30) + ' ' + startx + ',' + (verticalPos + 20))
|
||||
if (conf.rightAngles) {
|
||||
line = g.append('path').attr('d', `M ${startx},${verticalPos} H ${startx + (conf.width / 2)} V ${verticalPos + 25} H ${startx}`)
|
||||
} else {
|
||||
line = g.append('path')
|
||||
.attr('d', 'M ' + startx + ',' + verticalPos + ' C ' + (startx + 60) + ',' + (verticalPos - 10) + ' ' + (startx + 60) + ',' +
|
||||
(verticalPos + 30) + ' ' + startx + ',' + (verticalPos + 20))
|
||||
}
|
||||
|
||||
bounds.bumpVerticalPos(30)
|
||||
const dx = Math.max(textWidth / 2, 100)
|
||||
|
@ -338,7 +344,7 @@ export const draw = function (text, id) {
|
|||
activationData.starty = verticalPos - 6
|
||||
verticalPos += 12
|
||||
}
|
||||
svgDraw.drawActivation(diagram, activationData, verticalPos, conf)
|
||||
svgDraw.drawActivation(diagram, activationData, verticalPos, conf, actorActivations(msg.from.actor).length)
|
||||
|
||||
bounds.insert(activationData.startx, verticalPos - 10, activationData.stopx, verticalPos)
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ export const drawActor = function (elem, left, verticalPos, description, conf) {
|
|||
drawRect(g, rect)
|
||||
|
||||
_drawTextCandidateFunc(conf)(description, g,
|
||||
rect.x, rect.y, rect.width, rect.height, { 'class': 'actor' })
|
||||
rect.x, rect.y, rect.width, rect.height, { 'class': 'actor' }, conf)
|
||||
}
|
||||
|
||||
export const anchorElement = function (elem) {
|
||||
|
@ -101,12 +101,12 @@ export const anchorElement = function (elem) {
|
|||
* @param bounds - activation box bounds
|
||||
* @param verticalPos - precise y cooridnate of bottom activation box edge
|
||||
*/
|
||||
export const drawActivation = function (elem, bounds, verticalPos) {
|
||||
export const drawActivation = function (elem, bounds, verticalPos, conf, actorActivations) {
|
||||
const rect = getNoteRect()
|
||||
const g = bounds.anchored
|
||||
rect.x = bounds.startx
|
||||
rect.y = bounds.starty
|
||||
rect.fill = '#f4f4f4'
|
||||
rect.class = 'activation' + (actorActivations % 3) // Will evaluate to 0, 1 or 2
|
||||
rect.width = bounds.stopx - bounds.startx
|
||||
rect.height = verticalPos - bounds.starty
|
||||
drawRect(g, rect)
|
||||
|
@ -216,7 +216,7 @@ export const getTextObj = function () {
|
|||
const txt = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
'fill': 'black',
|
||||
'fill': undefined,
|
||||
'text-anchor': 'start',
|
||||
style: '#666',
|
||||
width: 100,
|
||||
|
@ -252,22 +252,30 @@ const _drawTextCandidateFunc = (function () {
|
|||
_setTextAttrs(text, textAttrs)
|
||||
}
|
||||
|
||||
function byTspan (content, g, x, y, width, height, textAttrs) {
|
||||
const text = g.append('text')
|
||||
.attr('x', x + width / 2).attr('y', y)
|
||||
.style('text-anchor', 'middle')
|
||||
text.append('tspan')
|
||||
.attr('x', x + width / 2).attr('dy', '0')
|
||||
.text(content)
|
||||
function byTspan (content, g, x, y, width, height, textAttrs, conf) {
|
||||
const { actorFontSize, actorFontFamily } = conf
|
||||
|
||||
text.attr('y', y + height / 2.0)
|
||||
.attr('dominant-baseline', 'central')
|
||||
.attr('alignment-baseline', 'central')
|
||||
const lines = content.split(/<br\/?>/ig)
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const dy = (i * actorFontSize) - (actorFontSize * (lines.length - 1) / 2)
|
||||
const text = g.append('text')
|
||||
.attr('x', x + width / 2).attr('y', y)
|
||||
.style('text-anchor', 'middle')
|
||||
.style('font-size', actorFontSize)
|
||||
.style('font-family', actorFontFamily)
|
||||
text.append('tspan')
|
||||
.attr('x', x + width / 2).attr('dy', dy)
|
||||
.text(lines[i])
|
||||
|
||||
_setTextAttrs(text, textAttrs)
|
||||
text.attr('y', y + height / 2.0)
|
||||
.attr('dominant-baseline', 'central')
|
||||
.attr('alignment-baseline', 'central')
|
||||
|
||||
_setTextAttrs(text, textAttrs)
|
||||
}
|
||||
}
|
||||
|
||||
function byFo (content, g, x, y, width, height, textAttrs) {
|
||||
function byFo (content, g, x, y, width, height, textAttrs, conf) {
|
||||
const s = g.append('switch')
|
||||
const f = s.append('foreignObject')
|
||||
.attr('x', x).attr('y', y)
|
||||
|
@ -280,7 +288,7 @@ const _drawTextCandidateFunc = (function () {
|
|||
.style('text-align', 'center').style('vertical-align', 'middle')
|
||||
.text(content)
|
||||
|
||||
byTspan(content, s, x, y, width, height, textAttrs)
|
||||
byTspan(content, s, x, y, width, height, textAttrs, conf)
|
||||
_setTextAttrs(text, textAttrs)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import moment from 'moment'
|
||||
import moment from 'moment-mini'
|
||||
|
||||
export const LEVELS = {
|
||||
debug: 1,
|
||||
|
|
|
@ -104,7 +104,7 @@ const init = function () {
|
|||
}
|
||||
|
||||
const initialize = function (config) {
|
||||
logger.debug('Initializing mermaid')
|
||||
logger.debug('Initializing mermaid ')
|
||||
if (typeof config.mermaid !== 'undefined') {
|
||||
if (typeof config.mermaid.startOnLoad !== 'undefined') {
|
||||
mermaid.startOnLoad = config.mermaid.startOnLoad
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
*/
|
||||
import * as d3 from 'd3'
|
||||
import scope from 'scope-css'
|
||||
import pkg from '../package.json'
|
||||
|
||||
import { logger, setLogLevel } from './logger'
|
||||
import utils from './utils'
|
||||
|
@ -31,6 +32,9 @@ import classDb from './diagrams/class/classDb'
|
|||
import gitGraphRenderer from './diagrams/git/gitGraphRenderer'
|
||||
import gitGraphParser from './diagrams/git/parser/gitGraph'
|
||||
import gitGraphAst from './diagrams/git/gitGraphAst'
|
||||
import infoRenderer from './diagrams/info/infoRenderer'
|
||||
import infoParser from './diagrams/info/parser/info'
|
||||
import infoDb from './diagrams/info/infoDb'
|
||||
|
||||
const themes = {}
|
||||
for (const themeName of ['default', 'forest', 'dark', 'neutral']) {
|
||||
|
@ -49,6 +53,17 @@ for (const themeName of ['default', 'forest', 'dark', 'neutral']) {
|
|||
* ```
|
||||
*/
|
||||
const config = {
|
||||
|
||||
/** theme , the CSS style sheet
|
||||
*
|
||||
* **theme** - Choose one of the built-in themes: default, forest, dark or neutral. To disable any pre-defined mermaid theme, use "null".
|
||||
* **themeCSS** - Use your own CSS. This overrides **theme**.
|
||||
*```
|
||||
* "theme": "forest",
|
||||
* "themeCSS": ".node rect { fill: red; }"
|
||||
*```
|
||||
*/
|
||||
|
||||
theme: 'default',
|
||||
themeCSS: undefined,
|
||||
|
||||
|
@ -153,7 +168,12 @@ const config = {
|
|||
* **useMaxWidth** - when this flag is set the height and width is set to 100% and is then scaling with the
|
||||
* available space if not the absolute space required is used
|
||||
*/
|
||||
useMaxWidth: true
|
||||
useMaxWidth: true,
|
||||
|
||||
/**
|
||||
* **rightAngles** - this will display arrows that start and begin at the same node as right angles, rather than a curve
|
||||
*/
|
||||
rightAngles: false
|
||||
},
|
||||
|
||||
/** ### gantt
|
||||
|
@ -220,6 +240,7 @@ function parse (text) {
|
|||
const graphType = utils.detectType(text)
|
||||
let parser
|
||||
|
||||
logger.debug('Type ' + graphType)
|
||||
switch (graphType) {
|
||||
case 'git':
|
||||
parser = gitGraphParser
|
||||
|
@ -241,6 +262,11 @@ function parse (text) {
|
|||
parser = classParser
|
||||
parser.parser.yy = classDb
|
||||
break
|
||||
case 'info':
|
||||
logger.debug('info info info')
|
||||
parser = infoParser
|
||||
parser.parser.yy = infoDb
|
||||
break
|
||||
}
|
||||
|
||||
parser.parser.yy.parseError = (str, hash) => {
|
||||
|
@ -413,6 +439,11 @@ const render = function (id, txt, cb, container) {
|
|||
classRenderer.setConf(config.class)
|
||||
classRenderer.draw(txt, id)
|
||||
break
|
||||
case 'info':
|
||||
config.class.arrowMarkerAbsolute = config.arrowMarkerAbsolute
|
||||
infoRenderer.setConf(config.class)
|
||||
infoRenderer.draw(txt, id, pkg.version)
|
||||
break
|
||||
}
|
||||
|
||||
d3.select(`[id="${id}"]`).selectAll('foreignobject > *').attr('xmlns', 'http://www.w3.org/1999/xhtml')
|
||||
|
@ -431,6 +462,7 @@ const render = function (id, txt, cb, container) {
|
|||
|
||||
if (typeof cb !== 'undefined') {
|
||||
cb(svgCode, flowDb.bindFunctions)
|
||||
cb(svgCode, ganttDb.bindFunctions)
|
||||
} else {
|
||||
logger.warn('CB = undefined!')
|
||||
}
|
||||
|
@ -465,7 +497,7 @@ const setConf = function (cnf) {
|
|||
}
|
||||
|
||||
function initialize (options) {
|
||||
logger.debug('Initializing mermaidAPI')
|
||||
logger.debug('Initializing mermaidAPI ', pkg.version)
|
||||
// Update default config with options supplied at initialization
|
||||
if (typeof options === 'object') {
|
||||
setConf(options)
|
||||
|
|
|
@ -27,9 +27,12 @@ $signalColor: $mainContrastColor;
|
|||
$signalTextColor: $mainContrastColor;
|
||||
$labelBoxBkgColor: $actorBkg;
|
||||
$labelBoxBorderColor: $actorBorder;
|
||||
$labelTextColor: $mainContrastColor;
|
||||
$labelTextColor: $darkTextColor;
|
||||
$loopTextColor: $mainContrastColor;
|
||||
$noteBorderColor: $border2;
|
||||
$noteBkgColor: #fff5ad;
|
||||
$activationBorderColor: #666;
|
||||
$activationBkgColor: #f4f4f4;
|
||||
|
||||
/* Gantt chart variables */
|
||||
|
||||
|
@ -41,6 +44,7 @@ $taskBkgColor: $mainBkg;
|
|||
$taskTextColor: $darkTextColor;
|
||||
$taskTextLightColor: $mainContrastColor;
|
||||
$taskTextOutsideColor: $taskTextLightColor;
|
||||
$taskTextClickableColor: #003163;
|
||||
$activeTaskBorderColor: rgba(255, 255, 255, 0.5);
|
||||
$activeTaskBkgColor: #81B1DB;
|
||||
$gridColor: $mainContrastColor;
|
||||
|
|
|
@ -26,8 +26,11 @@ $signalTextColor: #333;
|
|||
$labelBoxBkgColor: $actorBkg;
|
||||
$labelBoxBorderColor: $actorBorder;
|
||||
$labelTextColor: $actorTextColor;
|
||||
$loopTextColor: $actorTextColor;
|
||||
$noteBorderColor: $border2;
|
||||
$noteBkgColor: #fff5ad;
|
||||
$activationBorderColor: #666;
|
||||
$activationBkgColor: #f4f4f4;
|
||||
|
||||
/* Gantt chart variables */
|
||||
|
||||
|
@ -40,6 +43,7 @@ $taskTextLightColor: white;
|
|||
$taskTextColor: $taskTextLightColor;
|
||||
$taskTextDarkColor: black;
|
||||
$taskTextOutsideColor: $taskTextDarkColor;
|
||||
$taskTextClickableColor: #003163;
|
||||
$activeTaskBorderColor: #534fbc;
|
||||
$activeTaskBkgColor: #bfc7ff;
|
||||
$gridColor: lightgrey;
|
||||
|
|
|
@ -30,9 +30,9 @@
|
|||
}
|
||||
|
||||
.cluster rect {
|
||||
fill: $secondBkg !important;
|
||||
stroke: $clusterBorder !important;
|
||||
stroke-width: 1px !important;
|
||||
fill: $secondBkg;
|
||||
stroke: $clusterBorder;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.cluster text {
|
||||
|
|
|
@ -27,8 +27,11 @@ $signalTextColor: #333;
|
|||
$labelBoxBkgColor: $actorBkg;
|
||||
$labelBoxBorderColor: #326932;
|
||||
$labelTextColor: $actorTextColor;
|
||||
$loopTextColor: $actorTextColor;
|
||||
$noteBorderColor: $border2;
|
||||
$noteBkgColor: #fff5ad;
|
||||
$activationBorderColor: #666;
|
||||
$activationBkgColor: #f4f4f4;
|
||||
|
||||
/* Gantt chart variables */
|
||||
|
||||
|
@ -41,6 +44,7 @@ $taskTextLightColor: white;
|
|||
$taskTextColor: $taskTextLightColor;
|
||||
$taskTextDarkColor: black;
|
||||
$taskTextOutsideColor: $taskTextDarkColor;
|
||||
$taskTextClickableColor: #003163;
|
||||
$activeTaskBorderColor: $taskBorderColor;
|
||||
$activeTaskBkgColor: $mainBkg;
|
||||
$gridColor: lightgrey;
|
||||
|
|
|
@ -66,7 +66,6 @@
|
|||
|
||||
/* Task styling */
|
||||
|
||||
|
||||
/* Default task */
|
||||
|
||||
.task {
|
||||
|
@ -90,6 +89,27 @@
|
|||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* Special case clickable */
|
||||
.task.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
.taskText.clickable {
|
||||
cursor: pointer;
|
||||
fill: $taskTextClickableColor !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.taskTextOutsideLeft.clickable {
|
||||
cursor: pointer;
|
||||
fill: $taskTextClickableColor !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.taskTextOutsideRight.clickable {
|
||||
cursor: pointer;
|
||||
fill: $taskTextClickableColor !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Specific task settings for the sections*/
|
||||
|
||||
|
@ -109,7 +129,7 @@
|
|||
}
|
||||
|
||||
.taskTextOutside0,
|
||||
.taskTextOutside2,
|
||||
.taskTextOutside2
|
||||
{
|
||||
fill: $taskTextOutsideColor;
|
||||
}
|
||||
|
@ -188,6 +208,13 @@
|
|||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.milestone {
|
||||
transform: rotate(45deg) scale(0.8,0.8);
|
||||
}
|
||||
|
||||
.milestoneText {
|
||||
font-style: italic;
|
||||
}
|
||||
.doneCritText0,
|
||||
.doneCritText1,
|
||||
.doneCritText2,
|
||||
|
|
|
@ -30,9 +30,12 @@ $signalColor: $text;
|
|||
$signalTextColor: $text;
|
||||
$labelBoxBkgColor: $actorBkg;
|
||||
$labelBoxBorderColor: $actorBorder;
|
||||
$labelTextColor: white;
|
||||
$labelTextColor: $text;
|
||||
$loopTextColor: $text;
|
||||
$noteBorderColor: darken($note, 60%);
|
||||
$noteBkgColor: $note;
|
||||
$activationBorderColor: #666;
|
||||
$activationBkgColor: #f4f4f4;
|
||||
|
||||
/* Gantt chart variables */
|
||||
|
||||
|
@ -45,6 +48,7 @@ $taskTextLightColor: white;
|
|||
$taskTextColor: $taskTextLightColor;
|
||||
$taskTextDarkColor: $text;
|
||||
$taskTextOutsideColor: $taskTextDarkColor;
|
||||
$taskTextClickableColor: #003163;
|
||||
$activeTaskBorderColor: $taskBorderColor;
|
||||
$activeTaskBkgColor: $mainBkg;
|
||||
$gridColor: lighten($border1, 30%);
|
||||
|
|
|
@ -15,7 +15,6 @@ text.actor {
|
|||
.messageLine0 {
|
||||
stroke-width: 1.5;
|
||||
stroke-dasharray: '2 2';
|
||||
marker-end: 'url(#arrowhead)';
|
||||
stroke: $signalColor;
|
||||
}
|
||||
|
||||
|
@ -50,14 +49,13 @@ text.actor {
|
|||
}
|
||||
|
||||
.loopText {
|
||||
fill: $labelTextColor;
|
||||
fill: $loopTextColor;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.loopLine {
|
||||
stroke-width: 2;
|
||||
stroke-dasharray: '2 2';
|
||||
marker-end: 'url(#arrowhead)';
|
||||
stroke: $labelBoxBorderColor;
|
||||
}
|
||||
|
||||
|
@ -73,3 +71,18 @@ text.actor {
|
|||
font-family: 'trebuchet ms', verdana, arial;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.activation0 {
|
||||
fill: $activationBkgColor;
|
||||
stroke: $activationBorderColor;
|
||||
}
|
||||
|
||||
.activation1 {
|
||||
fill: $activationBkgColor;
|
||||
stroke: $activationBorderColor;
|
||||
}
|
||||
|
||||
.activation2 {
|
||||
fill: $activationBkgColor;
|
||||
stroke: $activationBorderColor;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import * as d3 from 'd3'
|
||||
import { logger } from './logger'
|
||||
|
||||
/**
|
||||
* @function detectType
|
||||
|
@ -19,6 +20,7 @@ import * as d3 from 'd3'
|
|||
*/
|
||||
export const detectType = function (text) {
|
||||
text = text.replace(/^\s*%%.*\n/g, '\n')
|
||||
logger.debug('Detecting diagram type based on the text ' + text)
|
||||
if (text.match(/^\s*sequenceDiagram/)) {
|
||||
return 'sequence'
|
||||
}
|
||||
|
@ -34,6 +36,11 @@ export const detectType = function (text) {
|
|||
if (text.match(/^\s*gitGraph/)) {
|
||||
return 'git'
|
||||
}
|
||||
|
||||
if (text.match(/^\s*info/)) {
|
||||
return 'info'
|
||||
}
|
||||
|
||||
return 'flowchart'
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = require('babel-jest').createTransformer({
|
||||
rootMode: 'upward'
|
||||
})
|
|
@ -8,7 +8,10 @@ const amdRule = {
|
|||
|
||||
const jsRule = {
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
include: [
|
||||
path.resolve(__dirname, './src'),
|
||||
path.resolve(__dirname, './node_modules/dagre-d3-renderer/lib')
|
||||
],
|
||||
use: {
|
||||
loader: 'babel-loader'
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
const path = require('path')
|
||||
|
||||
const jsRule = {
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'babel-loader'
|
||||
}
|
||||
}
|
||||
|
||||
const amdRule = {
|
||||
parser: {
|
||||
amd: false // https://github.com/lodash/lodash/issues/3052
|
||||
}
|
||||
}
|
||||
const scssRule = {
|
||||
// load scss to string
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
{ loader: 'css-to-string-loader' },
|
||||
{ loader: 'css-loader' },
|
||||
{ loader: 'sass-loader' }
|
||||
]
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
target: 'web',
|
||||
entry: {
|
||||
mermaid: './src/mermaid.js',
|
||||
e2e: './e2e/platform/viewer.js',
|
||||
'bundle-test': './e2e/platform/bundle-test.js'
|
||||
},
|
||||
node: {
|
||||
fs: 'empty' // jison generated code requires 'fs'
|
||||
},
|
||||
output: {
|
||||
path: path.join(__dirname, './dist/'),
|
||||
filename: '[name].js',
|
||||
library: 'mermaid',
|
||||
libraryTarget: 'umd',
|
||||
libraryExport: 'default'
|
||||
},
|
||||
devServer: {
|
||||
contentBase: [
|
||||
path.join(__dirname, 'e2e', 'platform'),
|
||||
path.join(__dirname, 'dist')
|
||||
],
|
||||
compress: true,
|
||||
port: 9000
|
||||
},
|
||||
module: {
|
||||
rules: [amdRule, jsRule, scssRule]
|
||||
},
|
||||
externals: {
|
||||
mermaid: 'mermaid'
|
||||
},
|
||||
devtool: 'source-map'
|
||||
}
|