mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-10-25 08:54:07 +02:00
Compare commits
759 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6b5c704e8 | ||
|
|
6fe62060c6 | ||
|
|
51fe2e8a9b | ||
|
|
a18179e6db | ||
|
|
89e1da4780 | ||
|
|
cb9a7f4cd1 | ||
|
|
dcad2e9a17 | ||
|
|
75eb6b1b6a | ||
|
|
47be3df96a | ||
|
|
b077918832 | ||
|
|
73a66b8206 | ||
|
|
89179ff5d1 | ||
|
|
12a4882efe | ||
|
|
4017628630 | ||
|
|
d596b58895 | ||
|
|
398f4bf56d | ||
|
|
e2b4fd4b20 | ||
|
|
e0c732ad47 | ||
|
|
3a0e665c13 | ||
|
|
1076d67137 | ||
|
|
b023892c57 | ||
|
|
5f5c8a2df0 | ||
|
|
0c4bab2966 | ||
|
|
054950ef16 | ||
|
|
e08496d84c | ||
|
|
ef07012359 | ||
|
|
952fe4923c | ||
|
|
fb94892ca6 | ||
|
|
b81b3bb118 | ||
|
|
5496f790bf | ||
|
|
a6ab0a5742 | ||
|
|
ef053a813d | ||
|
|
550e7beb61 | ||
|
|
e8930e7ded | ||
|
|
22ad977349 | ||
|
|
c223c2be5c | ||
|
|
e3585a7d66 | ||
|
|
905e0c20d9 | ||
|
|
981c3e0ce4 | ||
|
|
de2db39161 | ||
|
|
ef25bfee14 | ||
|
|
4cfd18e25f | ||
|
|
e0f3656629 | ||
|
|
2ddad07fc7 | ||
|
|
1dcb6cd390 | ||
|
|
1a3d37d8a5 | ||
|
|
5cd8ac1fac | ||
|
|
7889e35340 | ||
|
|
dcffde3e98 | ||
|
|
d38b9be891 | ||
|
|
42846cce50 | ||
|
|
2134dbb2fd | ||
|
|
ccec3810df | ||
|
|
12af9b0b66 | ||
|
|
97b9edf7c3 | ||
|
|
c63d556cf8 | ||
|
|
b4e8cffd0a | ||
|
|
4ba3ff6769 | ||
|
|
c56ac30d39 | ||
|
|
7072979cfc | ||
|
|
727f6c0e28 | ||
|
|
1c7c109960 | ||
|
|
391149dbd8 | ||
|
|
7bb988c48b | ||
|
|
1927ed47ff | ||
|
|
73027d5cec | ||
|
|
c52a8fc442 | ||
|
|
acab3a5ee1 | ||
|
|
0bd8aabd7c | ||
|
|
99de658093 | ||
|
|
a4d06742c3 | ||
|
|
3ebae8f331 | ||
|
|
7fed3b408e | ||
|
|
95712ea357 | ||
|
|
d5178f61f1 | ||
|
|
941a7f9ff5 | ||
|
|
0ccc216a2c | ||
|
|
f0dc7f89e4 | ||
|
|
332ceb0da6 | ||
|
|
189758cca4 | ||
|
|
2fa4df2830 | ||
|
|
a8ff7a65f4 | ||
|
|
5d68359eb6 | ||
|
|
4032824c79 | ||
|
|
0ebf162673 | ||
|
|
166acad749 | ||
|
|
15bfc12cbe | ||
|
|
3ac23c21d9 | ||
|
|
8b44401b1b | ||
|
|
5624453c87 | ||
|
|
87c7f73245 | ||
|
|
110e4ee586 | ||
|
|
6a7a5b9249 | ||
|
|
8832aa61fe | ||
|
|
e16e501b93 | ||
|
|
8536c975bf | ||
|
|
3d500800f1 | ||
|
|
b9b991c5fe | ||
|
|
701e638907 | ||
|
|
52d03f3ce3 | ||
|
|
ebf00eecaa | ||
|
|
262a917952 | ||
|
|
a730225700 | ||
|
|
538d2c2b83 | ||
|
|
736d57f3f2 | ||
|
|
053388a2d3 | ||
|
|
e17ec3e299 | ||
|
|
869a64e457 | ||
|
|
9d56595d48 | ||
|
|
ea11f8bff1 | ||
|
|
fd5b3083dd | ||
|
|
58a787b660 | ||
|
|
dffc53b7b1 | ||
|
|
173899fc76 | ||
|
|
7166ade310 | ||
|
|
7b935823da | ||
|
|
e945bf884e | ||
|
|
d87da684a9 | ||
|
|
15bdaca2f9 | ||
|
|
df8085bee0 | ||
|
|
f04d415b0a | ||
|
|
ab398c64a8 | ||
|
|
a2ce9e6a54 | ||
|
|
dc4edf775d | ||
|
|
e91229f69f | ||
|
|
f136a7fd8d | ||
|
|
413162dfad | ||
|
|
7d1c1e3845 | ||
|
|
620f3e8734 | ||
|
|
d46fda537d | ||
|
|
fc96360e17 | ||
|
|
bc94fc3d6d | ||
|
|
c1e5fe3281 | ||
|
|
69ceab9f44 | ||
|
|
546e766fd9 | ||
|
|
82220638a0 | ||
|
|
432f3446b0 | ||
|
|
19212eb4aa | ||
|
|
176ffa8e7c | ||
|
|
3caa755b28 | ||
|
|
e48ff4c7e9 | ||
|
|
e1e7be07f7 | ||
|
|
23dd96be97 | ||
|
|
2525929e8d | ||
|
|
2deacb8e97 | ||
|
|
3b2b0da3a1 | ||
|
|
ca5a96f53a | ||
|
|
c2227e1a48 | ||
|
|
dff8027be6 | ||
|
|
8f7f1ee0b5 | ||
|
|
c7c077c21c | ||
|
|
d7b1eb74e5 | ||
|
|
8772a5bba0 | ||
|
|
c6ccfa8b6a | ||
|
|
63057c2f60 | ||
|
|
3b9baeb866 | ||
|
|
38c5e65c35 | ||
|
|
fa7570efd8 | ||
|
|
b50c15e278 | ||
|
|
96be32225e | ||
|
|
dab3bc6ce4 | ||
|
|
b0e81bf188 | ||
|
|
acbbfbe419 | ||
|
|
c1478418ae | ||
|
|
37f9655714 | ||
|
|
4f12596e29 | ||
|
|
c714a7a28c | ||
|
|
b935108ed8 | ||
|
|
fb70b9742a | ||
|
|
8d91a42fe7 | ||
|
|
0be8d62f7d | ||
|
|
cac960c6b8 | ||
|
|
8596eb6091 | ||
|
|
7ff87fdac9 | ||
|
|
4c04001637 | ||
|
|
8c6cfebc1c | ||
|
|
ff0ab048df | ||
|
|
9b66b114cc | ||
|
|
957244583c | ||
|
|
a124be71b1 | ||
|
|
345d460643 | ||
|
|
7f0d4fff1b | ||
|
|
1903154d01 | ||
|
|
9558ebcb0b | ||
|
|
d63934637f | ||
|
|
256c529de8 | ||
|
|
a56fe7a8fe | ||
|
|
be4bbed687 | ||
|
|
2d2f0b9281 | ||
|
|
dfd9622ce7 | ||
|
|
71125e7b86 | ||
|
|
4a5bbe1791 | ||
|
|
0ecbbf8ddf | ||
|
|
b73698849d | ||
|
|
e13c939583 | ||
|
|
96a25935ac | ||
|
|
7d5c3144dc | ||
|
|
2cf9a07bb2 | ||
|
|
72718c14d1 | ||
|
|
fbe64b21bc | ||
|
|
d65741177a | ||
|
|
046b4fdae4 | ||
|
|
a179212c60 | ||
|
|
5d99e9c0b8 | ||
|
|
b426e600ce | ||
|
|
66d7c00e84 | ||
|
|
293c23ec52 | ||
|
|
9e701b0fb3 | ||
|
|
95a0cb53d3 | ||
|
|
3c03c84782 | ||
|
|
a3fa318edc | ||
|
|
0187407cff | ||
|
|
1424ff7d22 | ||
|
|
db62447c98 | ||
|
|
b96ae68aee | ||
|
|
7d3f0da75c | ||
|
|
4984f9e041 | ||
|
|
1e27238083 | ||
|
|
1e41cea107 | ||
|
|
da99d3b15e | ||
|
|
a0dafb4093 | ||
|
|
f5485c1265 | ||
|
|
24da004bd6 | ||
|
|
e8f28b71f7 | ||
|
|
fa91653beb | ||
|
|
aa93860466 | ||
|
|
ea26bd0ff2 | ||
|
|
214fc1d8c4 | ||
|
|
09558e6e9a | ||
|
|
cc069d3987 | ||
|
|
db3dc0ffab | ||
|
|
6a6d6b6f85 | ||
|
|
605d74dd0d | ||
|
|
0c62c08abd | ||
|
|
aa6e15e3ac | ||
|
|
537cba4bda | ||
|
|
7e9419f299 | ||
|
|
d59ac53f64 | ||
|
|
92c7fe9bf6 | ||
|
|
3f2cd65159 | ||
|
|
da717b5a91 | ||
|
|
e277f286c0 | ||
|
|
d45e21a786 | ||
|
|
22fc99d521 | ||
|
|
1026ad7011 | ||
|
|
f9491ef193 | ||
|
|
5302ca38da | ||
|
|
2830c70140 | ||
|
|
5e2d8d1c90 | ||
|
|
65797742ef | ||
|
|
1a5a52358e | ||
|
|
5852fc2d68 | ||
|
|
b8d3b01f64 | ||
|
|
165d43b0cd | ||
|
|
d8929db208 | ||
|
|
0ab0be01c2 | ||
|
|
f2e4b922a9 | ||
|
|
a7e5db6e19 | ||
|
|
e8be61e062 | ||
|
|
0cd04af6a4 | ||
|
|
23105211f8 | ||
|
|
618bc4fbc2 | ||
|
|
cbefb716e3 | ||
|
|
d2cecca297 | ||
|
|
8f8856b767 | ||
|
|
00682160b6 | ||
|
|
6ffef61afc | ||
|
|
75c9debb5e | ||
|
|
6521cc81fe | ||
|
|
b634439765 | ||
|
|
1d8d42ad08 | ||
|
|
4e26b8e645 | ||
|
|
6a674a0a40 | ||
|
|
a5a3bc8b7c | ||
|
|
6ff536fca7 | ||
|
|
719ee2e001 | ||
|
|
6c28c55ea7 | ||
|
|
6032e05581 | ||
|
|
0538048f24 | ||
|
|
89307d3f13 | ||
|
|
5dcd3e6f35 | ||
|
|
59ef2b0d4f | ||
|
|
a92e116a21 | ||
|
|
d8805ae4d7 | ||
|
|
942bd02add | ||
|
|
0d6d848325 | ||
|
|
c6494f92fa | ||
|
|
9cb9407e1c | ||
|
|
43ab44d8ba | ||
|
|
c2bc3317c3 | ||
|
|
413c6e12f5 | ||
|
|
65140f6b3f | ||
|
|
2fee6e62aa | ||
|
|
4b91dbfed6 | ||
|
|
e17b35a96a | ||
|
|
44dc30c02c | ||
|
|
038062b3c3 | ||
|
|
5cb44d55ff | ||
|
|
8befe916fb | ||
|
|
d3caa9b97d | ||
|
|
2d99ccfcda | ||
|
|
92be61dc48 | ||
|
|
2fecb4d945 | ||
|
|
462c3d3a6c | ||
|
|
422d78cbcd | ||
|
|
7b0e2513bd | ||
|
|
f8f0454f33 | ||
|
|
d937a3c552 | ||
|
|
dbd2855bfc | ||
|
|
8084d80d64 | ||
|
|
d585121bdb | ||
|
|
9e496175d9 | ||
|
|
e6fcc2b3e3 | ||
|
|
f748969186 | ||
|
|
4750769c63 | ||
|
|
8c5c2b0fe1 | ||
|
|
1b04dbf7f9 | ||
|
|
97e208f081 | ||
|
|
2de974f71d | ||
|
|
bda1aaa5db | ||
|
|
b69010d83b | ||
|
|
2e3bc443ff | ||
|
|
d2e3517c78 | ||
|
|
e930c5a30c | ||
|
|
fb500ace8a | ||
|
|
4f9a2a8083 | ||
|
|
2acaef67b1 | ||
|
|
1ad9e75a86 | ||
|
|
e9c6db4d8c | ||
|
|
d070029856 | ||
|
|
68b65ba5d4 | ||
|
|
c3f840126c | ||
|
|
7404de6776 | ||
|
|
d763fffd7a | ||
|
|
0435509952 | ||
|
|
bba1050e5e | ||
|
|
28f9e6f17b | ||
|
|
b3d147b97a | ||
|
|
3f85391bde | ||
|
|
513c35d0ef | ||
|
|
499a2582e6 | ||
|
|
d6363e908d | ||
|
|
325d0dd075 | ||
|
|
2c90034d39 | ||
|
|
614e9f4b87 | ||
|
|
2452532557 | ||
|
|
6c621f243d | ||
|
|
9cedbea2cf | ||
|
|
2168cce020 | ||
|
|
7858186ecd | ||
|
|
d2e9918e5f | ||
|
|
9a7b498818 | ||
|
|
3ff23b3c45 | ||
|
|
0f7dda9dfc | ||
|
|
eef6eb93f8 | ||
|
|
31f78d1aa9 | ||
|
|
193ee657e6 | ||
|
|
b5a030c78d | ||
|
|
5e447e8938 | ||
|
|
b495a7d834 | ||
|
|
9aa4a79d6e | ||
|
|
cabd0769a8 | ||
|
|
803916c99b | ||
|
|
54433ee555 | ||
|
|
6ee298dc7d | ||
|
|
9bd140ce42 | ||
|
|
f005c72669 | ||
|
|
d56af022c1 | ||
|
|
cc8c5aecaf | ||
|
|
8c8f5928f5 | ||
|
|
ab38e17936 | ||
|
|
52fcd18e6a | ||
|
|
9fb980ab2c | ||
|
|
b42939796c | ||
|
|
69ce29841d | ||
|
|
425a83c0da | ||
|
|
a9f4f8e06c | ||
|
|
989ff03e43 | ||
|
|
64b0517bac | ||
|
|
f52fa46326 | ||
|
|
51858c02eb | ||
|
|
4eb38e4cfd | ||
|
|
4a5828c732 | ||
|
|
774ebfe671 | ||
|
|
6e2534aa77 | ||
|
|
ad4e0a2ad5 | ||
|
|
24d1afd40f | ||
|
|
2278325822 | ||
|
|
49c13f19ff | ||
|
|
dbd63ff797 | ||
|
|
a59a5a2402 | ||
|
|
26ebbf82ee | ||
|
|
76130bc485 | ||
|
|
f79577c816 | ||
|
|
ecca3588bc | ||
|
|
34209db57b | ||
|
|
ec37fc897b | ||
|
|
5666d5f298 | ||
|
|
6ac0c00e30 | ||
|
|
990cab53a9 | ||
|
|
18766466c6 | ||
|
|
73e1957c62 | ||
|
|
e0a31feaec | ||
|
|
c62061090a | ||
|
|
0f689c2ea1 | ||
|
|
ef290796e0 | ||
|
|
c8fef693fa | ||
|
|
a9ac654040 | ||
|
|
17be68f5d9 | ||
|
|
5d7815c98a | ||
|
|
7fe5b5af7a | ||
|
|
068b7ce6a9 | ||
|
|
eec89e0442 | ||
|
|
7a58e8261f | ||
|
|
eec0aced7f | ||
|
|
74eb2e4493 | ||
|
|
01ddfea478 | ||
|
|
a694f61003 | ||
|
|
d3a025acd6 | ||
|
|
51d5c90c9f | ||
|
|
e4d25ed9cd | ||
|
|
1a60b6e412 | ||
|
|
dd6495abf6 | ||
|
|
35865ba70e | ||
|
|
cee267193f | ||
|
|
30149df88e | ||
|
|
841aea9d97 | ||
|
|
d68323d7bb | ||
|
|
a583daf712 | ||
|
|
cf1912523b | ||
|
|
eecd56d6e0 | ||
|
|
1cd1423780 | ||
|
|
68812b0f04 | ||
|
|
c9d29c16e2 | ||
|
|
185233c971 | ||
|
|
ca42976c62 | ||
|
|
9ff2b35ae0 | ||
|
|
ecb12ae2a8 | ||
|
|
b43e695da2 | ||
|
|
57b731842b | ||
|
|
a8ef091cd6 | ||
|
|
315923d1d3 | ||
|
|
632a564158 | ||
|
|
8365fcc2f9 | ||
|
|
2417741283 | ||
|
|
e0033965bc | ||
|
|
ae73583d53 | ||
|
|
b87764ed94 | ||
|
|
7e9e9cc98c | ||
|
|
658ed3d790 | ||
|
|
ed65e6df3b | ||
|
|
16906ee242 | ||
|
|
a611ff3abd | ||
|
|
8258fb059c | ||
|
|
122274bf52 | ||
|
|
c5d41c5a21 | ||
|
|
a2b6bc5213 | ||
|
|
cb5e88c2f1 | ||
|
|
8bc3bdd300 | ||
|
|
b3fa6378bd | ||
|
|
e406fda9cd | ||
|
|
30a755221b | ||
|
|
40efbc5b84 | ||
|
|
dc1a6ba8b5 | ||
|
|
709ebe524d | ||
|
|
fb94aaaa6f | ||
|
|
bae1e80ac0 | ||
|
|
4536930e17 | ||
|
|
189325508e | ||
|
|
c81e933dc3 | ||
|
|
85481b2310 | ||
|
|
b04f198f3e | ||
|
|
a989b5ccde | ||
|
|
4ee0f9b6be | ||
|
|
1d371e0a1e | ||
|
|
3ec7c6d18b | ||
|
|
80912ffaee | ||
|
|
4777fe0d91 | ||
|
|
feddb6d048 | ||
|
|
b148ec0850 | ||
|
|
1a36ed9786 | ||
|
|
1b7ad1fc38 | ||
|
|
71d4113ecf | ||
|
|
ed712fa673 | ||
|
|
fcb2af780d | ||
|
|
a8c8f9a472 | ||
|
|
ab1dd1e549 | ||
|
|
145983fc5d | ||
|
|
9fb45a3ab7 | ||
|
|
b97e5ff5b0 | ||
|
|
0ca04671ed | ||
|
|
6e062b241e | ||
|
|
df77276805 | ||
|
|
a85a694cc1 | ||
|
|
590fa0894b | ||
|
|
4d7f7d0756 | ||
|
|
af0d40e907 | ||
|
|
6f96b5dd14 | ||
|
|
0dc983d04a | ||
|
|
50219cf2d9 | ||
|
|
4ed345101a | ||
|
|
e5a701d04d | ||
|
|
342b83a010 | ||
|
|
eb555f7da6 | ||
|
|
8a0c857a92 | ||
|
|
31d5899011 | ||
|
|
14520e8bd5 | ||
|
|
efb4e464b2 | ||
|
|
6042ca9c56 | ||
|
|
e5c16fd960 | ||
|
|
6eb018489e | ||
|
|
5447a88c34 | ||
|
|
4fbebcfab4 | ||
|
|
35e4cd7c18 | ||
|
|
999430f9e0 | ||
|
|
be535604ab | ||
|
|
0703292fb9 | ||
|
|
c0ca932ccf | ||
|
|
08fa19bc83 | ||
|
|
55fa62d9c0 | ||
|
|
94e5177bb1 | ||
|
|
45f34d871b | ||
|
|
173a2b12aa | ||
|
|
3f0ead3e0a | ||
|
|
6864652b80 | ||
|
|
ca99a30b71 | ||
|
|
b2e489b689 | ||
|
|
cf67307711 | ||
|
|
2d1f5aa6cb | ||
|
|
56375faa65 | ||
|
|
1236c0c272 | ||
|
|
b4a96c9b21 | ||
|
|
05f3982632 | ||
|
|
9face45357 | ||
|
|
824a43f537 | ||
|
|
ae6bb57cf5 | ||
|
|
82dffe0a1d | ||
|
|
22cee7e4b0 | ||
|
|
c65f6aaed3 | ||
|
|
2204d46ce1 | ||
|
|
11a30dd505 | ||
|
|
e0d90def93 | ||
|
|
f310eb0574 | ||
|
|
2bd7dee3c7 | ||
|
|
594e69dd93 | ||
|
|
1fd94f9cf1 | ||
|
|
78d8ee01ac | ||
|
|
c966aad496 | ||
|
|
0ed5a01756 | ||
|
|
22b9ee4919 | ||
|
|
3781dea498 | ||
|
|
60392424dc | ||
|
|
23bd06c2d3 | ||
|
|
1644f06ec8 | ||
|
|
f591fedb77 | ||
|
|
b0cf9836af | ||
|
|
34ee8a0e21 | ||
|
|
d61aac362c | ||
|
|
6aff481e9b | ||
|
|
3ae1b5f1a3 | ||
|
|
cbebf126ce | ||
|
|
3fedc1263c | ||
|
|
f4b2211f8f | ||
|
|
bc5f73daa2 | ||
|
|
ec9094696b | ||
|
|
85cdfbaaf8 | ||
|
|
421409181d | ||
|
|
cc1a4be15a | ||
|
|
ead74558ea | ||
|
|
3c5a19e5a7 | ||
|
|
8598a7d712 | ||
|
|
0955c4c428 | ||
|
|
182729e015 | ||
|
|
a7dd6a6614 | ||
|
|
e5657d028d | ||
|
|
4078cf3aed | ||
|
|
a7339eaf1c | ||
|
|
2877501ff5 | ||
|
|
1b016bd412 | ||
|
|
2512666f49 | ||
|
|
074a819ca8 | ||
|
|
c211434c38 | ||
|
|
7896a7152e | ||
|
|
3a96682b9c | ||
|
|
c4639d11c8 | ||
|
|
10b47b5785 | ||
|
|
5452be9fed | ||
|
|
4562a811fc | ||
|
|
910b90b79c | ||
|
|
a38a156d3b | ||
|
|
d3d44ec806 | ||
|
|
6612b3e01e | ||
|
|
d0428d492b | ||
|
|
ee6ad01209 | ||
|
|
33973354ed | ||
|
|
2af4a1f9f8 | ||
|
|
44a2e0472a | ||
|
|
699d3bca52 | ||
|
|
50d285bc49 | ||
|
|
48500652b3 | ||
|
|
8e050d4223 | ||
|
|
73f567f3e2 | ||
|
|
1033a74970 | ||
|
|
595fe1fd32 | ||
|
|
9095b2df97 | ||
|
|
8e45e7284b | ||
|
|
fe437550fc | ||
|
|
84124c9427 | ||
|
|
3fe38237e6 | ||
|
|
9c8e36ea95 | ||
|
|
8e9890d6e1 | ||
|
|
1abb53ce00 | ||
|
|
fec3101f37 | ||
|
|
a23fce6c2d | ||
|
|
9f970ed953 | ||
|
|
9b892ef128 | ||
|
|
0b2afb8e71 | ||
|
|
cbd7393d36 | ||
|
|
a27b3d8fcb | ||
|
|
9246df4410 | ||
|
|
5ad30c86c5 | ||
|
|
43d2b0b9d7 | ||
|
|
367d620a82 | ||
|
|
1b76160a7d | ||
|
|
d9f8ef6d57 | ||
|
|
58659c4d85 | ||
|
|
911cd09dd8 | ||
|
|
27687fc742 | ||
|
|
3027882847 | ||
|
|
a7fd52237a | ||
|
|
36b389b1fd | ||
|
|
952723706b | ||
|
|
5a720b6d63 | ||
|
|
0ae130de0a | ||
|
|
273eb63efb | ||
|
|
c5c995c916 | ||
|
|
222a6e0682 | ||
|
|
7741f49ed8 | ||
|
|
786d9ddb54 | ||
|
|
03a711d82d | ||
|
|
758a9d7254 | ||
|
|
9bc23129d5 | ||
|
|
343bd52c17 | ||
|
|
f9943b6b82 | ||
|
|
c7e2c2b3c6 | ||
|
|
c10b4553ae | ||
|
|
c8d1b388d8 | ||
|
|
da37b4dee5 | ||
|
|
9f904da7b3 | ||
|
|
7bcbe6f469 | ||
|
|
439cca926d | ||
|
|
5bc20a4b08 | ||
|
|
26d0908b27 | ||
|
|
385e823c17 | ||
|
|
83d9a3269e | ||
|
|
c50178c640 | ||
|
|
96b12e876e | ||
|
|
9a8d7a8c57 | ||
|
|
d4b35b5594 | ||
|
|
0af92a1626 | ||
|
|
c3eb62255f | ||
|
|
5fc873468d | ||
|
|
307c599a0e | ||
|
|
a83639addd | ||
|
|
8920411b3c | ||
|
|
46a7eaea7d | ||
|
|
d2513e9b50 | ||
|
|
f349ddd796 | ||
|
|
c703c9a0d3 | ||
|
|
6a1550fbdf | ||
|
|
9458bfb24f | ||
|
|
7bb75c16da | ||
|
|
69c84df367 | ||
|
|
000ffbb622 | ||
|
|
53d5221aed | ||
|
|
0dc8aa32c6 | ||
|
|
8fa47dac37 | ||
|
|
cebe033b4b | ||
|
|
0a3e9ac8e8 | ||
|
|
a1e415686c | ||
|
|
21daaf3e29 | ||
|
|
5712c6de7b | ||
|
|
e32cbe3004 | ||
|
|
3279edab33 | ||
|
|
13a65db40e | ||
|
|
e550ef9e25 | ||
|
|
429b901e7a | ||
|
|
f656269a50 | ||
|
|
eb7c341046 | ||
|
|
71b6a664f7 | ||
|
|
7de9687911 | ||
|
|
7e3bd2e574 | ||
|
|
764907ce34 | ||
|
|
ae76dc957a | ||
|
|
6ef6d79b48 | ||
|
|
3fcbc7ae56 | ||
|
|
bb9e36d5fa | ||
|
|
8a129d7384 | ||
|
|
71fd8e1b70 | ||
|
|
28594138e0 | ||
|
|
7fc2a0a544 | ||
|
|
48af3b3590 | ||
|
|
8a13c4cc06 | ||
|
|
bfd93627a0 | ||
|
|
d4451ef8a1 | ||
|
|
3f3e23b419 | ||
|
|
728bd656d7 | ||
|
|
1cc3f16d7a | ||
|
|
92439fbdf3 | ||
|
|
0955305076 | ||
|
|
977df99c80 | ||
|
|
fcfbce8a97 | ||
|
|
202af046aa | ||
|
|
bd2633acf4 | ||
|
|
9aeff6be2d | ||
|
|
6988dea353 | ||
|
|
afe12b5636 | ||
|
|
7b79b0c8e0 | ||
|
|
2b9e464798 | ||
|
|
89780140c8 | ||
|
|
907bd62678 | ||
|
|
76d48db219 | ||
|
|
9e3253ee70 | ||
|
|
8949166a17 | ||
|
|
4c564ebe9e | ||
|
|
2a0a2a2269 | ||
|
|
53e3b431d5 | ||
|
|
dd26960852 | ||
|
|
fe2f3b403d | ||
|
|
dabedb379d | ||
|
|
1e53b588fc | ||
|
|
c0e2c86fd8 | ||
|
|
52a78375a4 | ||
|
|
c0e9638109 | ||
|
|
0539b8a74e | ||
|
|
7b09a6d1bf | ||
|
|
ab78295faf | ||
|
|
540c614c88 | ||
|
|
d0ed2d2735 | ||
|
|
fb58204900 | ||
|
|
240939a7ec | ||
|
|
c067b31a57 | ||
|
|
1502346172 | ||
|
|
9537f60b01 | ||
|
|
1a7997cdef | ||
|
|
3a65cd1df3 | ||
|
|
225560dcdf | ||
|
|
d6766de7be | ||
|
|
8906ee8097 | ||
|
|
fc4f6e617f | ||
|
|
8261429afb | ||
|
|
733cbc753c | ||
|
|
1d2327c4c2 | ||
|
|
5f8508ab52 | ||
|
|
cb7b8a4b98 | ||
|
|
cbd41dbb1b | ||
|
|
9d5c6d5d1d | ||
|
|
ba0ea20388 | ||
|
|
0c87128902 |
14
.codeclimate.yml
Normal file
14
.codeclimate.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
engines:
|
||||
duplication:
|
||||
enabled: true
|
||||
config:
|
||||
languages:
|
||||
- javascript
|
||||
eslint:
|
||||
enabled: true
|
||||
ratings:
|
||||
paths:
|
||||
- "**.js"
|
||||
exclude_paths:
|
||||
- node_modules/
|
||||
- dist/
|
||||
11
.editorconfig
Normal file
11
.editorconfig
Normal file
@@ -0,0 +1,11 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
indent_size = 4
|
||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.js text eol=lf
|
||||
16
.gitignore
vendored
16
.gitignore
vendored
@@ -1 +1,15 @@
|
||||
node_modules/*
|
||||
node_modules
|
||||
bower_components
|
||||
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
|
||||
.DS_Store
|
||||
.idea
|
||||
coverage
|
||||
|
||||
test/tmp_*
|
||||
test/fixtures/samples/*.actual*
|
||||
|
||||
dist/*.js
|
||||
dist/*.css
|
||||
|
||||
27
.travis.yml
27
.travis.yml
@@ -1,8 +1,19 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.10"
|
||||
addons:
|
||||
code_climate:
|
||||
repo_token: e87e6bf1c253e0555437ebd23235fdfe2749b889358e7c6d100e4ea5b4f2e091
|
||||
after_script:
|
||||
- cat coverage/lcov.info | codeclimate
|
||||
sudo: required
|
||||
dist: trusty
|
||||
addons:
|
||||
chrome: stable
|
||||
code_climate:
|
||||
repo_token: e87e6bf1c253e0555437ebd23235fdfe2749b889358e7c6d100e4ea5b4f2e091
|
||||
language: node_js
|
||||
node_js:
|
||||
- "8"
|
||||
before_install:
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start &
|
||||
- sleep 3
|
||||
before_script:
|
||||
- yarn build
|
||||
script:
|
||||
- yarn test
|
||||
after_script:
|
||||
- cat coverage/lcov.info | codeclimate
|
||||
|
||||
386
CHANGELOG.md
Normal file
386
CHANGELOG.md
Normal file
@@ -0,0 +1,386 @@
|
||||
# Change Log
|
||||
|
||||
## [Unreleased](https://github.com/knsv/mermaid/tree/HEAD)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.4.0...HEAD)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Add a css file, mermaid.css, with default styling [\#122](https://github.com/knsv/mermaid/issues/122)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Some examples not displayed on Firefox 36.0.1 [\#138](https://github.com/knsv/mermaid/issues/138)
|
||||
|
||||
- inoperable in an AMD/requirejs environment: IPython Notebook [\#127](https://github.com/knsv/mermaid/issues/127)
|
||||
|
||||
- Add capability for gantt diagrams [\#118](https://github.com/knsv/mermaid/issues/118)
|
||||
|
||||
- lower case v causes error in the parser [\#108](https://github.com/knsv/mermaid/issues/108)
|
||||
|
||||
- Label's css conflict with boostrap's .label [\#67](https://github.com/knsv/mermaid/issues/67)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- 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))
|
||||
|
||||
- Allow other forms of node selection for init\(\) [\#135](https://github.com/knsv/mermaid/pull/135) ([bollwyvl](https://github.com/bollwyvl))
|
||||
|
||||
- Use a library-level variable for assigning ids [\#134](https://github.com/knsv/mermaid/pull/134) ([bollwyvl](https://github.com/bollwyvl))
|
||||
|
||||
## [0.4.0](https://github.com/knsv/mermaid/tree/0.4.0) (2015-03-01)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.3.5...0.4.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Assymetric shapes not documented [\#82](https://github.com/knsv/mermaid/issues/82)
|
||||
|
||||
- Improve arrows [\#3](https://github.com/knsv/mermaid/issues/3)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- NoModificationAllowedError [\#23](https://github.com/knsv/mermaid/issues/23)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- subgraph background is black in rendered flowchart PNG via CLI [\#121](https://github.com/knsv/mermaid/issues/121)
|
||||
|
||||
- Integrate editor at https://github.com/naseer/mermaid-webapp [\#110](https://github.com/knsv/mermaid/issues/110)
|
||||
|
||||
- Internet Explorer Support [\#99](https://github.com/knsv/mermaid/issues/99)
|
||||
|
||||
## [0.3.5](https://github.com/knsv/mermaid/tree/0.3.5) (2015-02-15)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.3.4...0.3.5)
|
||||
|
||||
## [0.3.4](https://github.com/knsv/mermaid/tree/0.3.4) (2015-02-15)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.3.3...0.3.4)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Apply styling from css when using the CLI utility [\#85](https://github.com/knsv/mermaid/issues/85)
|
||||
|
||||
- Generated SVG works poorly outside web browsers [\#58](https://github.com/knsv/mermaid/issues/58)
|
||||
|
||||
- Generating SVG text blob for use in Node [\#2](https://github.com/knsv/mermaid/issues/2)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Subgraph syntax bug? [\#120](https://github.com/knsv/mermaid/issues/120)
|
||||
|
||||
- Live editor [\#115](https://github.com/knsv/mermaid/issues/115)
|
||||
|
||||
- Error in "Basic Syntax" wiki page [\#113](https://github.com/knsv/mermaid/issues/113)
|
||||
|
||||
- semicolons, anyone? [\#111](https://github.com/knsv/mermaid/issues/111)
|
||||
|
||||
- undefined `sequenceConfig` fails [\#109](https://github.com/knsv/mermaid/issues/109)
|
||||
|
||||
- Sequence Diagrams: Show Actors below as well [\#106](https://github.com/knsv/mermaid/issues/106)
|
||||
|
||||
- Allow overriding sequence diagram configuration \(SVG properties\) [\#103](https://github.com/knsv/mermaid/issues/103)
|
||||
|
||||
- Error when rendering A-- This is the text -- B [\#102](https://github.com/knsv/mermaid/issues/102)
|
||||
|
||||
- Clipping in documentation [\#97](https://github.com/knsv/mermaid/issues/97)
|
||||
|
||||
- isolate class styling to the svg container [\#92](https://github.com/knsv/mermaid/issues/92)
|
||||
|
||||
- Make the new graph declaration more visual [\#40](https://github.com/knsv/mermaid/issues/40)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Add live editor [\#119](https://github.com/knsv/mermaid/pull/119) ([naseer](https://github.com/naseer))
|
||||
|
||||
- Adds CSS option to the CLI [\#116](https://github.com/knsv/mermaid/pull/116) ([fardog](https://github.com/fardog))
|
||||
|
||||
- Update flowchart.md in response Issue \#113 [\#114](https://github.com/knsv/mermaid/pull/114) ([vijay40](https://github.com/vijay40))
|
||||
|
||||
- Ignore all files except the license and dist/ folder when installing with Bower. [\#112](https://github.com/knsv/mermaid/pull/112) ([jasonbellamy](https://github.com/jasonbellamy))
|
||||
|
||||
## [0.3.3](https://github.com/knsv/mermaid/tree/0.3.3) (2015-01-25)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.3.2...0.3.3)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Support for dotted links [\#26](https://github.com/knsv/mermaid/issues/26)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Missing arrows in sequence diagram [\#98](https://github.com/knsv/mermaid/issues/98)
|
||||
|
||||
- Error with \>9 linkStyles [\#95](https://github.com/knsv/mermaid/issues/95)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Require d3 directly to better support Node usage [\#107](https://github.com/knsv/mermaid/pull/107) ([markdalgleish](https://github.com/markdalgleish))
|
||||
|
||||
- update doc with -c option [\#105](https://github.com/knsv/mermaid/pull/105) ([jjmr](https://github.com/jjmr))
|
||||
|
||||
- Add new parameter to the console client to override the svg configuration in sequence diagrams [\#104](https://github.com/knsv/mermaid/pull/104) ([jjmr](https://github.com/jjmr))
|
||||
|
||||
- Text based labels, new shape [\#101](https://github.com/knsv/mermaid/pull/101) ([bjowes](https://github.com/bjowes))
|
||||
|
||||
- fix html tags in example usage [\#100](https://github.com/knsv/mermaid/pull/100) ([deiwin](https://github.com/deiwin))
|
||||
|
||||
## [0.3.2](https://github.com/knsv/mermaid/tree/0.3.2) (2015-01-11)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.3.1...0.3.2)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Make link text look like it is on the line [\#53](https://github.com/knsv/mermaid/issues/53)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- disable auto render [\#91](https://github.com/knsv/mermaid/issues/91)
|
||||
|
||||
- Tidy breaks mermaid \(linebreaks in <div\>\) [\#87](https://github.com/knsv/mermaid/issues/87)
|
||||
|
||||
- Bug: <br\> being rendered as text in node [\#73](https://github.com/knsv/mermaid/issues/73)
|
||||
|
||||
- Graph edges appear to render outside of the canvas [\#70](https://github.com/knsv/mermaid/issues/70)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Merge pull request \#1 from knsv/master [\#96](https://github.com/knsv/mermaid/pull/96) ([gkchic](https://github.com/gkchic))
|
||||
|
||||
- Removed duplicated section in flowchart docs [\#94](https://github.com/knsv/mermaid/pull/94) ([kaime](https://github.com/kaime))
|
||||
|
||||
- Grammar changes to sequence page [\#93](https://github.com/knsv/mermaid/pull/93) ([gkchic](https://github.com/gkchic))
|
||||
|
||||
- Grammar changes to development page [\#90](https://github.com/knsv/mermaid/pull/90) ([gkchic](https://github.com/gkchic))
|
||||
|
||||
- Github buttons [\#89](https://github.com/knsv/mermaid/pull/89) ([gkchic](https://github.com/gkchic))
|
||||
|
||||
- Template change [\#88](https://github.com/knsv/mermaid/pull/88) ([gkchic](https://github.com/gkchic))
|
||||
|
||||
- New content template [\#86](https://github.com/knsv/mermaid/pull/86) ([gkchic](https://github.com/gkchic))
|
||||
|
||||
## [0.3.1](https://github.com/knsv/mermaid/tree/0.3.1) (2015-01-05)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.3.0...0.3.1)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Support for sequence diagrams [\#16](https://github.com/knsv/mermaid/issues/16)
|
||||
|
||||
- Client utility for mermaid [\#6](https://github.com/knsv/mermaid/issues/6)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Non ASCII chars in labels [\#84](https://github.com/knsv/mermaid/issues/84)
|
||||
|
||||
- 'undefined' titles of Quicklinks on the usage page [\#80](https://github.com/knsv/mermaid/issues/80)
|
||||
|
||||
- \[cli\] Enhancement proposal: not fail --version / --help if phantomjs isn't installed [\#71](https://github.com/knsv/mermaid/issues/71)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Formatting of the CONTRIBUTING file [\#83](https://github.com/knsv/mermaid/pull/83) ([Grahack](https://github.com/Grahack))
|
||||
|
||||
- Flowchart doc: Text in the circle now in a circle [\#81](https://github.com/knsv/mermaid/pull/81) ([Grahack](https://github.com/Grahack))
|
||||
|
||||
- Fix for issue \#73 [\#79](https://github.com/knsv/mermaid/pull/79) ([it0a](https://github.com/it0a))
|
||||
|
||||
- Ink template [\#78](https://github.com/knsv/mermaid/pull/78) ([gkchic](https://github.com/gkchic))
|
||||
|
||||
- Index template file [\#77](https://github.com/knsv/mermaid/pull/77) ([gkchic](https://github.com/gkchic))
|
||||
|
||||
- Index template file [\#76](https://github.com/knsv/mermaid/pull/76) ([gkchic](https://github.com/gkchic))
|
||||
|
||||
- Show help and version even if phantom isn't present. Fixes \#71 [\#75](https://github.com/knsv/mermaid/pull/75) ([fardog](https://github.com/fardog))
|
||||
|
||||
- Add apostrophe & 'and' [\#72](https://github.com/knsv/mermaid/pull/72) ([sudodoki](https://github.com/sudodoki))
|
||||
|
||||
## [0.3.0](https://github.com/knsv/mermaid/tree/0.3.0) (2014-12-22)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.16...0.3.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- How do I do comments? [\#47](https://github.com/knsv/mermaid/issues/47)
|
||||
|
||||
- Improve readability with new line as terminator and whitespace [\#38](https://github.com/knsv/mermaid/issues/38)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- This characters failed the lexical parsing [\#46](https://github.com/knsv/mermaid/issues/46)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Trailing whitespace at the end of lines is not ignored [\#55](https://github.com/knsv/mermaid/issues/55)
|
||||
|
||||
- Use classes instead of inline style for easy styling [\#24](https://github.com/knsv/mermaid/issues/24)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Adds Command Line Interface for generating PNGs from mermaid description files [\#69](https://github.com/knsv/mermaid/pull/69) ([fardog](https://github.com/fardog))
|
||||
|
||||
- Allow special symbols for direction along with acronyms [\#66](https://github.com/knsv/mermaid/pull/66) ([vijay40](https://github.com/vijay40))
|
||||
|
||||
## [0.2.16](https://github.com/knsv/mermaid/tree/0.2.16) (2014-12-15)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.15...0.2.16)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Lines routed outside visible area [\#19](https://github.com/knsv/mermaid/issues/19)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Mermaid not rendering properly on Wordpress pages [\#59](https://github.com/knsv/mermaid/issues/59)
|
||||
|
||||
- Improve example page with live demo [\#52](https://github.com/knsv/mermaid/issues/52)
|
||||
|
||||
- Does not render upon AngularJS Updates [\#45](https://github.com/knsv/mermaid/issues/45)
|
||||
|
||||
- Download link in README.MD doesn't work. [\#42](https://github.com/knsv/mermaid/issues/42)
|
||||
|
||||
- linkStyle usage is not obvious [\#41](https://github.com/knsv/mermaid/issues/41)
|
||||
|
||||
- Move \*.spec.js in src/ to test/ [\#35](https://github.com/knsv/mermaid/issues/35)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- New grammar will allow statements ending without semicolon as disccused in Issue \#38 [\#63](https://github.com/knsv/mermaid/pull/63) ([vijay40](https://github.com/vijay40))
|
||||
|
||||
- Class based styling [\#62](https://github.com/knsv/mermaid/pull/62) ([bjowes](https://github.com/bjowes))
|
||||
|
||||
- Update from master [\#61](https://github.com/knsv/mermaid/pull/61) ([bjowes](https://github.com/bjowes))
|
||||
|
||||
- Fix typos [\#60](https://github.com/knsv/mermaid/pull/60) ([sublimino](https://github.com/sublimino))
|
||||
|
||||
- Included .DS\_Store in gitignore [\#57](https://github.com/knsv/mermaid/pull/57) ([alvynmcq](https://github.com/alvynmcq))
|
||||
|
||||
- Improves readablity discussed in issue \#38 [\#56](https://github.com/knsv/mermaid/pull/56) ([vijay40](https://github.com/vijay40))
|
||||
|
||||
- Added a linting task for gulp [\#43](https://github.com/knsv/mermaid/pull/43) ([serv](https://github.com/serv))
|
||||
|
||||
## [0.2.15](https://github.com/knsv/mermaid/tree/0.2.15) (2014-12-05)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.14...0.2.15)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Error with some characters [\#25](https://github.com/knsv/mermaid/issues/25)
|
||||
|
||||
- Cap-cased words break parser [\#8](https://github.com/knsv/mermaid/issues/8)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Question marks don't render properly with /dist/mermaid.full.min.js [\#30](https://github.com/knsv/mermaid/issues/30)
|
||||
|
||||
- Provide parse function in browser widthout `require`? [\#21](https://github.com/knsv/mermaid/issues/21)
|
||||
|
||||
- Better label text support [\#18](https://github.com/knsv/mermaid/issues/18)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Include bower\_components/ to .gitignore [\#33](https://github.com/knsv/mermaid/pull/33) ([serv](https://github.com/serv))
|
||||
|
||||
- Fixed reference to Git repo. [\#32](https://github.com/knsv/mermaid/pull/32) ([guyellis](https://github.com/guyellis))
|
||||
|
||||
## [0.2.14](https://github.com/knsv/mermaid/tree/0.2.14) (2014-12-03)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.13...0.2.14)
|
||||
|
||||
## [0.2.13](https://github.com/knsv/mermaid/tree/0.2.13) (2014-12-03)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.10...0.2.13)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Publish to NPM [\#7](https://github.com/knsv/mermaid/issues/7)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- modified init to be applied more than once [\#29](https://github.com/knsv/mermaid/issues/29)
|
||||
|
||||
- Wanted to know build process for the project. [\#28](https://github.com/knsv/mermaid/issues/28)
|
||||
|
||||
- can not support Chinese description [\#20](https://github.com/knsv/mermaid/issues/20)
|
||||
|
||||
- Support unicode chars in labels [\#9](https://github.com/knsv/mermaid/issues/9)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- initial setup for editor page to generate graph through textarea input [\#14](https://github.com/knsv/mermaid/pull/14) ([ImanimalXI](https://github.com/ImanimalXI))
|
||||
|
||||
## [0.2.10](https://github.com/knsv/mermaid/tree/0.2.10) (2014-12-01)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.9...0.2.10)
|
||||
|
||||
## [0.2.9](https://github.com/knsv/mermaid/tree/0.2.9) (2014-12-01)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.8...0.2.9)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Add link to jsbin playground to README [\#11](https://github.com/knsv/mermaid/issues/11)
|
||||
|
||||
- What are the requirements ? [\#10](https://github.com/knsv/mermaid/issues/10)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Allow unicode chars in labels [\#13](https://github.com/knsv/mermaid/pull/13) ([codebeige](https://github.com/codebeige))
|
||||
|
||||
- Formatting Update [\#12](https://github.com/knsv/mermaid/pull/12) ([darrencauthon](https://github.com/darrencauthon))
|
||||
|
||||
## [0.2.8](https://github.com/knsv/mermaid/tree/0.2.8) (2014-12-01)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.7...0.2.8)
|
||||
|
||||
## [0.2.7](https://github.com/knsv/mermaid/tree/0.2.7) (2014-12-01)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.6...0.2.7)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Provide parser as separate module [\#4](https://github.com/knsv/mermaid/issues/4)
|
||||
|
||||
## [0.2.6](https://github.com/knsv/mermaid/tree/0.2.6) (2014-11-27)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.5...0.2.6)
|
||||
|
||||
## [0.2.5](https://github.com/knsv/mermaid/tree/0.2.5) (2014-11-27)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.4...0.2.5)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Added new shapes! [\#1](https://github.com/knsv/mermaid/pull/1) ([bjowes](https://github.com/bjowes))
|
||||
|
||||
## [0.2.4](https://github.com/knsv/mermaid/tree/0.2.4) (2014-11-25)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.3...0.2.4)
|
||||
|
||||
## [0.2.3](https://github.com/knsv/mermaid/tree/0.2.3) (2014-11-24)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.2...0.2.3)
|
||||
|
||||
## [0.2.2](https://github.com/knsv/mermaid/tree/0.2.2) (2014-11-22)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.1...0.2.2)
|
||||
|
||||
## [0.2.1](https://github.com/knsv/mermaid/tree/0.2.1) (2014-11-22)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.0...0.2.1)
|
||||
|
||||
## [0.2.0](https://github.com/knsv/mermaid/tree/0.2.0) (2014-11-22)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.1.1...0.2.0)
|
||||
|
||||
## [0.1.1](https://github.com/knsv/mermaid/tree/0.1.1) (2014-11-17)
|
||||
|
||||
[Full Changelog](https://github.com/knsv/mermaid/compare/0.1.0...0.1.1)
|
||||
|
||||
## [0.1.0](https://github.com/knsv/mermaid/tree/0.1.0) (2014-11-16)
|
||||
|
||||
|
||||
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
|
||||
88
CONTRIBUTING.md
Normal file
88
CONTRIBUTING.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# How to contribute
|
||||
|
||||
Great that you want to be involved in this project! Contributing is fun and contributions are GREAT! :)
|
||||
|
||||
This page is currently a starting point and is not so rigorous to start with.
|
||||
|
||||
Some important guidlines:
|
||||
|
||||
* The work will be organized using the issues list
|
||||
* In the list there will be the bugs/enhancements etc we are working with in the project
|
||||
* There will be milestones outlineing the roadmap ahead
|
||||
* There will issues marked with help wanted
|
||||
|
||||
The issue list and the items marked with **help wanted** is a good starting point if you want to do some work.
|
||||
|
||||
|
||||
## Guidelines for avoiding duplicate work
|
||||
|
||||
Contributing is great. It is not so fun when you are done with your issue and just before you're about to push your
|
||||
change you can't because someone else just pushed the same fix so you have wasted your time. The guidelines below are in
|
||||
place to prevent this:
|
||||
|
||||
* Comment in the issue that you are working on it. You will then be added as an assignee (eventually).
|
||||
* When you pick an issue to work on.
|
||||
* Check that the issue not assigned
|
||||
* Also check the comments so that no one has started working on it before beeing officially assigned.
|
||||
|
||||
|
||||
## Submitting changes
|
||||
|
||||
Please send a GitHub Pull Request with a clear list of what you've done (read more about pull requests). When you send
|
||||
a pull request, we will love you forever if you include jasmine tests. We can always use more test coverage.
|
||||
|
||||
Always write a clear log message for your commits. One-line messages are fine for small changes, but bigger changes should look like this:
|
||||
|
||||
$ git commit -m "A brief summary of the commit
|
||||
>
|
||||
> A paragraph describing what changed and its impact."
|
||||
Coding conventions
|
||||
Start reading our code and you'll get the hang of it. We optimize for readability:
|
||||
|
||||
This is open source software. Consider the people who will read your code, and make it look nice for them. It's sort of
|
||||
like driving a car: Perhaps you love doing donuts when you're alone, but with passengers the goal is to make the ride as
|
||||
smooth as possible.
|
||||
|
||||
So that we can consistently serve images from the CDN, always use image_path or image_tag when referring to images.
|
||||
Never prepend "/images/" when using image_path or image_tag.
|
||||
Also for the CDN, always use cwd-relative paths rather than root-relative paths in image URLs in any CSS. So instead of
|
||||
url('/images/blah.gif'), use url('../images/blah.gif').
|
||||
|
||||
# Build instructions
|
||||
|
||||
Fork, then:
|
||||
|
||||
```
|
||||
yarn install
|
||||
```
|
||||
|
||||
Then the dependencies will have been installed. You use gulp and yarn calls as build tools.
|
||||
|
||||
The following targets are probably interesting:
|
||||
|
||||
* jison - compiles the jison grammars to parser files
|
||||
|
||||
for instance:
|
||||
```
|
||||
gulp jison
|
||||
```
|
||||
|
||||
To build:
|
||||
|
||||
```
|
||||
yarn build
|
||||
```
|
||||
|
||||
To run the tests:
|
||||
|
||||
```
|
||||
yarn test
|
||||
```
|
||||
|
||||
Make sure you have Chrome browser installed. We use Chrome headless for testing.
|
||||
|
||||
Manual test:
|
||||
|
||||
```
|
||||
open dist/demo/index.html
|
||||
```
|
||||
1
LICENSE
1
LICENSE
@@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
399
README.md
399
README.md
@@ -1,5 +1,10 @@
|
||||
mermaid [](https://travis-ci.org/knsv/mermaid)
|
||||
=======
|
||||
# mermaid
|
||||
|
||||
[](https://travis-ci.org/knsv/mermaid)
|
||||
[](https://codeclimate.com/github/knsv/mermaid)
|
||||
[](https://gitter.im/knsv/mermaid?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||

|
||||
|
||||
Generation of diagrams and flowcharts from text in a similar manner as markdown.
|
||||
|
||||
@@ -8,248 +13,226 @@ 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.
|
||||
|
||||
The code below would render the following image
|
||||
|
||||
```
|
||||
<table>
|
||||
<tr><th>Code</th><th>Rendered diagram</th></tr>
|
||||
<tr>
|
||||
<td>
|
||||
<pre>
|
||||
<code>
|
||||
graph TD;
|
||||
A-->B;
|
||||
A-->C;
|
||||
B-->D;
|
||||
C-->D;
|
||||
```
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td>
|
||||
<p align="center">
|
||||
<img src='./img/flow.png' alt='Flowchart'>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<pre>
|
||||
<code>
|
||||
sequenceDiagram
|
||||
participant Alice
|
||||
participant Bob
|
||||
Alice->>John: Hello John, how are you?
|
||||
loop Healthcheck
|
||||
John->>John: Fight against hypochondria
|
||||
end
|
||||
Note right of John: Rational thoughts <br/>prevail...
|
||||
John-->>Alice: Great!
|
||||
John->>Bob: How about you?
|
||||
Bob-->>John: Jolly good!
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td>
|
||||
<img src='./img/sequence.png' alt='Sequence Diagram'>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<pre>
|
||||
<code>
|
||||
gantt
|
||||
dateFormat YYYY-MM-DD
|
||||
title Adding GANTT diagram to mermaid
|
||||
|
||||
would render this lovely chart:
|
||||
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
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td>
|
||||
<img src='./img/gantt.png' alt='Gantt Diagram'>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<pre>
|
||||
<code>
|
||||
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
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td>
|
||||
<img src='./img/class.png' alt='Class Diagram'>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<pre>
|
||||
<code>
|
||||
gitGraph :
|
||||
options
|
||||
{
|
||||
"key": "value",
|
||||
"nodeWidth": 150,
|
||||
"nodeSpacing" : 150
|
||||
}
|
||||
end
|
||||
commit
|
||||
branch newbranch
|
||||
checkout newbranch
|
||||
commit
|
||||
commit
|
||||
checkout master
|
||||
commit
|
||||
commit
|
||||
merge newbranch
|
||||
</code>
|
||||
</pre>
|
||||
</td>
|
||||
<td>
|
||||
<img src='./img/git.png' alt='Git Graph'>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||

|
||||
</table>
|
||||
|
||||
|
||||
#Installation
|
||||
## Installation
|
||||
|
||||
Either use the bower package manager as per below:
|
||||
### CDN
|
||||
|
||||
```
|
||||
bower install mermaid --save-dev
|
||||
https://unpkg.com/mermaid@<version>/dist/
|
||||
```
|
||||
|
||||
Or download javascript files:
|
||||
Replace `<version>` with expected version number.
|
||||
|
||||
* [mermaid including dependencies](http://www.sveido.com/mermaid/dist/mermaid.full.min.js)
|
||||
Example: https://unpkg.com/mermaid@7.0.4/dist/
|
||||
|
||||
This file bundles mermaid with d3 and dagre-d3.
|
||||
|
||||
* [mermaid without dependencies](http://www.sveido.com/mermaid/dist/mermaid.slim.min.js)
|
||||
|
||||
With this file you will need to include d3 and dagre-d3 yourself.
|
||||
|
||||
# Usage
|
||||
|
||||
Include mermaid on your web page:
|
||||
### Node.js
|
||||
|
||||
```
|
||||
<script src="mermaid.full.min.js"></script>
|
||||
```
|
||||
|
||||
Further down on your page mermaid will look for tags with ```class="mermaid"```. From these tags mermaid will try to
|
||||
read the chart definiton which will be replaced with the svg chart.
|
||||
|
||||
|
||||
A chart defined like this:
|
||||
```
|
||||
<div class="mermaid">
|
||||
CHART DEFINITION GOES HERE
|
||||
</div>
|
||||
```
|
||||
|
||||
Would end up like this:
|
||||
```
|
||||
<div class="mermaid" id="mermaidChart0">
|
||||
<svg>
|
||||
Chart ends up here
|
||||
</svg>
|
||||
</div>
|
||||
```
|
||||
An id is also added to mermaid tags without id.
|
||||
|
||||
|
||||
# A graph example
|
||||
|
||||
```
|
||||
graph LR;
|
||||
A[Hard edge]-->|Link text|B(Round edge);
|
||||
B-->C{Decision};
|
||||
C-->|One|D[Result one];
|
||||
C-->|Two|E[Result two];
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
#Syntax
|
||||
## Graph
|
||||
This statement declares a new graph and the direction of the graph layout.
|
||||
|
||||
```
|
||||
graph TD
|
||||
```
|
||||
|
||||
This declares a graph oriented from top to bottom.
|
||||
|
||||

|
||||
|
||||
```
|
||||
graph LR
|
||||
```
|
||||
|
||||
This declares a graph oriented from left to right.
|
||||
|
||||
Possible directions are:
|
||||
|
||||
* TB - top bottom
|
||||
* BT - bottom top
|
||||
* RL - right left
|
||||
* LR - left right
|
||||
* TD - same as TB
|
||||
|
||||

|
||||
|
||||
## Nodes
|
||||
|
||||
### A node (default)
|
||||
```
|
||||
id1;
|
||||
```
|
||||
|
||||

|
||||
|
||||
Note that the id is what is displayed in the box.
|
||||
|
||||
### A node with text
|
||||
It is also possible to set text in the box that differs from the id. If this is done several times, it is the last text
|
||||
found for the node that will be used. Also if you define edges for the node later on, you can omit text definitions. The
|
||||
one previously defined will be used when rendering the box.
|
||||
|
||||
```
|
||||
id1[This is the text in the box];
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
### A node with round edges
|
||||
```
|
||||
id1(This is the text in the box);
|
||||
```
|
||||
|
||||

|
||||
|
||||
### A node in the form of a circle
|
||||
```
|
||||
id1((This is the text in the box));
|
||||
```
|
||||
|
||||

|
||||
|
||||
### A node in an asymetric shape
|
||||
```
|
||||
id1>This is the text in the box];
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
### A node (rhombus)
|
||||
```
|
||||
id1{This is the text in the box};
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Styling a node
|
||||
It is possible to apply specific styles such as a thicker border or a different background color to a node.
|
||||
|
||||
```
|
||||
graph LR;
|
||||
id1(Start)-->id2(Stop);
|
||||
style id1 fill:#f9f,stroke:#333,stroke-width:4px;
|
||||
style id2 fill:#ccf,stroke:#f66,stroke-width:2px,stroke-dasharray: 5, 5;
|
||||
```
|
||||
|
||||

|
||||
|
||||
#### Classes
|
||||
More convenient then defining the style everytime is to define a class of styles and attach this class to the nodes that
|
||||
should have a different look.
|
||||
|
||||
a class definition looks like the example below:
|
||||
|
||||
```
|
||||
classDef className fill:#f9f,stroke:#333,stroke-width:4px;
|
||||
```
|
||||
|
||||
Attachment of a class to a node is done as per below:
|
||||
|
||||
```
|
||||
class nodeId1 className;
|
||||
```
|
||||
|
||||
It is also possible to attach a class to a list of nodes in one statement:
|
||||
|
||||
```
|
||||
class nodeId1,nodeId2 className;
|
||||
```
|
||||
|
||||
#### Default class
|
||||
|
||||
If a class is named default it will be assigned to all classes without specific class definitions.
|
||||
|
||||
```
|
||||
classDef default fill:#f9f,stroke:#333,stroke-width:4px;
|
||||
yarn add mermaid
|
||||
```
|
||||
|
||||
|
||||
## Links between nodes
|
||||
## Further reading
|
||||
|
||||
Nodes can be connected with links/edges. It is possible to have different types of links or attach a text string to a link.
|
||||
* [Usage](https://mermaidjs.github.io/usage.html)
|
||||
* [Flowchart syntax](https://mermaidjs.github.io/flowchart.html)
|
||||
* [Sequence diagram syntax](https://mermaidjs.github.io/sequenceDiagram.html)
|
||||
* [Mermaid CLI](https://mermaidjs.github.io/mermaidCLI.html)
|
||||
* [Demos](https://mermaidjs.github.io/demos.html)
|
||||
|
||||
### A link with arrow head
|
||||
```
|
||||
A-->B;
|
||||
```
|
||||
|
||||

|
||||
# Request for assistance
|
||||
|
||||
### An open link
|
||||
Things are piling up and I have hard time keeping up. To remedy this
|
||||
it would be great if we could form a core team of developers to cooperate
|
||||
with the future development mermaid.
|
||||
|
||||
```
|
||||
A---B;
|
||||
```
|
||||
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:
|
||||
* port the code to es6
|
||||
* adding more typers of diagrams like mindmaps, ert digrams etc
|
||||
* improving existing diagrams
|
||||
|
||||
### Text on links
|
||||
Don't hesitate to contact me if you want to get involved.
|
||||
|
||||
```
|
||||
A---|This is the text|B;
|
||||
```
|
||||
|
||||

|
||||
# For contributors
|
||||
|
||||
### Styling links
|
||||
It is possible to style links for instance a link that is going back in the flow. This is done by the linkStyle statement as in the example below:
|
||||
## Setup
|
||||
|
||||
```
|
||||
linkStyle 3 stroke:#ff3,stroke-width:4px;
|
||||
```
|
||||
Make sure you have Chrome browser installed, this project uses Chrome headless to running tests.
|
||||
|
||||
## Interaction
|
||||
yarn install
|
||||
|
||||
It is possible to bind a click event to a node:
|
||||
|
||||
```
|
||||
click nodeId callback
|
||||
```
|
||||
## Build
|
||||
|
||||
yarn build
|
||||
|
||||
If you want real time incremental build:
|
||||
|
||||
yarn watch
|
||||
|
||||
|
||||
## Lint
|
||||
|
||||
yarn lint
|
||||
|
||||
We use [JavaScript Standard Style](https://github.com/feross/standard).
|
||||
We recommend you installing [editor plugins](https://github.com/feross/standard#are-there-text-editor-plugins) so you can get real time lint result.
|
||||
|
||||
|
||||
## Test
|
||||
|
||||
yarn test
|
||||
|
||||
Manual test in browser:
|
||||
|
||||
open dist/demo/index.html
|
||||
|
||||
Manual test in Node.js:
|
||||
|
||||
node dist/demo/index.js
|
||||
|
||||
|
||||
## Release
|
||||
|
||||
Update version number in `package.json`.
|
||||
|
||||
npm publish
|
||||
|
||||
Command above generates files into the `dist` folder and publishes them to npmjs.org.
|
||||
|
||||
* nodeId is the id of the node
|
||||
* callback is the name of a javascript function defined on the page displaying the graph, the function will be called with the nodeId as parameter.
|
||||
|
||||
# Credits
|
||||
|
||||
Many thanks to the [d3](http://d3js.org/) and [dagre-d3](https://github.com/cpettitt/dagre-d3) projects for providing the graphical layout and drawing libraries!
|
||||
|
||||
Thanks also to the [js-sequence-diagram](http://bramp.github.io/js-sequence-diagrams) project for usage of the grammar for the sequence diagrams. Thanks to Jessica Peter for inspiration and starting point for gantt rendering.
|
||||
|
||||
*Mermaid was created by Knut Sveidqvist for easier documentation.*
|
||||
|
||||
*[Tyler Long](https://github.com/tylerlong) has became a collaborator since April 2017.*
|
||||
|
||||
Here is the full list of the projects [contributors](https://github.com/knsv/mermaid/graphs/contributors).
|
||||
|
||||
24
bin/mermaid.js
Executable file
24
bin/mermaid.js
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var chalk = require('chalk')
|
||||
var cli = require('../lib/cli.js')
|
||||
var lib = require('../lib')
|
||||
|
||||
cli.parse(process.argv.slice(2), function (err, message, options) {
|
||||
if (err) {
|
||||
console.error(
|
||||
chalk.bold.red('\nYou had errors in your syntax. Use --help for further information.')
|
||||
)
|
||||
err.forEach(function (e) {
|
||||
console.error(e.message)
|
||||
})
|
||||
|
||||
return
|
||||
} else if (message) {
|
||||
console.log(message)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
lib.process(options.files, options, process.exit)
|
||||
})
|
||||
38
bower.json
38
bower.json
@@ -1,28 +1,38 @@
|
||||
{
|
||||
"name": "mermaid",
|
||||
"version": "0.2.5",
|
||||
"authors": [
|
||||
"knsv <knut@sveido.com>"
|
||||
"knsv <knut@sveido.com>",
|
||||
"Tyler Long (https://github.com/tylerlong)"
|
||||
],
|
||||
"description": "Markdownish syntax for generating flowcharts",
|
||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"main": "dist/mermaid.slim.js",
|
||||
"keywords": [
|
||||
"diagram",
|
||||
"markdown",
|
||||
"flowchart"
|
||||
"flowchart",
|
||||
"sequence diagram",
|
||||
"gantt",
|
||||
"class diagram",
|
||||
"git graph"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"vendor",
|
||||
"test",
|
||||
"tests"
|
||||
"*",
|
||||
"!dist/",
|
||||
"!dist/*",
|
||||
"!LICENSE"
|
||||
],
|
||||
"devDependencies": {
|
||||
"jasmine": "~2.0.4",
|
||||
"dagre": "0.6.2",
|
||||
"d3": "3.3.8",
|
||||
"dagre-d3": "0.3.2"
|
||||
"dependencies": {
|
||||
"chalk": "^2.1.0",
|
||||
"d3": "3.5.17",
|
||||
"dagre": "^0.7.4",
|
||||
"dagre-d3-renderer": "^0.1.6",
|
||||
"he": "^1.1.1",
|
||||
"lodash": "^4.17.4",
|
||||
"minimist": "^1.2.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"moment": "^2.18.1",
|
||||
"semver": "^5.4.1",
|
||||
"which": "^1.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
// Karma configuration
|
||||
// Generated on Mon Nov 03 2014 07:53:38 GMT+0100 (CET)
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
|
||||
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||
basePath: '',
|
||||
|
||||
// frameworks to use
|
||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
frameworks: ['jasmine'],
|
||||
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: [
|
||||
'../src/**/*.js'
|
||||
],
|
||||
|
||||
|
||||
// list of files to exclude
|
||||
exclude: ['../src/backup/**/*.js'],
|
||||
|
||||
|
||||
// preprocess matching files before serving them to the browser
|
||||
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
||||
|
||||
|
||||
// test results reporter to use
|
||||
// possible values: 'dots', 'progress'
|
||||
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
||||
reporters: ['progress'],
|
||||
|
||||
|
||||
// web server port
|
||||
port: 9876,
|
||||
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors: true,
|
||||
|
||||
|
||||
// level of logging
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
logLevel: config.LOG_DEBUG,
|
||||
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file changes
|
||||
autoWatch: true,
|
||||
|
||||
|
||||
// start these browsers
|
||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
browsers: ['Chrome'],
|
||||
|
||||
// Continuous Integration mode
|
||||
// if true, Karma captures browsers, runs the tests and exits
|
||||
singleRun: false
|
||||
});
|
||||
};
|
||||
106
dist/demo/index.html
vendored
Normal file
106
dist/demo/index.html
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
<!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=">
|
||||
<link rel="stylesheet" href="../mermaid.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="mermaid">
|
||||
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[Car]
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
<div class="mermaid">
|
||||
sequenceDiagram
|
||||
loop every day
|
||||
Alice->>John: Hello John, how are you?
|
||||
John-->>Alice: Great!
|
||||
end
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
<div class="mermaid">
|
||||
gantt
|
||||
dateFormat YYYY-MM-DD
|
||||
title Adding GANTT diagram to mermaid
|
||||
|
||||
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
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
<div class="mermaid">
|
||||
gitGraph:
|
||||
options
|
||||
{
|
||||
"nodeSpacing": 150,
|
||||
"nodeRadius": 10
|
||||
}
|
||||
end
|
||||
commit
|
||||
branch newbranch
|
||||
checkout newbranch
|
||||
commit
|
||||
commit
|
||||
checkout master
|
||||
commit
|
||||
commit
|
||||
merge newbranch
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
<div class="mermaid">
|
||||
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
|
||||
</div>
|
||||
<script src="../mermaid.js"></script>
|
||||
<script>
|
||||
mermaid.initialize({startOnLoad: true});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
9
dist/demo/index.js
vendored
Normal file
9
dist/demo/index.js
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
const api = require('../mermaidAPI.js')
|
||||
|
||||
const r = api.parse(`sequenceDiagram
|
||||
loop every day
|
||||
Alice->>John: Hello John, how are you?
|
||||
John-->>Alice: Great!
|
||||
end`)
|
||||
|
||||
console.log(r)
|
||||
2851
dist/mermaid.full.js
vendored
2851
dist/mermaid.full.js
vendored
File diff suppressed because one or more lines are too long
35
dist/mermaid.full.min.js
vendored
35
dist/mermaid.full.min.js
vendored
File diff suppressed because one or more lines are too long
2819
dist/mermaid.slim.js
vendored
2819
dist/mermaid.slim.js
vendored
File diff suppressed because one or more lines are too long
3
dist/mermaid.slim.min.js
vendored
3
dist/mermaid.slim.min.js
vendored
File diff suppressed because one or more lines are too long
26
gulp/tasks/dev.js
Normal file
26
gulp/tasks/dev.js
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Created by knut on 2015-12-26.
|
||||
*/
|
||||
var gulp = require('gulp')
|
||||
var shell = require('gulp-shell')
|
||||
var liveServer = require('live-server')
|
||||
|
||||
var params = {
|
||||
port: 8080, // Set the server port. Defaults to 8080.
|
||||
host: '0.0.0.0', // Set the address to bind to. Defaults to 0.0.0.0.
|
||||
root: './test/examples', // Set root directory that's being server. Defaults to cwd.
|
||||
open: true, // When false, it won't load your browser by default.
|
||||
ignore: 'scss,my/templates', // comma-separated string for paths to ignore
|
||||
// file: "index.html", // When set, serve this file for every 404 (useful for single-page applications)
|
||||
wait: 1000, // Waits for all changes, before reloading. Defaults to 0 sec.
|
||||
mount: [['/dist', './dist']] // Mount a directory to a route.
|
||||
}
|
||||
gulp.task('live-server', function () {
|
||||
liveServer.start(params)
|
||||
})
|
||||
|
||||
gulp.task('watch2', ['live-server'], function () {
|
||||
return shell.task([
|
||||
'yarn build -- --watch'
|
||||
])
|
||||
})
|
||||
23
gulp/tasks/jison.js
Normal file
23
gulp/tasks/jison.js
Normal file
@@ -0,0 +1,23 @@
|
||||
var gulp = require('gulp')
|
||||
var shell = require('gulp-shell')
|
||||
var jison = require('gulp-jison')
|
||||
var filelog = require('gulp-filelog')
|
||||
|
||||
gulp.task('jison', function () {
|
||||
return gulp.src('./src/**/*.jison')
|
||||
.pipe(filelog('Jison file:'))
|
||||
.pipe(jison({ moduleType: 'commonjs' }))
|
||||
.pipe(gulp.dest('./src/'))
|
||||
})
|
||||
|
||||
gulp.task('jison_legacy', function () {
|
||||
shell.task([
|
||||
'node node_modules/jison/lib/cli.js src/diagrams/classDiagram/parser/classDiagram.jison -o src/diagrams/classDiagram/parser/classDiagram.js',
|
||||
'node node_modules/jison/lib/cli.js src/diagrams/sequenceDiagram/parser/sequenceDiagram.jison -o src/diagrams/sequenceDiagram/parser/sequenceDiagram.js',
|
||||
'node node_modules/jison/lib/cli.js src/diagrams/example/parser/example.jison -o src/diagrams/example/parser/example.js',
|
||||
'node node_modules/jison/lib/cli.js src/diagrams/flowchart/parser/flow.jison -o src/diagrams/flowchart/parser/flow.js',
|
||||
'node node_modules/jison/lib/cli.js src/diagrams/flowchart/parser/dot.jison -o src/diagrams/flowchart/parser/dot.js',
|
||||
'node node_modules/jison/lib/cli.js src/diagrams/gitGraph/parser/gitGraph.jison -o src/diagrams/gitGraph/parser/gitGraph.js',
|
||||
'node node_modules/jison/lib/cli.js src/diagrams/gantt/parser/gantt.jison -o src/diagrams/gantt/parser/gantt.js'
|
||||
])
|
||||
})
|
||||
73
gulpfile.js
73
gulpfile.js
@@ -1,72 +1,3 @@
|
||||
var gulp = require('gulp');
|
||||
var jison = require('gulp-jison');
|
||||
var shell = require('gulp-shell');
|
||||
var concat = require('gulp-concat');
|
||||
var uglify = require('gulp-uglify');
|
||||
var extReplace = require('gulp-ext-replace');
|
||||
var rename = require('gulp-rename');
|
||||
var istanbul = require('gulp-istanbul');
|
||||
var requireDir = require('require-dir')
|
||||
|
||||
|
||||
gulp.task('jison2', function() {
|
||||
return gulp.src('./src/*.jison')
|
||||
.pipe(jison({ moduleType: 'amd' }))
|
||||
.pipe(gulp.dest('./src/'));
|
||||
});
|
||||
|
||||
gulp.task('jison', shell.task([
|
||||
'jison src/parser/flow.jison -o src/parser/flow.js'
|
||||
//'source scripts/compileJison.sh'
|
||||
// 'jison src/parser/flow.jison -o src/parser/flow.js',
|
||||
]))
|
||||
|
||||
gulp.task('jisonSd', shell.task([
|
||||
//'jison src/parser/flow.jison -o src/parser/flow.js',
|
||||
'jison src/parser/sequence.jison -o src/parser/sequence.js'
|
||||
//'source scripts/compileFlow.sh'
|
||||
]));
|
||||
|
||||
gulp.task('dist', ['slimDist', 'fullDist']);
|
||||
|
||||
var jasmine = require('gulp-jasmine');
|
||||
|
||||
gulp.task('jasmine',['jison'], function () {
|
||||
return gulp.src(['src/**/*.spec.js'])
|
||||
.pipe(jasmine());
|
||||
});
|
||||
|
||||
gulp.task('coverage', function (cb) {
|
||||
gulp.src(['src/**/*.js', '!src/**/*.spec.js'])
|
||||
.pipe(istanbul()) // Covering files
|
||||
.on('finish', function () {
|
||||
gulp.src(['src/**/*.spec.js'])
|
||||
.pipe(jasmine())
|
||||
.pipe(istanbul.writeReports()) // Creating the reports after tests runned
|
||||
.on('end', cb);
|
||||
});
|
||||
});
|
||||
|
||||
var browserify = require('gulp-browserify');
|
||||
|
||||
// Basic usage
|
||||
gulp.task('slimDist', function() {
|
||||
// Single entry point to browserify
|
||||
return gulp.src('src/main.js')
|
||||
.pipe(browserify())
|
||||
.pipe(rename('mermaid.slim.js'))
|
||||
.pipe(gulp.dest('./dist/'))
|
||||
.pipe(uglify())
|
||||
.pipe(extReplace('.min.js'))
|
||||
.pipe(gulp.dest('./dist/'));
|
||||
});
|
||||
|
||||
// Basic usage
|
||||
gulp.task('fullDist', ['slimDist'], function() {
|
||||
// Single entry point to browserify
|
||||
gulp.src(['node_modules/d3/d3.min.js','node_modules/dagre-d3/dist/dagre-d3.min.js','dist/mermaid.slim.js'])
|
||||
.pipe(concat('mermaid.full.js'))
|
||||
.pipe(gulp.dest('./dist/'));
|
||||
return gulp.src(['node_modules/d3/d3.min.js','node_modules/dagre-d3/dist/dagre-d3.min.js','dist/mermaid.slim.min.js'])
|
||||
.pipe(concat('mermaid.full.min.js'))
|
||||
.pipe(gulp.dest('./dist/'));
|
||||
});
|
||||
requireDir('./gulp/tasks')
|
||||
|
||||
BIN
img/class.png
Normal file
BIN
img/class.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
BIN
img/flow.png
Normal file
BIN
img/flow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.1 KiB |
BIN
img/gantt.png
Normal file
BIN
img/gantt.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
img/git.png
Normal file
BIN
img/git.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
img/header.png
Normal file
BIN
img/header.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
BIN
img/sequence.png
Normal file
BIN
img/sequence.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
99
karma.conf.js
Normal file
99
karma.conf.js
Normal file
@@ -0,0 +1,99 @@
|
||||
// Karma configuration
|
||||
// Generated on Mon Nov 03 2014 07:53:38 GMT+0100 (CET)
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
|
||||
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||
basePath: '.',
|
||||
|
||||
// frameworks to use
|
||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
frameworks: ['jasmine'],
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: [
|
||||
'./src/*.spec.js',
|
||||
'./src/diagrams/flowchart/**/*.spec.js',
|
||||
'./src/diagrams/example/**/*.spec.js',
|
||||
'./src/diagrams/sequenceDiagram/**/*.spec.js',
|
||||
'./src/diagrams/classDiagram/**/*.spec.js',
|
||||
'./src/diagrams/gantt/**/*.spec.js',
|
||||
'./src/diagrams/gitGraph/**/*.spec.js'
|
||||
],
|
||||
|
||||
preprocessors: {
|
||||
'src/**/*.spec.js': ['webpack']
|
||||
},
|
||||
|
||||
webpack: {
|
||||
externals: ['fs'],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: [
|
||||
['env', {
|
||||
'targets': {
|
||||
'browsers': ['last 3 versions']
|
||||
}
|
||||
}]
|
||||
],
|
||||
plugins: [
|
||||
'transform-remove-strict-mode'
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// test results reporter to use
|
||||
// possible values: 'dots', 'progress'
|
||||
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
||||
reporters: ['dots'],
|
||||
|
||||
// web server port
|
||||
port: 9876,
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors: true,
|
||||
|
||||
// level of logging
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
logLevel: config.LOG_INFO,
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file changes
|
||||
autoWatch: true,
|
||||
|
||||
customLaunchers: {
|
||||
ChromeHeadless: {
|
||||
base: 'Chrome',
|
||||
flags: [
|
||||
'--incognito',
|
||||
// '--headless',
|
||||
'--disable-gpu',
|
||||
'--no-sandbox',
|
||||
// Without a remote debugging port, Google Chrome exits immediately.
|
||||
'--remote-debugging-port=9222'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// start these browsers
|
||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
browsers: ['ChromeHeadless'],
|
||||
plugins: [
|
||||
'karma-jasmine',
|
||||
'karma-chrome-launcher',
|
||||
'karma-webpack'
|
||||
],
|
||||
// Continuous Integration mode
|
||||
// if true, Karma captures browsers, runs the tests and exits
|
||||
singleRun: false
|
||||
})
|
||||
}
|
||||
196
lib/cli.js
Normal file
196
lib/cli.js
Normal file
@@ -0,0 +1,196 @@
|
||||
var fs = require('fs')
|
||||
var exec = require('child_process').exec
|
||||
var chalk = require('chalk')
|
||||
var which = require('which')
|
||||
var parseArgs = require('minimist')
|
||||
var semver = require('semver')
|
||||
var path = require('path')
|
||||
|
||||
var PHANTOM_VERSION = '^2.1.0'
|
||||
|
||||
var info = chalk.blue.bold
|
||||
|
||||
module.exports = (function () {
|
||||
return new Cli()
|
||||
}())
|
||||
|
||||
function Cli (options) {
|
||||
this.options = {
|
||||
alias: {
|
||||
help: 'h',
|
||||
png: 'p',
|
||||
outputDir: 'o',
|
||||
outputSuffix: 'O',
|
||||
svg: 's',
|
||||
verbose: 'v',
|
||||
phantomPath: 'e',
|
||||
sequenceConfig: 'c',
|
||||
ganttConfig: 'g',
|
||||
css: 't',
|
||||
width: 'w'
|
||||
},
|
||||
'boolean': ['help', 'png', 'svg', 'verbose'],
|
||||
'string': ['outputDir', 'outputSuffix']
|
||||
}
|
||||
|
||||
this.errors = []
|
||||
this.message = null
|
||||
|
||||
this.helpMessage = [
|
||||
info('Usage: mermaid [options] <file>...'),
|
||||
'',
|
||||
'file The mermaid description file to be rendered',
|
||||
'',
|
||||
'Options:',
|
||||
' -s --svg Output SVG instead of PNG (experimental)',
|
||||
' -p --png If SVG was selected, and you also want PNG, set this flag',
|
||||
' -o --outputDir Directory to save files, will be created automatically, defaults to `cwd`',
|
||||
" -O --outputSuffix Suffix to output filenames in front of '.svg' or '.png', defaults to ''",
|
||||
' -e --phantomPath Specify the path to the phantomjs executable',
|
||||
' -t --css Specify the path to a CSS file to be included when processing output',
|
||||
' -c --sequenceConfig Specify the path to the file with the configuration to be applied in the sequence diagram',
|
||||
' -g --ganttConfig Specify the path to the file with the configuration to be applied in the gantt diagram',
|
||||
' -h --help Show this message',
|
||||
' -v --verbose Show logging',
|
||||
' -w --width width of the generated png (number)',
|
||||
' --version Print version and quit'
|
||||
]
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
Cli.prototype.parse = function (argv, next) {
|
||||
this.errors = [] // clear errors
|
||||
var options = parseArgs(argv, this.options)
|
||||
|
||||
if (options.version) {
|
||||
var pkg = require('../package.json')
|
||||
this.message = '' + pkg.version
|
||||
next(null, this.message)
|
||||
} else if (options.help) {
|
||||
this.message = this.helpMessage.join('\n')
|
||||
next(null, this.message)
|
||||
} else {
|
||||
options.files = options._
|
||||
|
||||
if (!options.files.length) {
|
||||
this.errors.push(new Error('You must specify at least one source file.'))
|
||||
}
|
||||
|
||||
// ensure that parameter-expecting options have parameters
|
||||
;['outputDir', 'outputSuffix', 'phantomPath', 'sequenceConfig', 'ganttConfig', 'css'].forEach(function (i) {
|
||||
if (typeof options[i] !== 'undefined') {
|
||||
if (typeof options[i] !== 'string' || options[i].length < 1) {
|
||||
this.errors.push(new Error(i + ' expects a value.'))
|
||||
}
|
||||
}
|
||||
}.bind(this))
|
||||
|
||||
// set svg/png flags appropriately
|
||||
if (options.svg && !options.png) {
|
||||
options.png = false
|
||||
} else {
|
||||
options.png = true
|
||||
}
|
||||
|
||||
if (options.sequenceConfig) {
|
||||
try {
|
||||
fs.accessSync(options.sequenceConfig, fs.R_OK)
|
||||
} catch (err) {
|
||||
this.errors.push(err)
|
||||
}
|
||||
} else {
|
||||
options.sequenceConfig = null
|
||||
}
|
||||
|
||||
if (options.ganttConfig) {
|
||||
try {
|
||||
fs.accessSync(options.ganttConfig, fs.R_OK)
|
||||
} catch (err) {
|
||||
this.errors.push(err)
|
||||
}
|
||||
} else {
|
||||
options.ganttConfig = null
|
||||
}
|
||||
|
||||
if (options.css) {
|
||||
try {
|
||||
fs.accessSync(options.css, fs.R_OK)
|
||||
} catch (err) {
|
||||
this.errors.push(err)
|
||||
}
|
||||
} else {
|
||||
options.css = path.join(__dirname, '..', 'dist', 'mermaid.css')
|
||||
}
|
||||
|
||||
// set svg/png flags appropriately
|
||||
if (!options.width) {
|
||||
options.width = 1200
|
||||
}
|
||||
|
||||
this.checkPhantom = createCheckPhantom(options.phantomPath)
|
||||
|
||||
this.checkPhantom(function (err, path) {
|
||||
if (err) {
|
||||
this.errors.push(err)
|
||||
}
|
||||
options.phantomPath = path
|
||||
next(
|
||||
this.errors.length > 0 ? this.errors : null
|
||||
, this.message
|
||||
, options
|
||||
)
|
||||
}.bind(this))
|
||||
}
|
||||
}
|
||||
|
||||
function createCheckPhantom (_phantomPath) {
|
||||
var phantomPath = _phantomPath
|
||||
|
||||
return function checkPhantom (_next) {
|
||||
var next = _next || function () { }
|
||||
var err
|
||||
|
||||
if (typeof phantomPath === 'undefined') {
|
||||
try {
|
||||
var phantom = require('phantomjs')
|
||||
phantomPath = phantom.path
|
||||
} catch (e) {
|
||||
try {
|
||||
phantomPath = which.sync('phantomjs')
|
||||
} catch (e) {
|
||||
if (!phantomPath) {
|
||||
phantomPath = null
|
||||
err = new Error(
|
||||
[
|
||||
'Cannot find phantomjs in your PATH. If phantomjs is installed',
|
||||
"you may need to specify its path manually with the '-e' option.",
|
||||
"Run this executable with '--help' or view the README for more",
|
||||
'details.'
|
||||
].join('\n')
|
||||
)
|
||||
|
||||
next(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we have phantompath, see if its version satisfies our requirements
|
||||
exec('"' + phantomPath + '" --version', function (err, stdout, stderr) {
|
||||
if (err) {
|
||||
next(new Error('Could not find phantomjs at the specified path.'))
|
||||
} else if (!semver.satisfies(stdout, PHANTOM_VERSION)) {
|
||||
next(new Error(
|
||||
'mermaid requires phantomjs ' +
|
||||
PHANTOM_VERSION +
|
||||
' to be installed, found version ' +
|
||||
stdout
|
||||
))
|
||||
} else {
|
||||
next(null, phantomPath)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
43
lib/index.js
Normal file
43
lib/index.js
Normal file
@@ -0,0 +1,43 @@
|
||||
var path = require('path')
|
||||
var spawn = require('child_process').spawn
|
||||
|
||||
var mkdirp = require('mkdirp')
|
||||
|
||||
var phantomscript = path.join(__dirname, 'phantomscript.js')
|
||||
|
||||
module.exports = { process: processMermaid }
|
||||
|
||||
function processMermaid (files, _options, _next) {
|
||||
var options = _options || {}
|
||||
var outputDir = options.outputDir || process.cwd()
|
||||
var outputSuffix = options.outputSuffix || ''
|
||||
var next = _next || function () { }
|
||||
var phantomArgs = [
|
||||
phantomscript,
|
||||
outputDir,
|
||||
options.png,
|
||||
options.svg,
|
||||
options.css,
|
||||
options.sequenceConfig,
|
||||
options.ganttConfig,
|
||||
options.verbose,
|
||||
options.width,
|
||||
outputSuffix
|
||||
]
|
||||
|
||||
files.forEach(function (file) {
|
||||
phantomArgs.push(file)
|
||||
})
|
||||
|
||||
mkdirp(outputDir, function (err) {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
var phantom = spawn(options.phantomPath, phantomArgs)
|
||||
|
||||
phantom.on('exit', next)
|
||||
|
||||
phantom.stderr.pipe(process.stderr)
|
||||
phantom.stdout.pipe(process.stdout)
|
||||
})
|
||||
}
|
||||
231
lib/phantomscript.js
Normal file
231
lib/phantomscript.js
Normal file
@@ -0,0 +1,231 @@
|
||||
/**
|
||||
* Credits:
|
||||
* - SVG Processing from the NYTimes svg-crowbar, under an MIT license
|
||||
* https://github.com/NYTimes/svg-crowbar
|
||||
* - Thanks to the grunticon project for some guidance
|
||||
* https://github.com/filamentgroup/grunticon
|
||||
*/
|
||||
|
||||
window.phantom.onError = function (msg, trace) {
|
||||
var msgStack = ['PHANTOM ERROR: ' + msg]
|
||||
if (trace && trace.length) {
|
||||
msgStack.push('TRACE:')
|
||||
trace.forEach(function (t) {
|
||||
msgStack.push(
|
||||
' -> ' +
|
||||
(t.file || t.sourceURL) +
|
||||
': ' +
|
||||
t.line +
|
||||
(t.function ? ' (in function ' + t.function + ')' : '')
|
||||
)
|
||||
})
|
||||
}
|
||||
system.stderr.write(msgStack.join('\n'))
|
||||
window.phantom.exit(1)
|
||||
}
|
||||
|
||||
var system = require('system')
|
||||
var fs = require('fs')
|
||||
var webpage = require('webpage')
|
||||
|
||||
var page = webpage.create()
|
||||
var files = system.args.slice(10, system.args.length)
|
||||
var width = system.args[8]
|
||||
|
||||
if (typeof width === 'undefined' || width === 'undefined') {
|
||||
width = 1200
|
||||
}
|
||||
var options = {
|
||||
outputDir: system.args[1],
|
||||
png: system.args[2] === 'true',
|
||||
svg: system.args[3] === 'true',
|
||||
css: fs.read(system.args[4]),
|
||||
sequenceConfig: system.args[5] !== 'null' ? JSON.parse(fs.read(system.args[5])) : {},
|
||||
ganttConfig: system.args[6] !== 'null' ? JSON.parse(fs.read(system.args[6])) : {},
|
||||
verbose: system.args[7] === 'true',
|
||||
width: width,
|
||||
outputSuffix: system.args[9]
|
||||
}
|
||||
var log = logger(options.verbose)
|
||||
options.sequenceConfig.useMaxWidth = false
|
||||
|
||||
page.content = [
|
||||
'<html>',
|
||||
'<head>',
|
||||
'<style type="text/css">body {background:white;font-family: Arial;}',
|
||||
options.css,
|
||||
'</style>',
|
||||
'</head>',
|
||||
'<body>',
|
||||
'</body>',
|
||||
'</html>'
|
||||
].join('\n')
|
||||
|
||||
page.injectJs('../dist/mermaid.js')
|
||||
page.onConsoleMessage = function (msg, lineNum, sourceId) {
|
||||
log('CONSOLE: ' + msg + ' (from line #' + lineNum + ' in "' + sourceId + '")')
|
||||
}
|
||||
|
||||
log('Num files to execute : ' + files.length)
|
||||
|
||||
files.forEach(function (file) {
|
||||
var contents = fs.read(file)
|
||||
var filename = file.split(fs.separator).slice(-1)
|
||||
var oParser = new window.DOMParser()
|
||||
var oDOM
|
||||
var svgContent
|
||||
|
||||
log('ready to execute: ' + file)
|
||||
|
||||
// this JS is executed in this statement is sandboxed, even though it doesn't
|
||||
// look like it. we need to serialize then unserialize the svgContent that's
|
||||
// taken from the DOM
|
||||
svgContent = page.evaluate(executeInPage, {
|
||||
contents: contents,
|
||||
ganttConfig: options.ganttConfig,
|
||||
sequenceConfig: options.sequenceConfig,
|
||||
confWidth: options.width
|
||||
})
|
||||
|
||||
oDOM = oParser.parseFromString(svgContent, 'text/xml')
|
||||
|
||||
resolveSVGElement(oDOM.firstChild)
|
||||
setSVGStyle(oDOM.firstChild, options.css)
|
||||
|
||||
var outputPath = options.outputDir + fs.separator + filename + options.outputSuffix
|
||||
if (options.png) {
|
||||
page.viewportSize = {
|
||||
width: ~~oDOM.documentElement.attributes.getNamedItem('width').value,
|
||||
height: ~~oDOM.documentElement.attributes.getNamedItem('height').value
|
||||
}
|
||||
|
||||
page.render(outputPath + '.png')
|
||||
log('saved png: ' + outputPath + '.png')
|
||||
}
|
||||
|
||||
if (options.svg) {
|
||||
var serialize = new window.XMLSerializer()
|
||||
fs.write(outputPath + '.svg'
|
||||
, serialize.serializeToString(oDOM) + '\n'
|
||||
, 'w'
|
||||
)
|
||||
log('saved svg: ' + outputPath + '.svg')
|
||||
}
|
||||
})
|
||||
|
||||
window.phantom.exit()
|
||||
|
||||
function logger (_verbose) {
|
||||
var verbose = _verbose
|
||||
|
||||
return function (_message, _level) {
|
||||
var level = _level
|
||||
var message = _message
|
||||
var log
|
||||
|
||||
log = level === 'error' ? system.stderr : system.stdout
|
||||
|
||||
if (verbose) {
|
||||
log.write(message + '\n')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resolveSVGElement (element) {
|
||||
var prefix = {
|
||||
xmlns: 'http://www.w3.org/2000/xmlns/',
|
||||
xlink: 'http://www.w3.org/1999/xlink',
|
||||
svg: 'http://www.w3.org/2000/svg'
|
||||
}
|
||||
|
||||
element.setAttribute('version', '1.1')
|
||||
// removing attributes so they aren't doubled up
|
||||
element.removeAttribute('xmlns')
|
||||
element.removeAttribute('xlink')
|
||||
// These are needed for the svg
|
||||
if (!element.hasAttributeNS(prefix.xmlns, 'xmlns')) {
|
||||
element.setAttributeNS(prefix.xmlns, 'xmlns', prefix.svg)
|
||||
}
|
||||
if (!element.hasAttributeNS(prefix.xmlns, 'xmlns:xlink')) {
|
||||
element.setAttributeNS(prefix.xmlns, 'xmlns:xlink', prefix.xlink)
|
||||
}
|
||||
}
|
||||
|
||||
function setSVGStyle (svg, css) {
|
||||
if (!css || !svg) { return }
|
||||
var styles = svg.getElementsByTagName('style')
|
||||
if (!styles || styles.length === 0) { return }
|
||||
styles[0].textContent = css
|
||||
}
|
||||
|
||||
// The sandboxed function that's executed in-page by phantom
|
||||
function executeInPage (data) {
|
||||
var xmlSerializer = new window.XMLSerializer()
|
||||
var contents = data.contents
|
||||
var sequenceConfig = JSON.stringify(data.sequenceConfig)
|
||||
var ganttConfig = JSON.stringify(data.ganttConfig).replace(/"(function.*})"/, '$1')
|
||||
var svg
|
||||
var svgValue
|
||||
var boundingBox
|
||||
var width
|
||||
var height
|
||||
var confWidth = data.confWidth
|
||||
|
||||
var toRemove = document.getElementsByClassName('mermaid')
|
||||
if (toRemove && toRemove.length) {
|
||||
for (var i = 0, len = toRemove.length; i < len; i++) {
|
||||
toRemove[i].parentNode.removeChild(toRemove[i])
|
||||
}
|
||||
}
|
||||
|
||||
var el = document.createElement('div')
|
||||
el.className = 'mermaid'
|
||||
el.appendChild(document.createTextNode(contents))
|
||||
document.body.appendChild(el)
|
||||
|
||||
var config = {
|
||||
sequenceDiagram: JSON.parse(sequenceConfig),
|
||||
flowchart: { useMaxWidth: false },
|
||||
logLevel: 1
|
||||
}
|
||||
|
||||
window.mermaid.initialize(config)
|
||||
|
||||
var sc = document.createElement('script')
|
||||
sc.appendChild(document.createTextNode('mermaid.ganttConfig = ' + ganttConfig + ';'))
|
||||
document.body.appendChild(sc)
|
||||
|
||||
window.mermaid.init()
|
||||
|
||||
svg = document.querySelector('svg')
|
||||
|
||||
boundingBox = svg.getBoundingClientRect() // the initial bonding box of the svg
|
||||
width = boundingBox.width * 1.5 // adding the scale factor for consistency with output in chrome browser
|
||||
height = boundingBox.height * 1.5 // adding the scale factor for consistency with output in chrome browser
|
||||
|
||||
var scalefactor = confWidth / (width - 8)
|
||||
|
||||
// resizing the body to fit the svg
|
||||
document.body.setAttribute(
|
||||
'style'
|
||||
, 'width: ' + (confWidth - 8) + '; height: ' + (height * scalefactor) + ';'
|
||||
)
|
||||
// resizing the svg via css for consistent display
|
||||
svg.setAttribute(
|
||||
'style'
|
||||
, 'width: ' + (confWidth - 8) + '; height: ' + (height * scalefactor) + ';'
|
||||
)
|
||||
|
||||
// set witdth and height attributes used to set the viewport when rending png image
|
||||
svg.setAttribute(
|
||||
'width'
|
||||
, confWidth
|
||||
)
|
||||
svg.setAttribute(
|
||||
'height'
|
||||
, height * scalefactor
|
||||
)
|
||||
|
||||
svgValue = xmlSerializer.serializeToString(svg) + '\n'
|
||||
return svgValue
|
||||
}
|
||||
629
node_modules/jison/node_modules/ebnf-parser/transform-parser.js
generated
vendored
629
node_modules/jison/node_modules/ebnf-parser/transform-parser.js
generated
vendored
@@ -1,629 +0,0 @@
|
||||
/* parser generated by jison 0.4.11 */
|
||||
/*
|
||||
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 ebnf = (function(){
|
||||
var parser = {trace: function trace() { },
|
||||
yy: {},
|
||||
symbols_: {"error":2,"production":3,"handle":4,"EOF":5,"handle_list":6,"|":7,"expression_suffix":8,"expression":9,"suffix":10,"ALIAS":11,"symbol":12,"(":13,")":14,"*":15,"?":16,"+":17,"$accept":0,"$end":1},
|
||||
terminals_: {2:"error",5:"EOF",7:"|",11:"ALIAS",12:"symbol",13:"(",14:")",15:"*",16:"?",17:"+"},
|
||||
productions_: [0,[3,2],[6,1],[6,3],[4,0],[4,2],[8,3],[8,2],[9,1],[9,3],[10,0],[10,1],[10,1],[10,1]],
|
||||
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
|
||||
/* this == yyval */
|
||||
|
||||
var $0 = $$.length - 1;
|
||||
switch (yystate) {
|
||||
case 1: return $$[$0-1];
|
||||
break;
|
||||
case 2: this.$ = [$$[$0]];
|
||||
break;
|
||||
case 3: $$[$0-2].push($$[$0]);
|
||||
break;
|
||||
case 4: this.$ = [];
|
||||
break;
|
||||
case 5: $$[$0-1].push($$[$0]);
|
||||
break;
|
||||
case 6: this.$ = ['xalias', $$[$0-1], $$[$0-2], $$[$0]];
|
||||
break;
|
||||
case 7: if ($$[$0]) this.$ = [$$[$0], $$[$0-1]]; else this.$ = $$[$0-1];
|
||||
break;
|
||||
case 8: this.$ = ['symbol', $$[$0]];
|
||||
break;
|
||||
case 9: this.$ = ['()', $$[$0-1]];
|
||||
break;
|
||||
}
|
||||
},
|
||||
table: [{3:1,4:2,5:[2,4],12:[2,4],13:[2,4]},{1:[3]},{5:[1,3],8:4,9:5,12:[1,6],13:[1,7]},{1:[2,1]},{5:[2,5],7:[2,5],12:[2,5],13:[2,5],14:[2,5]},{5:[2,10],7:[2,10],10:8,11:[2,10],12:[2,10],13:[2,10],14:[2,10],15:[1,9],16:[1,10],17:[1,11]},{5:[2,8],7:[2,8],11:[2,8],12:[2,8],13:[2,8],14:[2,8],15:[2,8],16:[2,8],17:[2,8]},{4:13,6:12,7:[2,4],12:[2,4],13:[2,4],14:[2,4]},{5:[2,7],7:[2,7],11:[1,14],12:[2,7],13:[2,7],14:[2,7]},{5:[2,11],7:[2,11],11:[2,11],12:[2,11],13:[2,11],14:[2,11]},{5:[2,12],7:[2,12],11:[2,12],12:[2,12],13:[2,12],14:[2,12]},{5:[2,13],7:[2,13],11:[2,13],12:[2,13],13:[2,13],14:[2,13]},{7:[1,16],14:[1,15]},{7:[2,2],8:4,9:5,12:[1,6],13:[1,7],14:[2,2]},{5:[2,6],7:[2,6],12:[2,6],13:[2,6],14:[2,6]},{5:[2,9],7:[2,9],11:[2,9],12:[2,9],13:[2,9],14:[2,9],15:[2,9],16:[2,9],17:[2,9]},{4:17,7:[2,4],12:[2,4],13:[2,4],14:[2,4]},{7:[2,3],8:4,9:5,12:[1,6],13:[1,7],14:[2,3]}],
|
||||
defaultActions: {3:[2,1]},
|
||||
parseError: function parseError(str, hash) {
|
||||
if (hash.recoverable) {
|
||||
this.trace(str);
|
||||
} else {
|
||||
throw new Error(str);
|
||||
}
|
||||
},
|
||||
parse: function parse(input) {
|
||||
var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
|
||||
var args = lstack.slice.call(arguments, 1);
|
||||
this.lexer.setInput(input);
|
||||
this.lexer.yy = this.yy;
|
||||
this.yy.lexer = this.lexer;
|
||||
this.yy.parser = this;
|
||||
if (typeof this.lexer.yylloc == 'undefined') {
|
||||
this.lexer.yylloc = {};
|
||||
}
|
||||
var yyloc = this.lexer.yylloc;
|
||||
lstack.push(yyloc);
|
||||
var ranges = this.lexer.options && this.lexer.options.ranges;
|
||||
if (typeof this.yy.parseError === 'function') {
|
||||
this.parseError = this.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 = self.lexer.lex() || EOF;
|
||||
if (typeof token !== 'number') {
|
||||
token = self.symbols_[token] || token;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
|
||||
while (true) {
|
||||
state = stack[stack.length - 1];
|
||||
if (this.defaultActions[state]) {
|
||||
action = this.defaultActions[state];
|
||||
} else {
|
||||
if (symbol === null || typeof symbol == 'undefined') {
|
||||
symbol = lex();
|
||||
}
|
||||
action = table[state] && table[state][symbol];
|
||||
}
|
||||
if (typeof action === 'undefined' || !action.length || !action[0]) {
|
||||
var errStr = '';
|
||||
expected = [];
|
||||
for (p in table[state]) {
|
||||
if (this.terminals_[p] && p > TERROR) {
|
||||
expected.push('\'' + this.terminals_[p] + '\'');
|
||||
}
|
||||
}
|
||||
if (this.lexer.showPosition) {
|
||||
errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + this.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: this.lexer.match,
|
||||
token: this.terminals_[symbol] || symbol,
|
||||
line: this.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(this.lexer.yytext);
|
||||
lstack.push(this.lexer.yylloc);
|
||||
stack.push(action[1]);
|
||||
symbol = null;
|
||||
if (!preErrorSymbol) {
|
||||
yyleng = this.lexer.yyleng;
|
||||
yytext = this.lexer.yytext;
|
||||
yylineno = this.lexer.yylineno;
|
||||
yyloc = this.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,
|
||||
this.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.2.1 */
|
||||
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) {
|
||||
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 - 1);
|
||||
//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: {},
|
||||
performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
|
||||
|
||||
var YYSTATE=YY_START;
|
||||
switch($avoiding_name_collisions) {
|
||||
case 0:/* skip whitespace */
|
||||
break;
|
||||
case 1:return 12;
|
||||
break;
|
||||
case 2:yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 11;
|
||||
break;
|
||||
case 3:return 12;
|
||||
break;
|
||||
case 4:return 12;
|
||||
break;
|
||||
case 5:return 'bar';
|
||||
break;
|
||||
case 6:return 13;
|
||||
break;
|
||||
case 7:return 14;
|
||||
break;
|
||||
case 8:return 15;
|
||||
break;
|
||||
case 9:return 16;
|
||||
break;
|
||||
case 10:return 7;
|
||||
break;
|
||||
case 11:return 17;
|
||||
break;
|
||||
case 12:return 5;
|
||||
break;
|
||||
}
|
||||
},
|
||||
rules: [/^(?:\s+)/,/^(?:([a-zA-Z][a-zA-Z0-9_-]*))/,/^(?:\[([a-zA-Z][a-zA-Z0-9_-]*)\])/,/^(?:'[^']*')/,/^(?:\.)/,/^(?:bar\b)/,/^(?:\()/,/^(?:\))/,/^(?:\*)/,/^(?:\?)/,/^(?:\|)/,/^(?:\+)/,/^(?:$)/],
|
||||
conditions: {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12],"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 = ebnf;
|
||||
exports.Parser = ebnf.Parser;
|
||||
exports.parse = function () { return ebnf.parse.apply(ebnf, 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));
|
||||
}
|
||||
}
|
||||
158
package.json
158
package.json
@@ -1,47 +1,129 @@
|
||||
{
|
||||
"name": "mermaid",
|
||||
"version": "0.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"version": "7.0.5",
|
||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"main": "src/mermaid.js",
|
||||
"keywords": [
|
||||
"diagram",
|
||||
"markdown",
|
||||
"flowchart",
|
||||
"sequence diagram",
|
||||
"gantt",
|
||||
"class diagram",
|
||||
"git graph"
|
||||
],
|
||||
"bin": {
|
||||
"mermaid": "./bin/mermaid.js"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "gulp coverage"
|
||||
"build": "node -r babel-register ./node_modules/.bin/webpack --progress --colors",
|
||||
"watch": "yarn build -- --watch",
|
||||
"release": "yarn build -- -p --config webpack.config.prod.js",
|
||||
"upgrade": "yarn-upgrade-all && yarn remove d3 && yarn add d3@3.5.17",
|
||||
"lint": "node_modules/.bin/standard",
|
||||
"karma": "node node_modules/karma/bin/karma start karma.conf.js --single-run",
|
||||
"tape": "node node_modules/tape/bin/tape test/cli_test-*.js",
|
||||
"test": "yarn lint && yarn tape && yarn karma",
|
||||
"live": "live-server ./test/examples",
|
||||
"jison": "gulp jison_legacy",
|
||||
"live_server": "gulp live-server",
|
||||
"prepublishOnly": "yarn build && yarn release && yarn test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/username/repository.git"
|
||||
"url": "https://github.com/knsv/mermaid"
|
||||
},
|
||||
"author": "Knut Sveidqvist",
|
||||
"license": "MIT",
|
||||
"standard": {
|
||||
"ignore": [
|
||||
"**/parser/*.js",
|
||||
"dist/**/*.js"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "^2.1.0",
|
||||
"d3": "3.5.17",
|
||||
"dagre": "^0.7.4",
|
||||
"dagre-d3-renderer": "^0.1.6",
|
||||
"he": "^1.1.1",
|
||||
"lodash": "^4.17.4",
|
||||
"minimist": "^1.2.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"moment": "^2.18.1",
|
||||
"semver": "^5.4.1",
|
||||
"which": "^1.3.0"
|
||||
},
|
||||
"author": "",
|
||||
"license": "BSD-2-Clause",
|
||||
"devDependencies": {
|
||||
"browserify": "~6.2.0",
|
||||
"d3": "~3.4.13",
|
||||
"dagre-d3": "~0.3.2",
|
||||
"gulp": "~3.8.9",
|
||||
"gulp-browserify": "^0.5.0",
|
||||
"gulp-concat": "~2.4.1",
|
||||
"gulp-ext-replace": "~0.1.0",
|
||||
"gulp-istanbul": "^0.4.0",
|
||||
"gulp-jasmine": "~1.0.1",
|
||||
"gulp-jison": "~1.0.0",
|
||||
"gulp-rename": "~1.2.0",
|
||||
"gulp-shell": "^0.2.10",
|
||||
"gulp-uglify": "~1.0.1",
|
||||
"he": "^0.5.0",
|
||||
"jasmine": "~2.0.1",
|
||||
"jison": "~0.4.15",
|
||||
"karma": "~0.12.20",
|
||||
"karma-chrome-launcher": "~0.1.5",
|
||||
"karma-jasmine": "~0.2.1",
|
||||
"karma-requirejs": "~0.2.2",
|
||||
"lodash": "^2.4.1",
|
||||
"lodash._escapestringchar": "^2.4.1",
|
||||
"lodash._objecttypes": "^2.4.1",
|
||||
"lodash._reinterpolate": "^2.4.1",
|
||||
"lodash._reunescapedhtml": "^2.4.1",
|
||||
"lodash.defaults": "^2.4.1",
|
||||
"lodash.templatesettings": "^2.4.1",
|
||||
"lodash.values": "^2.4.1",
|
||||
"mock-browser": "^0.90.27",
|
||||
"rewire": "^2.1.3"
|
||||
}
|
||||
"async": "^2.5.0",
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-loader": "^7.1.2",
|
||||
"babel-plugin-transform-remove-strict-mode": "^0.0.2",
|
||||
"babel-preset-env": "^1.6.0",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"clone": "^2.1.1",
|
||||
"codeclimate-test-reporter": "^0.5.0",
|
||||
"css-loader": "^0.28.7",
|
||||
"dox": "^0.9.0",
|
||||
"event-stream": "^3.3.4",
|
||||
"extract-text-webpack-plugin": "^3.0.0",
|
||||
"front-matter": "^2.2.0",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-bower": "^0.0.13",
|
||||
"gulp-bump": "^2.7.0",
|
||||
"gulp-concat": "^2.6.1",
|
||||
"gulp-data": "^1.2.1",
|
||||
"gulp-dox": "^0.1.6",
|
||||
"gulp-ext-replace": "^0.3.0",
|
||||
"gulp-filelog": "^0.4.1",
|
||||
"gulp-front-matter": "^1.3.0",
|
||||
"gulp-hogan": "^2.0.0",
|
||||
"gulp-if": "^2.0.2",
|
||||
"gulp-insert": "^0.5.0",
|
||||
"gulp-istanbul": "^1.1.2",
|
||||
"gulp-jasmine": "^2.4.2",
|
||||
"gulp-jasmine-browser": "^1.9.0",
|
||||
"gulp-jison": "^1.2.0",
|
||||
"gulp-less": "^3.3.2",
|
||||
"gulp-livereload": "^3.8.1",
|
||||
"gulp-marked": "^1.0.0",
|
||||
"gulp-mdvars": "^2.0.0",
|
||||
"gulp-qunit": "^1.5.0",
|
||||
"gulp-rename": "^1.2.2",
|
||||
"gulp-shell": "^0.6.3",
|
||||
"gulp-tag-version": "^1.3.0",
|
||||
"gulp-util": "^3.0.8",
|
||||
"gulp-vartree": "^2.0.1",
|
||||
"hogan.js": "^3.0.2",
|
||||
"inject-loader": "^3.0.1",
|
||||
"jasmine": "^2.8.0",
|
||||
"jasmine-es6": "^0.4.1",
|
||||
"jison": "^0.4.17",
|
||||
"jsdom": "^11.2.0",
|
||||
"karma": "^1.7.1",
|
||||
"karma-chrome-launcher": "^2.2.0",
|
||||
"karma-jasmine": "^1.1.0",
|
||||
"karma-webpack": "^2.0.4",
|
||||
"less": "^2.7.2",
|
||||
"less-loader": "^4.0.5",
|
||||
"live-server": "^1.2.0",
|
||||
"map-stream": "^0.0.7",
|
||||
"marked": "^0.3.6",
|
||||
"mock-browser": "^0.92.14",
|
||||
"phantomjs-prebuilt": "^2.1.15",
|
||||
"require-dir": "^0.3.2",
|
||||
"rimraf": "^2.6.1",
|
||||
"standard": "^10.0.3",
|
||||
"style-loader": "^0.18.2",
|
||||
"tape": "^4.8.0",
|
||||
"webpack": "^3.5.5",
|
||||
"webpack-node-externals": "^1.6.0",
|
||||
"yarn-upgrade-all": "^0.1.8"
|
||||
},
|
||||
"files": [
|
||||
"bin",
|
||||
"dist",
|
||||
"lib",
|
||||
"src"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
sed s/this.parseError\(errStr/console.log\(errStr/< src/parser/flow.js > src/parser/flowParser.js
|
||||
cp src/parser/flowParser.js src/parser/flow.js
|
||||
rm src/parser/flowParser.js
|
||||
@@ -1,3 +0,0 @@
|
||||
#sed s/this.parseError\(errStr/console.log\(errStr/< src/parser/flow.js > src/parser/flowParser.js
|
||||
#cp src/parser/flowParser.js src/parser/flow.js
|
||||
#rm src/parser/flowParser.js
|
||||
7
scripts/jison.sh
Normal file
7
scripts/jison.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
node node_modules/jison/lib/cli.js src/diagrams/classDiagram/parser/classDiagram.jison -o src/diagrams/classDiagram/parser/classDiagram.js
|
||||
node node_modules/jison/lib/cli.js src/diagrams/sequenceDiagram/parser/sequenceDiagram.jison -o src/diagrams/sequenceDiagram/parser/sequenceDiagram.js
|
||||
node node_modules/jison/lib/cli.js src/diagrams/example/parser/example.jison -o src/diagrams/example/parser/example.js
|
||||
node node_modules/jison/lib/cli.js src/diagrams/flowchart/parser/flow.jison -o src/diagrams/flowchart/parser/flow.js
|
||||
node node_modules/jison/lib/cli.js src/diagrams/flowchart/parser/dot.jison -o src/diagrams/flowchart/parser/dot.js
|
||||
node node_modules/jison/lib/cli.js src/diagrams/gantt/parser/gantt.jison -o src/diagrams/gantt/parser/gantt.js
|
||||
node node_modules/jison/lib/cli.js src/diagrams/gitGraph/parser/gitGraph.jison -o src/diagrams/gitGraph/parser/gitGraph.js
|
||||
447
src/d3.js
vendored
Normal file
447
src/d3.js
vendored
Normal file
@@ -0,0 +1,447 @@
|
||||
const d3 = require('d3')
|
||||
|
||||
module.exports = d3;
|
||||
|
||||
/*
|
||||
D3 Text Wrap
|
||||
By Vijith Assar
|
||||
http://www.vijithassar.com
|
||||
http://www.github.com/vijithassar
|
||||
@vijithassar
|
||||
|
||||
Detailed instructions at http://www.github.com/vijithassar/d3textwrap
|
||||
*/
|
||||
|
||||
(function () {
|
||||
// set this variable to a string value to always force a particular
|
||||
// wrap method for development purposes, for example to check tspan
|
||||
// rendering using a foreignobject-enabled browser. set to 'tspan' to
|
||||
// use tspans and 'foreignobject' to use foreignobject
|
||||
var forceWrapMethod = false // by default no wrap method is forced
|
||||
forceWrapMethod = 'tspans' // uncomment this statement to force tspans
|
||||
// force_wrap_method = 'foreignobjects'; // uncomment this statement to force foreignobjects
|
||||
|
||||
// exit immediately if something in this location
|
||||
// has already been defined; the plugin will defer to whatever
|
||||
// else you're doing in your code
|
||||
if (d3.selection.prototype.textwrap) {
|
||||
return false
|
||||
}
|
||||
|
||||
// double check the force_wrap_method flag
|
||||
// and reset if someone screwed up the above
|
||||
// settings
|
||||
if (typeof forceWrapMethod === 'undefined') {
|
||||
forceWrapMethod = false
|
||||
}
|
||||
|
||||
// create the plugin method twice, both for regular use
|
||||
// and again for use inside the enter() selection
|
||||
d3.selection.prototype.textwrap = d3.selection.enter.prototype.textwrap = function (bounds, padding) {
|
||||
// default value of padding is zero if it's undefined
|
||||
padding = parseInt(padding) || 0
|
||||
|
||||
// save callee into a variable so we can continue to refer to it
|
||||
// as the function scope changes
|
||||
var selection = this
|
||||
|
||||
// create a variable to store desired return values in
|
||||
var returnValue
|
||||
|
||||
// extract wrap boundaries from any d3-selected rect and return them
|
||||
// in a format that matches the simpler object argument option
|
||||
var extractBounds = function (bounds) {
|
||||
// discard the nested array wrappers added by d3
|
||||
var boundingRect = bounds[0][0]
|
||||
// sanitize the svg element name so we can test against it
|
||||
var elementType = boundingRect.tagName.toString()
|
||||
// if it's not a rect, exit
|
||||
if (elementType !== 'rect') {
|
||||
return false
|
||||
// if it's a rect, proceed to extracting the position attributes
|
||||
} else {
|
||||
var boundsExtracted = {}
|
||||
boundsExtracted.x = d3.select(boundingRect).attr('x') || 0
|
||||
boundsExtracted.y = d3.select(boundingRect).attr('y') || 0
|
||||
boundsExtracted.width = d3.select(boundingRect).attr('width') || 0
|
||||
boundsExtracted.height = d3.select(boundingRect).attr('height') || 0
|
||||
// also pass along the getter function
|
||||
boundsExtracted.attr = bounds.attr
|
||||
}
|
||||
return boundsExtracted
|
||||
}
|
||||
|
||||
// double check the input argument for the wrapping
|
||||
// boundaries to make sure it actually contains all
|
||||
// the information we'll need in order to wrap successfully
|
||||
var verifyBounds = function (bounds) {
|
||||
// quickly add a simple getter method so you can use either
|
||||
// bounds.x or bounds.attr('x') as your notation,
|
||||
// the latter being a common convention among D3
|
||||
// developers
|
||||
if (!bounds.attr) {
|
||||
bounds.attr = function (property) {
|
||||
if (this[property]) {
|
||||
return this[property]
|
||||
}
|
||||
}
|
||||
}
|
||||
// if it's an associative array, make sure it has all the
|
||||
// necessary properties represented directly
|
||||
if (
|
||||
(typeof bounds === 'object') &&
|
||||
(typeof bounds.x !== 'undefined') &&
|
||||
(typeof bounds.y !== 'undefined') &&
|
||||
(typeof bounds.width !== 'undefined') &&
|
||||
(typeof bounds.height !== 'undefined')
|
||||
// if that's the case, then the bounds are fine
|
||||
) {
|
||||
// return the lightly modified bounds
|
||||
return bounds
|
||||
// if it's a numerically indexed array, assume it's a
|
||||
// d3-selected rect and try to extract the positions
|
||||
} else if (
|
||||
// first try to make sure it's an array using Array.isArray
|
||||
(
|
||||
(typeof Array.isArray === 'function') &&
|
||||
(Array.isArray(bounds))
|
||||
) ||
|
||||
// but since Array.isArray isn't always supported, fall
|
||||
// back to casting to the object to string when it's not
|
||||
(Object.prototype.toString.call(bounds) === '[object Array]')
|
||||
) {
|
||||
// once you're sure it's an array, extract the boundaries
|
||||
// from the rect
|
||||
var extractedBounds = extractBounds(bounds)
|
||||
return extractedBounds
|
||||
} else {
|
||||
// but if the bounds are neither an object nor a numerical
|
||||
// array, then the bounds argument is invalid and you'll
|
||||
// need to fix it
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var applyPadding = function (bounds, padding) {
|
||||
var paddedBounds = bounds
|
||||
if (padding !== 0) {
|
||||
paddedBounds.x = parseInt(paddedBounds.x) + padding
|
||||
paddedBounds.y = parseInt(paddedBounds.y) + padding
|
||||
paddedBounds.width -= padding * 2
|
||||
paddedBounds.height -= padding * 2
|
||||
}
|
||||
return paddedBounds
|
||||
}
|
||||
|
||||
// verify bounds
|
||||
var verifiedBounds = verifyBounds(bounds)
|
||||
|
||||
// modify bounds if a padding value is provided
|
||||
if (padding) {
|
||||
verifiedBounds = applyPadding(verifiedBounds, padding)
|
||||
}
|
||||
|
||||
// check that we have the necessary conditions for this function to operate properly
|
||||
if (
|
||||
// selection it's operating on cannot be not empty
|
||||
(selection.length === 0) ||
|
||||
// d3 must be available
|
||||
(!d3) ||
|
||||
// desired wrapping bounds must be provided as an input argument
|
||||
(!bounds) ||
|
||||
// input bounds must validate
|
||||
(!verifiedBounds)
|
||||
) {
|
||||
// try to return the calling selection if possible
|
||||
// so as not to interfere with methods downstream in the
|
||||
// chain
|
||||
if (selection) {
|
||||
return selection
|
||||
// if all else fails, just return false. if you hit this point then you're
|
||||
// almost certainly trying to call the textwrap() method on something that
|
||||
// doesn't make sense!
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
// if we've validated everything then we can finally proceed
|
||||
// to the meat of this operation
|
||||
} else {
|
||||
// reassign the verified bounds as the set we want
|
||||
// to work with from here on; this ensures that we're
|
||||
// using the same data structure for our bounds regardless
|
||||
// of whether the input argument was a simple object or
|
||||
// a d3 selection
|
||||
bounds = verifiedBounds
|
||||
|
||||
// wrap using html and foreignObjects if they are supported
|
||||
var wrapWithForeignobjects = function (item) {
|
||||
// establish variables to quickly reference target nodes later
|
||||
var parent = d3.select(item[0].parentNode)
|
||||
var textNode = parent.select('text')
|
||||
var styledLineHeight = textNode.style('line-height')
|
||||
// extract our desired content from the single text element
|
||||
var textToWrap = textNode.text()
|
||||
// remove the text node and replace with a foreign object
|
||||
textNode.remove()
|
||||
var foreignObject = parent.append('foreignObject')
|
||||
// add foreign object and set dimensions, position, etc
|
||||
foreignObject
|
||||
.attr('requiredFeatures', 'http://www.w3.org/TR/SVG11/feature#Extensibility')
|
||||
.attr('x', bounds.x)
|
||||
.attr('y', bounds.y)
|
||||
.attr('width', bounds.width)
|
||||
.attr('height', bounds.height)
|
||||
// insert an HTML div
|
||||
var wrapDiv = foreignObject
|
||||
.append('xhtml:div')
|
||||
// this class is currently hardcoded
|
||||
// probably not necessary but easy to
|
||||
// override using .classed() and for now
|
||||
// it's nice to avoid a litany of input
|
||||
// arguments
|
||||
.attr('class', 'wrapped')
|
||||
// set div to same dimensions as foreign object
|
||||
wrapDiv
|
||||
.style('height', bounds.height)
|
||||
.style('width', bounds.width)
|
||||
// insert text content
|
||||
.html(textToWrap)
|
||||
if (styledLineHeight) {
|
||||
wrapDiv.style('line-height', styledLineHeight)
|
||||
}
|
||||
returnValue = parent.select('foreignObject')
|
||||
}
|
||||
|
||||
// wrap with tspans if foreignObject is undefined
|
||||
var wrapWithTspans = function (item) {
|
||||
// operate on the first text item in the selection
|
||||
var textNode = item[0]
|
||||
var parent = textNode.parentNode
|
||||
var textNodeSelected = d3.select(textNode)
|
||||
// measure initial size of the text node as rendered
|
||||
var textNodeHeight = textNode.getBBox().height
|
||||
var textNodeWidth = textNode.getBBox().width
|
||||
// figure out the line height, either from rendered height
|
||||
// of the font or attached styling
|
||||
var lineHeight
|
||||
var renderedLineHeight = textNodeHeight
|
||||
var styledLineHeight = textNodeSelected.style('line-height')
|
||||
if (
|
||||
(styledLineHeight) &&
|
||||
(parseInt(styledLineHeight))
|
||||
) {
|
||||
lineHeight = parseInt(styledLineHeight.replace('px', ''))
|
||||
} else {
|
||||
lineHeight = renderedLineHeight
|
||||
}
|
||||
// only fire the rest of this if the text content
|
||||
// overflows the desired dimensions
|
||||
if (textNodeWidth > bounds.width) {
|
||||
// store whatever is inside the text node
|
||||
// in a variable and then zero out the
|
||||
// initial content; we'll reinsert in a moment
|
||||
// using tspan elements.
|
||||
var textToWrap = textNodeSelected.text()
|
||||
textNodeSelected.text('')
|
||||
if (textToWrap) {
|
||||
// keep track of whether we are splitting by spaces
|
||||
// so we know whether to reinsert those spaces later
|
||||
var breakDelimiter
|
||||
// split at spaces to create an array of individual words
|
||||
var textToWrapArray
|
||||
if (textToWrap.indexOf(' ') !== -1) {
|
||||
breakDelimiter = ' '
|
||||
textToWrapArray = textToWrap.split(' ')
|
||||
} else {
|
||||
// if there are no spaces, figure out the split
|
||||
// points by comparing rendered text width against
|
||||
// bounds and translating that into character position
|
||||
// cuts
|
||||
breakDelimiter = ''
|
||||
var stringLength = textToWrap.length
|
||||
var numberOfSubstrings = Math.ceil(textNodeWidth / bounds.width)
|
||||
var spliceInterval = Math.floor(stringLength / numberOfSubstrings)
|
||||
if (
|
||||
!(spliceInterval * numberOfSubstrings >= stringLength)
|
||||
) {
|
||||
numberOfSubstrings++
|
||||
}
|
||||
textToWrapArray = []
|
||||
var substring
|
||||
var startPosition
|
||||
for (var i = 0; i < numberOfSubstrings; i++) {
|
||||
startPosition = i * spliceInterval
|
||||
substring = textToWrap.substr(startPosition, spliceInterval)
|
||||
textToWrapArray.push(substring)
|
||||
}
|
||||
}
|
||||
|
||||
// new array where we'll store the words re-assembled into
|
||||
// substrings that have been tested against the desired
|
||||
// maximum wrapping width
|
||||
var substrings = []
|
||||
// computed text length is arguably incorrectly reported for
|
||||
// all tspans after the first one, in that they will include
|
||||
// the width of previous separate tspans. to compensate we need
|
||||
// to manually track the computed text length of all those
|
||||
// previous tspans and substrings, and then use that to offset
|
||||
// the miscalculation. this then gives us the actual correct
|
||||
// position we want to use in rendering the text in the SVG.
|
||||
var totalOffset = 0
|
||||
// object for storing the results of text length computations later
|
||||
var temp = {}
|
||||
// loop through the words and test the computed text length
|
||||
// of the string against the maximum desired wrapping width
|
||||
for (i = 0; i < textToWrapArray.length; i++) {
|
||||
var word = textToWrapArray[i]
|
||||
var previousString = textNodeSelected.text()
|
||||
var previousWidth = textNode.getComputedTextLength()
|
||||
// initialize the current word as the first word
|
||||
// or append to the previous string if one exists
|
||||
var newstring
|
||||
if (previousString) {
|
||||
newstring = previousString + breakDelimiter + word
|
||||
} else {
|
||||
newstring = word
|
||||
}
|
||||
// add the newest substring back to the text node and
|
||||
// measure the length
|
||||
textNodeSelected.text(newstring)
|
||||
var newWidth = textNode.getComputedTextLength()
|
||||
// adjust the length by the offset we've tracked
|
||||
// due to the misreported length discussed above
|
||||
|
||||
// if our latest version of the string is too
|
||||
// big for the bounds, use the previous
|
||||
// version of the string (without the newest word
|
||||
// added) and use the latest word to restart the
|
||||
// process with a new tspan
|
||||
if (newWidth > bounds.width) {
|
||||
if (
|
||||
(previousString) &&
|
||||
(previousString !== '')
|
||||
) {
|
||||
totalOffset = totalOffset + previousWidth
|
||||
temp = { string: previousString, width: previousWidth, offset: totalOffset }
|
||||
substrings.push(temp)
|
||||
textNodeSelected.text('')
|
||||
textNodeSelected.text(word)
|
||||
// Handle case where there is just one more word to be wrapped
|
||||
if (i === textToWrapArray.length - 1) {
|
||||
newstring = word
|
||||
textNodeSelected.text(newstring)
|
||||
newWidth = textNode.getComputedTextLength()
|
||||
}
|
||||
}
|
||||
}
|
||||
// if we're up to the last word in the array,
|
||||
// get the computed length as is without
|
||||
// appending anything further to it
|
||||
if (i === textToWrapArray.length - 1) {
|
||||
textNodeSelected.text('')
|
||||
var finalString = newstring
|
||||
if (
|
||||
(finalString) &&
|
||||
(finalString !== '')
|
||||
) {
|
||||
if ((newWidth - totalOffset) > 0) { newWidth = newWidth - totalOffset }
|
||||
temp = { string: finalString, width: newWidth, offset: totalOffset }
|
||||
substrings.push(temp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// append each substring as a tspan
|
||||
var currentTspan
|
||||
// var tspanCount
|
||||
// double check that the text content has been removed
|
||||
// before we start appending tspans
|
||||
textNodeSelected.text('')
|
||||
for (i = 0; i < substrings.length; i++) {
|
||||
substring = substrings[i].string
|
||||
// only append if we're sure it won't make the tspans
|
||||
// overflow the bounds.
|
||||
if ((i) * lineHeight < bounds.height - (lineHeight * 1.5)) {
|
||||
currentTspan = textNodeSelected.append('tspan')
|
||||
.text(substring)
|
||||
// vertical shift to all tspans after the first one
|
||||
currentTspan
|
||||
.attr('dy', function (d) {
|
||||
if (i > 0) {
|
||||
return lineHeight
|
||||
}
|
||||
})
|
||||
// shift left from default position, which
|
||||
// is probably based on the full length of the
|
||||
// text string until we make this adjustment
|
||||
currentTspan
|
||||
.attr('x', function () {
|
||||
var xOffset = bounds.x
|
||||
if (padding) { xOffset += padding }
|
||||
return xOffset
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// position the overall text node, whether wrapped or not
|
||||
textNodeSelected.attr('y', function () {
|
||||
var yOffset = bounds.y
|
||||
// shift by line-height to move the baseline into
|
||||
// the bounds – otherwise the text baseline would be
|
||||
// at the top of the bounds
|
||||
if (lineHeight) { yOffset += lineHeight }
|
||||
// shift by padding, if it's there
|
||||
if (padding) { yOffset += padding }
|
||||
return yOffset
|
||||
})
|
||||
// shift to the right by the padding value
|
||||
textNodeSelected.attr('x', function () {
|
||||
var xOffset = bounds.x
|
||||
if (padding) { xOffset += padding }
|
||||
return xOffset
|
||||
})
|
||||
|
||||
// assign our modified text node with tspans
|
||||
// to the return value
|
||||
returnValue = d3.select(parent).selectAll('text')
|
||||
}
|
||||
|
||||
// variable used to hold the functions that let us
|
||||
// switch between the wrap methods
|
||||
var wrapMethod
|
||||
|
||||
// if a wrap method if being forced, assign that
|
||||
// function
|
||||
if (forceWrapMethod) {
|
||||
if (forceWrapMethod === 'foreignobjects') {
|
||||
wrapMethod = wrapWithForeignobjects
|
||||
} else if (forceWrapMethod === 'tspans') {
|
||||
wrapMethod = wrapWithTspans
|
||||
}
|
||||
}
|
||||
|
||||
// if no wrap method is being forced, then instead
|
||||
// test for browser support of foreignobject and
|
||||
// use whichever wrap method makes sense accordingly
|
||||
if (!forceWrapMethod) {
|
||||
if (typeof SVGForeignObjectElement !== 'undefined') {
|
||||
wrapMethod = wrapWithForeignobjects
|
||||
} else {
|
||||
wrapMethod = wrapWithTspans
|
||||
}
|
||||
}
|
||||
|
||||
// run the desired wrap function for each item
|
||||
// in the d3 selection that called .textwrap()
|
||||
for (var i = 0; i < selection.length; i++) {
|
||||
var item = selection[i]
|
||||
wrapMethod(item)
|
||||
}
|
||||
|
||||
// return the modified nodes so we can chain other
|
||||
// methods to them.
|
||||
return returnValue
|
||||
}
|
||||
}
|
||||
})()
|
||||
80
src/diagrams/classDiagram/classDb.js
Normal file
80
src/diagrams/classDiagram/classDb.js
Normal file
@@ -0,0 +1,80 @@
|
||||
|
||||
var Logger = require('../../logger')
|
||||
var log = Logger.Log
|
||||
var relations = []
|
||||
|
||||
var classes
|
||||
classes = {
|
||||
}
|
||||
|
||||
/**
|
||||
* Function called by parser when a node definition has been found.
|
||||
* @param id
|
||||
* @param text
|
||||
* @param type
|
||||
* @param style
|
||||
*/
|
||||
exports.addClass = function (id) {
|
||||
if (typeof classes[id] === 'undefined') {
|
||||
classes[id] = {
|
||||
id: id,
|
||||
methods: [],
|
||||
members: []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exports.clear = function () {
|
||||
relations = []
|
||||
classes = {}
|
||||
}
|
||||
|
||||
module.exports.getClass = function (id) {
|
||||
return classes[id]
|
||||
}
|
||||
module.exports.getClasses = function () {
|
||||
return classes
|
||||
}
|
||||
|
||||
module.exports.getRelations = function () {
|
||||
return relations
|
||||
}
|
||||
|
||||
exports.addRelation = function (relation) {
|
||||
log.warn('Adding relation: ' + JSON.stringify(relation))
|
||||
exports.addClass(relation.id1)
|
||||
exports.addClass(relation.id2)
|
||||
|
||||
relations.push(relation)
|
||||
}
|
||||
|
||||
exports.addMembers = function (className, MembersArr) {
|
||||
var theClass = classes[className]
|
||||
if (typeof MembersArr === 'string') {
|
||||
if (MembersArr.substr(-1) === ')') {
|
||||
theClass.methods.push(MembersArr)
|
||||
} else {
|
||||
theClass.members.push(MembersArr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exports.cleanupLabel = function (label) {
|
||||
if (label.substring(0, 1) === ':') {
|
||||
return label.substr(2).trim()
|
||||
} else {
|
||||
return label.trim()
|
||||
}
|
||||
}
|
||||
|
||||
exports.lineType = {
|
||||
LINE: 0,
|
||||
DOTTED_LINE: 1
|
||||
}
|
||||
|
||||
exports.relationType = {
|
||||
AGGREGATION: 0,
|
||||
EXTENSION: 1,
|
||||
COMPOSITION: 2,
|
||||
DEPENDENCY: 3
|
||||
}
|
||||
214
src/diagrams/classDiagram/classDiagram.spec.js
Normal file
214
src/diagrams/classDiagram/classDiagram.spec.js
Normal file
@@ -0,0 +1,214 @@
|
||||
/* eslint-env jasmine */
|
||||
/**
|
||||
* Created by knut on 14-11-18.
|
||||
*/
|
||||
describe('class diagram, ', function () {
|
||||
describe('when parsing an info graph it', function () {
|
||||
var cd, cDDb
|
||||
beforeEach(function () {
|
||||
cd = require('./parser/classDiagram').parser
|
||||
cDDb = require('./classDb')
|
||||
cd.yy = cDDb
|
||||
})
|
||||
|
||||
it('should handle relation definitions', function () {
|
||||
var str = 'classDiagram\n' +
|
||||
'Class01 <|-- Class02\n' +
|
||||
'Class03 *-- Class04\n' +
|
||||
'Class05 o-- Class06\n' +
|
||||
'Class07 .. Class08\n' +
|
||||
'Class09 -- Class1'
|
||||
|
||||
cd.parse(str)
|
||||
})
|
||||
it('should handle relation definition of different types and directions', function () {
|
||||
var str = 'classDiagram\n' +
|
||||
'Class11 <|.. Class12\n' +
|
||||
'Class13 --> Class14\n' +
|
||||
'Class15 ..> Class16\n' +
|
||||
'Class17 ..|> Class18\n' +
|
||||
'Class19 <--* Class20'
|
||||
|
||||
cd.parse(str)
|
||||
})
|
||||
|
||||
it('should handle cardinality and labels', function () {
|
||||
var str = 'classDiagram\n' +
|
||||
'Class01 "1" *-- "many" Class02 : contains\n' +
|
||||
'Class03 o-- Class04 : aggregation\n' +
|
||||
'Class05 --> "1" Class06'
|
||||
|
||||
cd.parse(str)
|
||||
})
|
||||
it('should handle class definitions', function () {
|
||||
var str = 'classDiagram\n' +
|
||||
'class Car\n' +
|
||||
'Driver -- Car : drives >\n' +
|
||||
'Car *-- Wheel : have 4 >\n' +
|
||||
'Car -- Person : < owns'
|
||||
|
||||
cd.parse(str)
|
||||
})
|
||||
|
||||
it('should handle method statements', function () {
|
||||
var str = 'classDiagram\n' +
|
||||
'Object <|-- ArrayList\n' +
|
||||
'Object : equals()\n' +
|
||||
'ArrayList : Object[] elementData\n' +
|
||||
'ArrayList : size()'
|
||||
|
||||
cd.parse(str)
|
||||
})
|
||||
it('should handle parsing of method statements grouped by brackets', function () {
|
||||
var str = 'classDiagram\n' +
|
||||
'class Dummy {\n' +
|
||||
'String data\n' +
|
||||
' void methods()\n' +
|
||||
'}\n' +
|
||||
'\n' +
|
||||
'class Flight {\n' +
|
||||
' flightNumber : Integer\n' +
|
||||
' departureTime : Date\n' +
|
||||
'}'
|
||||
|
||||
cd.parse(str)
|
||||
})
|
||||
|
||||
it('should handle parsing of separators', function () {
|
||||
var str = 'classDiagram\n' +
|
||||
'class Foo1 {\n' +
|
||||
' You can use\n' +
|
||||
' several lines\n' +
|
||||
'..\n' +
|
||||
'as you want\n' +
|
||||
'and group\n' +
|
||||
'==\n' +
|
||||
'things together.\n' +
|
||||
'__\n' +
|
||||
'You can have as many groups\n' +
|
||||
'as you want\n' +
|
||||
'--\n' +
|
||||
'End of class\n' +
|
||||
'}\n' +
|
||||
'\n' +
|
||||
'class User {\n' +
|
||||
'.. Simple Getter ..\n' +
|
||||
'+ getName()\n' +
|
||||
'+ getAddress()\n' +
|
||||
'.. Some setter ..\n' +
|
||||
'+ setName()\n' +
|
||||
'__ private data __\n' +
|
||||
'int age\n' +
|
||||
'-- encrypted --\n' +
|
||||
'String password\n' +
|
||||
'}'
|
||||
|
||||
cd.parse(str)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when fetching data from an classDiagram graph it', function () {
|
||||
var cd, cDDb
|
||||
beforeEach(function () {
|
||||
cd = require('./parser/classDiagram').parser
|
||||
cDDb = require('./classDb')
|
||||
cd.yy = cDDb
|
||||
cd.yy.clear()
|
||||
})
|
||||
it('should handle relation definitions EXTENSION', function () {
|
||||
var str = 'classDiagram\n' +
|
||||
'Class01 <|-- Class02'
|
||||
|
||||
cd.parse(str)
|
||||
|
||||
var relations = cd.yy.getRelations()
|
||||
|
||||
expect(cd.yy.getClass('Class01').id).toBe('Class01')
|
||||
expect(cd.yy.getClass('Class02').id).toBe('Class02')
|
||||
expect(relations[0].relation.type1).toBe(cDDb.relationType.EXTENSION)
|
||||
expect(relations[0].relation.type2).toBe('none')
|
||||
expect(relations[0].relation.lineType).toBe(cDDb.lineType.LINE)
|
||||
})
|
||||
it('should handle relation definitions AGGREGATION and dotted line', function () {
|
||||
var str = 'classDiagram\n' +
|
||||
'Class01 o.. Class02'
|
||||
|
||||
cd.parse(str)
|
||||
|
||||
var relations = cd.yy.getRelations()
|
||||
|
||||
expect(cd.yy.getClass('Class01').id).toBe('Class01')
|
||||
expect(cd.yy.getClass('Class02').id).toBe('Class02')
|
||||
expect(relations[0].relation.type1).toBe(cDDb.relationType.AGGREGATION)
|
||||
expect(relations[0].relation.type2).toBe('none')
|
||||
expect(relations[0].relation.lineType).toBe(cDDb.lineType.DOTTED_LINE)
|
||||
})
|
||||
it('should handle relation definitions COMPOSITION on both sides', function () {
|
||||
var str = 'classDiagram\n' +
|
||||
'Class01 *--* Class02'
|
||||
|
||||
cd.parse(str)
|
||||
|
||||
var relations = cd.yy.getRelations()
|
||||
|
||||
expect(cd.yy.getClass('Class01').id).toBe('Class01')
|
||||
expect(cd.yy.getClass('Class02').id).toBe('Class02')
|
||||
expect(relations[0].relation.type1).toBe(cDDb.relationType.COMPOSITION)
|
||||
expect(relations[0].relation.type2).toBe(cDDb.relationType.COMPOSITION)
|
||||
expect(relations[0].relation.lineType).toBe(cDDb.lineType.LINE)
|
||||
})
|
||||
it('should handle relation definitions no types', function () {
|
||||
var str = 'classDiagram\n' +
|
||||
'Class01 -- Class02'
|
||||
|
||||
cd.parse(str)
|
||||
|
||||
var relations = cd.yy.getRelations()
|
||||
|
||||
expect(cd.yy.getClass('Class01').id).toBe('Class01')
|
||||
expect(cd.yy.getClass('Class02').id).toBe('Class02')
|
||||
expect(relations[0].relation.type1).toBe('none')
|
||||
expect(relations[0].relation.type2).toBe('none')
|
||||
expect(relations[0].relation.lineType).toBe(cDDb.lineType.LINE)
|
||||
})
|
||||
it('should handle relation definitions with type only on right side', function () {
|
||||
var str = 'classDiagram\n' +
|
||||
'Class01 --|> Class02'
|
||||
|
||||
cd.parse(str)
|
||||
|
||||
var relations = cd.yy.getRelations()
|
||||
|
||||
expect(cd.yy.getClass('Class01').id).toBe('Class01')
|
||||
expect(cd.yy.getClass('Class02').id).toBe('Class02')
|
||||
expect(relations[0].relation.type1).toBe('none')
|
||||
expect(relations[0].relation.type2).toBe(cDDb.relationType.EXTENSION)
|
||||
expect(relations[0].relation.lineType).toBe(cDDb.lineType.LINE)
|
||||
})
|
||||
|
||||
it('should handle multiple classes and relation definitions', function () {
|
||||
var str = 'classDiagram\n' +
|
||||
'Class01 <|-- Class02\n' +
|
||||
'Class03 *-- Class04\n' +
|
||||
'Class05 o-- Class06\n' +
|
||||
'Class07 .. Class08\n' +
|
||||
'Class09 -- Class10'
|
||||
|
||||
cd.parse(str)
|
||||
|
||||
var relations = cd.yy.getRelations()
|
||||
|
||||
expect(cd.yy.getClass('Class01').id).toBe('Class01')
|
||||
expect(cd.yy.getClass('Class10').id).toBe('Class10')
|
||||
|
||||
expect(relations.length).toBe(5)
|
||||
|
||||
expect(relations[0].relation.type1).toBe(cDDb.relationType.EXTENSION)
|
||||
expect(relations[0].relation.type2).toBe('none')
|
||||
expect(relations[0].relation.lineType).toBe(cDDb.lineType.LINE)
|
||||
expect(relations[3].relation.type1).toBe('none')
|
||||
expect(relations[3].relation.type2).toBe('none')
|
||||
expect(relations[3].relation.lineType).toBe(cDDb.lineType.DOTTED_LINE)
|
||||
})
|
||||
})
|
||||
})
|
||||
366
src/diagrams/classDiagram/classRenderer.js
Normal file
366
src/diagrams/classDiagram/classRenderer.js
Normal file
@@ -0,0 +1,366 @@
|
||||
/**
|
||||
* Created by knut on 14-11-23.
|
||||
*/
|
||||
|
||||
var cd = require('./parser/classDiagram').parser
|
||||
var cDDb = require('./classDb')
|
||||
cd.yy = cDDb
|
||||
var d3 = require('../../d3')
|
||||
var Logger = require('../../logger')
|
||||
var log = Logger.Log
|
||||
var dagre = require('dagre')
|
||||
|
||||
var idCache
|
||||
idCache = {}
|
||||
|
||||
var classCnt = 0
|
||||
var conf = {
|
||||
dividerMargin: 10,
|
||||
padding: 5,
|
||||
textHeight: 10
|
||||
}
|
||||
|
||||
// Todo optimize
|
||||
var getGraphId = function (label) {
|
||||
var keys = Object.keys(idCache)
|
||||
|
||||
var i
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
if (idCache[keys[i]].label === label) {
|
||||
return keys[i]
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup arrow head and define the marker. The result is appended to the svg.
|
||||
*/
|
||||
var insertMarkers = function (elem) {
|
||||
elem.append('defs').append('marker')
|
||||
.attr('id', 'extensionStart')
|
||||
.attr('class', 'extension')
|
||||
.attr('refX', 0)
|
||||
.attr('refY', 7)
|
||||
.attr('markerWidth', 190)
|
||||
.attr('markerHeight', 240)
|
||||
.attr('orient', 'auto')
|
||||
.append('path')
|
||||
.attr('d', 'M 1,7 L18,13 V 1 Z')
|
||||
|
||||
elem.append('defs').append('marker')
|
||||
.attr('id', 'extensionEnd')
|
||||
.attr('refX', 19)
|
||||
.attr('refY', 7)
|
||||
.attr('markerWidth', 20)
|
||||
.attr('markerHeight', 28)
|
||||
.attr('orient', 'auto')
|
||||
.append('path')
|
||||
.attr('d', 'M 1,1 V 13 L18,7 Z') // this is actual shape for arrowhead
|
||||
|
||||
elem.append('defs').append('marker')
|
||||
.attr('id', 'compositionStart')
|
||||
.attr('class', 'extension')
|
||||
.attr('refX', 0)
|
||||
.attr('refY', 7)
|
||||
.attr('markerWidth', 190)
|
||||
.attr('markerHeight', 240)
|
||||
.attr('orient', 'auto')
|
||||
.append('path')
|
||||
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z')
|
||||
|
||||
elem.append('defs').append('marker')
|
||||
.attr('id', 'compositionEnd')
|
||||
.attr('refX', 19)
|
||||
.attr('refY', 7)
|
||||
.attr('markerWidth', 20)
|
||||
.attr('markerHeight', 28)
|
||||
.attr('orient', 'auto')
|
||||
.append('path')
|
||||
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z')
|
||||
|
||||
elem.append('defs').append('marker')
|
||||
.attr('id', 'aggregationStart')
|
||||
.attr('class', 'extension')
|
||||
.attr('refX', 0)
|
||||
.attr('refY', 7)
|
||||
.attr('markerWidth', 190)
|
||||
.attr('markerHeight', 240)
|
||||
.attr('orient', 'auto')
|
||||
.append('path')
|
||||
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z')
|
||||
|
||||
elem.append('defs').append('marker')
|
||||
.attr('id', 'aggregationEnd')
|
||||
.attr('refX', 19)
|
||||
.attr('refY', 7)
|
||||
.attr('markerWidth', 20)
|
||||
.attr('markerHeight', 28)
|
||||
.attr('orient', 'auto')
|
||||
.append('path')
|
||||
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z')
|
||||
|
||||
elem.append('defs').append('marker')
|
||||
.attr('id', 'dependencyStart')
|
||||
.attr('class', 'extension')
|
||||
.attr('refX', 0)
|
||||
.attr('refY', 7)
|
||||
.attr('markerWidth', 190)
|
||||
.attr('markerHeight', 240)
|
||||
.attr('orient', 'auto')
|
||||
.append('path')
|
||||
.attr('d', 'M 5,7 L9,13 L1,7 L9,1 Z')
|
||||
|
||||
elem.append('defs').append('marker')
|
||||
.attr('id', 'dependencyEnd')
|
||||
.attr('refX', 19)
|
||||
.attr('refY', 7)
|
||||
.attr('markerWidth', 20)
|
||||
.attr('markerHeight', 28)
|
||||
.attr('orient', 'auto')
|
||||
.append('path')
|
||||
.attr('d', 'M 18,7 L9,13 L14,7 L9,1 Z')
|
||||
}
|
||||
|
||||
var edgeCount = 0
|
||||
var drawEdge = function (elem, path, relation) {
|
||||
var getRelationType = function (type) {
|
||||
switch (type) {
|
||||
case cDDb.relationType.AGGREGATION:
|
||||
return 'aggregation'
|
||||
case cDDb.relationType.EXTENSION:
|
||||
return 'extension'
|
||||
case cDDb.relationType.COMPOSITION:
|
||||
return 'composition'
|
||||
case cDDb.relationType.DEPENDENCY:
|
||||
return 'dependency'
|
||||
}
|
||||
}
|
||||
|
||||
// The data for our line
|
||||
var lineData = path.points
|
||||
|
||||
// This is the accessor function we talked about above
|
||||
var lineFunction = d3.svg.line()
|
||||
.x(function (d) {
|
||||
return d.x
|
||||
})
|
||||
.y(function (d) {
|
||||
return d.y
|
||||
})
|
||||
.interpolate('basis')
|
||||
|
||||
var svgPath = elem.append('path')
|
||||
.attr('d', lineFunction(lineData))
|
||||
.attr('id', 'edge' + edgeCount)
|
||||
.attr('class', 'relation')
|
||||
var url = ''
|
||||
if (conf.arrowMarkerAbsolute) {
|
||||
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' + ')')
|
||||
}
|
||||
if (relation.relation.type2 !== 'none') {
|
||||
svgPath.attr('marker-end', 'url(' + url + '#' + getRelationType(relation.relation.type2) + 'End' + ')')
|
||||
}
|
||||
|
||||
var x, y
|
||||
var l = path.points.length
|
||||
if ((l % 2) !== 0) {
|
||||
var p1 = path.points[Math.floor(l / 2)]
|
||||
var p2 = path.points[Math.ceil(l / 2)]
|
||||
x = (p1.x + p2.x) / 2
|
||||
y = (p1.y + p2.y) / 2
|
||||
} else {
|
||||
var p = path.points[Math.floor(l / 2)]
|
||||
x = p.x
|
||||
y = p.y
|
||||
}
|
||||
|
||||
if (typeof relation.title !== 'undefined') {
|
||||
var g = elem.append('g')
|
||||
.attr('class', 'classLabel')
|
||||
var label = g.append('text')
|
||||
.attr('class', 'label')
|
||||
.attr('x', x)
|
||||
.attr('y', y)
|
||||
.attr('fill', 'red')
|
||||
.attr('text-anchor', 'middle')
|
||||
.text(relation.title)
|
||||
|
||||
window.label = label
|
||||
var bounds = label.node().getBBox()
|
||||
|
||||
g.insert('rect', ':first-child')
|
||||
.attr('class', 'box')
|
||||
.attr('x', bounds.x - conf.padding / 2)
|
||||
.attr('y', bounds.y - conf.padding / 2)
|
||||
.attr('width', bounds.width + conf.padding)
|
||||
.attr('height', bounds.height + conf.padding)
|
||||
}
|
||||
|
||||
edgeCount++
|
||||
}
|
||||
|
||||
var drawClass = function (elem, classDef) {
|
||||
log.info('Rendering class ' + classDef)
|
||||
|
||||
var addTspan = function (textEl, txt, isFirst) {
|
||||
var tSpan = textEl.append('tspan')
|
||||
.attr('x', conf.padding)
|
||||
.text(txt)
|
||||
if (!isFirst) {
|
||||
tSpan.attr('dy', conf.textHeight)
|
||||
}
|
||||
}
|
||||
|
||||
var id = 'classId' + classCnt
|
||||
var classInfo = {
|
||||
id: id,
|
||||
label: classDef.id,
|
||||
width: 0,
|
||||
height: 0
|
||||
}
|
||||
|
||||
var g = elem.append('g')
|
||||
.attr('id', id)
|
||||
.attr('class', 'classGroup')
|
||||
var title = g.append('text')
|
||||
.attr('x', conf.padding)
|
||||
.attr('y', conf.textHeight + conf.padding)
|
||||
.text(classDef.id)
|
||||
|
||||
var titleHeight = title.node().getBBox().height
|
||||
|
||||
var 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)
|
||||
|
||||
var members = g.append('text') // text label for the x axis
|
||||
.attr('x', conf.padding)
|
||||
.attr('y', titleHeight + (conf.dividerMargin) + conf.textHeight)
|
||||
.attr('fill', 'white')
|
||||
.attr('class', 'classText')
|
||||
|
||||
var isFirst = true
|
||||
|
||||
classDef.members.forEach(function (member) {
|
||||
addTspan(members, member, isFirst)
|
||||
isFirst = false
|
||||
})
|
||||
|
||||
var membersBox = members.node().getBBox()
|
||||
|
||||
var 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)
|
||||
|
||||
var 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('fill', 'white')
|
||||
.attr('class', 'classText')
|
||||
|
||||
isFirst = true
|
||||
|
||||
classDef.methods.forEach(function (method) {
|
||||
addTspan(methods, method, isFirst)
|
||||
isFirst = false
|
||||
})
|
||||
|
||||
var classBox = g.node().getBBox()
|
||||
g.insert('rect', ':first-child')
|
||||
.attr('x', 0)
|
||||
.attr('y', 0)
|
||||
.attr('width', classBox.width + 2 * conf.padding)
|
||||
.attr('height', classBox.height + conf.padding + 0.5 * conf.dividerMargin)
|
||||
|
||||
membersLine.attr('x2', classBox.width + 2 * conf.padding)
|
||||
methodsLine.attr('x2', classBox.width + 2 * conf.padding)
|
||||
|
||||
classInfo.width = classBox.width + 2 * conf.padding
|
||||
classInfo.height = classBox.height + conf.padding + 0.5 * conf.dividerMargin
|
||||
|
||||
idCache[id] = classInfo
|
||||
classCnt++
|
||||
return classInfo
|
||||
}
|
||||
|
||||
module.exports.setConf = function (cnf) {
|
||||
var keys = Object.keys(cnf)
|
||||
|
||||
keys.forEach(function (key) {
|
||||
conf[key] = cnf[key]
|
||||
})
|
||||
}
|
||||
/**
|
||||
* Draws a flowchart in the tag with id: id based on the graph definition in text.
|
||||
* @param text
|
||||
* @param id
|
||||
*/
|
||||
module.exports.draw = function (text, id) {
|
||||
cd.yy.clear()
|
||||
cd.parse(text)
|
||||
|
||||
log.info('Rendering diagram ' + text)
|
||||
|
||||
/// / Fetch the default direction, use TD if none was found
|
||||
var diagram = d3.select('#' + id)
|
||||
insertMarkers(diagram)
|
||||
|
||||
// Layout graph, Create a new directed graph
|
||||
var g = new dagre.graphlib.Graph({
|
||||
multigraph: true
|
||||
})
|
||||
|
||||
// Set an object for the graph label
|
||||
g.setGraph({
|
||||
isMultiGraph: true
|
||||
})
|
||||
|
||||
// Default to assigning a new object as a label for each new edge.
|
||||
g.setDefaultEdgeLabel(function () {
|
||||
return {}
|
||||
})
|
||||
|
||||
var classes = cDDb.getClasses()
|
||||
var keys = Object.keys(classes)
|
||||
var i
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
var classDef = classes[keys[i]]
|
||||
var node = drawClass(diagram, classDef)
|
||||
// Add nodes to the graph. The first argument is the node id. The second is
|
||||
// metadata about the node. In this case we're going to add labels to each of
|
||||
// our nodes.
|
||||
g.setNode(node.id, node)
|
||||
log.info('Org height: ' + node.height)
|
||||
}
|
||||
|
||||
var relations = cDDb.getRelations()
|
||||
relations.forEach(function (relation) {
|
||||
log.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') {
|
||||
log.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)) + ' )')
|
||||
}
|
||||
})
|
||||
g.edges().forEach(function (e) {
|
||||
log.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))
|
||||
}
|
||||
28
src/diagrams/classDiagram/classRenderer.spec.js
Normal file
28
src/diagrams/classDiagram/classRenderer.spec.js
Normal file
@@ -0,0 +1,28 @@
|
||||
/* eslint-env jasmine */
|
||||
/**
|
||||
* Created by knut on 14-11-18.
|
||||
*/
|
||||
|
||||
describe('class diagram, ', function () {
|
||||
describe('when rendering a classDiagram', function () {
|
||||
beforeEach(function () {
|
||||
Object.defineProperties(window.HTMLElement.prototype, {
|
||||
getBBox: {
|
||||
get: function () { return { x: 10, y: 10, width: 100, height: 100 } }
|
||||
},
|
||||
offsetLeft: {
|
||||
get: function () { return parseFloat(window.getComputedStyle(this).marginLeft) || 0 }
|
||||
},
|
||||
offsetTop: {
|
||||
get: function () { return parseFloat(window.getComputedStyle(this).marginTop) || 0 }
|
||||
},
|
||||
offsetHeight: {
|
||||
get: function () { return parseFloat(window.getComputedStyle(this).height) || 0 }
|
||||
},
|
||||
offsetWidth: {
|
||||
get: function () { return parseFloat(window.getComputedStyle(this).width) || 0 }
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
196
src/diagrams/classDiagram/parser/classDiagram.jison
Normal file
196
src/diagrams/classDiagram/parser/classDiagram.jison
Normal file
@@ -0,0 +1,196 @@
|
||||
/** mermaid
|
||||
* https://mermaidjs.github.io/
|
||||
* (c) 2015 Knut Sveidqvist
|
||||
* MIT license.
|
||||
*/
|
||||
|
||||
/* lexical grammar */
|
||||
%lex
|
||||
%x string struct
|
||||
|
||||
%%
|
||||
\%\%[^\n]* /* do nothing */
|
||||
\n+ return 'NEWLINE';
|
||||
\s+ /* skip whitespace */
|
||||
"classDiagram" return 'CLASS_DIAGRAM';
|
||||
[\{] { this.begin("struct"); /*console.log('Starting struct');*/return 'STRUCT_START';}
|
||||
<struct>\} { /*console.log('Ending struct');*/this.popState(); return 'STRUCT_STOP';}}
|
||||
<struct>[\n] /* nothing */
|
||||
<struct>[^\{\}\n]* { /*console.log('lex-member: ' + yytext);*/ return "MEMBER";}
|
||||
|
||||
|
||||
|
||||
"class" return 'CLASS';
|
||||
["] this.begin("string");
|
||||
<string>["] this.popState();
|
||||
<string>[^"]* return "STR";
|
||||
|
||||
|
||||
\s*\<\| return 'EXTENSION';
|
||||
\s*\|\> return 'EXTENSION';
|
||||
\s*\> return 'DEPENDENCY';
|
||||
\s*\< return 'DEPENDENCY';
|
||||
\s*\* return 'COMPOSITION';
|
||||
\s*o return 'AGGREGATION';
|
||||
\-\- return 'LINE';
|
||||
\.\. return 'DOTTED_LINE';
|
||||
":"[^#\n;]+ return 'LABEL';
|
||||
\- return 'MINUS';
|
||||
"." return 'DOT';
|
||||
\+ return 'PLUS';
|
||||
\% return 'PCT';
|
||||
"=" return 'EQUALS';
|
||||
\= return 'EQUALS';
|
||||
[A-Za-z]+ return 'ALPHA';
|
||||
[!"#$%&'*+,-.`?\\_/] return 'PUNCTUATION';
|
||||
[0-9]+ return 'NUM';
|
||||
[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|
|
||||
[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|
|
||||
[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|
|
||||
[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|
|
||||
[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]|
|
||||
[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]|
|
||||
[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]|
|
||||
[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]|
|
||||
[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]|
|
||||
[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]|
|
||||
[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]|
|
||||
[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]|
|
||||
[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]|
|
||||
[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]|
|
||||
[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]|
|
||||
[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]|
|
||||
[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]|
|
||||
[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]|
|
||||
[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]|
|
||||
[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]|
|
||||
[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]|
|
||||
[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]|
|
||||
[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]|
|
||||
[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]|
|
||||
[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]|
|
||||
[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]|
|
||||
[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]|
|
||||
[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]|
|
||||
[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]|
|
||||
[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]|
|
||||
[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]|
|
||||
[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]|
|
||||
[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]|
|
||||
[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]|
|
||||
[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]|
|
||||
[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]|
|
||||
[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]|
|
||||
[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]|
|
||||
[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]|
|
||||
[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]|
|
||||
[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]|
|
||||
[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]|
|
||||
[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]|
|
||||
[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]|
|
||||
[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]|
|
||||
[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]|
|
||||
[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]|
|
||||
[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]|
|
||||
[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]|
|
||||
[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]|
|
||||
[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]|
|
||||
[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]|
|
||||
[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]|
|
||||
[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]|
|
||||
[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]|
|
||||
[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]|
|
||||
[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]|
|
||||
[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]|
|
||||
[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]|
|
||||
[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|
|
||||
[\uFFD2-\uFFD7\uFFDA-\uFFDC]
|
||||
return 'UNICODE_TEXT';
|
||||
\s return 'SPACE';
|
||||
<<EOF>> return 'EOF';
|
||||
|
||||
/lex
|
||||
|
||||
/* operator associations and precedence */
|
||||
|
||||
%left '^'
|
||||
|
||||
%start mermaidDoc
|
||||
|
||||
%% /* language grammar */
|
||||
|
||||
mermaidDoc: graphConfig;
|
||||
|
||||
graphConfig
|
||||
: CLASS_DIAGRAM NEWLINE statements EOF
|
||||
;
|
||||
|
||||
statements
|
||||
: statement
|
||||
| statement NEWLINE statements
|
||||
;
|
||||
|
||||
|
||||
className
|
||||
: alphaNumToken className { $$=$1+$2; }
|
||||
| alphaNumToken { $$=$1; }
|
||||
;
|
||||
|
||||
statement
|
||||
: relationStatement { yy.addRelation($1); }
|
||||
| relationStatement LABEL { $1.title = yy.cleanupLabel($2); yy.addRelation($1); }
|
||||
| classStatement
|
||||
| methodStatement
|
||||
;
|
||||
|
||||
classStatement
|
||||
: CLASS className
|
||||
| CLASS className STRUCT_START members STRUCT_STOP {/*console.log($2,JSON.stringify($4));*/yy.addMembers($2,$4);}
|
||||
;
|
||||
|
||||
members
|
||||
: MEMBER { $$ = [$1]; }
|
||||
| MEMBER members { $2.push($1);$$=$2;}
|
||||
;
|
||||
|
||||
methodStatement
|
||||
: className {/*console.log('Rel found',$1);*/}
|
||||
| className LABEL {yy.addMembers($1,yy.cleanupLabel($2));}
|
||||
| MEMBER {console.warn('Member',$1);}
|
||||
| SEPARATOR {/*console.log('sep found',$1);*/}
|
||||
;
|
||||
|
||||
relationStatement
|
||||
: className relation className { $$ = {'id1':$1,'id2':$3, relation:$2, relationTitle1:'none', relationTitle2:'none'}; }
|
||||
| className STR relation className { $$ = {id1:$1, id2:$4, relation:$3, relationTitle1:$2, relationTitle2:'none'}}
|
||||
| className relation STR className { $$ = {id1:$1, id2:$4, relation:$2, relationTitle1:'none', relationTitle2:$3}; }
|
||||
| className STR relation STR className { $$ = {id1:$1, id2:$5, relation:$3, relationTitle1:$2, relationTitle2:$4} }
|
||||
;
|
||||
|
||||
relation
|
||||
: relationType lineType relationType { $$={type1:$1,type2:$3,lineType:$2}; }
|
||||
| lineType relationType { $$={type1:'none',type2:$2,lineType:$1}; }
|
||||
| relationType lineType { $$={type1:$1,type2:'none',lineType:$2}; }
|
||||
| lineType { $$={type1:'none',type2:'none',lineType:$1}; }
|
||||
;
|
||||
|
||||
relationType
|
||||
: AGGREGATION { $$=yy.relationType.AGGREGATION;}
|
||||
| EXTENSION { $$=yy.relationType.EXTENSION;}
|
||||
| COMPOSITION { $$=yy.relationType.COMPOSITION;}
|
||||
| DEPENDENCY { $$=yy.relationType.DEPENDENCY;}
|
||||
;
|
||||
|
||||
lineType
|
||||
: LINE {$$=yy.lineType.LINE;}
|
||||
| DOTTED_LINE {$$=yy.lineType.DOTTED_LINE;}
|
||||
;
|
||||
|
||||
commentToken : textToken | graphCodeTokens ;
|
||||
|
||||
textToken : textNoTagsToken | TAGSTART | TAGEND | '==' | '--' | PCT | DEFAULT;
|
||||
|
||||
textNoTagsToken: alphaNumToken | SPACE | MINUS | keywords ;
|
||||
|
||||
alphaNumToken : UNICODE_TEXT | NUM | ALPHA;
|
||||
%%
|
||||
736
src/diagrams/classDiagram/parser/classDiagram.js
Normal file
736
src/diagrams/classDiagram/parser/classDiagram.js
Normal file
@@ -0,0 +1,736 @@
|
||||
/* parser generated by jison 0.4.17 */
|
||||
/*
|
||||
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 = [1, 11], $V1 = [1, 12], $V2 = [1, 13], $V3 = [1, 15], $V4 = [1, 16], $V5 = [1, 17], $V6 = [6, 8], $V7 = [1, 26], $V8 = [1, 27], $V9 = [1, 28], $Va = [1, 29], $Vb = [1, 30], $Vc = [1, 31], $Vd = [6, 8, 13, 17, 23, 26, 27, 28, 29, 30, 31], $Ve = [6, 8, 13, 17, 23, 26, 27, 28, 29, 30, 31, 45, 46, 47], $Vf = [23, 45, 46, 47], $Vg = [23, 30, 31, 45, 46, 47], $Vh = [23, 26, 27, 28, 29, 45, 46, 47], $Vi = [6, 8, 13], $Vj = [1, 46]
|
||||
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]],
|
||||
performAction: function anonymous (yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
|
||||
/* this == yyval */
|
||||
|
||||
var $0 = $$.length - 1
|
||||
switch (yystate) {
|
||||
case 5:
|
||||
this.$ = $$[$0 - 1] + $$[$0]
|
||||
break
|
||||
case 6:
|
||||
this.$ = $$[$0]
|
||||
break
|
||||
case 7:
|
||||
yy.addRelation($$[$0])
|
||||
break
|
||||
case 8:
|
||||
$$[$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])
|
||||
break
|
||||
case 13:
|
||||
this.$ = [$$[$0]]
|
||||
break
|
||||
case 14:
|
||||
$$[$0].push($$[$0 - 1]); this.$ = $$[$0]
|
||||
break
|
||||
case 15:
|
||||
/* console.log('Rel found',$$[$0]); */
|
||||
break
|
||||
case 16:
|
||||
yy.addMembers($$[$0 - 1], yy.cleanupLabel($$[$0]))
|
||||
break
|
||||
case 17:
|
||||
console.warn('Member', $$[$0])
|
||||
break
|
||||
case 18:
|
||||
/* console.log('sep found',$$[$0]); */
|
||||
break
|
||||
case 19:
|
||||
this.$ = {'id1': $$[$0 - 2], 'id2': $$[$0], relation: $$[$0 - 1], relationTitle1: 'none', relationTitle2: 'none'}
|
||||
break
|
||||
case 20:
|
||||
this.$ = {id1: $$[$0 - 3], id2: $$[$0], relation: $$[$0 - 1], relationTitle1: $$[$0 - 2], relationTitle2: 'none'}
|
||||
break
|
||||
case 21:
|
||||
this.$ = {id1: $$[$0 - 3], id2: $$[$0], relation: $$[$0 - 2], relationTitle1: 'none', relationTitle2: $$[$0 - 1]}
|
||||
break
|
||||
case 22:
|
||||
this.$ = {id1: $$[$0 - 4], id2: $$[$0], relation: $$[$0 - 2], relationTitle1: $$[$0 - 3], relationTitle2: $$[$0 - 1]}
|
||||
break
|
||||
case 23:
|
||||
this.$ = {type1: $$[$0 - 2], type2: $$[$0], lineType: $$[$0 - 1]}
|
||||
break
|
||||
case 24:
|
||||
this.$ = {type1: 'none', type2: $$[$0], lineType: $$[$0 - 1]}
|
||||
break
|
||||
case 25:
|
||||
this.$ = {type1: $$[$0 - 1], type2: 'none', lineType: $$[$0]}
|
||||
break
|
||||
case 26:
|
||||
this.$ = {type1: 'none', type2: 'none', lineType: $$[$0]}
|
||||
break
|
||||
case 27:
|
||||
this.$ = yy.relationType.AGGREGATION
|
||||
break
|
||||
case 28:
|
||||
this.$ = yy.relationType.EXTENSION
|
||||
break
|
||||
case 29:
|
||||
this.$ = yy.relationType.COMPOSITION
|
||||
break
|
||||
case 30:
|
||||
this.$ = yy.relationType.DEPENDENCY
|
||||
break
|
||||
case 31:
|
||||
this.$ = yy.lineType.LINE
|
||||
break
|
||||
case 32:
|
||||
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]},
|
||||
parseError: function parseError (str, hash) {
|
||||
if (hash.recoverable) {
|
||||
this.trace(str)
|
||||
} else {
|
||||
function _parseError (msg, hash) {
|
||||
this.message = msg
|
||||
this.hash = hash
|
||||
}
|
||||
_parseError.prototype = Error
|
||||
|
||||
throw new _parseError(str, hash)
|
||||
}
|
||||
},
|
||||
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
|
||||
}
|
||||
var lex = function () {
|
||||
var token
|
||||
token = lexer.lex() || EOF
|
||||
if (typeof token !== 'number') {
|
||||
token = self.symbols_[token] || token
|
||||
}
|
||||
return token
|
||||
}
|
||||
var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected
|
||||
while (true) {
|
||||
state = stack[stack.length - 1]
|
||||
if (this.defaultActions[state]) {
|
||||
action = this.defaultActions[state]
|
||||
} else {
|
||||
if (symbol === null || typeof symbol === 'undefined') {
|
||||
symbol = lex()
|
||||
}
|
||||
action = table[state] && table[state][symbol]
|
||||
}
|
||||
if (typeof action === 'undefined' || !action.length || !action[0]) {
|
||||
var errStr = ''
|
||||
expected = []
|
||||
for (p in table[state]) {
|
||||
if (this.terminals_[p] && p > TERROR) {
|
||||
expected.push('\'' + this.terminals_[p] + '\'')
|
||||
}
|
||||
}
|
||||
if (lexer.showPosition) {
|
||||
errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\''
|
||||
} else {
|
||||
errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'')
|
||||
}
|
||||
this.parseError(errStr, {
|
||||
text: lexer.match,
|
||||
token: this.terminals_[symbol] || symbol,
|
||||
line: lexer.yylineno,
|
||||
loc: yyloc,
|
||||
expected: expected
|
||||
})
|
||||
}
|
||||
if (action[0] instanceof Array && action.length > 1) {
|
||||
throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol)
|
||||
}
|
||||
switch (action[0]) {
|
||||
case 1:
|
||||
stack.push(symbol)
|
||||
vstack.push(lexer.yytext)
|
||||
lstack.push(lexer.yylloc)
|
||||
stack.push(action[1])
|
||||
symbol = null
|
||||
if (!preErrorSymbol) {
|
||||
yyleng = lexer.yyleng
|
||||
yytext = lexer.yytext
|
||||
yylineno = lexer.yylineno
|
||||
yyloc = lexer.yylloc
|
||||
if (recovering > 0) {
|
||||
recovering--
|
||||
}
|
||||
} else {
|
||||
symbol = preErrorSymbol
|
||||
preErrorSymbol = null
|
||||
}
|
||||
break
|
||||
case 2:
|
||||
len = this.productions_[action[1]][1]
|
||||
yyval.$ = vstack[vstack.length - len]
|
||||
yyval._$ = {
|
||||
first_line: lstack[lstack.length - (len || 1)].first_line,
|
||||
last_line: lstack[lstack.length - 1].last_line,
|
||||
first_column: lstack[lstack.length - (len || 1)].first_column,
|
||||
last_column: lstack[lstack.length - 1].last_column
|
||||
}
|
||||
if (ranges) {
|
||||
yyval._$.range = [
|
||||
lstack[lstack.length - (len || 1)].range[0],
|
||||
lstack[lstack.length - 1].range[1]
|
||||
]
|
||||
}
|
||||
r = this.performAction.apply(yyval, [
|
||||
yytext,
|
||||
yyleng,
|
||||
yylineno,
|
||||
sharedState.yy,
|
||||
action[1],
|
||||
vstack,
|
||||
lstack
|
||||
].concat(args))
|
||||
if (typeof r !== 'undefined') {
|
||||
return r
|
||||
}
|
||||
if (len) {
|
||||
stack = stack.slice(0, -1 * len * 2)
|
||||
vstack = vstack.slice(0, -1 * len)
|
||||
lstack = lstack.slice(0, -1 * len)
|
||||
}
|
||||
stack.push(this.productions_[action[1]][0])
|
||||
vstack.push(yyval.$)
|
||||
lstack.push(yyval._$)
|
||||
newState = table[stack[stack.length - 2]][stack[stack.length - 1]]
|
||||
stack.push(newState)
|
||||
break
|
||||
case 3:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
}}
|
||||
|
||||
/* generated by jison-lex 0.3.4 */
|
||||
var lexer = (function () {
|
||||
var lexer = ({
|
||||
|
||||
EOF: 1,
|
||||
|
||||
parseError: function parseError (str, hash) {
|
||||
if (this.yy.parser) {
|
||||
this.yy.parser.parseError(str, hash)
|
||||
} else {
|
||||
throw new Error(str)
|
||||
}
|
||||
},
|
||||
|
||||
// resets the lexer, sets new input
|
||||
setInput: function (input, yy) {
|
||||
this.yy = yy || this.yy || {}
|
||||
this._input = input
|
||||
this._more = this._backtrack = this.done = false
|
||||
this.yylineno = this.yyleng = 0
|
||||
this.yytext = this.matched = this.match = ''
|
||||
this.conditionStack = ['INITIAL']
|
||||
this.yylloc = {
|
||||
first_line: 1,
|
||||
first_column: 0,
|
||||
last_line: 1,
|
||||
last_column: 0
|
||||
}
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range = [0, 0]
|
||||
}
|
||||
this.offset = 0
|
||||
return this
|
||||
},
|
||||
|
||||
// consumes and returns one char from the input
|
||||
input: function () {
|
||||
var ch = this._input[0]
|
||||
this.yytext += ch
|
||||
this.yyleng++
|
||||
this.offset++
|
||||
this.match += ch
|
||||
this.matched += ch
|
||||
var lines = ch.match(/(?:\r\n?|\n).*/g)
|
||||
if (lines) {
|
||||
this.yylineno++
|
||||
this.yylloc.last_line++
|
||||
} else {
|
||||
this.yylloc.last_column++
|
||||
}
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range[1]++
|
||||
}
|
||||
|
||||
this._input = this._input.slice(1)
|
||||
return ch
|
||||
},
|
||||
|
||||
// unshifts one char (or a string) into the input
|
||||
unput: function (ch) {
|
||||
var len = ch.length
|
||||
var lines = ch.split(/(?:\r\n?|\n)/g)
|
||||
|
||||
this._input = ch + this._input
|
||||
this.yytext = this.yytext.substr(0, this.yytext.length - len)
|
||||
// this.yyleng -= len;
|
||||
this.offset -= len
|
||||
var oldLines = this.match.split(/(?:\r\n?|\n)/g)
|
||||
this.match = this.match.substr(0, this.match.length - 1)
|
||||
this.matched = this.matched.substr(0, this.matched.length - 1)
|
||||
|
||||
if (lines.length - 1) {
|
||||
this.yylineno -= lines.length - 1
|
||||
}
|
||||
var r = this.yylloc.range
|
||||
|
||||
this.yylloc = {
|
||||
first_line: this.yylloc.first_line,
|
||||
last_line: this.yylineno + 1,
|
||||
first_column: this.yylloc.first_column,
|
||||
last_column: lines
|
||||
? (lines.length === oldLines.length ? this.yylloc.first_column : 0) +
|
||||
oldLines[oldLines.length - lines.length].length - lines[0].length
|
||||
: this.yylloc.first_column - len
|
||||
}
|
||||
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range = [r[0], r[0] + this.yyleng - len]
|
||||
}
|
||||
this.yyleng = this.yytext.length
|
||||
return this
|
||||
},
|
||||
|
||||
// When called from action, caches matched text and appends it on next action
|
||||
more: function () {
|
||||
this._more = true
|
||||
return this
|
||||
},
|
||||
|
||||
// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
|
||||
reject: function () {
|
||||
if (this.options.backtrack_lexer) {
|
||||
this._backtrack = true
|
||||
} else {
|
||||
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), {
|
||||
text: '',
|
||||
token: null,
|
||||
line: this.yylineno
|
||||
})
|
||||
}
|
||||
return this
|
||||
},
|
||||
|
||||
// retain first n characters of the match
|
||||
less: function (n) {
|
||||
this.unput(this.match.slice(n))
|
||||
},
|
||||
|
||||
// displays already matched input, i.e. for error messages
|
||||
pastInput: function () {
|
||||
var past = this.matched.substr(0, this.matched.length - this.match.length)
|
||||
return (past.length > 20 ? '...' : '') + past.substr(-20).replace(/\n/g, '')
|
||||
},
|
||||
|
||||
// displays upcoming input, i.e. for error messages
|
||||
upcomingInput: function () {
|
||||
var next = this.match
|
||||
if (next.length < 20) {
|
||||
next += this._input.substr(0, 20 - next.length)
|
||||
}
|
||||
return (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace(/\n/g, '')
|
||||
},
|
||||
|
||||
// displays the character position where the lexing error occurred, i.e. for error messages
|
||||
showPosition: function () {
|
||||
var pre = this.pastInput()
|
||||
var c = new Array(pre.length + 1).join('-')
|
||||
return pre + this.upcomingInput() + '\n' + c + '^'
|
||||
},
|
||||
|
||||
// test the lexed token: return FALSE when not a match, otherwise return token
|
||||
test_match: function (match, indexed_rule) {
|
||||
var token,
|
||||
lines,
|
||||
backup
|
||||
|
||||
if (this.options.backtrack_lexer) {
|
||||
// save context
|
||||
backup = {
|
||||
yylineno: this.yylineno,
|
||||
yylloc: {
|
||||
first_line: this.yylloc.first_line,
|
||||
last_line: this.last_line,
|
||||
first_column: this.yylloc.first_column,
|
||||
last_column: this.yylloc.last_column
|
||||
},
|
||||
yytext: this.yytext,
|
||||
match: this.match,
|
||||
matches: this.matches,
|
||||
matched: this.matched,
|
||||
yyleng: this.yyleng,
|
||||
offset: this.offset,
|
||||
_more: this._more,
|
||||
_input: this._input,
|
||||
yy: this.yy,
|
||||
conditionStack: this.conditionStack.slice(0),
|
||||
done: this.done
|
||||
}
|
||||
if (this.options.ranges) {
|
||||
backup.yylloc.range = this.yylloc.range.slice(0)
|
||||
}
|
||||
}
|
||||
|
||||
lines = match[0].match(/(?:\r\n?|\n).*/g)
|
||||
if (lines) {
|
||||
this.yylineno += lines.length
|
||||
}
|
||||
this.yylloc = {
|
||||
first_line: this.yylloc.last_line,
|
||||
last_line: this.yylineno + 1,
|
||||
first_column: this.yylloc.last_column,
|
||||
last_column: lines
|
||||
? lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length
|
||||
: this.yylloc.last_column + match[0].length
|
||||
}
|
||||
this.yytext += match[0]
|
||||
this.match += match[0]
|
||||
this.matches = match
|
||||
this.yyleng = this.yytext.length
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range = [this.offset, this.offset += this.yyleng]
|
||||
}
|
||||
this._more = false
|
||||
this._backtrack = false
|
||||
this._input = this._input.slice(match[0].length)
|
||||
this.matched += match[0]
|
||||
token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1])
|
||||
if (this.done && this._input) {
|
||||
this.done = false
|
||||
}
|
||||
if (token) {
|
||||
return token
|
||||
} else if (this._backtrack) {
|
||||
// recover context
|
||||
for (var k in backup) {
|
||||
this[k] = backup[k]
|
||||
}
|
||||
return false // rule action called reject() implying the next rule should be tested instead.
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
// return next match in input
|
||||
next: function () {
|
||||
if (this.done) {
|
||||
return this.EOF
|
||||
}
|
||||
if (!this._input) {
|
||||
this.done = true
|
||||
}
|
||||
|
||||
var token,
|
||||
match,
|
||||
tempMatch,
|
||||
index
|
||||
if (!this._more) {
|
||||
this.yytext = ''
|
||||
this.match = ''
|
||||
}
|
||||
var rules = this._currentRules()
|
||||
for (var i = 0; i < rules.length; i++) {
|
||||
tempMatch = this._input.match(this.rules[rules[i]])
|
||||
if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
|
||||
match = tempMatch
|
||||
index = i
|
||||
if (this.options.backtrack_lexer) {
|
||||
token = this.test_match(tempMatch, rules[i])
|
||||
if (token !== false) {
|
||||
return token
|
||||
} else if (this._backtrack) {
|
||||
match = false
|
||||
continue // rule action called reject() implying a rule MISmatch.
|
||||
} else {
|
||||
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
|
||||
return false
|
||||
}
|
||||
} else if (!this.options.flex) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
token = this.test_match(match, rules[index])
|
||||
if (token !== false) {
|
||||
return token
|
||||
}
|
||||
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
|
||||
return false
|
||||
}
|
||||
if (this._input === '') {
|
||||
return this.EOF
|
||||
} else {
|
||||
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
|
||||
text: '',
|
||||
token: null,
|
||||
line: this.yylineno
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// return next match that has a token
|
||||
lex: function lex () {
|
||||
var r = this.next()
|
||||
if (r) {
|
||||
return r
|
||||
} else {
|
||||
return this.lex()
|
||||
}
|
||||
},
|
||||
|
||||
// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
|
||||
begin: function begin (condition) {
|
||||
this.conditionStack.push(condition)
|
||||
},
|
||||
|
||||
// pop the previously active lexer condition state off the condition stack
|
||||
popState: function popState () {
|
||||
var n = this.conditionStack.length - 1
|
||||
if (n > 0) {
|
||||
return this.conditionStack.pop()
|
||||
} else {
|
||||
return this.conditionStack[0]
|
||||
}
|
||||
},
|
||||
|
||||
// produce the lexer rule set which is active for the currently active lexer condition state
|
||||
_currentRules: function _currentRules () {
|
||||
if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
|
||||
return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules
|
||||
} else {
|
||||
return this.conditions['INITIAL'].rules
|
||||
}
|
||||
},
|
||||
|
||||
// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
|
||||
topState: function topState (n) {
|
||||
n = this.conditionStack.length - 1 - Math.abs(n || 0)
|
||||
if (n >= 0) {
|
||||
return this.conditionStack[n]
|
||||
} else {
|
||||
return 'INITIAL'
|
||||
}
|
||||
},
|
||||
|
||||
// alias for begin(condition)
|
||||
pushState: function pushState (condition) {
|
||||
this.begin(condition)
|
||||
},
|
||||
|
||||
// return the number of states currently on the stack
|
||||
stateStackSize: function stateStackSize () {
|
||||
return this.conditionStack.length
|
||||
},
|
||||
options: {},
|
||||
performAction: function anonymous (yy, yy_, $avoiding_name_collisions, YY_START) {
|
||||
var YYSTATE = YY_START
|
||||
switch ($avoiding_name_collisions) {
|
||||
case 0:/* do nothing */
|
||||
break
|
||||
case 1:return 6
|
||||
break
|
||||
case 2:/* skip whitespace */
|
||||
break
|
||||
case 3:return 5
|
||||
break
|
||||
case 4: this.begin('struct'); /* console.log('Starting struct'); */return 17
|
||||
break
|
||||
case 5: /* console.log('Ending struct'); */this.popState(); return 19
|
||||
break
|
||||
case 6:/* nothing */
|
||||
break
|
||||
case 7: return 'MEMBER'
|
||||
break
|
||||
case 8:return 16
|
||||
break
|
||||
case 9:this.begin('string')
|
||||
break
|
||||
case 10:this.popState()
|
||||
break
|
||||
case 11:return 'STR'
|
||||
break
|
||||
case 12:return 27
|
||||
break
|
||||
case 13:return 27
|
||||
break
|
||||
case 14:return 29
|
||||
break
|
||||
case 15:return 29
|
||||
break
|
||||
case 16:return 28
|
||||
break
|
||||
case 17:return 26
|
||||
break
|
||||
case 18:return 30
|
||||
break
|
||||
case 19:return 31
|
||||
break
|
||||
case 20:return 13
|
||||
break
|
||||
case 21:return 43
|
||||
break
|
||||
case 22:return 'DOT'
|
||||
break
|
||||
case 23:return 'PLUS'
|
||||
break
|
||||
case 24:return 40
|
||||
break
|
||||
case 25:return 'EQUALS'
|
||||
break
|
||||
case 26:return 'EQUALS'
|
||||
break
|
||||
case 27:return 47
|
||||
break
|
||||
case 28:return 'PUNCTUATION'
|
||||
break
|
||||
case 29:return 46
|
||||
break
|
||||
case 30:return 45
|
||||
break
|
||||
case 31:return 42
|
||||
break
|
||||
case 32:return 8
|
||||
break
|
||||
}
|
||||
},
|
||||
rules: [/^(?:%%[^\n]*)/, /^(?:\n+)/, /^(?:\s+)/, /^(?:classDiagram\b)/, /^(?:[\{])/, /^(?:\})/, /^(?:[\n])/, /^(?:[^\{\}\n]*)/, /^(?:class\b)/, /^(?:["])/, /^(?:["])/, /^(?:[^"]*)/, /^(?:\s*<\|)/, /^(?:\s*\|>)/, /^(?:\s*>)/, /^(?:\s*<)/, /^(?:\s*\*)/, /^(?:\s*o\b)/, /^(?:--)/, /^(?:\.\.)/, /^(?::[^#\n;]+)/, /^(?:-)/, /^(?:\.)/, /^(?:\+)/, /^(?:%)/, /^(?:=)/, /^(?:=)/, /^(?:[A-Za-z]+)/, /^(?:[!"#$%&'*+,-.`?\\_\/])/, /^(?:[0-9]+)/, /^(?:[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]|[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]|[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]|[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]|[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]|[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]|[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]|[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]|[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]|[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]|[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]|[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]|[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]|[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]|[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]|[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]|[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]|[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]|[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]|[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]|[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]|[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]|[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]|[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]|[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]|[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]|[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]|[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]|[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]|[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]|[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]|[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]|[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]|[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]|[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]|[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]|[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]|[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]|[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]|[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]|[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]|[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]|[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]|[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]|[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]|[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]|[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]|[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]|[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]|[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]|[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]|[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]|[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]|[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]|[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]|[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|[\uFFD2-\uFFD7\uFFDA-\uFFDC])/, /^(?:\s)/, /^(?:$)/],
|
||||
conditions: {'string': {'rules': [10, 11], 'inclusive': false}, 'struct': {'rules': [5, 6, 7], 'inclusive': false}, 'INITIAL': {'rules': [0, 1, 2, 3, 4, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32], '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))
|
||||
}
|
||||
}
|
||||
22
src/diagrams/example/example.spec.js
Normal file
22
src/diagrams/example/example.spec.js
Normal file
@@ -0,0 +1,22 @@
|
||||
/* eslint-env jasmine */
|
||||
/**
|
||||
* Created by knut on 14-11-18.
|
||||
*/
|
||||
describe('when parsing an info graph it', function () {
|
||||
var ex
|
||||
beforeEach(function () {
|
||||
ex = require('./parser/example').parser
|
||||
ex.yy = require('./exampleDb')
|
||||
})
|
||||
|
||||
it('should handle an info definition', function () {
|
||||
var str = 'info\nsay: hello'
|
||||
|
||||
ex.parse(str)
|
||||
})
|
||||
it('should handle an showMessage statement definition', function () {
|
||||
var str = 'info\nshowInfo'
|
||||
|
||||
ex.parse(str)
|
||||
})
|
||||
})
|
||||
29
src/diagrams/example/exampleDb.js
Normal file
29
src/diagrams/example/exampleDb.js
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Created by knut on 15-01-14.
|
||||
*/
|
||||
var Logger = require('../../logger')
|
||||
var log = Logger.Log
|
||||
|
||||
var message = ''
|
||||
var info = false
|
||||
|
||||
exports.setMessage = function (txt) {
|
||||
log.debug('Setting message to: ' + txt)
|
||||
message = txt
|
||||
}
|
||||
|
||||
exports.getMessage = function () {
|
||||
return message
|
||||
}
|
||||
|
||||
exports.setInfo = function (inf) {
|
||||
info = inf
|
||||
}
|
||||
|
||||
exports.getInfo = function () {
|
||||
return info
|
||||
}
|
||||
|
||||
exports.parseError = function (err, hash) {
|
||||
global.mermaidAPI.parseError(err, hash)
|
||||
}
|
||||
39
src/diagrams/example/exampleRenderer.js
Normal file
39
src/diagrams/example/exampleRenderer.js
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Created by knut on 14-12-11.
|
||||
*/
|
||||
var db = require('./exampleDb')
|
||||
var exampleParser = require('./parser/example.js')
|
||||
var d3 = require('../../d3')
|
||||
|
||||
var Logger = require('../../logger')
|
||||
var log = Logger.Log
|
||||
|
||||
/**
|
||||
* Draws a an info picture in the tag with id: id based on the graph definition in text.
|
||||
* @param text
|
||||
* @param id
|
||||
*/
|
||||
exports.draw = function (txt, id, ver) {
|
||||
var parser
|
||||
parser = exampleParser.parser
|
||||
parser.yy = db
|
||||
log.debug('Renering example diagram')
|
||||
// Parse the graph definition
|
||||
parser.parse(txt)
|
||||
|
||||
// Fetch the default direction, use TD if none was found
|
||||
var svg = d3.select('#' + id)
|
||||
|
||||
var 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('mermaid ' + ver)
|
||||
|
||||
svg.attr('height', 100)
|
||||
svg.attr('width', 400)
|
||||
}
|
||||
53
src/diagrams/example/parser/example.jison
Normal file
53
src/diagrams/example/parser/example.jison
Normal file
@@ -0,0 +1,53 @@
|
||||
/** mermaid
|
||||
* https://mermaidjs.github.io/
|
||||
* (c) 2015 Knut Sveidqvist
|
||||
* MIT license.
|
||||
*/
|
||||
%lex
|
||||
|
||||
%options case-insensitive
|
||||
|
||||
%{
|
||||
// Pre-lexer code can go here
|
||||
%}
|
||||
|
||||
%%
|
||||
|
||||
[\n]+ return 'NL';
|
||||
"showInfo" return 'showInfo';
|
||||
"info" return 'info';
|
||||
"say" return 'say';
|
||||
":"[^#\n;]+ return 'TXT';
|
||||
<<EOF>> return 'EOF';
|
||||
. return 'INVALID';
|
||||
|
||||
/lex
|
||||
|
||||
%start start
|
||||
|
||||
%% /* language grammar */
|
||||
|
||||
start
|
||||
: info document 'EOF' { return yy; }
|
||||
;
|
||||
|
||||
document
|
||||
: /* empty */
|
||||
| document line
|
||||
;
|
||||
|
||||
line
|
||||
: statement { }
|
||||
| 'NL'
|
||||
;
|
||||
|
||||
statement
|
||||
: showInfo { yy.setInfo(true); }
|
||||
| message { yy.setMessage($1); }
|
||||
;
|
||||
|
||||
message
|
||||
: 'say' TXT { $$ = $1.substring(1).trim().replace(/\\n/gm, "\n"); }
|
||||
;
|
||||
|
||||
%%
|
||||
625
src/diagrams/example/parser/example.js
Normal file
625
src/diagrams/example/parser/example.js
Normal file
@@ -0,0 +1,625 @@
|
||||
/* parser generated by jison 0.4.17 */
|
||||
/*
|
||||
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, 12]
|
||||
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, 'message': 11, 'say': 12, 'TXT': 13, '$accept': 0, '$end': 1},
|
||||
terminals_: {2: 'error', 4: 'info', 6: 'EOF', 9: 'NL', 10: 'showInfo', 12: 'say', 13: 'TXT'},
|
||||
productions_: [0, [3, 3], [5, 0], [5, 2], [7, 1], [7, 1], [8, 1], [8, 1], [11, 2]],
|
||||
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
|
||||
case 7:
|
||||
yy.setMessage($$[$0])
|
||||
break
|
||||
case 8:
|
||||
this.$ = $$[$0 - 1].substring(1).trim().replace(/\\n/gm, '\n')
|
||||
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], 11: 9, 12: [1, 10]}, {1: [2, 1]}, o($V0, [2, 3]), o($V0, [2, 4]), o($V0, [2, 5]), o($V0, [2, 6]), o($V0, [2, 7]), {13: [1, 11]}, o($V0, [2, 8])],
|
||||
defaultActions: {4: [2, 1]},
|
||||
parseError: function parseError (str, hash) {
|
||||
if (hash.recoverable) {
|
||||
this.trace(str)
|
||||
} else {
|
||||
function _parseError (msg, hash) {
|
||||
this.message = msg
|
||||
this.hash = hash
|
||||
}
|
||||
_parseError.prototype = Error
|
||||
|
||||
throw new _parseError(str, hash)
|
||||
}
|
||||
},
|
||||
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
|
||||
}
|
||||
var lex = function () {
|
||||
var token
|
||||
token = lexer.lex() || EOF
|
||||
if (typeof token !== 'number') {
|
||||
token = self.symbols_[token] || token
|
||||
}
|
||||
return token
|
||||
}
|
||||
var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected
|
||||
while (true) {
|
||||
state = stack[stack.length - 1]
|
||||
if (this.defaultActions[state]) {
|
||||
action = this.defaultActions[state]
|
||||
} else {
|
||||
if (symbol === null || typeof symbol === 'undefined') {
|
||||
symbol = lex()
|
||||
}
|
||||
action = table[state] && table[state][symbol]
|
||||
}
|
||||
if (typeof action === 'undefined' || !action.length || !action[0]) {
|
||||
var errStr = ''
|
||||
expected = []
|
||||
for (p in table[state]) {
|
||||
if (this.terminals_[p] && p > TERROR) {
|
||||
expected.push('\'' + this.terminals_[p] + '\'')
|
||||
}
|
||||
}
|
||||
if (lexer.showPosition) {
|
||||
errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\''
|
||||
} else {
|
||||
errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'')
|
||||
}
|
||||
this.parseError(errStr, {
|
||||
text: lexer.match,
|
||||
token: this.terminals_[symbol] || symbol,
|
||||
line: lexer.yylineno,
|
||||
loc: yyloc,
|
||||
expected: expected
|
||||
})
|
||||
}
|
||||
if (action[0] instanceof Array && action.length > 1) {
|
||||
throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol)
|
||||
}
|
||||
switch (action[0]) {
|
||||
case 1:
|
||||
stack.push(symbol)
|
||||
vstack.push(lexer.yytext)
|
||||
lstack.push(lexer.yylloc)
|
||||
stack.push(action[1])
|
||||
symbol = null
|
||||
if (!preErrorSymbol) {
|
||||
yyleng = lexer.yyleng
|
||||
yytext = lexer.yytext
|
||||
yylineno = lexer.yylineno
|
||||
yyloc = lexer.yylloc
|
||||
if (recovering > 0) {
|
||||
recovering--
|
||||
}
|
||||
} else {
|
||||
symbol = preErrorSymbol
|
||||
preErrorSymbol = null
|
||||
}
|
||||
break
|
||||
case 2:
|
||||
len = this.productions_[action[1]][1]
|
||||
yyval.$ = vstack[vstack.length - len]
|
||||
yyval._$ = {
|
||||
first_line: lstack[lstack.length - (len || 1)].first_line,
|
||||
last_line: lstack[lstack.length - 1].last_line,
|
||||
first_column: lstack[lstack.length - (len || 1)].first_column,
|
||||
last_column: lstack[lstack.length - 1].last_column
|
||||
}
|
||||
if (ranges) {
|
||||
yyval._$.range = [
|
||||
lstack[lstack.length - (len || 1)].range[0],
|
||||
lstack[lstack.length - 1].range[1]
|
||||
]
|
||||
}
|
||||
r = this.performAction.apply(yyval, [
|
||||
yytext,
|
||||
yyleng,
|
||||
yylineno,
|
||||
sharedState.yy,
|
||||
action[1],
|
||||
vstack,
|
||||
lstack
|
||||
].concat(args))
|
||||
if (typeof r !== 'undefined') {
|
||||
return r
|
||||
}
|
||||
if (len) {
|
||||
stack = stack.slice(0, -1 * len * 2)
|
||||
vstack = vstack.slice(0, -1 * len)
|
||||
lstack = lstack.slice(0, -1 * len)
|
||||
}
|
||||
stack.push(this.productions_[action[1]][0])
|
||||
vstack.push(yyval.$)
|
||||
lstack.push(yyval._$)
|
||||
newState = table[stack[stack.length - 2]][stack[stack.length - 1]]
|
||||
stack.push(newState)
|
||||
break
|
||||
case 3:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
}}
|
||||
/* generated by jison-lex 0.3.4 */
|
||||
var lexer = (function () {
|
||||
var lexer = ({
|
||||
|
||||
EOF: 1,
|
||||
|
||||
parseError: function parseError (str, hash) {
|
||||
if (this.yy.parser) {
|
||||
this.yy.parser.parseError(str, hash)
|
||||
} else {
|
||||
throw new Error(str)
|
||||
}
|
||||
},
|
||||
|
||||
// resets the lexer, sets new input
|
||||
setInput: function (input, yy) {
|
||||
this.yy = yy || this.yy || {}
|
||||
this._input = input
|
||||
this._more = this._backtrack = this.done = false
|
||||
this.yylineno = this.yyleng = 0
|
||||
this.yytext = this.matched = this.match = ''
|
||||
this.conditionStack = ['INITIAL']
|
||||
this.yylloc = {
|
||||
first_line: 1,
|
||||
first_column: 0,
|
||||
last_line: 1,
|
||||
last_column: 0
|
||||
}
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range = [0, 0]
|
||||
}
|
||||
this.offset = 0
|
||||
return this
|
||||
},
|
||||
|
||||
// consumes and returns one char from the input
|
||||
input: function () {
|
||||
var ch = this._input[0]
|
||||
this.yytext += ch
|
||||
this.yyleng++
|
||||
this.offset++
|
||||
this.match += ch
|
||||
this.matched += ch
|
||||
var lines = ch.match(/(?:\r\n?|\n).*/g)
|
||||
if (lines) {
|
||||
this.yylineno++
|
||||
this.yylloc.last_line++
|
||||
} else {
|
||||
this.yylloc.last_column++
|
||||
}
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range[1]++
|
||||
}
|
||||
|
||||
this._input = this._input.slice(1)
|
||||
return ch
|
||||
},
|
||||
|
||||
// unshifts one char (or a string) into the input
|
||||
unput: function (ch) {
|
||||
var len = ch.length
|
||||
var lines = ch.split(/(?:\r\n?|\n)/g)
|
||||
|
||||
this._input = ch + this._input
|
||||
this.yytext = this.yytext.substr(0, this.yytext.length - len)
|
||||
// this.yyleng -= len;
|
||||
this.offset -= len
|
||||
var oldLines = this.match.split(/(?:\r\n?|\n)/g)
|
||||
this.match = this.match.substr(0, this.match.length - 1)
|
||||
this.matched = this.matched.substr(0, this.matched.length - 1)
|
||||
|
||||
if (lines.length - 1) {
|
||||
this.yylineno -= lines.length - 1
|
||||
}
|
||||
var r = this.yylloc.range
|
||||
|
||||
this.yylloc = {
|
||||
first_line: this.yylloc.first_line,
|
||||
last_line: this.yylineno + 1,
|
||||
first_column: this.yylloc.first_column,
|
||||
last_column: lines
|
||||
? (lines.length === oldLines.length ? this.yylloc.first_column : 0) +
|
||||
oldLines[oldLines.length - lines.length].length - lines[0].length
|
||||
: this.yylloc.first_column - len
|
||||
}
|
||||
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range = [r[0], r[0] + this.yyleng - len]
|
||||
}
|
||||
this.yyleng = this.yytext.length
|
||||
return this
|
||||
},
|
||||
|
||||
// When called from action, caches matched text and appends it on next action
|
||||
more: function () {
|
||||
this._more = true
|
||||
return this
|
||||
},
|
||||
|
||||
// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
|
||||
reject: function () {
|
||||
if (this.options.backtrack_lexer) {
|
||||
this._backtrack = true
|
||||
} else {
|
||||
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), {
|
||||
text: '',
|
||||
token: null,
|
||||
line: this.yylineno
|
||||
})
|
||||
}
|
||||
return this
|
||||
},
|
||||
|
||||
// retain first n characters of the match
|
||||
less: function (n) {
|
||||
this.unput(this.match.slice(n))
|
||||
},
|
||||
|
||||
// displays already matched input, i.e. for error messages
|
||||
pastInput: function () {
|
||||
var past = this.matched.substr(0, this.matched.length - this.match.length)
|
||||
return (past.length > 20 ? '...' : '') + past.substr(-20).replace(/\n/g, '')
|
||||
},
|
||||
|
||||
// displays upcoming input, i.e. for error messages
|
||||
upcomingInput: function () {
|
||||
var next = this.match
|
||||
if (next.length < 20) {
|
||||
next += this._input.substr(0, 20 - next.length)
|
||||
}
|
||||
return (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace(/\n/g, '')
|
||||
},
|
||||
|
||||
// displays the character position where the lexing error occurred, i.e. for error messages
|
||||
showPosition: function () {
|
||||
var pre = this.pastInput()
|
||||
var c = new Array(pre.length + 1).join('-')
|
||||
return pre + this.upcomingInput() + '\n' + c + '^'
|
||||
},
|
||||
|
||||
// test the lexed token: return FALSE when not a match, otherwise return token
|
||||
test_match: function (match, indexed_rule) {
|
||||
var token,
|
||||
lines,
|
||||
backup
|
||||
|
||||
if (this.options.backtrack_lexer) {
|
||||
// save context
|
||||
backup = {
|
||||
yylineno: this.yylineno,
|
||||
yylloc: {
|
||||
first_line: this.yylloc.first_line,
|
||||
last_line: this.last_line,
|
||||
first_column: this.yylloc.first_column,
|
||||
last_column: this.yylloc.last_column
|
||||
},
|
||||
yytext: this.yytext,
|
||||
match: this.match,
|
||||
matches: this.matches,
|
||||
matched: this.matched,
|
||||
yyleng: this.yyleng,
|
||||
offset: this.offset,
|
||||
_more: this._more,
|
||||
_input: this._input,
|
||||
yy: this.yy,
|
||||
conditionStack: this.conditionStack.slice(0),
|
||||
done: this.done
|
||||
}
|
||||
if (this.options.ranges) {
|
||||
backup.yylloc.range = this.yylloc.range.slice(0)
|
||||
}
|
||||
}
|
||||
|
||||
lines = match[0].match(/(?:\r\n?|\n).*/g)
|
||||
if (lines) {
|
||||
this.yylineno += lines.length
|
||||
}
|
||||
this.yylloc = {
|
||||
first_line: this.yylloc.last_line,
|
||||
last_line: this.yylineno + 1,
|
||||
first_column: this.yylloc.last_column,
|
||||
last_column: lines
|
||||
? lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length
|
||||
: this.yylloc.last_column + match[0].length
|
||||
}
|
||||
this.yytext += match[0]
|
||||
this.match += match[0]
|
||||
this.matches = match
|
||||
this.yyleng = this.yytext.length
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range = [this.offset, this.offset += this.yyleng]
|
||||
}
|
||||
this._more = false
|
||||
this._backtrack = false
|
||||
this._input = this._input.slice(match[0].length)
|
||||
this.matched += match[0]
|
||||
token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1])
|
||||
if (this.done && this._input) {
|
||||
this.done = false
|
||||
}
|
||||
if (token) {
|
||||
return token
|
||||
} else if (this._backtrack) {
|
||||
// recover context
|
||||
for (var k in backup) {
|
||||
this[k] = backup[k]
|
||||
}
|
||||
return false // rule action called reject() implying the next rule should be tested instead.
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
// return next match in input
|
||||
next: function () {
|
||||
if (this.done) {
|
||||
return this.EOF
|
||||
}
|
||||
if (!this._input) {
|
||||
this.done = true
|
||||
}
|
||||
|
||||
var token,
|
||||
match,
|
||||
tempMatch,
|
||||
index
|
||||
if (!this._more) {
|
||||
this.yytext = ''
|
||||
this.match = ''
|
||||
}
|
||||
var rules = this._currentRules()
|
||||
for (var i = 0; i < rules.length; i++) {
|
||||
tempMatch = this._input.match(this.rules[rules[i]])
|
||||
if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
|
||||
match = tempMatch
|
||||
index = i
|
||||
if (this.options.backtrack_lexer) {
|
||||
token = this.test_match(tempMatch, rules[i])
|
||||
if (token !== false) {
|
||||
return token
|
||||
} else if (this._backtrack) {
|
||||
match = false
|
||||
continue // rule action called reject() implying a rule MISmatch.
|
||||
} else {
|
||||
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
|
||||
return false
|
||||
}
|
||||
} else if (!this.options.flex) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
token = this.test_match(match, rules[index])
|
||||
if (token !== false) {
|
||||
return token
|
||||
}
|
||||
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
|
||||
return false
|
||||
}
|
||||
if (this._input === '') {
|
||||
return this.EOF
|
||||
} else {
|
||||
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
|
||||
text: '',
|
||||
token: null,
|
||||
line: this.yylineno
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// return next match that has a token
|
||||
lex: function lex () {
|
||||
var r = this.next()
|
||||
if (r) {
|
||||
return r
|
||||
} else {
|
||||
return this.lex()
|
||||
}
|
||||
},
|
||||
|
||||
// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
|
||||
begin: function begin (condition) {
|
||||
this.conditionStack.push(condition)
|
||||
},
|
||||
|
||||
// pop the previously active lexer condition state off the condition stack
|
||||
popState: function popState () {
|
||||
var n = this.conditionStack.length - 1
|
||||
if (n > 0) {
|
||||
return this.conditionStack.pop()
|
||||
} else {
|
||||
return this.conditionStack[0]
|
||||
}
|
||||
},
|
||||
|
||||
// produce the lexer rule set which is active for the currently active lexer condition state
|
||||
_currentRules: function _currentRules () {
|
||||
if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
|
||||
return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules
|
||||
} else {
|
||||
return this.conditions['INITIAL'].rules
|
||||
}
|
||||
},
|
||||
|
||||
// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
|
||||
topState: function topState (n) {
|
||||
n = this.conditionStack.length - 1 - Math.abs(n || 0)
|
||||
if (n >= 0) {
|
||||
return this.conditionStack[n]
|
||||
} else {
|
||||
return 'INITIAL'
|
||||
}
|
||||
},
|
||||
|
||||
// alias for begin(condition)
|
||||
pushState: function pushState (condition) {
|
||||
this.begin(condition)
|
||||
},
|
||||
|
||||
// return the number of states currently on the stack
|
||||
stateStackSize: function stateStackSize () {
|
||||
return this.conditionStack.length
|
||||
},
|
||||
options: {'case-insensitive': true},
|
||||
performAction: function anonymous (yy, yy_, $avoiding_name_collisions, YY_START) {
|
||||
// Pre-lexer code can go here
|
||||
|
||||
var YYSTATE = YY_START
|
||||
switch ($avoiding_name_collisions) {
|
||||
case 0:return 9
|
||||
break
|
||||
case 1:return 10
|
||||
break
|
||||
case 2:return 4
|
||||
break
|
||||
case 3:return 12
|
||||
break
|
||||
case 4:return 13
|
||||
break
|
||||
case 5:return 6
|
||||
break
|
||||
case 6:return 'INVALID'
|
||||
break
|
||||
}
|
||||
},
|
||||
rules: [/^(?:[\n]+)/i, /^(?:showInfo\b)/i, /^(?:info\b)/i, /^(?:say\b)/i, /^(?::[^#\n;]+)/i, /^(?:$)/i, /^(?:.)/i],
|
||||
conditions: {'INITIAL': {'rules': [0, 1, 2, 3, 4, 5, 6], '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))
|
||||
}
|
||||
}
|
||||
507
src/diagrams/flowchart/flowRenderer.js
Normal file
507
src/diagrams/flowchart/flowRenderer.js
Normal file
@@ -0,0 +1,507 @@
|
||||
/**
|
||||
* Created by knut on 14-12-11.
|
||||
*/
|
||||
var graph = require('./graphDb')
|
||||
var flow = require('./parser/flow')
|
||||
var dot = require('./parser/dot')
|
||||
var d3 = require('../../d3')
|
||||
var dagreD3 = require('dagre-d3-renderer')
|
||||
var Logger = require('../../logger')
|
||||
var log = Logger.Log
|
||||
|
||||
var conf = {
|
||||
}
|
||||
module.exports.setConf = function (cnf) {
|
||||
var keys = Object.keys(cnf)
|
||||
var i
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
conf[keys[i]] = cnf[keys[i]]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that adds the vertices found in the graph definition to the graph to be rendered.
|
||||
* @param vert Object containing the vertices.
|
||||
* @param g The graph that is to be drawn.
|
||||
*/
|
||||
exports.addVertices = function (vert, g) {
|
||||
var keys = Object.keys(vert)
|
||||
|
||||
var styleFromStyleArr = function (styleStr, arr) {
|
||||
var i
|
||||
// Create a compound style definition from the style definitions found for the node in the graph definition
|
||||
for (i = 0; i < arr.length; i++) {
|
||||
if (typeof arr[i] !== 'undefined') {
|
||||
styleStr = styleStr + arr[i] + ';'
|
||||
}
|
||||
}
|
||||
|
||||
return styleStr
|
||||
}
|
||||
|
||||
// Iterate through each item in the vertice object (containing all the vertices found) in the graph definition
|
||||
keys.forEach(function (id) {
|
||||
var vertice = vert[id]
|
||||
var verticeText
|
||||
|
||||
/**
|
||||
* Variable for storing the classes for the vertice
|
||||
* @type {string}
|
||||
*/
|
||||
var classStr = ''
|
||||
|
||||
if (vertice.classes.length > 0) {
|
||||
classStr = vertice.classes.join(' ')
|
||||
}
|
||||
|
||||
/**
|
||||
* Variable for storing the extracted style for the vertice
|
||||
* @type {string}
|
||||
*/
|
||||
var style = ''
|
||||
// Create a compound style definition from the style definitions found for the node in the graph definition
|
||||
style = styleFromStyleArr(style, vertice.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
|
||||
}
|
||||
|
||||
var labelTypeStr = ''
|
||||
if (conf.htmlLabels) {
|
||||
labelTypeStr = 'html'
|
||||
verticeText = verticeText.replace(/fa:fa[\w-]+/g, function (s) {
|
||||
return '<i class="fa ' + s.substring(3) + '"></i>'
|
||||
})
|
||||
} else {
|
||||
var svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text')
|
||||
|
||||
var rows = verticeText.split(/<br>/)
|
||||
|
||||
var j = 0
|
||||
for (j = 0; j < rows.length; j++) {
|
||||
var tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan')
|
||||
tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve')
|
||||
tspan.setAttribute('dy', '1em')
|
||||
tspan.setAttribute('x', '1')
|
||||
tspan.textContent = rows[j]
|
||||
svgLabel.appendChild(tspan)
|
||||
}
|
||||
|
||||
labelTypeStr = 'svg'
|
||||
verticeText = svgLabel
|
||||
}
|
||||
|
||||
var radious = 0
|
||||
var _shape = ''
|
||||
|
||||
// Set the shape based parameters
|
||||
switch (vertice.type) {
|
||||
case 'round':
|
||||
radious = 5
|
||||
_shape = 'rect'
|
||||
break
|
||||
case 'square':
|
||||
_shape = 'rect'
|
||||
break
|
||||
case 'diamond':
|
||||
_shape = 'question'
|
||||
break
|
||||
case 'odd':
|
||||
_shape = 'rect_left_inv_arrow'
|
||||
break
|
||||
case 'odd_right':
|
||||
_shape = 'rect_left_inv_arrow'
|
||||
break
|
||||
case 'circle':
|
||||
_shape = 'circle'
|
||||
break
|
||||
case 'ellipse':
|
||||
_shape = 'ellipse'
|
||||
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 })
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Add edges to graph based on parsed graph defninition
|
||||
* @param {Object} edges The edges to add to the graph
|
||||
* @param {Object} g The graph object
|
||||
*/
|
||||
exports.addEdges = function (edges, g) {
|
||||
var cnt = 0
|
||||
|
||||
var defaultStyle
|
||||
if (typeof edges.defaultStyle !== 'undefined') {
|
||||
defaultStyle = edges.defaultStyle.toString().replace(/,/g, ';')
|
||||
}
|
||||
|
||||
edges.forEach(function (edge) {
|
||||
cnt++
|
||||
var edgeData = {}
|
||||
|
||||
// Set link type for rendering
|
||||
if (edge.type === 'arrow_open') {
|
||||
edgeData.arrowhead = 'none'
|
||||
} else {
|
||||
edgeData.arrowhead = 'normal'
|
||||
}
|
||||
|
||||
var style = ''
|
||||
|
||||
if (typeof edge.style !== 'undefined') {
|
||||
edge.style.forEach(function (s) {
|
||||
style = style + s + ';'
|
||||
})
|
||||
} else {
|
||||
switch (edge.stroke) {
|
||||
case 'normal':
|
||||
style = 'fill:none'
|
||||
if (typeof defaultStyle !== 'undefined') {
|
||||
style = defaultStyle
|
||||
}
|
||||
break
|
||||
case 'dotted':
|
||||
style = 'stroke: #333; fill:none;stroke-width:2px;stroke-dasharray:3;'
|
||||
break
|
||||
case 'thick':
|
||||
style = 'stroke: #333; stroke-width: 3.5px;fill:none'
|
||||
break
|
||||
}
|
||||
}
|
||||
edgeData.style = style
|
||||
|
||||
if (typeof edge.interpolate !== 'undefined') {
|
||||
edgeData.lineInterpolate = edge.interpolate
|
||||
} else {
|
||||
if (typeof edges.defaultInterpolate !== 'undefined') {
|
||||
edgeData.lineInterpolate = edges.defaultInterpolate
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof edge.text === 'undefined') {
|
||||
if (typeof edge.style !== 'undefined') {
|
||||
edgeData.arrowheadStyle = 'fill: #333'
|
||||
}
|
||||
} else {
|
||||
edgeData.arrowheadStyle = 'fill: #333'
|
||||
if (typeof edge.style === 'undefined') {
|
||||
edgeData.labelpos = 'c'
|
||||
if (conf.htmlLabels) {
|
||||
edgeData.labelType = 'html'
|
||||
edgeData.label = '<span class="edgeLabel">' + edge.text + '</span>'
|
||||
} else {
|
||||
edgeData.labelType = 'text'
|
||||
edgeData.style = 'stroke: #333; stroke-width: 1.5px;fill:none'
|
||||
edgeData.label = edge.text.replace(/<br>/g, '\n')
|
||||
}
|
||||
} else {
|
||||
edgeData.label = edge.text.replace(/<br>/g, '\n')
|
||||
}
|
||||
}
|
||||
// Add the edge to the graph
|
||||
g.setEdge(edge.start, edge.end, edgeData, cnt)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the all the styles from classDef statements in the graph definition.
|
||||
* @returns {object} classDef styles
|
||||
*/
|
||||
exports.getClasses = function (text, isDot) {
|
||||
var parser
|
||||
graph.clear()
|
||||
if (isDot) {
|
||||
parser = dot.parser
|
||||
} else {
|
||||
parser = flow.parser
|
||||
}
|
||||
parser.yy = graph
|
||||
|
||||
// Parse the graph definition
|
||||
parser.parse(text)
|
||||
|
||||
var classes = graph.getClasses()
|
||||
|
||||
// Add default class if undefined
|
||||
if (typeof (classes.default) === 'undefined') {
|
||||
classes.default = { id: 'default' }
|
||||
classes.default.styles = []
|
||||
classes.default.clusterStyles = ['rx:4px', 'fill: rgb(255, 255, 222)', 'rx: 4px', 'stroke: rgb(170, 170, 51)', 'stroke-width: 1px']
|
||||
classes.default.nodeLabelStyles = ['fill:#000', 'stroke:none', 'font-weight:300', 'font-family:"Helvetica Neue",Helvetica,Arial,sans-serf', 'font-size:14px']
|
||||
classes.default.edgeLabelStyles = ['fill:#000', 'stroke:none', 'font-weight:300', 'font-family:"Helvetica Neue",Helvetica,Arial,sans-serf', 'font-size:14px']
|
||||
}
|
||||
return classes
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a flowchart in the tag with id: id based on the graph definition in text.
|
||||
* @param text
|
||||
* @param id
|
||||
*/
|
||||
exports.draw = function (text, id, isDot) {
|
||||
log.debug('Drawing flowchart')
|
||||
var parser
|
||||
graph.clear()
|
||||
if (isDot) {
|
||||
parser = dot.parser
|
||||
} else {
|
||||
parser = flow.parser
|
||||
}
|
||||
parser.yy = graph
|
||||
|
||||
// Parse the graph definition
|
||||
try {
|
||||
parser.parse(text)
|
||||
} catch (err) {
|
||||
log.debug('Parsing failed')
|
||||
}
|
||||
|
||||
// Fetch the default direction, use TD if none was found
|
||||
var dir
|
||||
dir = graph.getDirection()
|
||||
if (typeof dir === 'undefined') {
|
||||
dir = 'TD'
|
||||
}
|
||||
|
||||
// Create the input mermaid.graph
|
||||
var g = new dagreD3.graphlib.Graph({
|
||||
multigraph: true,
|
||||
compound: true
|
||||
})
|
||||
.setGraph({
|
||||
rankdir: dir,
|
||||
marginx: 20,
|
||||
marginy: 20
|
||||
|
||||
})
|
||||
.setDefaultEdgeLabel(function () {
|
||||
return {}
|
||||
})
|
||||
|
||||
var subG
|
||||
var subGraphs = graph.getSubGraphs()
|
||||
var i = 0
|
||||
for (i = subGraphs.length - 1; i >= 0; i--) {
|
||||
subG = subGraphs[i]
|
||||
graph.addVertex(subG.id, subG.title, 'group', undefined)
|
||||
}
|
||||
|
||||
// Fetch the verices/nodes and edges/links from the parsed graph definition
|
||||
var vert = graph.getVertices()
|
||||
|
||||
var edges = graph.getEdges()
|
||||
|
||||
i = 0
|
||||
var j
|
||||
for (i = subGraphs.length - 1; i >= 0; i--) {
|
||||
subG = subGraphs[i]
|
||||
|
||||
d3.selectAll('cluster').append('text')
|
||||
|
||||
for (j = 0; j < subG.nodes.length; j++) {
|
||||
g.setParent(subG.nodes[j], subG.id)
|
||||
}
|
||||
}
|
||||
exports.addVertices(vert, g)
|
||||
exports.addEdges(edges, g)
|
||||
|
||||
// Create the renderer
|
||||
var Render = dagreD3.render
|
||||
var render = new Render()
|
||||
|
||||
// Add custom shape for rhombus type of boc (decision)
|
||||
render.shapes().question = function (parent, bbox, node) {
|
||||
var w = bbox.width
|
||||
var h = bbox.height
|
||||
var s = (w + h) * 0.8
|
||||
var points = [
|
||||
{ x: s / 2, y: 0 },
|
||||
{ x: s, y: -s / 2 },
|
||||
{ x: s / 2, y: -s },
|
||||
{ x: 0, y: -s / 2 }
|
||||
]
|
||||
var shapeSvg = parent.insert('polygon', ':first-child')
|
||||
.attr('points', points.map(function (d) {
|
||||
return d.x + ',' + d.y
|
||||
}).join(' '))
|
||||
.attr('rx', 5)
|
||||
.attr('ry', 5)
|
||||
.attr('transform', 'translate(' + (-s / 2) + ',' + (s * 2 / 4) + ')')
|
||||
node.intersect = function (point) {
|
||||
return dagreD3.intersect.polygon(node, points, point)
|
||||
}
|
||||
return shapeSvg
|
||||
}
|
||||
|
||||
// Add custom shape for box with inverted arrow on left side
|
||||
render.shapes().rect_left_inv_arrow = function (parent, bbox, node) {
|
||||
var w = bbox.width
|
||||
var h = bbox.height
|
||||
var points = [
|
||||
{ x: -h / 2, y: 0 },
|
||||
{ x: w, y: 0 },
|
||||
{ x: w, y: -h },
|
||||
{ x: -h / 2, y: -h },
|
||||
{ x: 0, y: -h / 2 }
|
||||
]
|
||||
var shapeSvg = parent.insert('polygon', ':first-child')
|
||||
.attr('points', points.map(function (d) {
|
||||
return d.x + ',' + d.y
|
||||
}).join(' '))
|
||||
.attr('transform', 'translate(' + (-w / 2) + ',' + (h * 2 / 4) + ')')
|
||||
node.intersect = function (point) {
|
||||
return dagreD3.intersect.polygon(node, points, point)
|
||||
}
|
||||
return shapeSvg
|
||||
}
|
||||
|
||||
// Add custom shape for box with inverted arrow on right side
|
||||
render.shapes().rect_right_inv_arrow = function (parent, bbox, node) {
|
||||
var w = bbox.width
|
||||
var h = bbox.height
|
||||
var points = [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: w + h / 2, y: 0 },
|
||||
{ x: w, y: -h / 2 },
|
||||
{ x: w + h / 2, y: -h },
|
||||
{ x: 0, y: -h }
|
||||
]
|
||||
var shapeSvg = parent.insert('polygon', ':first-child')
|
||||
.attr('points', points.map(function (d) {
|
||||
return d.x + ',' + d.y
|
||||
}).join(' '))
|
||||
.attr('transform', 'translate(' + (-w / 2) + ',' + (h * 2 / 4) + ')')
|
||||
node.intersect = function (point) {
|
||||
return dagreD3.intersect.polygon(node, points, point)
|
||||
}
|
||||
return shapeSvg
|
||||
}
|
||||
|
||||
// Add our custom arrow - an empty arrowhead
|
||||
render.arrows().none = function normal (parent, id, edge, type) {
|
||||
var marker = parent.append('marker')
|
||||
.attr('id', id)
|
||||
.attr('viewBox', '0 0 10 10')
|
||||
.attr('refX', 9)
|
||||
.attr('refY', 5)
|
||||
.attr('markerUnits', 'strokeWidth')
|
||||
.attr('markerWidth', 8)
|
||||
.attr('markerHeight', 6)
|
||||
.attr('orient', 'auto')
|
||||
|
||||
var path = marker.append('path')
|
||||
.attr('d', 'M 0 0 L 0 0 L 0 0 z')
|
||||
dagreD3.util.applyStyle(path, edge[type + 'Style'])
|
||||
}
|
||||
|
||||
// Override normal arrowhead defined in d3. Remove style & add class to allow css styling.
|
||||
render.arrows().normal = function normal (parent, id, edge, type) {
|
||||
var marker = parent.append('marker')
|
||||
.attr('id', id)
|
||||
.attr('viewBox', '0 0 10 10')
|
||||
.attr('refX', 9)
|
||||
.attr('refY', 5)
|
||||
.attr('markerUnits', 'strokeWidth')
|
||||
.attr('markerWidth', 8)
|
||||
.attr('markerHeight', 6)
|
||||
.attr('orient', 'auto')
|
||||
|
||||
marker.append('path')
|
||||
.attr('d', 'M 0 0 L 10 5 L 0 10 z')
|
||||
.attr('class', 'arrowheadPath')
|
||||
.style('stroke-width', 1)
|
||||
.style('stroke-dasharray', '1,0')
|
||||
}
|
||||
|
||||
// Set up an SVG group so that we can translate the final graph.
|
||||
var svg = d3.select('#' + id)
|
||||
|
||||
// Run the renderer. This is what draws the final graph.
|
||||
var element = d3.select('#' + id + ' g')
|
||||
render(element, g)
|
||||
|
||||
element.selectAll('g.node')
|
||||
.attr('title', function () {
|
||||
return graph.getTooltip(this.id)
|
||||
})
|
||||
|
||||
if (conf.useMaxWidth) {
|
||||
// Center the graph
|
||||
svg.attr('height', '100%')
|
||||
svg.attr('width', conf.width)
|
||||
svg.attr('viewBox', '0 0 ' + (g.graph().width + 20) + ' ' + (g.graph().height + 20))
|
||||
svg.attr('style', 'max-width:' + (g.graph().width + 20) + 'px;')
|
||||
} else {
|
||||
// Center the graph
|
||||
svg.attr('height', g.graph().height)
|
||||
if (typeof conf.width === 'undefined') {
|
||||
svg.attr('width', g.graph().width)
|
||||
} else {
|
||||
svg.attr('width', conf.width)
|
||||
}
|
||||
svg.attr('viewBox', '0 0 ' + (g.graph().width + 20) + ' ' + (g.graph().height + 20))
|
||||
}
|
||||
|
||||
// Index nodes
|
||||
graph.indexNodes('subGraph' + i)
|
||||
|
||||
for (i = 0; i < subGraphs.length; i++) {
|
||||
subG = subGraphs[i]
|
||||
|
||||
if (subG.title !== 'undefined') {
|
||||
var clusterRects = document.querySelectorAll('#' + id + ' #' + subG.id + ' rect')
|
||||
var clusterEl = document.querySelectorAll('#' + id + ' #' + subG.id)
|
||||
|
||||
var xPos = clusterRects[0].x.baseVal.value
|
||||
var yPos = clusterRects[0].y.baseVal.value
|
||||
var width = clusterRects[0].width.baseVal.value
|
||||
var cluster = d3.select(clusterEl[0])
|
||||
var te = cluster.append('text')
|
||||
te.attr('x', xPos + width / 2)
|
||||
te.attr('y', yPos + 14)
|
||||
te.attr('fill', 'black')
|
||||
te.attr('stroke', 'none')
|
||||
te.attr('id', id + 'Text')
|
||||
te.style('text-anchor', 'middle')
|
||||
|
||||
if (typeof subG.title === 'undefined') {
|
||||
te.text('Undef')
|
||||
} else {
|
||||
te.text(subG.title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add label rects for non html labels
|
||||
if (!conf.htmlLabels) {
|
||||
var labels = document.querySelectorAll('#' + id + ' .edgeLabel .label')
|
||||
var k
|
||||
for (k = 0; k < labels.length; k++) {
|
||||
var label = labels[i]
|
||||
|
||||
// Get dimensions of label
|
||||
var dim = label.getBBox()
|
||||
|
||||
var rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
|
||||
rect.setAttribute('rx', 0)
|
||||
rect.setAttribute('ry', 0)
|
||||
rect.setAttribute('width', dim.width)
|
||||
rect.setAttribute('height', dim.height)
|
||||
rect.setAttribute('style', 'fill:#e8e8e8;')
|
||||
|
||||
label.insertBefore(rect, label.firstChild)
|
||||
}
|
||||
}
|
||||
}
|
||||
406
src/diagrams/flowchart/graphDb.js
Normal file
406
src/diagrams/flowchart/graphDb.js
Normal file
@@ -0,0 +1,406 @@
|
||||
/**
|
||||
* Created by knut on 14-11-03.
|
||||
*/
|
||||
var Logger = require('../../logger')
|
||||
var log = Logger.Log
|
||||
var utils = require('../../utils')
|
||||
|
||||
var d3 = require('../../d3')
|
||||
var vertices = {}
|
||||
var edges = []
|
||||
var classes = []
|
||||
var subGraphs = []
|
||||
var tooltips = {}
|
||||
var subCount = 0
|
||||
var direction
|
||||
// Functions to be run after graph rendering
|
||||
var funs = []
|
||||
/**
|
||||
* Function called by parser when a node definition has been found
|
||||
* @param id
|
||||
* @param text
|
||||
* @param type
|
||||
* @param style
|
||||
*/
|
||||
exports.addVertex = function (id, text, type, style) {
|
||||
var txt
|
||||
|
||||
if (typeof id === 'undefined') {
|
||||
return
|
||||
}
|
||||
if (id.trim().length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof vertices[id] === 'undefined') {
|
||||
vertices[id] = { id: id, styles: [], classes: [] }
|
||||
}
|
||||
if (typeof text !== 'undefined') {
|
||||
txt = text.trim()
|
||||
|
||||
// strip quotes if string starts and exnds with a quote
|
||||
if (txt[0] === '"' && txt[txt.length - 1] === '"') {
|
||||
txt = txt.substring(1, txt.length - 1)
|
||||
}
|
||||
|
||||
vertices[id].text = txt
|
||||
}
|
||||
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) {
|
||||
vertices[id].styles.push(s)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function called by parser when a link/edge definition has been found
|
||||
* @param start
|
||||
* @param end
|
||||
* @param type
|
||||
* @param linktext
|
||||
*/
|
||||
exports.addLink = function (start, end, type, linktext) {
|
||||
log.info('Got edge...', start, end)
|
||||
var edge = { start: start, end: end, type: undefined, text: '' }
|
||||
linktext = type.text
|
||||
|
||||
if (typeof linktext !== 'undefined') {
|
||||
edge.text = linktext.trim()
|
||||
|
||||
// strip quotes if string starts and exnds with a quote
|
||||
if (edge.text[0] === '"' && edge.text[edge.text.length - 1] === '"') {
|
||||
edge.text = edge.text.substring(1, edge.text.length - 1)
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof type !== 'undefined') {
|
||||
edge.type = type.type
|
||||
edge.stroke = type.stroke
|
||||
}
|
||||
edges.push(edge)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a link's line interpolation algorithm
|
||||
* @param pos
|
||||
* @param interpolate
|
||||
*/
|
||||
exports.updateLinkInterpolate = function (pos, interp) {
|
||||
if (pos === 'default') {
|
||||
edges.defaultInterpolate = interp
|
||||
} else {
|
||||
edges[pos].interpolate = interp
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a link with a style
|
||||
* @param pos
|
||||
* @param style
|
||||
*/
|
||||
exports.updateLink = function (pos, style) {
|
||||
if (pos === 'default') {
|
||||
edges.defaultStyle = style
|
||||
} else {
|
||||
if (utils.isSubstringInArray('fill', style) === -1) {
|
||||
style.push('fill:none')
|
||||
}
|
||||
edges[pos].style = style
|
||||
}
|
||||
}
|
||||
|
||||
exports.addClass = function (id, style) {
|
||||
if (typeof classes[id] === 'undefined') {
|
||||
classes[id] = { id: id, styles: [] }
|
||||
}
|
||||
|
||||
if (typeof style !== 'undefined') {
|
||||
if (style !== null) {
|
||||
style.forEach(function (s) {
|
||||
classes[id].styles.push(s)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by parser when a graph definition is found, stores the direction of the chart.
|
||||
* @param dir
|
||||
*/
|
||||
exports.setDirection = function (dir) {
|
||||
direction = dir
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by parser when a graph definition is found, stores the direction of the chart.
|
||||
* @param dir
|
||||
*/
|
||||
exports.setClass = function (id, className) {
|
||||
if (id.indexOf(',') > 0) {
|
||||
id.split(',').forEach(function (id2) {
|
||||
if (typeof vertices[id2] !== 'undefined') {
|
||||
vertices[id2].classes.push(className)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if (typeof vertices[id] !== 'undefined') {
|
||||
vertices[id].classes.push(className)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var setTooltip = function (id, tooltip) {
|
||||
if (typeof tooltip !== 'undefined') {
|
||||
tooltips[id] = tooltip
|
||||
}
|
||||
}
|
||||
|
||||
var setClickFun = function (id, functionName) {
|
||||
if (typeof functionName === 'undefined') {
|
||||
return
|
||||
}
|
||||
if (typeof vertices[id] !== 'undefined') {
|
||||
funs.push(function (element) {
|
||||
var elem = d3.select(element).select('#' + id)
|
||||
if (elem !== null) {
|
||||
elem.on('click', function () {
|
||||
window[functionName](id)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var setLink = function (id, linkStr) {
|
||||
if (typeof linkStr === 'undefined') {
|
||||
return
|
||||
}
|
||||
if (typeof vertices[id] !== 'undefined') {
|
||||
funs.push(function (element) {
|
||||
var elem = d3.select(element).select('#' + id)
|
||||
if (elem !== null) {
|
||||
elem.on('click', function () {
|
||||
window.open(linkStr, 'newTab')
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
exports.getTooltip = function (id) {
|
||||
return tooltips[id]
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by parser when a graph definition is found, stores the direction of the chart.
|
||||
* @param dir
|
||||
*/
|
||||
exports.setClickEvent = function (id, functionName, link, tooltip) {
|
||||
if (id.indexOf(',') > 0) {
|
||||
id.split(',').forEach(function (id2) {
|
||||
setTooltip(id2, tooltip)
|
||||
setClickFun(id2, functionName)
|
||||
setLink(id2, link)
|
||||
})
|
||||
} else {
|
||||
setTooltip(id, tooltip)
|
||||
setClickFun(id, functionName)
|
||||
setLink(id, link)
|
||||
}
|
||||
}
|
||||
|
||||
exports.bindFunctions = function (element) {
|
||||
funs.forEach(function (fun) {
|
||||
fun(element)
|
||||
})
|
||||
}
|
||||
exports.getDirection = function () {
|
||||
return direction
|
||||
}
|
||||
/**
|
||||
* Retrieval function for fetching the found nodes after parsing has completed.
|
||||
* @returns {{}|*|vertices}
|
||||
*/
|
||||
exports.getVertices = function () {
|
||||
return vertices
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieval function for fetching the found links after parsing has completed.
|
||||
* @returns {{}|*|edges}
|
||||
*/
|
||||
exports.getEdges = function () {
|
||||
return edges
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieval function for fetching the found class definitions after parsing has completed.
|
||||
* @returns {{}|*|classes}
|
||||
*/
|
||||
exports.getClasses = function () {
|
||||
return classes
|
||||
}
|
||||
|
||||
var setupToolTips = function (element) {
|
||||
var tooltipElem = d3.select('.mermaidTooltip')
|
||||
if (tooltipElem[0][0] === null) {
|
||||
tooltipElem = d3.select('body')
|
||||
.append('div')
|
||||
.attr('class', 'mermaidTooltip')
|
||||
.style('opacity', 0)
|
||||
}
|
||||
|
||||
var svg = d3.select(element).select('svg')
|
||||
|
||||
var nodes = svg.selectAll('g.node')
|
||||
nodes
|
||||
.on('mouseover', function () {
|
||||
var el = d3.select(this)
|
||||
var title = el.attr('title')
|
||||
// Dont try to draw a tooltip if no data is provided
|
||||
if (title === null) {
|
||||
return
|
||||
}
|
||||
var rect = this.getBoundingClientRect()
|
||||
|
||||
tooltipElem.transition()
|
||||
.duration(200)
|
||||
.style('opacity', '.9')
|
||||
tooltipElem.html(el.attr('title'))
|
||||
.style('left', (rect.left + (rect.right - rect.left) / 2) + 'px')
|
||||
.style('top', (rect.top - 14 + document.body.scrollTop) + 'px')
|
||||
el.classed('hover', true)
|
||||
})
|
||||
.on('mouseout', function () {
|
||||
tooltipElem.transition()
|
||||
.duration(500)
|
||||
.style('opacity', 0)
|
||||
var el = d3.select(this)
|
||||
el.classed('hover', false)
|
||||
})
|
||||
}
|
||||
funs.push(setupToolTips)
|
||||
|
||||
/**
|
||||
* Clears the internal graph db so that a new graph can be parsed.
|
||||
*/
|
||||
exports.clear = function () {
|
||||
vertices = {}
|
||||
classes = {}
|
||||
edges = []
|
||||
funs = []
|
||||
funs.push(setupToolTips)
|
||||
subGraphs = []
|
||||
subCount = 0
|
||||
tooltips = []
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
exports.defaultStyle = function () {
|
||||
return 'fill:#ffa;stroke: #f66; stroke-width: 3px; stroke-dasharray: 5, 5;fill:#ffa;stroke: #666;'
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the internal graph db so that a new graph can be parsed.
|
||||
*/
|
||||
exports.addSubGraph = function (list, title) {
|
||||
function uniq (a) {
|
||||
var prims = { 'boolean': {}, 'number': {}, 'string': {} }
|
||||
var objs = []
|
||||
|
||||
return a.filter(function (item) {
|
||||
var type = typeof item
|
||||
if (item === ' ') {
|
||||
return false
|
||||
}
|
||||
if (type in prims) { return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true) } else { return objs.indexOf(item) >= 0 ? false : objs.push(item) }
|
||||
})
|
||||
}
|
||||
|
||||
var nodeList = []
|
||||
|
||||
nodeList = uniq(nodeList.concat.apply(nodeList, list))
|
||||
|
||||
var subGraph = { id: 'subGraph' + subCount, nodes: nodeList, title: title }
|
||||
subGraphs.push(subGraph)
|
||||
subCount = subCount + 1
|
||||
return subGraph.id
|
||||
}
|
||||
|
||||
var getPosForId = function (id) {
|
||||
var i
|
||||
for (i = 0; i < subGraphs.length; i++) {
|
||||
if (subGraphs[i].id === id) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
var secCount = -1
|
||||
var posCrossRef = []
|
||||
var indexNodes = function (id, pos) {
|
||||
var nodes = subGraphs[pos].nodes
|
||||
secCount = secCount + 1
|
||||
if (secCount > 2000) {
|
||||
return
|
||||
}
|
||||
posCrossRef[secCount] = pos
|
||||
// Check if match
|
||||
if (subGraphs[pos].id === id) {
|
||||
return {
|
||||
result: true,
|
||||
count: 0
|
||||
}
|
||||
}
|
||||
|
||||
var count = 0
|
||||
var posCount = 1
|
||||
while (count < nodes.length) {
|
||||
var childPos = getPosForId(nodes[count])
|
||||
// Ignore regular nodes (pos will be -1)
|
||||
if (childPos >= 0) {
|
||||
var res = indexNodes(id, childPos)
|
||||
if (res.result) {
|
||||
return {
|
||||
result: true,
|
||||
count: posCount + res.count
|
||||
}
|
||||
} else {
|
||||
posCount = posCount + res.count
|
||||
}
|
||||
}
|
||||
count = count + 1
|
||||
}
|
||||
|
||||
return {
|
||||
result: false,
|
||||
count: posCount
|
||||
}
|
||||
}
|
||||
|
||||
exports.getDepthFirstPos = function (pos) {
|
||||
return posCrossRef[pos]
|
||||
}
|
||||
exports.indexNodes = function () {
|
||||
secCount = -1
|
||||
if (subGraphs.length > 0) {
|
||||
indexNodes('none', subGraphs.length - 1, 0)
|
||||
}
|
||||
}
|
||||
|
||||
exports.getSubGraphs = function () {
|
||||
return subGraphs
|
||||
}
|
||||
|
||||
exports.parseError = function (err, hash) {
|
||||
global.mermaidAPI.parseError(err, hash)
|
||||
}
|
||||
266
src/diagrams/flowchart/parser/dot.jison
Normal file
266
src/diagrams/flowchart/parser/dot.jison
Normal file
@@ -0,0 +1,266 @@
|
||||
/* description: Parses end executes mathematical expressions. */
|
||||
|
||||
/* lexical grammar */
|
||||
%lex
|
||||
|
||||
%%
|
||||
"style" return 'STYLE';
|
||||
"linkStyle" return 'LINKSTYLE';
|
||||
"classDef" return 'CLASSDEF';
|
||||
"class" return 'CLASS';
|
||||
"click" return 'CLICK';
|
||||
"graph" return 'GRAPH';
|
||||
"digraph" return 'DIGRAPH';
|
||||
"subgraph" return 'SUBGRAPH';
|
||||
"node" return 'NODE';
|
||||
"edge" return 'EDGE';
|
||||
"LR" return 'DIR';
|
||||
"RL" return 'DIR';
|
||||
"TB" return 'DIR';
|
||||
"BT" return 'DIR';
|
||||
"TD" return 'DIR';
|
||||
"BR" return 'DIR';
|
||||
[0-9] return 'NUM';
|
||||
\# return 'BRKT';
|
||||
":" return 'COLON';
|
||||
";" return ';';
|
||||
"," return ',';
|
||||
"=" return '=';
|
||||
"*" return 'MULT';
|
||||
"." return 'DOT';
|
||||
\-\-[x] return 'ARROW_CROSS';
|
||||
\-\> return 'ARROW_POINT';
|
||||
\-\-[o] return 'ARROW_CIRCLE';
|
||||
\-\- return 'ARROW_OPEN';
|
||||
\- return 'MINUS';
|
||||
\+ return 'PLUS';
|
||||
\= return 'EQUALS';
|
||||
[\u0021-\u0027\u002A-\u002E\u003F\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|
|
||||
[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|
|
||||
[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|
|
||||
[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|
|
||||
[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]|
|
||||
[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]|
|
||||
[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]|
|
||||
[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]|
|
||||
[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]|
|
||||
[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]|
|
||||
[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]|
|
||||
[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]|
|
||||
[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]|
|
||||
[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]|
|
||||
[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]|
|
||||
[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]|
|
||||
[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]|
|
||||
[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]|
|
||||
[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]|
|
||||
[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]|
|
||||
[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]|
|
||||
[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]|
|
||||
[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]|
|
||||
[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]|
|
||||
[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]|
|
||||
[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]|
|
||||
[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]|
|
||||
[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]|
|
||||
[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]|
|
||||
[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]|
|
||||
[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]|
|
||||
[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]|
|
||||
[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]|
|
||||
[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]|
|
||||
[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]|
|
||||
[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]|
|
||||
[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]|
|
||||
[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]|
|
||||
[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]|
|
||||
[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]|
|
||||
[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]|
|
||||
[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]|
|
||||
[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]|
|
||||
[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]|
|
||||
[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]|
|
||||
[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]|
|
||||
[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]|
|
||||
[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]|
|
||||
[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]|
|
||||
[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]|
|
||||
[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]|
|
||||
[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]|
|
||||
[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]|
|
||||
[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]|
|
||||
[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]|
|
||||
[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]|
|
||||
[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]|
|
||||
[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]|
|
||||
[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]]|
|
||||
[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|
|
||||
[\uFFD2-\uFFD7\uFFDA-\uFFDC_]
|
||||
return 'ALPHA';
|
||||
"|" return 'PIPE';
|
||||
"(" return 'PS';
|
||||
")" return 'PE';
|
||||
"[" return '[';
|
||||
"]" return ']';
|
||||
"{" return '{'
|
||||
"}" return '}'
|
||||
"\"" return 'QUOTE';
|
||||
\s return 'SPACE';
|
||||
\n return 'NEWLINE';
|
||||
|
||||
<<EOF>> return 'EOF';
|
||||
|
||||
/lex
|
||||
|
||||
/* operator associations and precedence */
|
||||
|
||||
%left '^'
|
||||
|
||||
%start expressions
|
||||
|
||||
%% /* language grammar */
|
||||
|
||||
expressions
|
||||
: graph EOF
|
||||
{$$=$1;}
|
||||
;
|
||||
|
||||
|
||||
graph : graphStatement idStatement '{' stmt_list '}'
|
||||
{$$=$1;}
|
||||
| strict graphStatement idStatement '{' stmt_list '}'
|
||||
{$$=$1;}
|
||||
|graphStatement '{' stmt_list '}'
|
||||
{$$=$1;}
|
||||
;
|
||||
|
||||
graphStatement: GRAPH
|
||||
| DIGRAPH
|
||||
;
|
||||
|
||||
idStatement:
|
||||
textNoTags
|
||||
;
|
||||
textNoTags: textNoTagsToken
|
||||
{$$=$1;}
|
||||
| textNoTags textNoTagsToken
|
||||
{$$=$1+''+$2;}
|
||||
;
|
||||
|
||||
textNoTagsToken: ALPHA
|
||||
{$$=$1;}
|
||||
| NUM
|
||||
{$$=$1;}
|
||||
| COLON
|
||||
{$$ = $1;}
|
||||
| PLUS
|
||||
{$$ = $1;}
|
||||
| EQUALS
|
||||
{$$ = $1;}
|
||||
| MULT
|
||||
{$$ = $1;}
|
||||
| DOT
|
||||
{$$ = $1;}
|
||||
| BRKT
|
||||
{$$ = '<br>';}
|
||||
| SPACE
|
||||
{$$ = $1;}
|
||||
| MINUS
|
||||
{$$ = $1;}
|
||||
| keywords
|
||||
{$$ = $1;}
|
||||
;
|
||||
|
||||
stmt_list : stmt
|
||||
| stmt ';' stmt_list
|
||||
;
|
||||
stmt : node_stmt
|
||||
| edge_stmt
|
||||
| attr_stmt
|
||||
| idStatement '=' idStatement
|
||||
| subgraph
|
||||
;
|
||||
|
||||
attr_stmt : GRAPH attr_list
|
||||
| NODE attr_list
|
||||
| EDGE attr_list
|
||||
;
|
||||
|
||||
attr_list
|
||||
: '[' a_list ']' attr_list
|
||||
| '[' ']' attr_list
|
||||
| '[' a_list ']'
|
||||
| '[' ']'
|
||||
;
|
||||
|
||||
a_list
|
||||
: idStatement '=' idStatement ';' a_list
|
||||
| idStatement '=' idStatement ',' a_list
|
||||
| idStatement '=' idStatement
|
||||
;
|
||||
|
||||
edge_stmt
|
||||
: subgraph edgeRHS attr_list
|
||||
| node_id edgeRHS attr_list
|
||||
{$$='oy';}
|
||||
| node_id edgeRHS
|
||||
{
|
||||
yy.addLink($1,$2.id,$2.op);
|
||||
$$='oy';}
|
||||
| subgraph edgeRHS
|
||||
;
|
||||
|
||||
edgeRHS
|
||||
: edgeop node_id edgeRHS
|
||||
{
|
||||
yy.addLink($2,$3.id,$3.op);
|
||||
$$={op:$1,id:$2};
|
||||
}
|
||||
| edgeop subgraph edgeRHS
|
||||
| edgeop node_id
|
||||
{
|
||||
$$={op:$1,id:$2};
|
||||
}
|
||||
| edgeop subgraph
|
||||
;
|
||||
|
||||
node_stmt
|
||||
: node_id attr_list
|
||||
| node_id
|
||||
;
|
||||
node_id
|
||||
: idStatement port
|
||||
{yy.addVertex($1);$$=$1;}
|
||||
| idStatement
|
||||
{yy.addVertex($1);$$=$1;}
|
||||
;
|
||||
|
||||
port
|
||||
: ':' idStatement ':' compass_pt
|
||||
| ':' idStatement
|
||||
| ':' compass_pt
|
||||
;
|
||||
subgraph
|
||||
: SUBGRAPH idStatement '{' stmt_list '}'
|
||||
| SUBGRAPH '{' stmt_list '}'
|
||||
| '{' stmt_list '}'
|
||||
;
|
||||
compass_pt
|
||||
: 'n'
|
||||
| ne
|
||||
| e
|
||||
| se
|
||||
| s
|
||||
| sw
|
||||
| w
|
||||
| nw
|
||||
| c
|
||||
| _
|
||||
;
|
||||
edgeop
|
||||
: ARROW_POINT
|
||||
{$$='arrow';}
|
||||
| ARROW_OPEN
|
||||
{$$='arrow_open';}
|
||||
;
|
||||
%%
|
||||
736
src/diagrams/flowchart/parser/dot.js
Normal file
736
src/diagrams/flowchart/parser/dot.js
Normal file
File diff suppressed because one or more lines are too long
452
src/diagrams/flowchart/parser/flow.jison
Normal file
452
src/diagrams/flowchart/parser/flow.jison
Normal file
@@ -0,0 +1,452 @@
|
||||
/** mermaid
|
||||
* https://mermaidjs.github.io/
|
||||
* (c) 2015 Knut Sveidqvist
|
||||
* MIT license.
|
||||
*/
|
||||
|
||||
/* lexical grammar */
|
||||
%lex
|
||||
%x string
|
||||
|
||||
%%
|
||||
\%\%[^\n]* /* do nothing */
|
||||
["] this.begin("string");
|
||||
<string>["] this.popState();
|
||||
<string>[^"]* return "STR";
|
||||
"style" return 'STYLE';
|
||||
"default" return 'DEFAULT';
|
||||
"linkStyle" return 'LINKSTYLE';
|
||||
"interpolate" return 'INTERPOLATE';
|
||||
"classDef" return 'CLASSDEF';
|
||||
"class" return 'CLASS';
|
||||
"click" return 'CLICK';
|
||||
"graph" return 'GRAPH';
|
||||
"subgraph" return 'subgraph';
|
||||
"end"\b\s* return 'end';
|
||||
"LR" return 'DIR';
|
||||
"RL" return 'DIR';
|
||||
"TB" return 'DIR';
|
||||
"BT" return 'DIR';
|
||||
"TD" return 'DIR';
|
||||
"BR" return 'DIR';
|
||||
[0-9]+ return 'NUM';
|
||||
\# return 'BRKT';
|
||||
":" return 'COLON';
|
||||
";" return 'SEMI';
|
||||
"," return 'COMMA';
|
||||
"*" return 'MULT';
|
||||
"<" return 'TAGSTART';
|
||||
">" return 'TAGEND';
|
||||
"^" return 'UP';
|
||||
"v" return 'DOWN';
|
||||
\s*\-\-[x]\s* return 'ARROW_CROSS';
|
||||
\s*\-\-\>\s* return 'ARROW_POINT';
|
||||
\s*\-\-[o]\s* return 'ARROW_CIRCLE';
|
||||
\s*\-\-\-\s* return 'ARROW_OPEN';
|
||||
\s*\-\.\-[x]\s* return 'DOTTED_ARROW_CROSS';
|
||||
\s*\-\.\-\>\s* return 'DOTTED_ARROW_POINT';
|
||||
\s*\-\.\-[o]\s* return 'DOTTED_ARROW_CIRCLE';
|
||||
\s*\-\.\-\s* return 'DOTTED_ARROW_OPEN';
|
||||
\s*.\-[x]\s* return 'DOTTED_ARROW_CROSS';
|
||||
\s*\.\-\>\s* return 'DOTTED_ARROW_POINT';
|
||||
\s*\.\-[o]\s* return 'DOTTED_ARROW_CIRCLE';
|
||||
\s*\.\-\s* return 'DOTTED_ARROW_OPEN';
|
||||
\s*\=\=[x]\s* return 'THICK_ARROW_CROSS';
|
||||
\s*\=\=\>\s* return 'THICK_ARROW_POINT';
|
||||
\s*\=\=[o]\s* return 'THICK_ARROW_CIRCLE';
|
||||
\s*\=\=[\=]\s* return 'THICK_ARROW_OPEN';
|
||||
\s*\-\-\s* return '--';
|
||||
\s*\-\.\s* return '-.';
|
||||
\s*\=\=\s* return '==';
|
||||
"(-" return '(-';
|
||||
"-)" return '-)';
|
||||
\- return 'MINUS';
|
||||
"." return 'DOT';
|
||||
\+ return 'PLUS';
|
||||
\% return 'PCT';
|
||||
"=" return 'EQUALS';
|
||||
\= return 'EQUALS';
|
||||
[A-Za-z]+ return 'ALPHA';
|
||||
[!"#$%&'*+,-.`?\\_/] return 'PUNCTUATION';
|
||||
[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|
|
||||
[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|
|
||||
[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|
|
||||
[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|
|
||||
[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]|
|
||||
[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]|
|
||||
[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]|
|
||||
[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]|
|
||||
[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]|
|
||||
[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]|
|
||||
[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]|
|
||||
[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]|
|
||||
[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]|
|
||||
[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]|
|
||||
[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]|
|
||||
[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]|
|
||||
[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]|
|
||||
[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]|
|
||||
[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]|
|
||||
[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]|
|
||||
[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]|
|
||||
[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]|
|
||||
[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]|
|
||||
[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]|
|
||||
[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]|
|
||||
[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]|
|
||||
[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]|
|
||||
[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]|
|
||||
[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]|
|
||||
[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]|
|
||||
[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]|
|
||||
[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]|
|
||||
[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]|
|
||||
[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]|
|
||||
[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]|
|
||||
[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]|
|
||||
[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]|
|
||||
[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]|
|
||||
[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]|
|
||||
[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]|
|
||||
[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]|
|
||||
[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]|
|
||||
[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]|
|
||||
[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]|
|
||||
[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]|
|
||||
[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]|
|
||||
[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]|
|
||||
[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]|
|
||||
[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]|
|
||||
[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]|
|
||||
[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]|
|
||||
[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]|
|
||||
[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]|
|
||||
[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]|
|
||||
[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]|
|
||||
[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]|
|
||||
[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]|
|
||||
[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]|
|
||||
[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]|
|
||||
[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|
|
||||
[\uFFD2-\uFFD7\uFFDA-\uFFDC]
|
||||
return 'UNICODE_TEXT';
|
||||
"|" return 'PIPE';
|
||||
"(" return 'PS';
|
||||
")" return 'PE';
|
||||
"[" return 'SQS';
|
||||
"]" return 'SQE';
|
||||
"{" return 'DIAMOND_START'
|
||||
"}" return 'DIAMOND_STOP'
|
||||
"\"" return 'QUOTE';
|
||||
\n+ return 'NEWLINE';
|
||||
\s return 'SPACE';
|
||||
<<EOF>> return 'EOF';
|
||||
|
||||
/lex
|
||||
|
||||
/* operator associations and precedence */
|
||||
|
||||
%left '^'
|
||||
|
||||
%start mermaidDoc
|
||||
|
||||
%% /* language grammar */
|
||||
|
||||
mermaidDoc: graphConfig document;
|
||||
|
||||
document
|
||||
: /* empty */
|
||||
{ $$ = [];}
|
||||
| document line
|
||||
{
|
||||
if($2 !== []){
|
||||
$1.push($2);
|
||||
}
|
||||
$$=$1;}
|
||||
;
|
||||
|
||||
line
|
||||
: statement
|
||||
{$$=$1;}
|
||||
| SEMI
|
||||
| NEWLINE
|
||||
| SPACE
|
||||
| EOF
|
||||
;
|
||||
|
||||
graphConfig
|
||||
: SPACE graphConfig
|
||||
| NEWLINE graphConfig
|
||||
| GRAPH SPACE DIR FirstStmtSeperator
|
||||
{ yy.setDirection($3);$$ = $3;}
|
||||
| GRAPH SPACE TAGEND FirstStmtSeperator
|
||||
{ yy.setDirection("LR");$$ = $3;}
|
||||
| GRAPH SPACE TAGSTART FirstStmtSeperator
|
||||
{ yy.setDirection("RL");$$ = $3;}
|
||||
| GRAPH SPACE UP FirstStmtSeperator
|
||||
{ yy.setDirection("BT");$$ = $3;}
|
||||
| GRAPH SPACE DOWN FirstStmtSeperator
|
||||
{ yy.setDirection("TB");$$ = $3;}
|
||||
;
|
||||
|
||||
ending: endToken ending
|
||||
| endToken
|
||||
;
|
||||
|
||||
endToken: NEWLINE | SPACE | EOF;
|
||||
|
||||
FirstStmtSeperator
|
||||
: SEMI | NEWLINE | spaceList NEWLINE ;
|
||||
|
||||
|
||||
spaceListNewline
|
||||
: SPACE spaceListNewline
|
||||
| NEWLINE spaceListNewline
|
||||
| NEWLINE
|
||||
| SPACE
|
||||
;
|
||||
|
||||
|
||||
spaceList
|
||||
: SPACE spaceList
|
||||
| SPACE
|
||||
;
|
||||
|
||||
statement
|
||||
: verticeStatement separator
|
||||
{$$=$1}
|
||||
| styleStatement separator
|
||||
{$$=[];}
|
||||
| linkStyleStatement separator
|
||||
{$$=[];}
|
||||
| classDefStatement separator
|
||||
{$$=[];}
|
||||
| classStatement separator
|
||||
{$$=[];}
|
||||
| clickStatement separator
|
||||
{$$=[];}
|
||||
| subgraph text separator document end
|
||||
{$$=yy.addSubGraph($4,$2);}
|
||||
| subgraph separator document end
|
||||
{$$=yy.addSubGraph($3,undefined);}
|
||||
;
|
||||
|
||||
separator: NEWLINE | SEMI | EOF ;
|
||||
|
||||
verticeStatement:
|
||||
vertex link vertex
|
||||
{ yy.addLink($1,$3,$2);$$ = [$1,$3];}
|
||||
| vertex
|
||||
{$$ = [$1];}
|
||||
;
|
||||
|
||||
vertex: alphaNum SQS text SQE
|
||||
{$$ = $1;yy.addVertex($1,$3,'square');}
|
||||
| alphaNum SQS text SQE spaceList
|
||||
{$$ = $1;yy.addVertex($1,$3,'square');}
|
||||
| alphaNum PS PS text PE PE
|
||||
{$$ = $1;yy.addVertex($1,$4,'circle');}
|
||||
| alphaNum PS PS text PE PE spaceList
|
||||
{$$ = $1;yy.addVertex($1,$4,'circle');}
|
||||
| alphaNum '(-' text '-)'
|
||||
{$$ = $1;yy.addVertex($1,$3,'ellipse');}
|
||||
| alphaNum '(-' text '-)' spaceList
|
||||
{$$ = $1;yy.addVertex($1,$3,'ellipse');}
|
||||
| alphaNum PS text PE
|
||||
{$$ = $1;yy.addVertex($1,$3,'round');}
|
||||
| alphaNum PS text PE spaceList
|
||||
{$$ = $1;yy.addVertex($1,$3,'round');}
|
||||
| alphaNum DIAMOND_START text DIAMOND_STOP
|
||||
{$$ = $1;yy.addVertex($1,$3,'diamond');}
|
||||
| alphaNum DIAMOND_START text DIAMOND_STOP spaceList
|
||||
{$$ = $1;yy.addVertex($1,$3,'diamond');}
|
||||
| alphaNum TAGEND text SQE
|
||||
{$$ = $1;yy.addVertex($1,$3,'odd');}
|
||||
| alphaNum TAGEND text SQE spaceList
|
||||
{$$ = $1;yy.addVertex($1,$3,'odd');}
|
||||
/* | alphaNum SQS text TAGSTART
|
||||
{$$ = $1;yy.addVertex($1,$3,'odd_right');}
|
||||
| alphaNum SQS text TAGSTART spaceList
|
||||
{$$ = $1;yy.addVertex($1,$3,'odd_right');} */
|
||||
| alphaNum
|
||||
{$$ = $1;yy.addVertex($1);}
|
||||
| alphaNum spaceList
|
||||
{$$ = $1;yy.addVertex($1);}
|
||||
;
|
||||
|
||||
alphaNum
|
||||
: alphaNumStatement
|
||||
{$$=$1;}
|
||||
| alphaNum alphaNumStatement
|
||||
{$$=$1+''+$2;}
|
||||
;
|
||||
|
||||
alphaNumStatement
|
||||
: DIR
|
||||
{$$=$1;}
|
||||
| alphaNumToken
|
||||
{$$=$1;}
|
||||
| DOWN
|
||||
{$$='v';}
|
||||
| MINUS
|
||||
{$$='-';}
|
||||
;
|
||||
|
||||
link: linkStatement arrowText
|
||||
{$1.text = $2;$$ = $1;}
|
||||
| linkStatement TESTSTR SPACE
|
||||
{$1.text = $2;$$ = $1;}
|
||||
| linkStatement arrowText SPACE
|
||||
{$1.text = $2;$$ = $1;}
|
||||
| linkStatement
|
||||
{$$ = $1;}
|
||||
| '--' text ARROW_POINT
|
||||
{$$ = {"type":"arrow","stroke":"normal","text":$2};}
|
||||
| '--' text ARROW_CIRCLE
|
||||
{$$ = {"type":"arrow_circle","stroke":"normal","text":$2};}
|
||||
| '--' text ARROW_CROSS
|
||||
{$$ = {"type":"arrow_cross","stroke":"normal","text":$2};}
|
||||
| '--' text ARROW_OPEN
|
||||
{$$ = {"type":"arrow_open","stroke":"normal","text":$2};}
|
||||
| '-.' text DOTTED_ARROW_POINT
|
||||
{$$ = {"type":"arrow","stroke":"dotted","text":$2};}
|
||||
| '-.' text DOTTED_ARROW_CIRCLE
|
||||
{$$ = {"type":"arrow_circle","stroke":"dotted","text":$2};}
|
||||
| '-.' text DOTTED_ARROW_CROSS
|
||||
{$$ = {"type":"arrow_cross","stroke":"dotted","text":$2};}
|
||||
| '-.' text DOTTED_ARROW_OPEN
|
||||
{$$ = {"type":"arrow_open","stroke":"dotted","text":$2};}
|
||||
| '==' text THICK_ARROW_POINT
|
||||
{$$ = {"type":"arrow","stroke":"thick","text":$2};}
|
||||
| '==' text THICK_ARROW_CIRCLE
|
||||
{$$ = {"type":"arrow_circle","stroke":"thick","text":$2};}
|
||||
| '==' text THICK_ARROW_CROSS
|
||||
{$$ = {"type":"arrow_cross","stroke":"thick","text":$2};}
|
||||
| '==' text THICK_ARROW_OPEN
|
||||
{$$ = {"type":"arrow_open","stroke":"thick","text":$2};}
|
||||
;
|
||||
|
||||
linkStatement: ARROW_POINT
|
||||
{$$ = {"type":"arrow","stroke":"normal"};}
|
||||
| ARROW_CIRCLE
|
||||
{$$ = {"type":"arrow_circle","stroke":"normal"};}
|
||||
| ARROW_CROSS
|
||||
{$$ = {"type":"arrow_cross","stroke":"normal"};}
|
||||
| ARROW_OPEN
|
||||
{$$ = {"type":"arrow_open","stroke":"normal"};}
|
||||
| DOTTED_ARROW_POINT
|
||||
{$$ = {"type":"arrow","stroke":"dotted"};}
|
||||
| DOTTED_ARROW_CIRCLE
|
||||
{$$ = {"type":"arrow_circle","stroke":"dotted"};}
|
||||
| DOTTED_ARROW_CROSS
|
||||
{$$ = {"type":"arrow_cross","stroke":"dotted"};}
|
||||
| DOTTED_ARROW_OPEN
|
||||
{$$ = {"type":"arrow_open","stroke":"dotted"};}
|
||||
| THICK_ARROW_POINT
|
||||
{$$ = {"type":"arrow","stroke":"thick"};}
|
||||
| THICK_ARROW_CIRCLE
|
||||
{$$ = {"type":"arrow_circle","stroke":"thick"};}
|
||||
| THICK_ARROW_CROSS
|
||||
{$$ = {"type":"arrow_cross","stroke":"thick"};}
|
||||
| THICK_ARROW_OPEN
|
||||
{$$ = {"type":"arrow_open","stroke":"thick"};}
|
||||
;
|
||||
|
||||
arrowText:
|
||||
PIPE text PIPE
|
||||
{$$ = $2;}
|
||||
;
|
||||
|
||||
text: textToken
|
||||
{$$=$1;}
|
||||
| text textToken
|
||||
{$$=$1+''+$2;}
|
||||
| STR
|
||||
{$$=$1;}
|
||||
;
|
||||
|
||||
|
||||
|
||||
commentText: commentToken
|
||||
{$$=$1;}
|
||||
| commentText commentToken
|
||||
{$$=$1+''+$2;}
|
||||
;
|
||||
|
||||
|
||||
keywords
|
||||
: STYLE | LINKSTYLE | CLASSDEF | CLASS | CLICK | GRAPH | DIR | subgraph | end | DOWN | UP;
|
||||
|
||||
|
||||
textNoTags: textNoTagsToken
|
||||
{$$=$1;}
|
||||
| textNoTags textNoTagsToken
|
||||
{$$=$1+''+$2;}
|
||||
;
|
||||
|
||||
|
||||
classDefStatement:CLASSDEF SPACE DEFAULT SPACE stylesOpt
|
||||
{$$ = $1;yy.addClass($3,$5);}
|
||||
| CLASSDEF SPACE alphaNum SPACE stylesOpt
|
||||
{$$ = $1;yy.addClass($3,$5);}
|
||||
;
|
||||
|
||||
classStatement:CLASS SPACE alphaNum SPACE alphaNum
|
||||
{$$ = $1;yy.setClass($3, $5);}
|
||||
;
|
||||
|
||||
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 );}
|
||||
;
|
||||
|
||||
styleStatement:STYLE SPACE alphaNum SPACE stylesOpt
|
||||
{$$ = $1;yy.addVertex($3,undefined,undefined,$5);}
|
||||
| STYLE SPACE HEX SPACE stylesOpt
|
||||
{$$ = $1;yy.updateLink($3,$5);}
|
||||
;
|
||||
|
||||
linkStyleStatement
|
||||
: LINKSTYLE SPACE DEFAULT SPACE stylesOpt
|
||||
{$$ = $1;yy.updateLink($3,$5);}
|
||||
| LINKSTYLE SPACE NUM 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 DEFAULT SPACE INTERPOLATE SPACE alphaNum
|
||||
{$$ = $1;yy.updateLinkInterpolate($3,$7);}
|
||||
| LINKSTYLE SPACE NUM SPACE INTERPOLATE SPACE alphaNum
|
||||
{$$ = $1;yy.updateLinkInterpolate($3,$7);}
|
||||
;
|
||||
|
||||
commentStatement: PCT PCT commentText;
|
||||
|
||||
stylesOpt: style
|
||||
{$$ = [$1]}
|
||||
| stylesOpt COMMA style
|
||||
{$1.push($3);$$ = $1;}
|
||||
;
|
||||
|
||||
style: styleComponent
|
||||
|style styleComponent
|
||||
{$$ = $1 + $2;}
|
||||
;
|
||||
|
||||
styleComponent: ALPHA | COLON | MINUS | NUM | UNIT | SPACE | HEX | BRKT | DOT | STYLE | PCT ;
|
||||
|
||||
/* Token lists */
|
||||
|
||||
commentToken : textToken | graphCodeTokens ;
|
||||
|
||||
textToken : textNoTagsToken | TAGSTART | TAGEND | '==' | '--' | PCT | DEFAULT;
|
||||
|
||||
textNoTagsToken: alphaNumToken | SPACE | MINUS | keywords ;
|
||||
|
||||
alphaNumToken : ALPHA | PUNCTUATION | UNICODE_TEXT | NUM | COLON | COMMA | PLUS | EQUALS | MULT | DOT | BRKT ;
|
||||
|
||||
graphCodeTokens: PIPE | PS | PE | SQS | SQE | DIAMOND_START | DIAMOND_STOP | TAG_START | TAG_END | ARROW_CROSS | ARROW_POINT | ARROW_CIRCLE | ARROW_OPEN | QUOTE | SEMI ;
|
||||
%%
|
||||
957
src/diagrams/flowchart/parser/flow.js
Normal file
957
src/diagrams/flowchart/parser/flow.js
Normal file
File diff suppressed because one or more lines are too long
1342
src/diagrams/flowchart/parser/flow.spec.js
Normal file
1342
src/diagrams/flowchart/parser/flow.spec.js
Normal file
File diff suppressed because it is too large
Load Diff
23
src/diagrams/gantt/exGantt.md
Normal file
23
src/diagrams/gantt/exGantt.md
Normal file
@@ -0,0 +1,23 @@
|
||||
```
|
||||
gantt
|
||||
dateFormat yyyy-mm-dd
|
||||
title Adding gantt diagram functionality to mermaid
|
||||
|
||||
section Design
|
||||
Design jison grammar :des1, 2014-01-01, 2014-01-04
|
||||
Create example text :des2, 2014-01-01, 3d
|
||||
Bounce gantt example with users :des3, after des2, 5d
|
||||
|
||||
section Implementation
|
||||
update build script :2014-01-02,1h
|
||||
Implement parser and jison :after des1, 2d
|
||||
Create tests for parser :3d
|
||||
Create renderer :5d
|
||||
Create tests for renderer :2d
|
||||
Add to mermaid core :1d
|
||||
|
||||
section Documentation
|
||||
Describe gantt syntax :a1, 2014-01-01, 3d
|
||||
Add gantt diagram to demo page :after a1 , 2h
|
||||
Add gantt to diagram to demo page :after a1 , 2h
|
||||
```
|
||||
62
src/diagrams/gantt/gant.css
Normal file
62
src/diagrams/gantt/gant.css
Normal file
@@ -0,0 +1,62 @@
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body {
|
||||
background: #fff;
|
||||
font-family: 'Open-Sans',sans-serif;
|
||||
|
||||
}
|
||||
|
||||
#container{
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
width:800px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
|
||||
.svg {
|
||||
width:800px;
|
||||
height:400px;
|
||||
overflow: visible;
|
||||
position:absolute;
|
||||
}
|
||||
|
||||
.grid .tick {
|
||||
stroke: lightgrey;
|
||||
opacity: 0.3;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
.grid path {
|
||||
stroke-width: 0;
|
||||
}
|
||||
|
||||
|
||||
#tag {
|
||||
color: white;
|
||||
background: #FA283D;
|
||||
width: 150px;
|
||||
position: absolute;
|
||||
display: none;
|
||||
padding:3px 6px;
|
||||
margin-left: -80px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#tag:before {
|
||||
border: solid transparent;
|
||||
content: ' ';
|
||||
height: 0;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
border-width: 10px;
|
||||
border-bottom-color: #FA283D;
|
||||
top: -20px;
|
||||
}
|
||||
|
||||
52
src/diagrams/gantt/gantt.spec.js
Normal file
52
src/diagrams/gantt/gantt.spec.js
Normal file
@@ -0,0 +1,52 @@
|
||||
/* eslint-env jasmine */
|
||||
/**
|
||||
* Created by knut on 14-11-18.
|
||||
*/
|
||||
describe('when parsing a gantt diagram it', function () {
|
||||
var gantt
|
||||
beforeEach(function () {
|
||||
gantt = require('./parser/gantt').parser
|
||||
gantt.yy = require('./ganttDb')
|
||||
})
|
||||
|
||||
it('should handle an dateFormat definition', function () {
|
||||
var str = 'gantt\ndateFormat yyyy-mm-dd'
|
||||
|
||||
gantt.parse(str)
|
||||
})
|
||||
it('should handle an dateFormat definition', function () {
|
||||
var str = 'gantt\ndateFormat yyyy-mm-dd\ntitle Adding gantt diagram functionality to mermaid'
|
||||
|
||||
gantt.parse(str)
|
||||
})
|
||||
it('should handle an dateFormat definition', function () {
|
||||
var str = 'gantt\ndateFormat yyyy-mm-dd\ntitle Adding gantt diagram functionality to mermaid'
|
||||
|
||||
gantt.parse(str)
|
||||
})
|
||||
it('should handle an section definition', function () {
|
||||
var str = 'gantt\ndateFormat yyyy-mm-dd\ntitle Adding gantt diagram functionality to mermaid'
|
||||
|
||||
gantt.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 () {
|
||||
var 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'
|
||||
|
||||
gantt.parse(str)
|
||||
})
|
||||
})
|
||||
358
src/diagrams/gantt/ganttDb.js
Normal file
358
src/diagrams/gantt/ganttDb.js
Normal file
@@ -0,0 +1,358 @@
|
||||
/**
|
||||
* Created by knut on 15-01-14.
|
||||
*/
|
||||
var moment = require('moment')
|
||||
var Logger = require('../../logger')
|
||||
var log = Logger.Log
|
||||
|
||||
var dateFormat = ''
|
||||
var title = ''
|
||||
var sections = []
|
||||
var tasks = []
|
||||
var currentSection = ''
|
||||
|
||||
exports.clear = function () {
|
||||
sections = []
|
||||
tasks = []
|
||||
currentSection = ''
|
||||
title = ''
|
||||
taskCnt = 0
|
||||
lastTask = undefined
|
||||
lastTaskID = undefined
|
||||
rawTasks = []
|
||||
}
|
||||
|
||||
exports.setDateFormat = function (txt) {
|
||||
dateFormat = txt
|
||||
}
|
||||
|
||||
exports.getDateFormat = function () {
|
||||
return dateFormat
|
||||
}
|
||||
exports.setTitle = function (txt) {
|
||||
title = txt
|
||||
}
|
||||
|
||||
exports.getTitle = function () {
|
||||
return title
|
||||
}
|
||||
|
||||
exports.addSection = function (txt) {
|
||||
currentSection = txt
|
||||
sections.push(txt)
|
||||
}
|
||||
|
||||
exports.getTasks = function () {
|
||||
var allItemsPricessed = compileTasks()
|
||||
var maxDepth = 10
|
||||
var iterationCount = 0
|
||||
while (!allItemsPricessed && (iterationCount < maxDepth)) {
|
||||
allItemsPricessed = compileTasks()
|
||||
iterationCount++
|
||||
}
|
||||
|
||||
tasks = rawTasks
|
||||
|
||||
return tasks
|
||||
}
|
||||
|
||||
var getStartDate = function (prevTime, dateFormat, str) {
|
||||
str = str.trim()
|
||||
|
||||
// Test for after
|
||||
var re = /^after\s+([\d\w-]+)/
|
||||
var afterStatement = re.exec(str.trim())
|
||||
|
||||
if (afterStatement !== null) {
|
||||
var task = exports.findTaskById(afterStatement[1])
|
||||
|
||||
if (typeof task === 'undefined') {
|
||||
var dt = new Date()
|
||||
dt.setHours(0, 0, 0, 0)
|
||||
return dt
|
||||
}
|
||||
return task.endTime
|
||||
}
|
||||
|
||||
// Check for actual date set
|
||||
if (moment(str, dateFormat.trim(), true).isValid()) {
|
||||
return moment(str, dateFormat.trim(), true).toDate()
|
||||
} else {
|
||||
log.debug('Invalid date:' + str)
|
||||
log.debug('With date format:' + dateFormat.trim())
|
||||
}
|
||||
|
||||
// Default date - now
|
||||
return new Date()
|
||||
}
|
||||
|
||||
var 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()
|
||||
}
|
||||
|
||||
var d = moment(prevTime)
|
||||
// Check for length
|
||||
var re = /^([\d]+)([wdhms])/
|
||||
var durationStatement = re.exec(str.trim())
|
||||
|
||||
if (durationStatement !== null) {
|
||||
switch (durationStatement[2]) {
|
||||
case 's':
|
||||
d.add(durationStatement[1], 'seconds')
|
||||
break
|
||||
case 'm':
|
||||
d.add(durationStatement[1], 'minutes')
|
||||
break
|
||||
case 'h':
|
||||
d.add(durationStatement[1], 'hours')
|
||||
break
|
||||
case 'd':
|
||||
d.add(durationStatement[1], 'days')
|
||||
break
|
||||
case 'w':
|
||||
d.add(durationStatement[1], 'weeks')
|
||||
break
|
||||
}
|
||||
return d.toDate()
|
||||
}
|
||||
// Default date - now
|
||||
return d.toDate()
|
||||
}
|
||||
|
||||
var taskCnt = 0
|
||||
var parseId = function (idStr) {
|
||||
if (typeof idStr === 'undefined') {
|
||||
taskCnt = taskCnt + 1
|
||||
return 'task' + taskCnt
|
||||
}
|
||||
return idStr
|
||||
}
|
||||
// id, startDate, endDate
|
||||
// id, startDate, length
|
||||
// id, after x, endDate
|
||||
// id, after x, length
|
||||
// startDate, endDate
|
||||
// startDate, length
|
||||
// after x, endDate
|
||||
// after x, length
|
||||
// endDate
|
||||
// length
|
||||
|
||||
var compileData = function (prevTask, dataStr) {
|
||||
var ds
|
||||
|
||||
if (dataStr.substr(0, 1) === ':') {
|
||||
ds = dataStr.substr(1, dataStr.length)
|
||||
} else {
|
||||
ds = dataStr
|
||||
}
|
||||
|
||||
var data = ds.split(',')
|
||||
|
||||
var task = {}
|
||||
var df = exports.getDateFormat()
|
||||
|
||||
// Get tags like active, done cand crit
|
||||
var 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
|
||||
}
|
||||
}
|
||||
var i
|
||||
for (i = 0; i < data.length; i++) {
|
||||
data[i] = data[i].trim()
|
||||
}
|
||||
|
||||
switch (data.length) {
|
||||
case 1:
|
||||
task.id = parseId()
|
||||
task.startTime = prevTask.endTime
|
||||
task.endTime = getEndDate(task.startTime, df, data[0])
|
||||
break
|
||||
case 2:
|
||||
task.id = parseId()
|
||||
task.startTime = getStartDate(undefined, df, data[0])
|
||||
task.endTime = getEndDate(task.startTime, df, data[1])
|
||||
break
|
||||
case 3:
|
||||
task.id = parseId(data[0])
|
||||
task.startTime = getStartDate(undefined, df, data[1])
|
||||
task.endTime = getEndDate(task.startTime, df, data[2])
|
||||
break
|
||||
default:
|
||||
}
|
||||
|
||||
return task
|
||||
}
|
||||
|
||||
var parseData = function (prevTaskId, dataStr) {
|
||||
var ds
|
||||
|
||||
if (dataStr.substr(0, 1) === ':') {
|
||||
ds = dataStr.substr(1, dataStr.length)
|
||||
} else {
|
||||
ds = dataStr
|
||||
}
|
||||
|
||||
var data = ds.split(',')
|
||||
|
||||
var task = {}
|
||||
|
||||
// Get tags like active, done cand crit
|
||||
var 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
|
||||
}
|
||||
}
|
||||
var i
|
||||
for (i = 0; i < data.length; i++) {
|
||||
data[i] = data[i].trim()
|
||||
}
|
||||
|
||||
switch (data.length) {
|
||||
case 1:
|
||||
task.id = parseId()
|
||||
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] }
|
||||
break
|
||||
case 3:
|
||||
task.id = parseId(data[0])
|
||||
task.startTime = { type: 'getStartDate', startData: data[1] }
|
||||
task.endTime = { data: data[2] }
|
||||
break
|
||||
default:
|
||||
}
|
||||
|
||||
return task
|
||||
}
|
||||
|
||||
var lastTask
|
||||
var lastTaskID
|
||||
var rawTasks = []
|
||||
var taskDb = {}
|
||||
exports.addTask = function (descr, data) {
|
||||
var rawTask = {
|
||||
section: currentSection,
|
||||
type: currentSection,
|
||||
processed: false,
|
||||
raw: { data: data },
|
||||
task: descr
|
||||
}
|
||||
var taskInfo = parseData(lastTaskID, data)
|
||||
rawTask.raw.startTime = taskInfo.startTime
|
||||
rawTask.raw.endTime = taskInfo.endTime
|
||||
rawTask.id = taskInfo.id
|
||||
rawTask.prevTaskId = lastTaskID
|
||||
rawTask.active = taskInfo.active
|
||||
rawTask.done = taskInfo.done
|
||||
rawTask.crit = taskInfo.crit
|
||||
|
||||
var pos = rawTasks.push(rawTask)
|
||||
|
||||
lastTaskID = rawTask.id
|
||||
// Store cross ref
|
||||
taskDb[rawTask.id] = pos - 1
|
||||
}
|
||||
|
||||
exports.findTaskById = function (id) {
|
||||
var pos = taskDb[id]
|
||||
return rawTasks[pos]
|
||||
}
|
||||
|
||||
exports.addTaskOrg = function (descr, data) {
|
||||
var newTask = {
|
||||
section: currentSection,
|
||||
type: currentSection,
|
||||
description: descr,
|
||||
task: descr
|
||||
}
|
||||
var taskInfo = compileData(lastTask, data)
|
||||
newTask.startTime = taskInfo.startTime
|
||||
newTask.endTime = taskInfo.endTime
|
||||
newTask.id = taskInfo.id
|
||||
newTask.active = taskInfo.active
|
||||
newTask.done = taskInfo.done
|
||||
newTask.crit = taskInfo.crit
|
||||
lastTask = newTask
|
||||
tasks.push(newTask)
|
||||
}
|
||||
|
||||
var compileTasks = function () {
|
||||
var df = exports.getDateFormat()
|
||||
|
||||
var compileTask = function (pos) {
|
||||
var task = rawTasks[pos]
|
||||
var startTime = ''
|
||||
switch (rawTasks[pos].raw.startTime.type) {
|
||||
case 'prevTaskEnd':
|
||||
var prevTask = exports.findTaskById(task.prevTaskId)
|
||||
task.startTime = prevTask.endTime
|
||||
break
|
||||
case 'getStartDate':
|
||||
startTime = getStartDate(undefined, df, rawTasks[pos].raw.startTime.startData)
|
||||
if (startTime) {
|
||||
rawTasks[pos].startTime = startTime
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if (rawTasks[pos].startTime) {
|
||||
rawTasks[pos].endTime = getEndDate(rawTasks[pos].startTime, df, rawTasks[pos].raw.endTime.data)
|
||||
if (rawTasks[pos].endTime) {
|
||||
rawTasks[pos].processed = true
|
||||
}
|
||||
}
|
||||
|
||||
return rawTasks[pos].processed
|
||||
}
|
||||
|
||||
var i
|
||||
var allProcessed = true
|
||||
for (i = 0; i < rawTasks.length; i++) {
|
||||
compileTask(i)
|
||||
|
||||
allProcessed = allProcessed && rawTasks[i].processed
|
||||
}
|
||||
return allProcessed
|
||||
}
|
||||
|
||||
exports.parseError = function (err, hash) {
|
||||
global.mermaidAPI.parseError(err, hash)
|
||||
}
|
||||
179
src/diagrams/gantt/ganttDb.spec.js
Normal file
179
src/diagrams/gantt/ganttDb.spec.js
Normal file
@@ -0,0 +1,179 @@
|
||||
/* eslint-env jasmine */
|
||||
/**
|
||||
* Created by knut on 14-11-18.
|
||||
*/
|
||||
describe('when using the ganttDb', function () {
|
||||
var gDb
|
||||
var moment = require('moment')
|
||||
|
||||
beforeEach(function () {
|
||||
gDb = require('./ganttDb')
|
||||
gDb.clear()
|
||||
})
|
||||
|
||||
it('should handle an fixed dates', function () {
|
||||
gDb.setDateFormat('YYYY-MM-DD')
|
||||
gDb.addSection('testa1')
|
||||
gDb.addTask('test1', 'id1,2013-01-01,2013-01-12')
|
||||
var tasks = gDb.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 () {
|
||||
gDb.setDateFormat('YYYY-MM-DD')
|
||||
gDb.addSection('testa1')
|
||||
gDb.addTask('test1', 'id1,2013-01-01,2d')
|
||||
var tasks = gDb.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 () {
|
||||
gDb.setDateFormat('YYYY-MM-DD')
|
||||
gDb.addSection('testa1')
|
||||
gDb.addTask('test1', 'id1,2013-01-01,2h')
|
||||
var tasks = gDb.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 () {
|
||||
gDb.setDateFormat('YYYY-MM-DD')
|
||||
gDb.addSection('testa1')
|
||||
gDb.addTask('test1', 'id1,2013-01-01,2m')
|
||||
var tasks = gDb.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 () {
|
||||
gDb.setDateFormat('YYYY-MM-DD')
|
||||
gDb.addSection('testa1')
|
||||
gDb.addTask('test1', 'id1,2013-01-01,2s')
|
||||
var tasks = gDb.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 () {
|
||||
gDb.setDateFormat('YYYY-MM-DD')
|
||||
gDb.addSection('testa1')
|
||||
gDb.addTask('test1', 'id1,2013-01-01,2w')
|
||||
var tasks = gDb.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('should handle relative start date based on id', function () {
|
||||
gDb.setDateFormat('YYYY-MM-DD')
|
||||
gDb.addSection('testa1')
|
||||
gDb.addTask('test1', 'id1,2013-01-01,2w')
|
||||
gDb.addTask('test2', 'id2,after id1,1d')
|
||||
|
||||
var tasks = gDb.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 () {
|
||||
gDb.setDateFormat('YYYY-MM-DD')
|
||||
gDb.addSection('testa1')
|
||||
gDb.addTask('test1', 'id1,2013-01-01,2w')
|
||||
gDb.addTask('test2', 'id2,after id3,1d')
|
||||
var tasks = gDb.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 () {
|
||||
gDb.setDateFormat('YYYY-MM-DD')
|
||||
gDb.addSection('testa1')
|
||||
gDb.addTask('test1', '2013-01-01,2013-01-12')
|
||||
var tasks = gDb.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 () {
|
||||
gDb.setDateFormat('YYYY-MM-DD')
|
||||
gDb.addSection('testa1')
|
||||
gDb.addTask('test1', '2013-01-01,4d')
|
||||
var tasks = gDb.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 () {
|
||||
gDb.setDateFormat('YYYY-MM-DD')
|
||||
gDb.addSection('testa1')
|
||||
gDb.addTask('test1', 'id1,2013-01-01,2w')
|
||||
gDb.addTask('test2', 'after id1,1d')
|
||||
|
||||
var tasks = gDb.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 () {
|
||||
gDb.setDateFormat('YYYY-MM-DD')
|
||||
gDb.addSection('testa1')
|
||||
gDb.addTask('test1', 'id1,2013-01-01,2w')
|
||||
gDb.addTask('test2', '2013-01-26')
|
||||
|
||||
var tasks = gDb.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 () {
|
||||
gDb.setDateFormat('YYYY-MM-DD')
|
||||
gDb.addSection('testa1')
|
||||
gDb.addTask('test1', 'id1,2013-01-01,2w')
|
||||
gDb.addTask('test2', '2d')
|
||||
|
||||
var tasks = gDb.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 () {
|
||||
gDb.setDateFormat('YYYY-MM-DD')
|
||||
gDb.addSection('testa1')
|
||||
gDb.addTask('test1', 'id1,2013-01-01,2w')
|
||||
gDb.addTask('test2', 'id2,after id3,1d')
|
||||
gDb.addSection('testa2')
|
||||
gDb.addTask('test3', 'id3,after id1,2d')
|
||||
|
||||
var tasks = gDb.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].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())
|
||||
})
|
||||
})
|
||||
399
src/diagrams/gantt/ganttRenderer.js
Normal file
399
src/diagrams/gantt/ganttRenderer.js
Normal file
@@ -0,0 +1,399 @@
|
||||
var gantt = require('./parser/gantt').parser
|
||||
gantt.yy = require('./ganttDb')
|
||||
var d3 = require('../../d3')
|
||||
var moment = require('moment')
|
||||
|
||||
var daysInChart
|
||||
var conf = {
|
||||
titleTopMargin: 25,
|
||||
barHeight: 20,
|
||||
barGap: 4,
|
||||
topPadding: 50,
|
||||
rightPadding: 75,
|
||||
leftPadding: 75,
|
||||
gridLineStartPadding: 35,
|
||||
fontSize: 11,
|
||||
fontFamily: '"Open-Sans", "sans-serif"'
|
||||
}
|
||||
module.exports.setConf = function (cnf) {
|
||||
var keys = Object.keys(cnf)
|
||||
|
||||
keys.forEach(function (key) {
|
||||
conf[key] = cnf[key]
|
||||
})
|
||||
}
|
||||
var w
|
||||
module.exports.draw = function (text, id) {
|
||||
gantt.yy.clear()
|
||||
gantt.parse(text)
|
||||
|
||||
var elem = document.getElementById(id)
|
||||
w = elem.parentElement.offsetWidth
|
||||
|
||||
if (typeof w === 'undefined') {
|
||||
w = 1200
|
||||
}
|
||||
|
||||
if (typeof conf.useWidth !== 'undefined') {
|
||||
w = conf.useWidth
|
||||
}
|
||||
|
||||
var taskArray = gantt.yy.getTasks()
|
||||
|
||||
// Set height based on number of tasks
|
||||
var h = taskArray.length * (conf.barHeight + conf.barGap) + 2 * conf.topPadding
|
||||
|
||||
elem.setAttribute('height', '100%')
|
||||
// Set viewBox
|
||||
elem.setAttribute('viewBox', '0 0 ' + w + ' ' + h)
|
||||
var svg = d3.select('#' + id)
|
||||
|
||||
var startDate = d3.min(taskArray, function (d) {
|
||||
return d.startTime
|
||||
})
|
||||
var endDate = d3.max(taskArray, function (d) {
|
||||
return d.endTime
|
||||
})
|
||||
|
||||
// Set timescale
|
||||
var timeScale = d3.time.scale()
|
||||
.domain([d3.min(taskArray, function (d) {
|
||||
return d.startTime
|
||||
}),
|
||||
d3.max(taskArray, function (d) {
|
||||
return d.endTime
|
||||
})])
|
||||
.rangeRound([0, w - conf.leftPadding - conf.rightPadding])
|
||||
|
||||
var categories = []
|
||||
|
||||
daysInChart = moment.duration(endDate - startDate).asDays()
|
||||
|
||||
for (var i = 0; i < taskArray.length; i++) {
|
||||
categories.push(taskArray[i].type)
|
||||
}
|
||||
|
||||
var catsUnfiltered = categories // for vert labels
|
||||
|
||||
categories = checkUnique(categories)
|
||||
|
||||
makeGant(taskArray, w, h)
|
||||
if (typeof conf.useWidth !== 'undefined') {
|
||||
elem.setAttribute('width', w)
|
||||
}
|
||||
|
||||
svg.append('text')
|
||||
.text(gantt.yy.getTitle())
|
||||
.attr('x', w / 2)
|
||||
.attr('y', conf.titleTopMargin)
|
||||
.attr('class', 'titleText')
|
||||
|
||||
function makeGant (tasks, pageWidth, pageHeight) {
|
||||
var barHeight = conf.barHeight
|
||||
var gap = barHeight + conf.barGap
|
||||
var topPadding = conf.topPadding
|
||||
var leftPadding = conf.leftPadding
|
||||
|
||||
var colorScale = d3.scale.linear()
|
||||
.domain([0, categories.length])
|
||||
.range(['#00B9FA', '#F95002'])
|
||||
.interpolate(d3.interpolateHcl)
|
||||
|
||||
makeGrid(leftPadding, topPadding, pageWidth, pageHeight)
|
||||
drawRects(tasks, gap, topPadding, leftPadding, barHeight, colorScale, pageWidth, pageHeight)
|
||||
vertLabels(gap, topPadding, leftPadding, barHeight, colorScale)
|
||||
drawToday(leftPadding, topPadding, pageWidth, pageHeight)
|
||||
}
|
||||
|
||||
function drawRects (theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w, h) {
|
||||
svg.append('g')
|
||||
.selectAll('rect')
|
||||
.data(theArray)
|
||||
.enter()
|
||||
.append('rect')
|
||||
.attr('x', 0)
|
||||
.attr('y', function (d, i) {
|
||||
return i * theGap + theTopPad - 2
|
||||
})
|
||||
.attr('width', function () {
|
||||
return w - conf.rightPadding / 2
|
||||
})
|
||||
.attr('height', theGap)
|
||||
.attr('class', function (d) {
|
||||
for (var i = 0; i < categories.length; i++) {
|
||||
if (d.type === categories[i]) {
|
||||
return 'section section' + (i % conf.numberSectionStyles)
|
||||
}
|
||||
}
|
||||
return 'section section0'
|
||||
})
|
||||
|
||||
var rectangles = svg.append('g')
|
||||
.selectAll('rect')
|
||||
.data(theArray)
|
||||
.enter()
|
||||
|
||||
rectangles.append('rect')
|
||||
.attr('rx', 3)
|
||||
.attr('ry', 3)
|
||||
.attr('x', function (d) {
|
||||
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))
|
||||
})
|
||||
.attr('height', theBarHeight)
|
||||
.attr('class', function (d) {
|
||||
var res = 'task '
|
||||
|
||||
var secNum = 0
|
||||
for (var i = 0; i < categories.length; i++) {
|
||||
if (d.type === categories[i]) {
|
||||
secNum = (i % conf.numberSectionStyles)
|
||||
}
|
||||
}
|
||||
|
||||
if (d.active) {
|
||||
if (d.crit) {
|
||||
return res + ' activeCrit' + secNum
|
||||
} else {
|
||||
return res + ' active' + secNum
|
||||
}
|
||||
}
|
||||
|
||||
if (d.done) {
|
||||
if (d.crit) {
|
||||
return res + ' doneCrit' + secNum
|
||||
} else {
|
||||
return res + ' done' + secNum
|
||||
}
|
||||
}
|
||||
|
||||
if (d.crit) {
|
||||
return res + ' crit' + secNum
|
||||
}
|
||||
|
||||
return res + ' task' + secNum
|
||||
})
|
||||
|
||||
rectangles.append('text')
|
||||
.text(function (d) {
|
||||
return d.task
|
||||
})
|
||||
.attr('font-size', conf.fontSize)
|
||||
.attr('x', function (d) {
|
||||
var startX = timeScale(d.startTime)
|
||||
var endX = timeScale(d.endTime)
|
||||
var textWidth = this.getBBox().width
|
||||
|
||||
// Check id text width > width of rectangle
|
||||
if (textWidth > (endX - startX)) {
|
||||
if (endX + textWidth + 1.5 * conf.leftPadding > w) {
|
||||
return startX + theSidePad - 5
|
||||
} else {
|
||||
return endX + theSidePad + 5
|
||||
}
|
||||
} else {
|
||||
return (endX - startX) / 2 + startX + theSidePad
|
||||
}
|
||||
})
|
||||
.attr('y', function (d, i) {
|
||||
return i * theGap + (conf.barHeight / 2) + (conf.fontSize / 2 - 2) + theTopPad
|
||||
})
|
||||
.attr('text-height', theBarHeight)
|
||||
.attr('class', function (d) {
|
||||
var startX = timeScale(d.startTime)
|
||||
var endX = timeScale(d.endTime)
|
||||
var textWidth = this.getBBox().width
|
||||
var secNum = 0
|
||||
for (var i = 0; i < categories.length; i++) {
|
||||
if (d.type === categories[i]) {
|
||||
secNum = (i % conf.numberSectionStyles)
|
||||
}
|
||||
}
|
||||
|
||||
var taskType = ''
|
||||
if (d.active) {
|
||||
if (d.crit) {
|
||||
taskType = 'activeCritText' + secNum
|
||||
} else {
|
||||
taskType = 'activeText' + secNum
|
||||
}
|
||||
}
|
||||
|
||||
if (d.done) {
|
||||
if (d.crit) {
|
||||
taskType = taskType + ' doneCritText' + secNum
|
||||
} else {
|
||||
taskType = taskType + ' doneText' + secNum
|
||||
}
|
||||
} else {
|
||||
if (d.crit) {
|
||||
taskType = taskType + ' critText' + secNum
|
||||
}
|
||||
}
|
||||
|
||||
// Check id text width > width of rectangle
|
||||
if (textWidth > (endX - startX)) {
|
||||
if (endX + textWidth + 1.5 * conf.leftPadding > w) {
|
||||
return 'taskTextOutsideLeft taskTextOutside' + secNum + ' ' + taskType
|
||||
} else {
|
||||
return 'taskTextOutsideRight taskTextOutside' + secNum + ' ' + taskType
|
||||
}
|
||||
} else {
|
||||
return 'taskText taskText' + secNum + ' ' + taskType
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function makeGrid (theSidePad, theTopPad, w, h) {
|
||||
var pre = [
|
||||
['.%L', function (d) {
|
||||
return d.getMilliseconds()
|
||||
}],
|
||||
[':%S', function (d) {
|
||||
return d.getSeconds()
|
||||
}],
|
||||
// Within a hour
|
||||
['h1 %I:%M', function (d) {
|
||||
return d.getMinutes()
|
||||
}]]
|
||||
var post = [
|
||||
['%Y', function () {
|
||||
return true
|
||||
}]]
|
||||
|
||||
var mid = [
|
||||
// Within a day
|
||||
['%I:%M', function (d) {
|
||||
return d.getHours()
|
||||
}],
|
||||
// Day within a week (not monday)
|
||||
['%a %d', function (d) {
|
||||
return d.getDay() && d.getDate() !== 1
|
||||
}],
|
||||
// within a month
|
||||
['%b %d', function (d) {
|
||||
return d.getDate() !== 1
|
||||
}],
|
||||
// Month
|
||||
['%B', function (d) {
|
||||
return d.getMonth()
|
||||
}]
|
||||
]
|
||||
var formatter
|
||||
if (typeof conf.axisFormatter !== 'undefined') {
|
||||
mid = []
|
||||
conf.axisFormatter.forEach(function (item) {
|
||||
var n = []
|
||||
n[0] = item[0]
|
||||
n[1] = item[1]
|
||||
mid.push(n)
|
||||
})
|
||||
}
|
||||
formatter = pre.concat(mid).concat(post)
|
||||
|
||||
var xAxis = d3.svg.axis()
|
||||
.scale(timeScale)
|
||||
.orient('bottom')
|
||||
.tickSize(-h + theTopPad + conf.gridLineStartPadding, 0, 0)
|
||||
.tickFormat(d3.time.format.multi(formatter))
|
||||
|
||||
if (daysInChart > 7 && daysInChart < 230) {
|
||||
xAxis = xAxis.ticks(d3.time.monday.range)
|
||||
}
|
||||
|
||||
svg.append('g')
|
||||
.attr('class', 'grid')
|
||||
.attr('transform', 'translate(' + theSidePad + ', ' + (h - 50) + ')')
|
||||
.call(xAxis)
|
||||
.selectAll('text')
|
||||
.style('text-anchor', 'middle')
|
||||
.attr('fill', '#000')
|
||||
.attr('stroke', 'none')
|
||||
.attr('font-size', 10)
|
||||
.attr('dy', '1em')
|
||||
}
|
||||
|
||||
function vertLabels (theGap, theTopPad) {
|
||||
var numOccurances = []
|
||||
var prevGap = 0
|
||||
|
||||
for (var i = 0; i < categories.length; i++) {
|
||||
numOccurances[i] = [categories[i], getCount(categories[i], catsUnfiltered)]
|
||||
}
|
||||
|
||||
svg.append('g') // without doing this, impossible to put grid lines behind text
|
||||
.selectAll('text')
|
||||
.data(numOccurances)
|
||||
.enter()
|
||||
.append('text')
|
||||
.text(function (d) {
|
||||
return d[0]
|
||||
})
|
||||
.attr('x', 10)
|
||||
.attr('y', function (d, i) {
|
||||
if (i > 0) {
|
||||
for (var j = 0; j < i; j++) {
|
||||
prevGap += numOccurances[i - 1][1]
|
||||
return d[1] * theGap / 2 + prevGap * theGap + theTopPad
|
||||
}
|
||||
} else {
|
||||
return d[1] * theGap / 2 + theTopPad
|
||||
}
|
||||
})
|
||||
.attr('class', function (d) {
|
||||
for (var i = 0; i < categories.length; i++) {
|
||||
if (d[0] === categories[i]) {
|
||||
return 'sectionTitle sectionTitle' + (i % conf.numberSectionStyles)
|
||||
}
|
||||
}
|
||||
return 'sectionTitle'
|
||||
})
|
||||
}
|
||||
|
||||
function drawToday (theSidePad, theTopPad, w, h) {
|
||||
var todayG = svg.append('g')
|
||||
.attr('class', 'today')
|
||||
|
||||
var today = new Date()
|
||||
|
||||
todayG.append('line')
|
||||
.attr('x1', timeScale(today) + theSidePad)
|
||||
.attr('x2', timeScale(today) + theSidePad)
|
||||
.attr('y1', conf.titleTopMargin)
|
||||
.attr('y2', h - conf.titleTopMargin)
|
||||
.attr('class', 'today')
|
||||
}
|
||||
|
||||
// from this stackexchange question: http://stackoverflow.com/questions/1890203/unique-for-arrays-in-javascript
|
||||
function checkUnique (arr) {
|
||||
var hash = {}
|
||||
var result = []
|
||||
for (var i = 0, l = arr.length; i < l; ++i) {
|
||||
if (!hash.hasOwnProperty(arr[i])) { // it works with objects! in FF, at least
|
||||
hash[arr[i]] = true
|
||||
result.push(arr[i])
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// from this stackexchange question: http://stackoverflow.com/questions/14227981/count-how-many-strings-in-an-array-have-duplicates-in-the-same-array
|
||||
function getCounts (arr) {
|
||||
var i = arr.length // var to loop over
|
||||
var obj = {} // obj to store results
|
||||
while (i) {
|
||||
obj[arr[--i]] = (obj[arr[i]] || 0) + 1 // count occurrences
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
// get specific from everything
|
||||
function getCount (word, arr) {
|
||||
return getCounts(arr)[word] || 0
|
||||
}
|
||||
}
|
||||
62
src/diagrams/gantt/parser/gantt.jison
Normal file
62
src/diagrams/gantt/parser/gantt.jison
Normal file
@@ -0,0 +1,62 @@
|
||||
/** mermaid
|
||||
* https://mermaidjs.github.io/
|
||||
* (c) 2015 Knut Sveidqvist
|
||||
* MIT license.
|
||||
*/
|
||||
%lex
|
||||
|
||||
%options case-insensitive
|
||||
|
||||
%{
|
||||
// Pre-lexer code can go here
|
||||
%}
|
||||
|
||||
%%
|
||||
|
||||
[\n]+ return 'NL';
|
||||
\s+ /* skip whitespace */
|
||||
\#[^\n]* /* skip comments */
|
||||
\%%[^\n]* /* skip comments */
|
||||
"gantt" return 'gantt';
|
||||
"dateFormat"\s[^#\n;]+ return 'dateFormat';
|
||||
\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 'taskData';
|
||||
":" return ':';
|
||||
<<EOF>> return 'EOF';
|
||||
. return 'INVALID';
|
||||
|
||||
/lex
|
||||
|
||||
%left '^'
|
||||
|
||||
%start start
|
||||
|
||||
%% /* language grammar */
|
||||
|
||||
start
|
||||
: gantt document 'EOF' { return $2; }
|
||||
;
|
||||
|
||||
document
|
||||
: /* empty */ { $$ = [] }
|
||||
| document line {$1.push($2);$$ = $1}
|
||||
;
|
||||
|
||||
line
|
||||
: SPACE statement { $$ = $2 }
|
||||
| statement { $$ = $1 }
|
||||
| NL { $$=[];}
|
||||
| EOF { $$=[];}
|
||||
;
|
||||
|
||||
statement
|
||||
: 'dateFormat' {yy.setDateFormat($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';}
|
||||
;
|
||||
|
||||
%%
|
||||
653
src/diagrams/gantt/parser/gantt.js
Normal file
653
src/diagrams/gantt/parser/gantt.js
Normal file
@@ -0,0 +1,653 @@
|
||||
/* parser generated by jison 0.4.17 */
|
||||
/*
|
||||
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, 8, 10, 11, 12, 13, 14], $V1 = [1, 9], $V2 = [1, 10], $V3 = [1, 11], $V4 = [1, 12]
|
||||
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, 'title': 12, 'section': 13, 'taskTxt': 14, 'taskData': 15, '$accept': 0, '$end': 1 },
|
||||
terminals_: { 2: 'error', 4: 'gantt', 6: 'EOF', 8: 'SPACE', 10: 'NL', 11: 'dateFormat', 12: 'title', 13: 'section', 14: 'taskTxt', 15: 'taskData' },
|
||||
productions_: [0, [3, 3], [5, 0], [5, 2], [7, 2], [7, 1], [7, 1], [7, 1], [9, 1], [9, 1], [9, 1], [9, 2]],
|
||||
performAction: function anonymous (yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
|
||||
/* this == yyval */
|
||||
|
||||
var $0 = $$.length - 1
|
||||
switch (yystate) {
|
||||
case 1:
|
||||
return $$[$0 - 1]
|
||||
break
|
||||
case 2:
|
||||
this.$ = []
|
||||
break
|
||||
case 3:
|
||||
$$[$0 - 1].push($$[$0]); this.$ = $$[$0 - 1]
|
||||
break
|
||||
case 4: case 5:
|
||||
this.$ = $$[$0]
|
||||
break
|
||||
case 6: case 7:
|
||||
this.$ = []
|
||||
break
|
||||
case 8:
|
||||
yy.setDateFormat($$[$0].substr(11)); this.$ = $$[$0].substr(11)
|
||||
break
|
||||
case 9:
|
||||
yy.setTitle($$[$0].substr(6)); this.$ = $$[$0].substr(6)
|
||||
break
|
||||
case 10:
|
||||
yy.addSection($$[$0].substr(8)); this.$ = $$[$0].substr(8)
|
||||
break
|
||||
case 11:
|
||||
yy.addTask($$[$0 - 1], $$[$0]); this.$ = 'task'
|
||||
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 }, o($V0, [2, 7], { 1: [2, 1] }), o($V0, [2, 3]), { 9: 13, 11: $V1, 12: $V2, 13: $V3, 14: $V4 }, o($V0, [2, 5]), o($V0, [2, 6]), o($V0, [2, 8]), o($V0, [2, 9]), o($V0, [2, 10]), { 15: [1, 14] }, o($V0, [2, 4]), o($V0, [2, 11])],
|
||||
defaultActions: {},
|
||||
parseError: function parseError (str, hash) {
|
||||
if (hash.recoverable) {
|
||||
this.trace(str)
|
||||
} else {
|
||||
function _parseError (msg, hash) {
|
||||
this.message = msg
|
||||
this.hash = hash
|
||||
}
|
||||
_parseError.prototype = Error
|
||||
|
||||
throw new _parseError(str, hash)
|
||||
}
|
||||
},
|
||||
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
|
||||
}
|
||||
var lex = function () {
|
||||
var token
|
||||
token = lexer.lex() || EOF
|
||||
if (typeof token !== 'number') {
|
||||
token = self.symbols_[token] || token
|
||||
}
|
||||
return token
|
||||
}
|
||||
var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected
|
||||
while (true) {
|
||||
state = stack[stack.length - 1]
|
||||
if (this.defaultActions[state]) {
|
||||
action = this.defaultActions[state]
|
||||
} else {
|
||||
if (symbol === null || typeof symbol === 'undefined') {
|
||||
symbol = lex()
|
||||
}
|
||||
action = table[state] && table[state][symbol]
|
||||
}
|
||||
if (typeof action === 'undefined' || !action.length || !action[0]) {
|
||||
var errStr = ''
|
||||
expected = []
|
||||
for (p in table[state]) {
|
||||
if (this.terminals_[p] && p > TERROR) {
|
||||
expected.push('\'' + this.terminals_[p] + '\'')
|
||||
}
|
||||
}
|
||||
if (lexer.showPosition) {
|
||||
errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\''
|
||||
} else {
|
||||
errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'')
|
||||
}
|
||||
this.parseError(errStr, {
|
||||
text: lexer.match,
|
||||
token: this.terminals_[symbol] || symbol,
|
||||
line: lexer.yylineno,
|
||||
loc: yyloc,
|
||||
expected: expected
|
||||
})
|
||||
}
|
||||
if (action[0] instanceof Array && action.length > 1) {
|
||||
throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol)
|
||||
}
|
||||
switch (action[0]) {
|
||||
case 1:
|
||||
stack.push(symbol)
|
||||
vstack.push(lexer.yytext)
|
||||
lstack.push(lexer.yylloc)
|
||||
stack.push(action[1])
|
||||
symbol = null
|
||||
if (!preErrorSymbol) {
|
||||
yyleng = lexer.yyleng
|
||||
yytext = lexer.yytext
|
||||
yylineno = lexer.yylineno
|
||||
yyloc = lexer.yylloc
|
||||
if (recovering > 0) {
|
||||
recovering--
|
||||
}
|
||||
} else {
|
||||
symbol = preErrorSymbol
|
||||
preErrorSymbol = null
|
||||
}
|
||||
break
|
||||
case 2:
|
||||
len = this.productions_[action[1]][1]
|
||||
yyval.$ = vstack[vstack.length - len]
|
||||
yyval._$ = {
|
||||
first_line: lstack[lstack.length - (len || 1)].first_line,
|
||||
last_line: lstack[lstack.length - 1].last_line,
|
||||
first_column: lstack[lstack.length - (len || 1)].first_column,
|
||||
last_column: lstack[lstack.length - 1].last_column
|
||||
}
|
||||
if (ranges) {
|
||||
yyval._$.range = [
|
||||
lstack[lstack.length - (len || 1)].range[0],
|
||||
lstack[lstack.length - 1].range[1]
|
||||
]
|
||||
}
|
||||
r = this.performAction.apply(yyval, [
|
||||
yytext,
|
||||
yyleng,
|
||||
yylineno,
|
||||
sharedState.yy,
|
||||
action[1],
|
||||
vstack,
|
||||
lstack
|
||||
].concat(args))
|
||||
if (typeof r !== 'undefined') {
|
||||
return r
|
||||
}
|
||||
if (len) {
|
||||
stack = stack.slice(0, -1 * len * 2)
|
||||
vstack = vstack.slice(0, -1 * len)
|
||||
lstack = lstack.slice(0, -1 * len)
|
||||
}
|
||||
stack.push(this.productions_[action[1]][0])
|
||||
vstack.push(yyval.$)
|
||||
lstack.push(yyval._$)
|
||||
newState = table[stack[stack.length - 2]][stack[stack.length - 1]]
|
||||
stack.push(newState)
|
||||
break
|
||||
case 3:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
/* generated by jison-lex 0.3.4 */
|
||||
var lexer = (function () {
|
||||
var lexer = ({
|
||||
|
||||
EOF: 1,
|
||||
|
||||
parseError: function parseError (str, hash) {
|
||||
if (this.yy.parser) {
|
||||
this.yy.parser.parseError(str, hash)
|
||||
} else {
|
||||
throw new Error(str)
|
||||
}
|
||||
},
|
||||
|
||||
// resets the lexer, sets new input
|
||||
setInput: function (input, yy) {
|
||||
this.yy = yy || this.yy || {}
|
||||
this._input = input
|
||||
this._more = this._backtrack = this.done = false
|
||||
this.yylineno = this.yyleng = 0
|
||||
this.yytext = this.matched = this.match = ''
|
||||
this.conditionStack = ['INITIAL']
|
||||
this.yylloc = {
|
||||
first_line: 1,
|
||||
first_column: 0,
|
||||
last_line: 1,
|
||||
last_column: 0
|
||||
}
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range = [0, 0]
|
||||
}
|
||||
this.offset = 0
|
||||
return this
|
||||
},
|
||||
|
||||
// consumes and returns one char from the input
|
||||
input: function () {
|
||||
var ch = this._input[0]
|
||||
this.yytext += ch
|
||||
this.yyleng++
|
||||
this.offset++
|
||||
this.match += ch
|
||||
this.matched += ch
|
||||
var lines = ch.match(/(?:\r\n?|\n).*/g)
|
||||
if (lines) {
|
||||
this.yylineno++
|
||||
this.yylloc.last_line++
|
||||
} else {
|
||||
this.yylloc.last_column++
|
||||
}
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range[1]++
|
||||
}
|
||||
|
||||
this._input = this._input.slice(1)
|
||||
return ch
|
||||
},
|
||||
|
||||
// unshifts one char (or a string) into the input
|
||||
unput: function (ch) {
|
||||
var len = ch.length
|
||||
var lines = ch.split(/(?:\r\n?|\n)/g)
|
||||
|
||||
this._input = ch + this._input
|
||||
this.yytext = this.yytext.substr(0, this.yytext.length - len)
|
||||
// this.yyleng -= len;
|
||||
this.offset -= len
|
||||
var oldLines = this.match.split(/(?:\r\n?|\n)/g)
|
||||
this.match = this.match.substr(0, this.match.length - 1)
|
||||
this.matched = this.matched.substr(0, this.matched.length - 1)
|
||||
|
||||
if (lines.length - 1) {
|
||||
this.yylineno -= lines.length - 1
|
||||
}
|
||||
var r = this.yylloc.range
|
||||
|
||||
this.yylloc = {
|
||||
first_line: this.yylloc.first_line,
|
||||
last_line: this.yylineno + 1,
|
||||
first_column: this.yylloc.first_column,
|
||||
last_column: lines
|
||||
? (lines.length === oldLines.length ? this.yylloc.first_column : 0) +
|
||||
oldLines[oldLines.length - lines.length].length - lines[0].length
|
||||
: this.yylloc.first_column - len
|
||||
}
|
||||
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range = [r[0], r[0] + this.yyleng - len]
|
||||
}
|
||||
this.yyleng = this.yytext.length
|
||||
return this
|
||||
},
|
||||
|
||||
// When called from action, caches matched text and appends it on next action
|
||||
more: function () {
|
||||
this._more = true
|
||||
return this
|
||||
},
|
||||
|
||||
// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
|
||||
reject: function () {
|
||||
if (this.options.backtrack_lexer) {
|
||||
this._backtrack = true
|
||||
} else {
|
||||
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), {
|
||||
text: '',
|
||||
token: null,
|
||||
line: this.yylineno
|
||||
})
|
||||
}
|
||||
return this
|
||||
},
|
||||
|
||||
// retain first n characters of the match
|
||||
less: function (n) {
|
||||
this.unput(this.match.slice(n))
|
||||
},
|
||||
|
||||
// displays already matched input, i.e. for error messages
|
||||
pastInput: function () {
|
||||
var past = this.matched.substr(0, this.matched.length - this.match.length)
|
||||
return (past.length > 20 ? '...' : '') + past.substr(-20).replace(/\n/g, '')
|
||||
},
|
||||
|
||||
// displays upcoming input, i.e. for error messages
|
||||
upcomingInput: function () {
|
||||
var next = this.match
|
||||
if (next.length < 20) {
|
||||
next += this._input.substr(0, 20 - next.length)
|
||||
}
|
||||
return (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace(/\n/g, '')
|
||||
},
|
||||
|
||||
// displays the character position where the lexing error occurred, i.e. for error messages
|
||||
showPosition: function () {
|
||||
var pre = this.pastInput()
|
||||
var c = new Array(pre.length + 1).join('-')
|
||||
return pre + this.upcomingInput() + '\n' + c + '^'
|
||||
},
|
||||
|
||||
// test the lexed token: return FALSE when not a match, otherwise return token
|
||||
test_match: function (match, indexed_rule) {
|
||||
var token,
|
||||
lines,
|
||||
backup
|
||||
|
||||
if (this.options.backtrack_lexer) {
|
||||
// save context
|
||||
backup = {
|
||||
yylineno: this.yylineno,
|
||||
yylloc: {
|
||||
first_line: this.yylloc.first_line,
|
||||
last_line: this.last_line,
|
||||
first_column: this.yylloc.first_column,
|
||||
last_column: this.yylloc.last_column
|
||||
},
|
||||
yytext: this.yytext,
|
||||
match: this.match,
|
||||
matches: this.matches,
|
||||
matched: this.matched,
|
||||
yyleng: this.yyleng,
|
||||
offset: this.offset,
|
||||
_more: this._more,
|
||||
_input: this._input,
|
||||
yy: this.yy,
|
||||
conditionStack: this.conditionStack.slice(0),
|
||||
done: this.done
|
||||
}
|
||||
if (this.options.ranges) {
|
||||
backup.yylloc.range = this.yylloc.range.slice(0)
|
||||
}
|
||||
}
|
||||
|
||||
lines = match[0].match(/(?:\r\n?|\n).*/g)
|
||||
if (lines) {
|
||||
this.yylineno += lines.length
|
||||
}
|
||||
this.yylloc = {
|
||||
first_line: this.yylloc.last_line,
|
||||
last_line: this.yylineno + 1,
|
||||
first_column: this.yylloc.last_column,
|
||||
last_column: lines
|
||||
? lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length
|
||||
: this.yylloc.last_column + match[0].length
|
||||
}
|
||||
this.yytext += match[0]
|
||||
this.match += match[0]
|
||||
this.matches = match
|
||||
this.yyleng = this.yytext.length
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range = [this.offset, this.offset += this.yyleng]
|
||||
}
|
||||
this._more = false
|
||||
this._backtrack = false
|
||||
this._input = this._input.slice(match[0].length)
|
||||
this.matched += match[0]
|
||||
token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1])
|
||||
if (this.done && this._input) {
|
||||
this.done = false
|
||||
}
|
||||
if (token) {
|
||||
return token
|
||||
} else if (this._backtrack) {
|
||||
// recover context
|
||||
for (var k in backup) {
|
||||
this[k] = backup[k]
|
||||
}
|
||||
return false // rule action called reject() implying the next rule should be tested instead.
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
// return next match in input
|
||||
next: function () {
|
||||
if (this.done) {
|
||||
return this.EOF
|
||||
}
|
||||
if (!this._input) {
|
||||
this.done = true
|
||||
}
|
||||
|
||||
var token,
|
||||
match,
|
||||
tempMatch,
|
||||
index
|
||||
if (!this._more) {
|
||||
this.yytext = ''
|
||||
this.match = ''
|
||||
}
|
||||
var rules = this._currentRules()
|
||||
for (var i = 0; i < rules.length; i++) {
|
||||
tempMatch = this._input.match(this.rules[rules[i]])
|
||||
if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
|
||||
match = tempMatch
|
||||
index = i
|
||||
if (this.options.backtrack_lexer) {
|
||||
token = this.test_match(tempMatch, rules[i])
|
||||
if (token !== false) {
|
||||
return token
|
||||
} else if (this._backtrack) {
|
||||
match = false
|
||||
continue // rule action called reject() implying a rule MISmatch.
|
||||
} else {
|
||||
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
|
||||
return false
|
||||
}
|
||||
} else if (!this.options.flex) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
token = this.test_match(match, rules[index])
|
||||
if (token !== false) {
|
||||
return token
|
||||
}
|
||||
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
|
||||
return false
|
||||
}
|
||||
if (this._input === '') {
|
||||
return this.EOF
|
||||
} else {
|
||||
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
|
||||
text: '',
|
||||
token: null,
|
||||
line: this.yylineno
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// return next match that has a token
|
||||
lex: function lex () {
|
||||
var r = this.next()
|
||||
if (r) {
|
||||
return r
|
||||
} else {
|
||||
return this.lex()
|
||||
}
|
||||
},
|
||||
|
||||
// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
|
||||
begin: function begin (condition) {
|
||||
this.conditionStack.push(condition)
|
||||
},
|
||||
|
||||
// pop the previously active lexer condition state off the condition stack
|
||||
popState: function popState () {
|
||||
var n = this.conditionStack.length - 1
|
||||
if (n > 0) {
|
||||
return this.conditionStack.pop()
|
||||
} else {
|
||||
return this.conditionStack[0]
|
||||
}
|
||||
},
|
||||
|
||||
// produce the lexer rule set which is active for the currently active lexer condition state
|
||||
_currentRules: function _currentRules () {
|
||||
if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
|
||||
return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules
|
||||
} else {
|
||||
return this.conditions['INITIAL'].rules
|
||||
}
|
||||
},
|
||||
|
||||
// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
|
||||
topState: function topState (n) {
|
||||
n = this.conditionStack.length - 1 - Math.abs(n || 0)
|
||||
if (n >= 0) {
|
||||
return this.conditionStack[n]
|
||||
} else {
|
||||
return 'INITIAL'
|
||||
}
|
||||
},
|
||||
|
||||
// alias for begin(condition)
|
||||
pushState: function pushState (condition) {
|
||||
this.begin(condition)
|
||||
},
|
||||
|
||||
// return the number of states currently on the stack
|
||||
stateStackSize: function stateStackSize () {
|
||||
return this.conditionStack.length
|
||||
},
|
||||
options: { 'case-insensitive': true },
|
||||
performAction: function anonymous (yy, yy_, $avoiding_name_collisions, YY_START) {
|
||||
// Pre-lexer code can go here
|
||||
|
||||
var YYSTATE = YY_START
|
||||
switch ($avoiding_name_collisions) {
|
||||
case 0: return 10
|
||||
break
|
||||
case 1:/* skip whitespace */
|
||||
break
|
||||
case 2:/* skip comments */
|
||||
break
|
||||
case 3:/* skip comments */
|
||||
break
|
||||
case 4: return 4
|
||||
break
|
||||
case 5: return 11
|
||||
break
|
||||
case 6: return 'date'
|
||||
break
|
||||
case 7: return 12
|
||||
break
|
||||
case 8: return 13
|
||||
break
|
||||
case 9: return 14
|
||||
break
|
||||
case 10: return 15
|
||||
break
|
||||
case 11: return ':'
|
||||
break
|
||||
case 12: return 6
|
||||
break
|
||||
case 13: return 'INVALID'
|
||||
break
|
||||
}
|
||||
},
|
||||
rules: [/^(?:[\n]+)/i, /^(?:\s+)/i, /^(?:#[^\n]*)/i, /^(?:%[^\n]*)/i, /^(?:gantt\b)/i, /^(?:dateFormat\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], '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))
|
||||
}
|
||||
}
|
||||
208
src/diagrams/gitGraph/gitGraphAst.js
Normal file
208
src/diagrams/gitGraph/gitGraphAst.js
Normal file
@@ -0,0 +1,208 @@
|
||||
const Logger = require('../../logger')
|
||||
const log = Logger.Log
|
||||
const _ = require('lodash')
|
||||
|
||||
var commits = {}
|
||||
var head = null
|
||||
var branches = { 'master': head }
|
||||
var curBranch = 'master'
|
||||
var direction = 'LR'
|
||||
var seq = 0
|
||||
|
||||
function getRandomInt (min, max) {
|
||||
return Math.floor(Math.random() * (max - min)) + min
|
||||
}
|
||||
|
||||
function getId () {
|
||||
var pool = '0123456789abcdef'
|
||||
var id = ''
|
||||
for (var i = 0; i < 7; i++) {
|
||||
id += pool[getRandomInt(0, 16)]
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
function isfastforwardable (currentCommit, otherCommit) {
|
||||
log.debug('Entering isfastforwardable:', currentCommit.id, otherCommit.id)
|
||||
while (currentCommit.seq <= otherCommit.seq && currentCommit !== otherCommit) {
|
||||
// only if other branch has more commits
|
||||
if (otherCommit.parent == null) break
|
||||
if (Array.isArray(otherCommit.parent)) {
|
||||
log.debug('In merge commit:', otherCommit.parent)
|
||||
return isfastforwardable(currentCommit, commits[otherCommit.parent[0]]) ||
|
||||
isfastforwardable(currentCommit, commits[otherCommit.parent[1]])
|
||||
} else {
|
||||
otherCommit = commits[otherCommit.parent]
|
||||
}
|
||||
}
|
||||
log.debug(currentCommit.id, otherCommit.id)
|
||||
return currentCommit.id === otherCommit.id
|
||||
}
|
||||
|
||||
function isReachableFrom (currentCommit, otherCommit) {
|
||||
var currentSeq = currentCommit.seq
|
||||
var otherSeq = otherCommit.seq
|
||||
if (currentSeq > otherSeq) return isfastforwardable(otherCommit, currentCommit)
|
||||
return false
|
||||
}
|
||||
|
||||
exports.setDirection = function (dir) {
|
||||
direction = dir
|
||||
}
|
||||
var options = {}
|
||||
exports.setOptions = function (rawOptString) {
|
||||
log.debug('options str', rawOptString)
|
||||
rawOptString = rawOptString && rawOptString.trim()
|
||||
rawOptString = rawOptString || '{}'
|
||||
try {
|
||||
options = JSON.parse(rawOptString)
|
||||
} catch (e) {
|
||||
log.error('error while parsing gitGraph options', e.message)
|
||||
}
|
||||
}
|
||||
|
||||
exports.getOptions = function () {
|
||||
return options
|
||||
}
|
||||
|
||||
exports.commit = function (msg) {
|
||||
var commit = {
|
||||
id: getId(),
|
||||
message: msg,
|
||||
seq: seq++,
|
||||
parent: head == null ? null : head.id
|
||||
}
|
||||
head = commit
|
||||
commits[commit.id] = commit
|
||||
branches[curBranch] = commit.id
|
||||
log.debug('in pushCommit ' + commit.id)
|
||||
}
|
||||
|
||||
exports.branch = function (name) {
|
||||
branches[name] = head != null ? head.id : null
|
||||
log.debug('in createBranch')
|
||||
}
|
||||
|
||||
exports.merge = function (otherBranch) {
|
||||
var currentCommit = commits[branches[curBranch]]
|
||||
var otherCommit = commits[branches[otherBranch]]
|
||||
if (isReachableFrom(currentCommit, otherCommit)) {
|
||||
log.debug('Already merged')
|
||||
return
|
||||
}
|
||||
if (isfastforwardable(currentCommit, otherCommit)) {
|
||||
branches[curBranch] = branches[otherBranch]
|
||||
head = commits[branches[curBranch]]
|
||||
} else {
|
||||
// create merge commit
|
||||
var commit = {
|
||||
id: getId(),
|
||||
message: 'merged branch ' + otherBranch + ' into ' + curBranch,
|
||||
seq: seq++,
|
||||
parent: [head == null ? null : head.id, branches[otherBranch]]
|
||||
}
|
||||
head = commit
|
||||
commits[commit.id] = commit
|
||||
branches[curBranch] = commit.id
|
||||
}
|
||||
log.debug(branches)
|
||||
log.debug('in mergeBranch')
|
||||
}
|
||||
|
||||
exports.checkout = function (branch) {
|
||||
log.debug('in checkout')
|
||||
curBranch = branch
|
||||
var id = branches[curBranch]
|
||||
head = commits[id]
|
||||
}
|
||||
|
||||
exports.reset = function (commitRef) {
|
||||
log.debug('in reset', commitRef)
|
||||
var ref = commitRef.split(':')[0]
|
||||
var parentCount = parseInt(commitRef.split(':')[1])
|
||||
var commit = ref === 'HEAD' ? head : commits[branches[ref]]
|
||||
log.debug(commit, parentCount)
|
||||
while (parentCount > 0) {
|
||||
commit = commits[commit.parent]
|
||||
parentCount--
|
||||
if (!commit) {
|
||||
var err = 'Critical error - unique parent commit not found during reset'
|
||||
log.error(err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
head = commit
|
||||
branches[curBranch] = commit.id
|
||||
}
|
||||
|
||||
function upsert (arr, key, newval) {
|
||||
const index = arr.indexOf(key)
|
||||
if (index === -1) {
|
||||
arr.push(newval)
|
||||
} else {
|
||||
arr.splice(index, 1, newval)
|
||||
}
|
||||
}
|
||||
|
||||
function prettyPrintCommitHistory (commitArr) {
|
||||
var commit = _.maxBy(commitArr, 'seq')
|
||||
var line = ''
|
||||
commitArr.forEach(function (c) {
|
||||
if (c === commit) {
|
||||
line += '\t*'
|
||||
} else {
|
||||
line += '\t|'
|
||||
}
|
||||
})
|
||||
var label = [line, commit.id, commit.seq]
|
||||
_.each(branches, function (value, key) {
|
||||
if (value === commit.id) label.push(key)
|
||||
})
|
||||
log.debug(label.join(' '))
|
||||
if (Array.isArray(commit.parent)) {
|
||||
var newCommit = commits[commit.parent[0]]
|
||||
upsert(commitArr, commit, newCommit)
|
||||
commitArr.push(commits[commit.parent[1]])
|
||||
} else if (commit.parent == null) {
|
||||
return
|
||||
} else {
|
||||
var nextCommit = commits[commit.parent]
|
||||
upsert(commitArr, commit, nextCommit)
|
||||
}
|
||||
commitArr = _.uniqBy(commitArr, 'id')
|
||||
prettyPrintCommitHistory(commitArr)
|
||||
}
|
||||
|
||||
exports.prettyPrint = function () {
|
||||
log.debug(commits)
|
||||
var node = exports.getCommitsArray()[0]
|
||||
prettyPrintCommitHistory([node])
|
||||
}
|
||||
|
||||
exports.clear = function () {
|
||||
commits = {}
|
||||
head = null
|
||||
branches = { 'master': head }
|
||||
curBranch = 'master'
|
||||
seq = 0
|
||||
}
|
||||
|
||||
exports.getBranchesAsObjArray = function () {
|
||||
const branchArr = _.map(branches, function (value, key) {
|
||||
return { 'name': key, 'commit': commits[value] }
|
||||
})
|
||||
return branchArr
|
||||
}
|
||||
|
||||
exports.getBranches = function () { return branches }
|
||||
exports.getCommits = function () { return commits }
|
||||
exports.getCommitsArray = function () {
|
||||
var commitArr = Object.keys(commits).map(function (key) {
|
||||
return commits[key]
|
||||
})
|
||||
commitArr.forEach(function (o) { log.debug(o.id) })
|
||||
return _.orderBy(commitArr, ['seq'], ['desc'])
|
||||
}
|
||||
exports.getCurrentBranch = function () { return curBranch }
|
||||
exports.getDirection = function () { return direction }
|
||||
exports.getHead = function () { return head }
|
||||
243
src/diagrams/gitGraph/gitGraphParser.spec.js
Normal file
243
src/diagrams/gitGraph/gitGraphParser.spec.js
Normal file
@@ -0,0 +1,243 @@
|
||||
/* eslint-env jasmine */
|
||||
var parser = require('./parser/gitGraph').parser
|
||||
var ast = require('./gitGraphAst.js')
|
||||
describe('when parsing a gitGraph', function () {
|
||||
'use strict'
|
||||
beforeEach(function () {
|
||||
console.log('ast', ast)
|
||||
console.log('parser', parser)
|
||||
parser.yy = ast
|
||||
parser.yy.clear()
|
||||
})
|
||||
it('should handle a gitGraph defintion', function () {
|
||||
var str = 'gitGraph:\n' +
|
||||
'commit\n'
|
||||
|
||||
parser.parse(str)
|
||||
var commits = parser.yy.getCommits()
|
||||
|
||||
expect(Object.keys(commits).length).toBe(1)
|
||||
expect(parser.yy.getCurrentBranch()).toBe('master')
|
||||
expect(parser.yy.getDirection()).toBe('LR')
|
||||
expect(Object.keys(parser.yy.getBranches()).length).toBe(1)
|
||||
})
|
||||
|
||||
it('should handle a gitGraph defintion with empty options', function () {
|
||||
var str = 'gitGraph:\n' +
|
||||
'options\n' +
|
||||
'end\n' +
|
||||
'commit\n'
|
||||
|
||||
parser.parse(str)
|
||||
var commits = parser.yy.getCommits()
|
||||
|
||||
expect(parser.yy.getOptions()).toEqual({})
|
||||
expect(Object.keys(commits).length).toBe(1)
|
||||
expect(parser.yy.getCurrentBranch()).toBe('master')
|
||||
expect(parser.yy.getDirection()).toBe('LR')
|
||||
expect(Object.keys(parser.yy.getBranches()).length).toBe(1)
|
||||
})
|
||||
|
||||
it('should handle a gitGraph defintion with valid options', function () {
|
||||
var str = 'gitGraph:\n' +
|
||||
'options\n' +
|
||||
'{"key": "value"}\n' +
|
||||
'end\n' +
|
||||
'commit\n'
|
||||
|
||||
parser.parse(str)
|
||||
var commits = parser.yy.getCommits()
|
||||
expect(parser.yy.getOptions()['key']).toBe('value')
|
||||
expect(Object.keys(commits).length).toBe(1)
|
||||
expect(parser.yy.getCurrentBranch()).toBe('master')
|
||||
expect(parser.yy.getDirection()).toBe('LR')
|
||||
expect(Object.keys(parser.yy.getBranches()).length).toBe(1)
|
||||
})
|
||||
|
||||
it('should not fail on a gitGraph with malformed json', function () {
|
||||
var str = 'gitGraph:\n' +
|
||||
'options\n' +
|
||||
'{"key": "value"\n' +
|
||||
'end\n' +
|
||||
'commit\n'
|
||||
|
||||
parser.parse(str)
|
||||
var commits = parser.yy.getCommits()
|
||||
expect(Object.keys(commits).length).toBe(1)
|
||||
expect(parser.yy.getCurrentBranch()).toBe('master')
|
||||
expect(parser.yy.getDirection()).toBe('LR')
|
||||
expect(Object.keys(parser.yy.getBranches()).length).toBe(1)
|
||||
})
|
||||
|
||||
it('should handle set direction', function () {
|
||||
var str = 'gitGraph BT:\n' +
|
||||
'commit\n'
|
||||
|
||||
parser.parse(str)
|
||||
var commits = parser.yy.getCommits()
|
||||
|
||||
expect(Object.keys(commits).length).toBe(1)
|
||||
expect(parser.yy.getCurrentBranch()).toBe('master')
|
||||
expect(parser.yy.getDirection()).toBe('BT')
|
||||
expect(Object.keys(parser.yy.getBranches()).length).toBe(1)
|
||||
})
|
||||
|
||||
it('should checkout a branch', function () {
|
||||
var str = 'gitGraph:\n' +
|
||||
'branch new\n' +
|
||||
'checkout new\n'
|
||||
|
||||
parser.parse(str)
|
||||
var commits = parser.yy.getCommits()
|
||||
|
||||
expect(Object.keys(commits).length).toBe(0)
|
||||
expect(parser.yy.getCurrentBranch()).toBe('new')
|
||||
})
|
||||
|
||||
it('should add commits to checked out branch', function () {
|
||||
var str = 'gitGraph:\n' +
|
||||
'branch new\n' +
|
||||
'checkout new\n' +
|
||||
'commit\n' +
|
||||
'commit\n'
|
||||
|
||||
parser.parse(str)
|
||||
var commits = parser.yy.getCommits()
|
||||
|
||||
expect(Object.keys(commits).length).toBe(2)
|
||||
expect(parser.yy.getCurrentBranch()).toBe('new')
|
||||
var branchCommit = parser.yy.getBranches()['new']
|
||||
expect(branchCommit).not.toBeNull()
|
||||
expect(commits[branchCommit].parent).not.toBeNull()
|
||||
})
|
||||
it('should handle commit with args', function () {
|
||||
var str = 'gitGraph:\n' +
|
||||
'commit "a commit"\n'
|
||||
|
||||
parser.parse(str)
|
||||
var commits = parser.yy.getCommits()
|
||||
|
||||
expect(Object.keys(commits).length).toBe(1)
|
||||
var key = Object.keys(commits)[0]
|
||||
expect(commits[key].message).toBe('a commit')
|
||||
expect(parser.yy.getCurrentBranch()).toBe('master')
|
||||
})
|
||||
|
||||
it('it should reset a branch', function () {
|
||||
var str = 'gitGraph:\n' +
|
||||
'commit\n' +
|
||||
'commit\n' +
|
||||
'branch newbranch\n' +
|
||||
'checkout newbranch\n' +
|
||||
'commit\n' +
|
||||
'reset master\n'
|
||||
|
||||
parser.parse(str)
|
||||
|
||||
var commits = parser.yy.getCommits()
|
||||
expect(Object.keys(commits).length).toBe(3)
|
||||
expect(parser.yy.getCurrentBranch()).toBe('newbranch')
|
||||
expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master'])
|
||||
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch'])
|
||||
})
|
||||
|
||||
it('reset can take an argument', function () {
|
||||
var str = 'gitGraph:\n' +
|
||||
'commit\n' +
|
||||
'commit\n' +
|
||||
'branch newbranch\n' +
|
||||
'checkout newbranch\n' +
|
||||
'commit\n' +
|
||||
'reset master^\n'
|
||||
|
||||
parser.parse(str)
|
||||
|
||||
var commits = parser.yy.getCommits()
|
||||
expect(Object.keys(commits).length).toBe(3)
|
||||
expect(parser.yy.getCurrentBranch()).toBe('newbranch')
|
||||
var master = commits[parser.yy.getBranches()['master']]
|
||||
expect(parser.yy.getHead().id).toEqual(master.parent)
|
||||
})
|
||||
|
||||
it('it should handle fast forwardable merges', function () {
|
||||
var str = 'gitGraph:\n' +
|
||||
'commit\n' +
|
||||
'branch newbranch\n' +
|
||||
'checkout newbranch\n' +
|
||||
'commit\n' +
|
||||
'commit\n' +
|
||||
'checkout master\n' +
|
||||
'merge newbranch\n'
|
||||
|
||||
parser.parse(str)
|
||||
|
||||
var commits = parser.yy.getCommits()
|
||||
expect(Object.keys(commits).length).toBe(3)
|
||||
expect(parser.yy.getCurrentBranch()).toBe('master')
|
||||
expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master'])
|
||||
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch'])
|
||||
})
|
||||
|
||||
it('it should handle cases when merge is a noop', function () {
|
||||
var str = 'gitGraph:\n' +
|
||||
'commit\n' +
|
||||
'branch newbranch\n' +
|
||||
'checkout newbranch\n' +
|
||||
'commit\n' +
|
||||
'commit\n' +
|
||||
'merge master\n'
|
||||
|
||||
parser.parse(str)
|
||||
|
||||
var commits = parser.yy.getCommits()
|
||||
expect(Object.keys(commits).length).toBe(3)
|
||||
expect(parser.yy.getCurrentBranch()).toBe('newbranch')
|
||||
expect(parser.yy.getBranches()['newbranch']).not.toEqual(parser.yy.getBranches()['master'])
|
||||
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch'])
|
||||
})
|
||||
|
||||
it('it should handle merge with 2 parents', function () {
|
||||
var str = 'gitGraph:\n' +
|
||||
'commit\n' +
|
||||
'branch newbranch\n' +
|
||||
'checkout newbranch\n' +
|
||||
'commit\n' +
|
||||
'commit\n' +
|
||||
'checkout master\n' +
|
||||
'commit\n' +
|
||||
'merge newbranch\n'
|
||||
|
||||
parser.parse(str)
|
||||
|
||||
var commits = parser.yy.getCommits()
|
||||
expect(Object.keys(commits).length).toBe(5)
|
||||
expect(parser.yy.getCurrentBranch()).toBe('master')
|
||||
expect(parser.yy.getBranches()['newbranch']).not.toEqual(parser.yy.getBranches()['master'])
|
||||
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['master'])
|
||||
})
|
||||
|
||||
it('it should handle ff merge when history walk has two parents (merge commit)', function () {
|
||||
var str = 'gitGraph:\n' +
|
||||
'commit\n' +
|
||||
'branch newbranch\n' +
|
||||
'checkout newbranch\n' +
|
||||
'commit\n' +
|
||||
'commit\n' +
|
||||
'checkout master\n' +
|
||||
'commit\n' +
|
||||
'merge newbranch\n' +
|
||||
'commit\n' +
|
||||
'checkout newbranch\n' +
|
||||
'merge master\n'
|
||||
|
||||
parser.parse(str)
|
||||
|
||||
var commits = parser.yy.getCommits()
|
||||
expect(Object.keys(commits).length).toBe(6)
|
||||
expect(parser.yy.getCurrentBranch()).toBe('newbranch')
|
||||
expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master'])
|
||||
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['master'])
|
||||
|
||||
parser.yy.prettyPrint()
|
||||
})
|
||||
})
|
||||
274
src/diagrams/gitGraph/gitGraphRenderer.js
Normal file
274
src/diagrams/gitGraph/gitGraphRenderer.js
Normal file
@@ -0,0 +1,274 @@
|
||||
const db = require('./gitGraphAst')
|
||||
const _ = require('lodash')
|
||||
const gitGraphParser = require('./parser/gitGraph')
|
||||
const d3 = require('../../d3')
|
||||
const Logger = require('../../logger')
|
||||
const log = Logger.Log
|
||||
|
||||
var allCommitsDict = {}
|
||||
var branchNum
|
||||
var config = {
|
||||
nodeSpacing: 150,
|
||||
nodeFillColor: 'yellow',
|
||||
nodeStrokeWidth: 2,
|
||||
nodeStrokeColor: 'grey',
|
||||
lineStrokeWidth: 4,
|
||||
branchOffset: 50,
|
||||
lineColor: 'grey',
|
||||
leftMargin: 50,
|
||||
branchColors: ['#442f74', '#983351', '#609732', '#AA9A39'],
|
||||
nodeRadius: 10,
|
||||
nodeLabel: {
|
||||
width: 75,
|
||||
height: 100,
|
||||
x: -25,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
var apiConfig = {}
|
||||
exports.setConf = function (c) {
|
||||
apiConfig = c
|
||||
}
|
||||
|
||||
function svgCreateDefs (svg) {
|
||||
svg
|
||||
.append('defs')
|
||||
.append('g')
|
||||
.attr('id', 'def-commit')
|
||||
.append('circle')
|
||||
.attr('r', config.nodeRadius)
|
||||
.attr('cx', 0)
|
||||
.attr('cy', 0)
|
||||
svg.select('#def-commit')
|
||||
.append('foreignObject')
|
||||
.attr('width', config.nodeLabel.width)
|
||||
.attr('height', config.nodeLabel.height)
|
||||
.attr('x', config.nodeLabel.x)
|
||||
.attr('y', config.nodeLabel.y)
|
||||
.attr('class', 'node-label')
|
||||
.attr('requiredFeatures', 'http://www.w3.org/TR/SVG11/feature#Extensibility')
|
||||
.append('xhtml:p')
|
||||
.html('')
|
||||
}
|
||||
|
||||
function svgDrawLine (svg, points, colorIdx, interpolate) {
|
||||
interpolate = interpolate || 'basis'
|
||||
var color = config.branchColors[colorIdx % config.branchColors.length]
|
||||
var lineGen = d3.svg.line()
|
||||
.x(function (d) {
|
||||
return Math.round(d.x)
|
||||
})
|
||||
.y(function (d) {
|
||||
return Math.round(d.y)
|
||||
})
|
||||
.interpolate(interpolate)
|
||||
|
||||
svg
|
||||
.append('svg:path')
|
||||
.attr('d', lineGen(points))
|
||||
.style('stroke', color)
|
||||
.style('stroke-width', config.lineStrokeWidth)
|
||||
.style('fill', 'none')
|
||||
}
|
||||
// Pass in the element and its pre-transform coords
|
||||
function getElementCoords (element, coords) {
|
||||
coords = coords || element.node().getBBox()
|
||||
var ctm = element.node().getCTM()
|
||||
var xn = ctm.e + coords.x * ctm.a
|
||||
var yn = ctm.f + coords.y * ctm.d
|
||||
return {
|
||||
left: xn,
|
||||
top: yn,
|
||||
width: coords.width,
|
||||
height: coords.height
|
||||
}
|
||||
}
|
||||
|
||||
function svgDrawLineForCommits (svg, fromId, toId, direction, color) {
|
||||
log.debug('svgDrawLineForCommits: ', fromId, toId)
|
||||
var fromBbox = getElementCoords(svg.select('#node-' + fromId + ' circle'))
|
||||
var toBbox = getElementCoords(svg.select('#node-' + toId + ' circle'))
|
||||
switch (direction) {
|
||||
case 'LR':
|
||||
// (toBbox)
|
||||
// +--------
|
||||
// + (fromBbox)
|
||||
if (fromBbox.left - toBbox.left > config.nodeSpacing) {
|
||||
var lineStart = { x: fromBbox.left - config.nodeSpacing, y: toBbox.top + toBbox.height / 2 }
|
||||
var lineEnd = { x: toBbox.left + toBbox.width, y: toBbox.top + toBbox.height / 2 }
|
||||
svgDrawLine(svg, [lineStart, lineEnd], color, 'linear')
|
||||
svgDrawLine(svg, [
|
||||
{ x: fromBbox.left, y: fromBbox.top + fromBbox.height / 2 },
|
||||
{ x: fromBbox.left - config.nodeSpacing / 2, y: fromBbox.top + fromBbox.height / 2 },
|
||||
{ x: fromBbox.left - config.nodeSpacing / 2, y: lineStart.y },
|
||||
lineStart], color)
|
||||
} else {
|
||||
svgDrawLine(svg, [{
|
||||
'x': fromBbox.left,
|
||||
'y': fromBbox.top + fromBbox.height / 2
|
||||
}, {
|
||||
'x': fromBbox.left - config.nodeSpacing / 2,
|
||||
'y': fromBbox.top + fromBbox.height / 2
|
||||
}, {
|
||||
'x': fromBbox.left - config.nodeSpacing / 2,
|
||||
'y': toBbox.top + toBbox.height / 2
|
||||
}, {
|
||||
'x': toBbox.left + toBbox.width,
|
||||
'y': toBbox.top + toBbox.height / 2
|
||||
}], color)
|
||||
}
|
||||
break
|
||||
case 'BT':
|
||||
// + (fromBbox)
|
||||
// |
|
||||
// |
|
||||
// + (toBbox)
|
||||
if (toBbox.top - fromBbox.top > config.nodeSpacing) {
|
||||
lineStart = { x: toBbox.left + toBbox.width / 2, y: fromBbox.top + fromBbox.height + config.nodeSpacing }
|
||||
lineEnd = { x: toBbox.left + toBbox.width / 2, y: toBbox.top }
|
||||
svgDrawLine(svg, [lineStart, lineEnd], color, 'linear')
|
||||
svgDrawLine(svg, [
|
||||
{ x: fromBbox.left + fromBbox.width / 2, y: fromBbox.top + fromBbox.height },
|
||||
{ x: fromBbox.left + fromBbox.width / 2, y: fromBbox.top + fromBbox.height + config.nodeSpacing / 2 },
|
||||
{ x: toBbox.left + toBbox.width / 2, y: lineStart.y - config.nodeSpacing / 2 },
|
||||
lineStart], color)
|
||||
} else {
|
||||
svgDrawLine(svg, [{
|
||||
'x': fromBbox.left + fromBbox.width / 2,
|
||||
'y': fromBbox.top + fromBbox.height
|
||||
}, {
|
||||
'x': fromBbox.left + fromBbox.width / 2,
|
||||
'y': fromBbox.top + config.nodeSpacing / 2
|
||||
}, {
|
||||
'x': toBbox.left + toBbox.width / 2,
|
||||
'y': toBbox.top - config.nodeSpacing / 2
|
||||
}, {
|
||||
'x': toBbox.left + toBbox.width / 2,
|
||||
'y': toBbox.top
|
||||
}], color)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
function cloneNode (svg, selector) {
|
||||
return svg.select(selector).node().cloneNode(true)
|
||||
}
|
||||
|
||||
function renderCommitHistory (svg, commitid, branches, direction) {
|
||||
var commit
|
||||
var numCommits = Object.keys(allCommitsDict).length
|
||||
if (_.isString(commitid)) {
|
||||
do {
|
||||
commit = allCommitsDict[commitid]
|
||||
log.debug('in renderCommitHistory', commit.id, commit.seq)
|
||||
if (svg.select('#node-' + commitid).size() > 0) {
|
||||
return
|
||||
}
|
||||
svg
|
||||
.append(function () {
|
||||
return cloneNode(svg, '#def-commit')
|
||||
})
|
||||
.attr('class', 'commit')
|
||||
.attr('id', function () {
|
||||
return 'node-' + commit.id
|
||||
})
|
||||
.attr('transform', function () {
|
||||
switch (direction) {
|
||||
case 'LR':
|
||||
return 'translate(' + (commit.seq * config.nodeSpacing + config.leftMargin) + ', ' +
|
||||
(branchNum * config.branchOffset) + ')'
|
||||
case 'BT':
|
||||
return 'translate(' + (branchNum * config.branchOffset + config.leftMargin) + ', ' +
|
||||
((numCommits - commit.seq) * config.nodeSpacing) + ')'
|
||||
}
|
||||
})
|
||||
.attr('fill', config.nodeFillColor)
|
||||
.attr('stroke', config.nodeStrokeColor)
|
||||
.attr('stroke-width', config.nodeStrokeWidth)
|
||||
|
||||
var branch = _.find(branches, ['commit', commit])
|
||||
if (branch) {
|
||||
log.debug('found branch ', branch.name)
|
||||
svg.select('#node-' + commit.id + ' p')
|
||||
.append('xhtml:span')
|
||||
.attr('class', 'branch-label')
|
||||
.text(branch.name + ', ')
|
||||
}
|
||||
svg.select('#node-' + commit.id + ' p')
|
||||
.append('xhtml:span')
|
||||
.attr('class', 'commit-id')
|
||||
.text(commit.id)
|
||||
if (commit.message !== '' && direction === 'BT') {
|
||||
svg.select('#node-' + commit.id + ' p')
|
||||
.append('xhtml:span')
|
||||
.attr('class', 'commit-msg')
|
||||
.text(', ' + commit.message)
|
||||
}
|
||||
commitid = commit.parent
|
||||
} while (commitid && allCommitsDict[commitid])
|
||||
}
|
||||
|
||||
if (_.isArray(commitid)) {
|
||||
log.debug('found merge commmit', commitid)
|
||||
renderCommitHistory(svg, commitid[0], branches, direction)
|
||||
branchNum++
|
||||
renderCommitHistory(svg, commitid[1], branches, direction)
|
||||
branchNum--
|
||||
}
|
||||
}
|
||||
|
||||
function renderLines (svg, commit, direction, branchColor) {
|
||||
branchColor = branchColor || 0
|
||||
while (commit.seq > 0 && !commit.lineDrawn) {
|
||||
if (_.isString(commit.parent)) {
|
||||
svgDrawLineForCommits(svg, commit.id, commit.parent, direction, branchColor)
|
||||
commit.lineDrawn = true
|
||||
commit = allCommitsDict[commit.parent]
|
||||
} else if (_.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)
|
||||
commit.lineDrawn = true
|
||||
commit = allCommitsDict[commit.parent[0]]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exports.draw = function (txt, id, ver) {
|
||||
try {
|
||||
var parser
|
||||
parser = gitGraphParser.parser
|
||||
parser.yy = db
|
||||
|
||||
log.debug('in gitgraph renderer', txt, id, ver)
|
||||
// Parse the graph definition
|
||||
parser.parse(txt + '\n')
|
||||
|
||||
config = _.extend(config, apiConfig, db.getOptions())
|
||||
log.debug('effective options', config)
|
||||
var direction = db.getDirection()
|
||||
allCommitsDict = db.getCommits()
|
||||
var branches = db.getBranchesAsObjArray()
|
||||
if (direction === 'BT') {
|
||||
config.nodeLabel.x = branches.length * config.branchOffset
|
||||
config.nodeLabel.width = '100%'
|
||||
config.nodeLabel.y = -1 * 2 * config.nodeRadius
|
||||
}
|
||||
var svg = d3.select('#' + id)
|
||||
svgCreateDefs(svg)
|
||||
branchNum = 1
|
||||
_.each(branches, function (v) {
|
||||
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
|
||||
})
|
||||
} catch (e) {
|
||||
log.error('Error while rendering gitgraph')
|
||||
log.error(e.message)
|
||||
}
|
||||
}
|
||||
92
src/diagrams/gitGraph/parser/gitGraph.jison
Normal file
92
src/diagrams/gitGraph/parser/gitGraph.jison
Normal file
@@ -0,0 +1,92 @@
|
||||
|
||||
/*
|
||||
* Parse following
|
||||
* gitGraph:
|
||||
* commit
|
||||
* commit
|
||||
* branch
|
||||
*/
|
||||
%lex
|
||||
|
||||
%x string
|
||||
%x options
|
||||
%options case-insensitive
|
||||
|
||||
%%
|
||||
|
||||
(\r?\n)+ return 'NL';
|
||||
\s+ /* skip all whitespace */
|
||||
\#[^\n]* /* skip comments */
|
||||
\%%[^\n]* /* skip comments */
|
||||
"gitGraph" return 'GG';
|
||||
"commit" return 'COMMIT';
|
||||
"branch" return 'BRANCH';
|
||||
"merge" return 'MERGE';
|
||||
"reset" return 'RESET';
|
||||
"checkout" return 'CHECKOUT';
|
||||
"LR" return 'DIR';
|
||||
"BT" return 'DIR';
|
||||
":" return ':';
|
||||
"^" return 'CARET'
|
||||
"options"\r?\n this.begin("options");
|
||||
<options>"end"\r?\n this.popState();
|
||||
<options>[^\n]+\r?\n return 'OPT';
|
||||
["] this.begin("string");
|
||||
<string>["] this.popState();
|
||||
<string>[^"]* return 'STR';
|
||||
[a-zA-Z][a-zA-Z0-9_]+ return 'ID';
|
||||
<<EOF>> return 'EOF';
|
||||
|
||||
/lex
|
||||
|
||||
%left '^'
|
||||
|
||||
%start start
|
||||
|
||||
%% /* language grammar */
|
||||
|
||||
start
|
||||
: GG ':' document EOF{ return $3; }
|
||||
| GG DIR ':' document EOF {yy.setDirection($2); return $4;}
|
||||
;
|
||||
|
||||
|
||||
document
|
||||
: /*empty*/
|
||||
| options body { yy.setOptions($1); $$ = $2}
|
||||
;
|
||||
|
||||
options
|
||||
: options OPT {$1 +=$2; $$=$1}
|
||||
| NL
|
||||
;
|
||||
body
|
||||
: /*emmpty*/ {$$ = []}
|
||||
| body line {$1.push($2); $$=$1;}
|
||||
;
|
||||
line
|
||||
: statement NL{$$ =$1}
|
||||
| NL
|
||||
;
|
||||
|
||||
statement
|
||||
: COMMIT commit_arg {yy.commit($2)}
|
||||
| BRANCH ID {yy.branch($2)}
|
||||
| CHECKOUT ID {yy.checkout($2)}
|
||||
| MERGE ID {yy.merge($2)}
|
||||
| RESET reset_arg {yy.reset($2)}
|
||||
;
|
||||
|
||||
commit_arg
|
||||
: /* empty */ {$$ = ""}
|
||||
| STR {$$=$1}
|
||||
;
|
||||
|
||||
reset_arg
|
||||
: 'HEAD' reset_parents{$$ = $1+ ":" + $2 }
|
||||
| ID reset_parents{$$ = $1+ ":" + yy.count; yy.count = 0}
|
||||
;
|
||||
reset_parents
|
||||
: /* empty */ {yy.count = 0}
|
||||
| CARET reset_parents { yy.count += 1 }
|
||||
;
|
||||
692
src/diagrams/gitGraph/parser/gitGraph.js
Normal file
692
src/diagrams/gitGraph/parser/gitGraph.js
Normal file
@@ -0,0 +1,692 @@
|
||||
/* parser generated by jison 0.4.17 */
|
||||
/*
|
||||
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 = [2, 3], $V1 = [1, 7], $V2 = [7, 12, 15, 17, 19, 20, 21], $V3 = [7, 11, 12, 15, 17, 19, 20, 21], $V4 = [2, 20], $V5 = [1, 32]
|
||||
var parser = {trace: function trace () { },
|
||||
yy: {},
|
||||
symbols_: {'error': 2, 'start': 3, 'GG': 4, ':': 5, 'document': 6, 'EOF': 7, 'DIR': 8, 'options': 9, 'body': 10, 'OPT': 11, 'NL': 12, 'line': 13, 'statement': 14, 'COMMIT': 15, 'commit_arg': 16, 'BRANCH': 17, 'ID': 18, 'CHECKOUT': 19, 'MERGE': 20, 'RESET': 21, 'reset_arg': 22, 'STR': 23, 'HEAD': 24, 'reset_parents': 25, 'CARET': 26, '$accept': 0, '$end': 1},
|
||||
terminals_: {2: 'error', 4: 'GG', 5: ':', 7: 'EOF', 8: 'DIR', 11: 'OPT', 12: 'NL', 15: 'COMMIT', 17: 'BRANCH', 18: 'ID', 19: 'CHECKOUT', 20: 'MERGE', 21: 'RESET', 23: 'STR', 24: 'HEAD', 26: 'CARET'},
|
||||
productions_: [0, [3, 4], [3, 5], [6, 0], [6, 2], [9, 2], [9, 1], [10, 0], [10, 2], [13, 2], [13, 1], [14, 2], [14, 2], [14, 2], [14, 2], [14, 2], [16, 0], [16, 1], [22, 2], [22, 2], [25, 0], [25, 2]],
|
||||
performAction: function anonymous (yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
|
||||
/* this == yyval */
|
||||
|
||||
var $0 = $$.length - 1
|
||||
switch (yystate) {
|
||||
case 1:
|
||||
return $$[$0 - 1]
|
||||
break
|
||||
case 2:
|
||||
yy.setDirection($$[$0 - 3]); return $$[$0 - 1]
|
||||
break
|
||||
case 4:
|
||||
yy.setOptions($$[$0 - 1]); this.$ = $$[$0]
|
||||
break
|
||||
case 5:
|
||||
$$[$0 - 1] += $$[$0]; this.$ = $$[$0 - 1]
|
||||
break
|
||||
case 7:
|
||||
this.$ = []
|
||||
break
|
||||
case 8:
|
||||
$$[$0 - 1].push($$[$0]); this.$ = $$[$0 - 1]
|
||||
break
|
||||
case 9:
|
||||
this.$ = $$[$0 - 1]
|
||||
break
|
||||
case 11:
|
||||
yy.commit($$[$0])
|
||||
break
|
||||
case 12:
|
||||
yy.branch($$[$0])
|
||||
break
|
||||
case 13:
|
||||
yy.checkout($$[$0])
|
||||
break
|
||||
case 14:
|
||||
yy.merge($$[$0])
|
||||
break
|
||||
case 15:
|
||||
yy.reset($$[$0])
|
||||
break
|
||||
case 16:
|
||||
this.$ = ''
|
||||
break
|
||||
case 17:
|
||||
this.$ = $$[$0]
|
||||
break
|
||||
case 18:
|
||||
this.$ = $$[$0 - 1] + ':' + $$[$0]
|
||||
break
|
||||
case 19:
|
||||
this.$ = $$[$0 - 1] + ':' + yy.count; yy.count = 0
|
||||
break
|
||||
case 20:
|
||||
yy.count = 0
|
||||
break
|
||||
case 21:
|
||||
yy.count += 1
|
||||
break
|
||||
}
|
||||
},
|
||||
table: [{3: 1, 4: [1, 2]}, {1: [3]}, {5: [1, 3], 8: [1, 4]}, {6: 5, 7: $V0, 9: 6, 12: $V1}, {5: [1, 8]}, {7: [1, 9]}, o($V2, [2, 7], {10: 10, 11: [1, 11]}), o($V3, [2, 6]), {6: 12, 7: $V0, 9: 6, 12: $V1}, {1: [2, 1]}, {7: [2, 4], 12: [1, 15], 13: 13, 14: 14, 15: [1, 16], 17: [1, 17], 19: [1, 18], 20: [1, 19], 21: [1, 20]}, o($V3, [2, 5]), {7: [1, 21]}, o($V2, [2, 8]), {12: [1, 22]}, o($V2, [2, 10]), {12: [2, 16], 16: 23, 23: [1, 24]}, {18: [1, 25]}, {18: [1, 26]}, {18: [1, 27]}, {18: [1, 30], 22: 28, 24: [1, 29]}, {1: [2, 2]}, o($V2, [2, 9]), {12: [2, 11]}, {12: [2, 17]}, {12: [2, 12]}, {12: [2, 13]}, {12: [2, 14]}, {12: [2, 15]}, {12: $V4, 25: 31, 26: $V5}, {12: $V4, 25: 33, 26: $V5}, {12: [2, 18]}, {12: $V4, 25: 34, 26: $V5}, {12: [2, 19]}, {12: [2, 21]}],
|
||||
defaultActions: {9: [2, 1], 21: [2, 2], 23: [2, 11], 24: [2, 17], 25: [2, 12], 26: [2, 13], 27: [2, 14], 28: [2, 15], 31: [2, 18], 33: [2, 19], 34: [2, 21]},
|
||||
parseError: function parseError (str, hash) {
|
||||
if (hash.recoverable) {
|
||||
this.trace(str)
|
||||
} else {
|
||||
function _parseError (msg, hash) {
|
||||
this.message = msg
|
||||
this.hash = hash
|
||||
}
|
||||
_parseError.prototype = Error
|
||||
|
||||
throw new _parseError(str, hash)
|
||||
}
|
||||
},
|
||||
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
|
||||
}
|
||||
var lex = function () {
|
||||
var token
|
||||
token = lexer.lex() || EOF
|
||||
if (typeof token !== 'number') {
|
||||
token = self.symbols_[token] || token
|
||||
}
|
||||
return token
|
||||
}
|
||||
var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected
|
||||
while (true) {
|
||||
state = stack[stack.length - 1]
|
||||
if (this.defaultActions[state]) {
|
||||
action = this.defaultActions[state]
|
||||
} else {
|
||||
if (symbol === null || typeof symbol === 'undefined') {
|
||||
symbol = lex()
|
||||
}
|
||||
action = table[state] && table[state][symbol]
|
||||
}
|
||||
if (typeof action === 'undefined' || !action.length || !action[0]) {
|
||||
var errStr = ''
|
||||
expected = []
|
||||
for (p in table[state]) {
|
||||
if (this.terminals_[p] && p > TERROR) {
|
||||
expected.push('\'' + this.terminals_[p] + '\'')
|
||||
}
|
||||
}
|
||||
if (lexer.showPosition) {
|
||||
errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\''
|
||||
} else {
|
||||
errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'')
|
||||
}
|
||||
this.parseError(errStr, {
|
||||
text: lexer.match,
|
||||
token: this.terminals_[symbol] || symbol,
|
||||
line: lexer.yylineno,
|
||||
loc: yyloc,
|
||||
expected: expected
|
||||
})
|
||||
}
|
||||
if (action[0] instanceof Array && action.length > 1) {
|
||||
throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol)
|
||||
}
|
||||
switch (action[0]) {
|
||||
case 1:
|
||||
stack.push(symbol)
|
||||
vstack.push(lexer.yytext)
|
||||
lstack.push(lexer.yylloc)
|
||||
stack.push(action[1])
|
||||
symbol = null
|
||||
if (!preErrorSymbol) {
|
||||
yyleng = lexer.yyleng
|
||||
yytext = lexer.yytext
|
||||
yylineno = lexer.yylineno
|
||||
yyloc = lexer.yylloc
|
||||
if (recovering > 0) {
|
||||
recovering--
|
||||
}
|
||||
} else {
|
||||
symbol = preErrorSymbol
|
||||
preErrorSymbol = null
|
||||
}
|
||||
break
|
||||
case 2:
|
||||
len = this.productions_[action[1]][1]
|
||||
yyval.$ = vstack[vstack.length - len]
|
||||
yyval._$ = {
|
||||
first_line: lstack[lstack.length - (len || 1)].first_line,
|
||||
last_line: lstack[lstack.length - 1].last_line,
|
||||
first_column: lstack[lstack.length - (len || 1)].first_column,
|
||||
last_column: lstack[lstack.length - 1].last_column
|
||||
}
|
||||
if (ranges) {
|
||||
yyval._$.range = [
|
||||
lstack[lstack.length - (len || 1)].range[0],
|
||||
lstack[lstack.length - 1].range[1]
|
||||
]
|
||||
}
|
||||
r = this.performAction.apply(yyval, [
|
||||
yytext,
|
||||
yyleng,
|
||||
yylineno,
|
||||
sharedState.yy,
|
||||
action[1],
|
||||
vstack,
|
||||
lstack
|
||||
].concat(args))
|
||||
if (typeof r !== 'undefined') {
|
||||
return r
|
||||
}
|
||||
if (len) {
|
||||
stack = stack.slice(0, -1 * len * 2)
|
||||
vstack = vstack.slice(0, -1 * len)
|
||||
lstack = lstack.slice(0, -1 * len)
|
||||
}
|
||||
stack.push(this.productions_[action[1]][0])
|
||||
vstack.push(yyval.$)
|
||||
lstack.push(yyval._$)
|
||||
newState = table[stack[stack.length - 2]][stack[stack.length - 1]]
|
||||
stack.push(newState)
|
||||
break
|
||||
case 3:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
}}
|
||||
/* generated by jison-lex 0.3.4 */
|
||||
var lexer = (function () {
|
||||
var lexer = ({
|
||||
|
||||
EOF: 1,
|
||||
|
||||
parseError: function parseError (str, hash) {
|
||||
if (this.yy.parser) {
|
||||
this.yy.parser.parseError(str, hash)
|
||||
} else {
|
||||
throw new Error(str)
|
||||
}
|
||||
},
|
||||
|
||||
// resets the lexer, sets new input
|
||||
setInput: function (input, yy) {
|
||||
this.yy = yy || this.yy || {}
|
||||
this._input = input
|
||||
this._more = this._backtrack = this.done = false
|
||||
this.yylineno = this.yyleng = 0
|
||||
this.yytext = this.matched = this.match = ''
|
||||
this.conditionStack = ['INITIAL']
|
||||
this.yylloc = {
|
||||
first_line: 1,
|
||||
first_column: 0,
|
||||
last_line: 1,
|
||||
last_column: 0
|
||||
}
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range = [0, 0]
|
||||
}
|
||||
this.offset = 0
|
||||
return this
|
||||
},
|
||||
|
||||
// consumes and returns one char from the input
|
||||
input: function () {
|
||||
var ch = this._input[0]
|
||||
this.yytext += ch
|
||||
this.yyleng++
|
||||
this.offset++
|
||||
this.match += ch
|
||||
this.matched += ch
|
||||
var lines = ch.match(/(?:\r\n?|\n).*/g)
|
||||
if (lines) {
|
||||
this.yylineno++
|
||||
this.yylloc.last_line++
|
||||
} else {
|
||||
this.yylloc.last_column++
|
||||
}
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range[1]++
|
||||
}
|
||||
|
||||
this._input = this._input.slice(1)
|
||||
return ch
|
||||
},
|
||||
|
||||
// unshifts one char (or a string) into the input
|
||||
unput: function (ch) {
|
||||
var len = ch.length
|
||||
var lines = ch.split(/(?:\r\n?|\n)/g)
|
||||
|
||||
this._input = ch + this._input
|
||||
this.yytext = this.yytext.substr(0, this.yytext.length - len)
|
||||
// this.yyleng -= len;
|
||||
this.offset -= len
|
||||
var oldLines = this.match.split(/(?:\r\n?|\n)/g)
|
||||
this.match = this.match.substr(0, this.match.length - 1)
|
||||
this.matched = this.matched.substr(0, this.matched.length - 1)
|
||||
|
||||
if (lines.length - 1) {
|
||||
this.yylineno -= lines.length - 1
|
||||
}
|
||||
var r = this.yylloc.range
|
||||
|
||||
this.yylloc = {
|
||||
first_line: this.yylloc.first_line,
|
||||
last_line: this.yylineno + 1,
|
||||
first_column: this.yylloc.first_column,
|
||||
last_column: lines
|
||||
? (lines.length === oldLines.length ? this.yylloc.first_column : 0) +
|
||||
oldLines[oldLines.length - lines.length].length - lines[0].length
|
||||
: this.yylloc.first_column - len
|
||||
}
|
||||
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range = [r[0], r[0] + this.yyleng - len]
|
||||
}
|
||||
this.yyleng = this.yytext.length
|
||||
return this
|
||||
},
|
||||
|
||||
// When called from action, caches matched text and appends it on next action
|
||||
more: function () {
|
||||
this._more = true
|
||||
return this
|
||||
},
|
||||
|
||||
// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
|
||||
reject: function () {
|
||||
if (this.options.backtrack_lexer) {
|
||||
this._backtrack = true
|
||||
} else {
|
||||
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), {
|
||||
text: '',
|
||||
token: null,
|
||||
line: this.yylineno
|
||||
})
|
||||
}
|
||||
return this
|
||||
},
|
||||
|
||||
// retain first n characters of the match
|
||||
less: function (n) {
|
||||
this.unput(this.match.slice(n))
|
||||
},
|
||||
|
||||
// displays already matched input, i.e. for error messages
|
||||
pastInput: function () {
|
||||
var past = this.matched.substr(0, this.matched.length - this.match.length)
|
||||
return (past.length > 20 ? '...' : '') + past.substr(-20).replace(/\n/g, '')
|
||||
},
|
||||
|
||||
// displays upcoming input, i.e. for error messages
|
||||
upcomingInput: function () {
|
||||
var next = this.match
|
||||
if (next.length < 20) {
|
||||
next += this._input.substr(0, 20 - next.length)
|
||||
}
|
||||
return (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace(/\n/g, '')
|
||||
},
|
||||
|
||||
// displays the character position where the lexing error occurred, i.e. for error messages
|
||||
showPosition: function () {
|
||||
var pre = this.pastInput()
|
||||
var c = new Array(pre.length + 1).join('-')
|
||||
return pre + this.upcomingInput() + '\n' + c + '^'
|
||||
},
|
||||
|
||||
// test the lexed token: return FALSE when not a match, otherwise return token
|
||||
test_match: function (match, indexed_rule) {
|
||||
var token,
|
||||
lines,
|
||||
backup
|
||||
|
||||
if (this.options.backtrack_lexer) {
|
||||
// save context
|
||||
backup = {
|
||||
yylineno: this.yylineno,
|
||||
yylloc: {
|
||||
first_line: this.yylloc.first_line,
|
||||
last_line: this.last_line,
|
||||
first_column: this.yylloc.first_column,
|
||||
last_column: this.yylloc.last_column
|
||||
},
|
||||
yytext: this.yytext,
|
||||
match: this.match,
|
||||
matches: this.matches,
|
||||
matched: this.matched,
|
||||
yyleng: this.yyleng,
|
||||
offset: this.offset,
|
||||
_more: this._more,
|
||||
_input: this._input,
|
||||
yy: this.yy,
|
||||
conditionStack: this.conditionStack.slice(0),
|
||||
done: this.done
|
||||
}
|
||||
if (this.options.ranges) {
|
||||
backup.yylloc.range = this.yylloc.range.slice(0)
|
||||
}
|
||||
}
|
||||
|
||||
lines = match[0].match(/(?:\r\n?|\n).*/g)
|
||||
if (lines) {
|
||||
this.yylineno += lines.length
|
||||
}
|
||||
this.yylloc = {
|
||||
first_line: this.yylloc.last_line,
|
||||
last_line: this.yylineno + 1,
|
||||
first_column: this.yylloc.last_column,
|
||||
last_column: lines
|
||||
? lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length
|
||||
: this.yylloc.last_column + match[0].length
|
||||
}
|
||||
this.yytext += match[0]
|
||||
this.match += match[0]
|
||||
this.matches = match
|
||||
this.yyleng = this.yytext.length
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range = [this.offset, this.offset += this.yyleng]
|
||||
}
|
||||
this._more = false
|
||||
this._backtrack = false
|
||||
this._input = this._input.slice(match[0].length)
|
||||
this.matched += match[0]
|
||||
token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1])
|
||||
if (this.done && this._input) {
|
||||
this.done = false
|
||||
}
|
||||
if (token) {
|
||||
return token
|
||||
} else if (this._backtrack) {
|
||||
// recover context
|
||||
for (var k in backup) {
|
||||
this[k] = backup[k]
|
||||
}
|
||||
return false // rule action called reject() implying the next rule should be tested instead.
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
// return next match in input
|
||||
next: function () {
|
||||
if (this.done) {
|
||||
return this.EOF
|
||||
}
|
||||
if (!this._input) {
|
||||
this.done = true
|
||||
}
|
||||
|
||||
var token,
|
||||
match,
|
||||
tempMatch,
|
||||
index
|
||||
if (!this._more) {
|
||||
this.yytext = ''
|
||||
this.match = ''
|
||||
}
|
||||
var rules = this._currentRules()
|
||||
for (var i = 0; i < rules.length; i++) {
|
||||
tempMatch = this._input.match(this.rules[rules[i]])
|
||||
if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
|
||||
match = tempMatch
|
||||
index = i
|
||||
if (this.options.backtrack_lexer) {
|
||||
token = this.test_match(tempMatch, rules[i])
|
||||
if (token !== false) {
|
||||
return token
|
||||
} else if (this._backtrack) {
|
||||
match = false
|
||||
continue // rule action called reject() implying a rule MISmatch.
|
||||
} else {
|
||||
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
|
||||
return false
|
||||
}
|
||||
} else if (!this.options.flex) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
token = this.test_match(match, rules[index])
|
||||
if (token !== false) {
|
||||
return token
|
||||
}
|
||||
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
|
||||
return false
|
||||
}
|
||||
if (this._input === '') {
|
||||
return this.EOF
|
||||
} else {
|
||||
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
|
||||
text: '',
|
||||
token: null,
|
||||
line: this.yylineno
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// return next match that has a token
|
||||
lex: function lex () {
|
||||
var r = this.next()
|
||||
if (r) {
|
||||
return r
|
||||
} else {
|
||||
return this.lex()
|
||||
}
|
||||
},
|
||||
|
||||
// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
|
||||
begin: function begin (condition) {
|
||||
this.conditionStack.push(condition)
|
||||
},
|
||||
|
||||
// pop the previously active lexer condition state off the condition stack
|
||||
popState: function popState () {
|
||||
var n = this.conditionStack.length - 1
|
||||
if (n > 0) {
|
||||
return this.conditionStack.pop()
|
||||
} else {
|
||||
return this.conditionStack[0]
|
||||
}
|
||||
},
|
||||
|
||||
// produce the lexer rule set which is active for the currently active lexer condition state
|
||||
_currentRules: function _currentRules () {
|
||||
if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
|
||||
return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules
|
||||
} else {
|
||||
return this.conditions['INITIAL'].rules
|
||||
}
|
||||
},
|
||||
|
||||
// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
|
||||
topState: function topState (n) {
|
||||
n = this.conditionStack.length - 1 - Math.abs(n || 0)
|
||||
if (n >= 0) {
|
||||
return this.conditionStack[n]
|
||||
} else {
|
||||
return 'INITIAL'
|
||||
}
|
||||
},
|
||||
|
||||
// alias for begin(condition)
|
||||
pushState: function pushState (condition) {
|
||||
this.begin(condition)
|
||||
},
|
||||
|
||||
// return the number of states currently on the stack
|
||||
stateStackSize: function stateStackSize () {
|
||||
return this.conditionStack.length
|
||||
},
|
||||
options: {'case-insensitive': true},
|
||||
performAction: function anonymous (yy, yy_, $avoiding_name_collisions, YY_START) {
|
||||
var YYSTATE = YY_START
|
||||
switch ($avoiding_name_collisions) {
|
||||
case 0:return 12
|
||||
break
|
||||
case 1:/* skip all whitespace */
|
||||
break
|
||||
case 2:/* skip comments */
|
||||
break
|
||||
case 3:/* skip comments */
|
||||
break
|
||||
case 4:return 4
|
||||
break
|
||||
case 5:return 15
|
||||
break
|
||||
case 6:return 17
|
||||
break
|
||||
case 7:return 20
|
||||
break
|
||||
case 8:return 21
|
||||
break
|
||||
case 9:return 19
|
||||
break
|
||||
case 10:return 8
|
||||
break
|
||||
case 11:return 8
|
||||
break
|
||||
case 12:return 5
|
||||
break
|
||||
case 13:return 26
|
||||
break
|
||||
case 14:this.begin('options')
|
||||
break
|
||||
case 15:this.popState()
|
||||
break
|
||||
case 16:return 11
|
||||
break
|
||||
case 17:this.begin('string')
|
||||
break
|
||||
case 18:this.popState()
|
||||
break
|
||||
case 19:return 23
|
||||
break
|
||||
case 20:return 18
|
||||
break
|
||||
case 21:return 7
|
||||
break
|
||||
}
|
||||
},
|
||||
rules: [/^(?:(\r?\n)+)/i, /^(?:\s+)/i, /^(?:#[^\n]*)/i, /^(?:%[^\n]*)/i, /^(?:gitGraph\b)/i, /^(?:commit\b)/i, /^(?:branch\b)/i, /^(?:merge\b)/i, /^(?:reset\b)/i, /^(?:checkout\b)/i, /^(?:LR\b)/i, /^(?:BT\b)/i, /^(?::)/i, /^(?:\^)/i, /^(?:options\r?\n)/i, /^(?:end\r?\n)/i, /^(?:[^\n]+\r?\n)/i, /^(?:["])/i, /^(?:["])/i, /^(?:[^"]*)/i, /^(?:[a-zA-Z][a-zA-Z0-9_]+)/i, /^(?:$)/i],
|
||||
conditions: {'options': {'rules': [15, 16], 'inclusive': false}, 'string': {'rules': [18, 19], 'inclusive': false}, 'INITIAL': {'rules': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 20, 21], '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))
|
||||
}
|
||||
}
|
||||
191
src/diagrams/sequenceDiagram/parser/sequenceDiagram.jison
Normal file
191
src/diagrams/sequenceDiagram/parser/sequenceDiagram.jison
Normal file
@@ -0,0 +1,191 @@
|
||||
/** mermaid
|
||||
* https://mermaidjs.github.io/
|
||||
* (c) 2014-2015 Knut Sveidqvist
|
||||
* MIT license.
|
||||
*
|
||||
* Based on js sequence diagrams jison grammr
|
||||
* http://bramp.github.io/js-sequence-diagrams/
|
||||
* (c) 2012-2013 Andrew Brampton (bramp.net)
|
||||
* Simplified BSD license.
|
||||
*/
|
||||
%lex
|
||||
|
||||
%options case-insensitive
|
||||
|
||||
// Special states for recognizing aliases
|
||||
%x ID
|
||||
%x ALIAS
|
||||
|
||||
// A special state for grabbing text up to the first comment/newline
|
||||
%x LINE
|
||||
|
||||
%%
|
||||
|
||||
[\n]+ return 'NL';
|
||||
\s+ /* skip all whitespace */
|
||||
<ID,ALIAS,LINE>((?!\n)\s)+ /* skip same-line whitespace */
|
||||
<INITIAL,ID,ALIAS,LINE>\#[^\n]* /* skip comments */
|
||||
\%%[^\n]* /* skip comments */
|
||||
"participant" { this.begin('ID'); return 'participant'; }
|
||||
<ID>[^\->:\n,;]+?(?=((?!\n)\s)+"as"(?!\n)\s|[#\n;]|$) { this.begin('ALIAS'); return 'ACTOR'; }
|
||||
<ALIAS>"as" { this.popState(); this.popState(); this.begin('LINE'); return 'AS'; }
|
||||
<ALIAS>(?:) { this.popState(); this.popState(); return 'NL'; }
|
||||
"loop" { this.begin('LINE'); return 'loop'; }
|
||||
"opt" { this.begin('LINE'); return 'opt'; }
|
||||
"alt" { this.begin('LINE'); return 'alt'; }
|
||||
"else" { this.begin('LINE'); return 'else'; }
|
||||
"par" { this.begin('LINE'); return 'par'; }
|
||||
"and" { this.begin('LINE'); return 'and'; }
|
||||
<LINE>[^#\n;]* { this.popState(); return 'restOfLine'; }
|
||||
"end" return 'end';
|
||||
"left of" return 'left_of';
|
||||
"right of" return 'right_of';
|
||||
"over" return 'over';
|
||||
"note" return 'note';
|
||||
"activate" { this.begin('ID'); return 'activate'; }
|
||||
"deactivate" { this.begin('ID'); return 'deactivate'; }
|
||||
"title" return 'title';
|
||||
"sequenceDiagram" return 'SD';
|
||||
"," return ',';
|
||||
";" return 'NL';
|
||||
[^\+\->:\n,;]+ { yytext = yytext.trim(); return 'ACTOR'; }
|
||||
"->>" return 'SOLID_ARROW';
|
||||
"-->>" return 'DOTTED_ARROW';
|
||||
"->" return 'SOLID_OPEN_ARROW';
|
||||
"-->" return 'DOTTED_OPEN_ARROW';
|
||||
\-[x] return 'SOLID_CROSS';
|
||||
\-\-[x] return 'DOTTED_CROSS';
|
||||
":"[^#\n;]+ return 'TXT';
|
||||
"+" return '+';
|
||||
"-" return '-';
|
||||
<<EOF>> return 'NL';
|
||||
. return 'INVALID';
|
||||
|
||||
/lex
|
||||
|
||||
%left '^'
|
||||
|
||||
%start start
|
||||
|
||||
%% /* language grammar */
|
||||
|
||||
start
|
||||
: SPACE start
|
||||
| NL start
|
||||
| SD document { yy.apply($2);return $2; }
|
||||
;
|
||||
|
||||
document
|
||||
: /* empty */ { $$ = [] }
|
||||
| document line {$1.push($2);$$ = $1}
|
||||
;
|
||||
|
||||
line
|
||||
: SPACE statement { $$ = $2 }
|
||||
| statement { $$ = $1 }
|
||||
| NL { $$=[];}
|
||||
;
|
||||
|
||||
statement
|
||||
: 'participant' actor 'AS' restOfLine 'NL' {$2.description=$4; $$=$2;}
|
||||
| 'participant' actor 'NL' {$$=$2;}
|
||||
| signal 'NL'
|
||||
| 'activate' actor 'NL' {$$={type: 'activeStart', signalType: yy.LINETYPE.ACTIVE_START, actor: $2};}
|
||||
| 'deactivate' actor 'NL' {$$={type: 'activeEnd', signalType: yy.LINETYPE.ACTIVE_END, actor: $2};}
|
||||
| note_statement 'NL'
|
||||
| title text2 'NL' {$$=[{type:'setTitle', text:$2}]}
|
||||
| 'loop' restOfLine document end
|
||||
{
|
||||
$3.unshift({type: 'loopStart', loopText:$2, signalType: yy.LINETYPE.LOOP_START});
|
||||
$3.push({type: 'loopEnd', loopText:$2, signalType: yy.LINETYPE.LOOP_END});
|
||||
$$=$3;}
|
||||
| opt restOfLine document end
|
||||
{
|
||||
$3.unshift({type: 'optStart', optText:$2, signalType: yy.LINETYPE.OPT_START});
|
||||
$3.push({type: 'optEnd', optText:$2, signalType: yy.LINETYPE.OPT_END});
|
||||
$$=$3;}
|
||||
| alt restOfLine document else restOfLine document end
|
||||
{
|
||||
// Alt start
|
||||
$3.unshift({type: 'altStart', altText:$2, signalType: yy.LINETYPE.ALT_START});
|
||||
// Content in alt is already in $3
|
||||
// Else
|
||||
$3.push({type: 'else', altText:$5, signalType: yy.LINETYPE.ALT_ELSE});
|
||||
// Content in other alt
|
||||
$3 = $3.concat($6);
|
||||
// End
|
||||
$3.push({type: 'altEnd', signalType: yy.LINETYPE.ALT_END});
|
||||
|
||||
$$=$3;}
|
||||
| par restOfLine par_sections end
|
||||
{
|
||||
// Parallel start
|
||||
$3.unshift({type: 'parStart', parText:$2, signalType: yy.LINETYPE.PAR_START});
|
||||
// Content in par is already in $3
|
||||
// End
|
||||
$3.push({type: 'parEnd', signalType: yy.LINETYPE.PAR_END});
|
||||
$$=$3;}
|
||||
;
|
||||
|
||||
par_sections
|
||||
: document
|
||||
| document and restOfLine par_sections
|
||||
{ $$ = $1.concat([{type: 'and', parText:$3, signalType: yy.LINETYPE.PAR_AND}, $4]); }
|
||||
;
|
||||
|
||||
note_statement
|
||||
: 'note' placement actor text2
|
||||
{
|
||||
$$ = [$3, {type:'addNote', placement:$2, actor:$3.actor, text:$4}];}
|
||||
| 'note' 'over' actor_pair text2
|
||||
{
|
||||
// Coerce actor_pair into a [to, from, ...] array
|
||||
$2 = [].concat($3, $3).slice(0, 2);
|
||||
$2[0] = $2[0].actor;
|
||||
$2[1] = $2[1].actor;
|
||||
$$ = [$3, {type:'addNote', placement:yy.PLACEMENT.OVER, actor:$2.slice(0, 2), text:$4}];}
|
||||
;
|
||||
|
||||
spaceList
|
||||
: SPACE spaceList
|
||||
| SPACE
|
||||
;
|
||||
actor_pair
|
||||
: actor ',' actor { $$ = [$1, $3]; }
|
||||
| actor { $$ = $1; }
|
||||
;
|
||||
|
||||
placement
|
||||
: 'left_of' { $$ = yy.PLACEMENT.LEFTOF; }
|
||||
| 'right_of' { $$ = yy.PLACEMENT.RIGHTOF; }
|
||||
;
|
||||
|
||||
signal
|
||||
: actor signaltype '+' actor text2
|
||||
{ $$ = [$1,$4,{type: 'addMessage', from:$1.actor, to:$4.actor, signalType:$2, msg:$5},
|
||||
{type: 'activeStart', signalType: yy.LINETYPE.ACTIVE_START, actor: $4}
|
||||
]}
|
||||
| actor signaltype '-' actor text2
|
||||
{ $$ = [$1,$4,{type: 'addMessage', from:$1.actor, to:$4.actor, signalType:$2, msg:$5},
|
||||
{type: 'activeEnd', signalType: yy.LINETYPE.ACTIVE_END, actor: $1}
|
||||
]}
|
||||
| actor signaltype actor text2
|
||||
{ $$ = [$1,$3,{type: 'addMessage', from:$1.actor, to:$3.actor, signalType:$2, msg:$4}]}
|
||||
;
|
||||
|
||||
actor
|
||||
: ACTOR {$$={type: 'addActor', actor:$1}}
|
||||
;
|
||||
|
||||
signaltype
|
||||
: SOLID_OPEN_ARROW { $$ = yy.LINETYPE.SOLID_OPEN; }
|
||||
| DOTTED_OPEN_ARROW { $$ = yy.LINETYPE.DOTTED_OPEN; }
|
||||
| SOLID_ARROW { $$ = yy.LINETYPE.SOLID; }
|
||||
| DOTTED_ARROW { $$ = yy.LINETYPE.DOTTED; }
|
||||
| SOLID_CROSS { $$ = yy.LINETYPE.SOLID_CROSS; }
|
||||
| DOTTED_CROSS { $$ = yy.LINETYPE.DOTTED_CROSS; }
|
||||
;
|
||||
|
||||
text2: TXT {$$ = $1.substring(1).trim().replace(/\\n/gm, "\n");} ;
|
||||
|
||||
%%
|
||||
798
src/diagrams/sequenceDiagram/parser/sequenceDiagram.js
Normal file
798
src/diagrams/sequenceDiagram/parser/sequenceDiagram.js
Normal file
@@ -0,0 +1,798 @@
|
||||
/* parser generated by jison 0.4.17 */
|
||||
/*
|
||||
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 = [1, 2], $V1 = [1, 3], $V2 = [1, 4], $V3 = [2, 4], $V4 = [1, 9], $V5 = [1, 11], $V6 = [1, 12], $V7 = [1, 14], $V8 = [1, 15], $V9 = [1, 17], $Va = [1, 18], $Vb = [1, 19], $Vc = [1, 20], $Vd = [1, 21], $Ve = [1, 23], $Vf = [1, 24], $Vg = [1, 4, 5, 10, 15, 16, 18, 20, 21, 22, 23, 24, 25, 27, 28, 39], $Vh = [1, 32], $Vi = [4, 5, 10, 15, 16, 18, 20, 21, 22, 23, 25, 28, 39], $Vj = [4, 5, 10, 15, 16, 18, 20, 21, 22, 23, 25, 27, 28, 39], $Vk = [37, 38, 39]
|
||||
var parser = {trace: function trace () { },
|
||||
yy: {},
|
||||
symbols_: {'error': 2, 'start': 3, 'SPACE': 4, 'NL': 5, 'SD': 6, 'document': 7, 'line': 8, 'statement': 9, 'participant': 10, 'actor': 11, 'AS': 12, 'restOfLine': 13, 'signal': 14, 'activate': 15, 'deactivate': 16, 'note_statement': 17, 'title': 18, 'text2': 19, 'loop': 20, 'end': 21, 'opt': 22, 'alt': 23, 'else': 24, 'par': 25, 'par_sections': 26, 'and': 27, 'note': 28, 'placement': 29, 'over': 30, 'actor_pair': 31, 'spaceList': 32, ',': 33, 'left_of': 34, 'right_of': 35, 'signaltype': 36, '+': 37, '-': 38, 'ACTOR': 39, 'SOLID_OPEN_ARROW': 40, 'DOTTED_OPEN_ARROW': 41, 'SOLID_ARROW': 42, 'DOTTED_ARROW': 43, 'SOLID_CROSS': 44, 'DOTTED_CROSS': 45, 'TXT': 46, '$accept': 0, '$end': 1},
|
||||
terminals_: {2: 'error', 4: 'SPACE', 5: 'NL', 6: 'SD', 10: 'participant', 12: 'AS', 13: 'restOfLine', 15: 'activate', 16: 'deactivate', 18: 'title', 20: 'loop', 21: 'end', 22: 'opt', 23: 'alt', 24: 'else', 25: 'par', 27: 'and', 28: 'note', 30: 'over', 33: ',', 34: 'left_of', 35: 'right_of', 37: '+', 38: '-', 39: 'ACTOR', 40: 'SOLID_OPEN_ARROW', 41: 'DOTTED_OPEN_ARROW', 42: 'SOLID_ARROW', 43: 'DOTTED_ARROW', 44: 'SOLID_CROSS', 45: 'DOTTED_CROSS', 46: 'TXT'},
|
||||
productions_: [0, [3, 2], [3, 2], [3, 2], [7, 0], [7, 2], [8, 2], [8, 1], [8, 1], [9, 5], [9, 3], [9, 2], [9, 3], [9, 3], [9, 2], [9, 3], [9, 4], [9, 4], [9, 7], [9, 4], [26, 1], [26, 4], [17, 4], [17, 4], [32, 2], [32, 1], [31, 3], [31, 1], [29, 1], [29, 1], [14, 5], [14, 5], [14, 4], [11, 1], [36, 1], [36, 1], [36, 1], [36, 1], [36, 1], [36, 1], [19, 1]],
|
||||
performAction: function anonymous (yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
|
||||
/* this == yyval */
|
||||
|
||||
var $0 = $$.length - 1
|
||||
switch (yystate) {
|
||||
case 3:
|
||||
yy.apply($$[$0]); return $$[$0]
|
||||
break
|
||||
case 4:
|
||||
this.$ = []
|
||||
break
|
||||
case 5:
|
||||
$$[$0 - 1].push($$[$0]); this.$ = $$[$0 - 1]
|
||||
break
|
||||
case 6: case 7:
|
||||
this.$ = $$[$0]
|
||||
break
|
||||
case 8:
|
||||
this.$ = []
|
||||
break
|
||||
case 9:
|
||||
$$[$0 - 3].description = $$[$0 - 1]; this.$ = $$[$0 - 3]
|
||||
break
|
||||
case 10:
|
||||
this.$ = $$[$0 - 1]
|
||||
break
|
||||
case 12:
|
||||
this.$ = {type: 'activeStart', signalType: yy.LINETYPE.ACTIVE_START, actor: $$[$0 - 1]}
|
||||
break
|
||||
case 13:
|
||||
this.$ = {type: 'activeEnd', signalType: yy.LINETYPE.ACTIVE_END, actor: $$[$0 - 1]}
|
||||
break
|
||||
case 15:
|
||||
this.$ = [{type: 'setTitle', text: $$[$0 - 1]}]
|
||||
break
|
||||
case 16:
|
||||
|
||||
$$[$0 - 1].unshift({type: 'loopStart', loopText: $$[$0 - 2], signalType: yy.LINETYPE.LOOP_START})
|
||||
$$[$0 - 1].push({type: 'loopEnd', loopText: $$[$0 - 2], signalType: yy.LINETYPE.LOOP_END})
|
||||
this.$ = $$[$0 - 1]
|
||||
break
|
||||
case 17:
|
||||
|
||||
$$[$0 - 1].unshift({type: 'optStart', optText: $$[$0 - 2], signalType: yy.LINETYPE.OPT_START})
|
||||
$$[$0 - 1].push({type: 'optEnd', optText: $$[$0 - 2], signalType: yy.LINETYPE.OPT_END})
|
||||
this.$ = $$[$0 - 1]
|
||||
break
|
||||
case 18:
|
||||
|
||||
// Alt start
|
||||
$$[$0 - 4].unshift({type: 'altStart', altText: $$[$0 - 5], signalType: yy.LINETYPE.ALT_START})
|
||||
// Content in alt is already in $$[$0-4]
|
||||
// Else
|
||||
$$[$0 - 4].push({type: 'else', altText: $$[$0 - 2], signalType: yy.LINETYPE.ALT_ELSE})
|
||||
// Content in other alt
|
||||
$$[$0 - 4] = $$[$0 - 4].concat($$[$0 - 1])
|
||||
// End
|
||||
$$[$0 - 4].push({type: 'altEnd', signalType: yy.LINETYPE.ALT_END})
|
||||
|
||||
this.$ = $$[$0 - 4]
|
||||
break
|
||||
case 19:
|
||||
|
||||
// Parallel start
|
||||
$$[$0 - 1].unshift({type: 'parStart', parText: $$[$0 - 2], signalType: yy.LINETYPE.PAR_START})
|
||||
// Content in par is already in $$[$0-1]
|
||||
// End
|
||||
$$[$0 - 1].push({type: 'parEnd', signalType: yy.LINETYPE.PAR_END})
|
||||
this.$ = $$[$0 - 1]
|
||||
break
|
||||
case 21:
|
||||
this.$ = $$[$0 - 3].concat([{type: 'and', parText: $$[$0 - 1], signalType: yy.LINETYPE.PAR_AND}, $$[$0]])
|
||||
break
|
||||
case 22:
|
||||
|
||||
this.$ = [$$[$0 - 1], {type: 'addNote', placement: $$[$0 - 2], actor: $$[$0 - 1].actor, text: $$[$0]}]
|
||||
break
|
||||
case 23:
|
||||
|
||||
// Coerce actor_pair into a [to, from, ...] array
|
||||
$$[$0 - 2] = [].concat($$[$0 - 1], $$[$0 - 1]).slice(0, 2)
|
||||
$$[$0 - 2][0] = $$[$0 - 2][0].actor
|
||||
$$[$0 - 2][1] = $$[$0 - 2][1].actor
|
||||
this.$ = [$$[$0 - 1], {type: 'addNote', placement: yy.PLACEMENT.OVER, actor: $$[$0 - 2].slice(0, 2), text: $$[$0]}]
|
||||
break
|
||||
case 26:
|
||||
this.$ = [$$[$0 - 2], $$[$0]]
|
||||
break
|
||||
case 27:
|
||||
this.$ = $$[$0]
|
||||
break
|
||||
case 28:
|
||||
this.$ = yy.PLACEMENT.LEFTOF
|
||||
break
|
||||
case 29:
|
||||
this.$ = yy.PLACEMENT.RIGHTOF
|
||||
break
|
||||
case 30:
|
||||
this.$ = [$$[$0 - 4], $$[$0 - 1], {type: 'addMessage', from: $$[$0 - 4].actor, to: $$[$0 - 1].actor, signalType: $$[$0 - 3], msg: $$[$0]},
|
||||
{type: 'activeStart', signalType: yy.LINETYPE.ACTIVE_START, actor: $$[$0 - 1]}
|
||||
]
|
||||
break
|
||||
case 31:
|
||||
this.$ = [$$[$0 - 4], $$[$0 - 1], {type: 'addMessage', from: $$[$0 - 4].actor, to: $$[$0 - 1].actor, signalType: $$[$0 - 3], msg: $$[$0]},
|
||||
{type: 'activeEnd', signalType: yy.LINETYPE.ACTIVE_END, actor: $$[$0 - 4]}
|
||||
]
|
||||
break
|
||||
case 32:
|
||||
this.$ = [$$[$0 - 3], $$[$0 - 1], {type: 'addMessage', from: $$[$0 - 3].actor, to: $$[$0 - 1].actor, signalType: $$[$0 - 2], msg: $$[$0]}]
|
||||
break
|
||||
case 33:
|
||||
this.$ = {type: 'addActor', actor: $$[$0]}
|
||||
break
|
||||
case 34:
|
||||
this.$ = yy.LINETYPE.SOLID_OPEN
|
||||
break
|
||||
case 35:
|
||||
this.$ = yy.LINETYPE.DOTTED_OPEN
|
||||
break
|
||||
case 36:
|
||||
this.$ = yy.LINETYPE.SOLID
|
||||
break
|
||||
case 37:
|
||||
this.$ = yy.LINETYPE.DOTTED
|
||||
break
|
||||
case 38:
|
||||
this.$ = yy.LINETYPE.SOLID_CROSS
|
||||
break
|
||||
case 39:
|
||||
this.$ = yy.LINETYPE.DOTTED_CROSS
|
||||
break
|
||||
case 40:
|
||||
this.$ = $$[$0].substring(1).trim().replace(/\\n/gm, '\n')
|
||||
break
|
||||
}
|
||||
},
|
||||
table: [{3: 1, 4: $V0, 5: $V1, 6: $V2}, {1: [3]}, {3: 5, 4: $V0, 5: $V1, 6: $V2}, {3: 6, 4: $V0, 5: $V1, 6: $V2}, o([1, 4, 5, 10, 15, 16, 18, 20, 22, 23, 25, 28, 39], $V3, {7: 7}), {1: [2, 1]}, {1: [2, 2]}, {1: [2, 3], 4: $V4, 5: $V5, 8: 8, 9: 10, 10: $V6, 11: 22, 14: 13, 15: $V7, 16: $V8, 17: 16, 18: $V9, 20: $Va, 22: $Vb, 23: $Vc, 25: $Vd, 28: $Ve, 39: $Vf}, o($Vg, [2, 5]), {9: 25, 10: $V6, 11: 22, 14: 13, 15: $V7, 16: $V8, 17: 16, 18: $V9, 20: $Va, 22: $Vb, 23: $Vc, 25: $Vd, 28: $Ve, 39: $Vf}, o($Vg, [2, 7]), o($Vg, [2, 8]), {11: 26, 39: $Vf}, {5: [1, 27]}, {11: 28, 39: $Vf}, {11: 29, 39: $Vf}, {5: [1, 30]}, {19: 31, 46: $Vh}, {13: [1, 33]}, {13: [1, 34]}, {13: [1, 35]}, {13: [1, 36]}, {36: 37, 40: [1, 38], 41: [1, 39], 42: [1, 40], 43: [1, 41], 44: [1, 42], 45: [1, 43]}, {29: 44, 30: [1, 45], 34: [1, 46], 35: [1, 47]}, o([5, 12, 33, 40, 41, 42, 43, 44, 45, 46], [2, 33]), o($Vg, [2, 6]), {5: [1, 49], 12: [1, 48]}, o($Vg, [2, 11]), {5: [1, 50]}, {5: [1, 51]}, o($Vg, [2, 14]), {5: [1, 52]}, {5: [2, 40]}, o($Vi, $V3, {7: 53}), o($Vi, $V3, {7: 54}), o([4, 5, 10, 15, 16, 18, 20, 22, 23, 24, 25, 28, 39], $V3, {7: 55}), o($Vj, $V3, {26: 56, 7: 57}), {11: 60, 37: [1, 58], 38: [1, 59], 39: $Vf}, o($Vk, [2, 34]), o($Vk, [2, 35]), o($Vk, [2, 36]), o($Vk, [2, 37]), o($Vk, [2, 38]), o($Vk, [2, 39]), {11: 61, 39: $Vf}, {11: 63, 31: 62, 39: $Vf}, {39: [2, 28]}, {39: [2, 29]}, {13: [1, 64]}, o($Vg, [2, 10]), o($Vg, [2, 12]), o($Vg, [2, 13]), o($Vg, [2, 15]), {4: $V4, 5: $V5, 8: 8, 9: 10, 10: $V6, 11: 22, 14: 13, 15: $V7, 16: $V8, 17: 16, 18: $V9, 20: $Va, 21: [1, 65], 22: $Vb, 23: $Vc, 25: $Vd, 28: $Ve, 39: $Vf}, {4: $V4, 5: $V5, 8: 8, 9: 10, 10: $V6, 11: 22, 14: 13, 15: $V7, 16: $V8, 17: 16, 18: $V9, 20: $Va, 21: [1, 66], 22: $Vb, 23: $Vc, 25: $Vd, 28: $Ve, 39: $Vf}, {4: $V4, 5: $V5, 8: 8, 9: 10, 10: $V6, 11: 22, 14: 13, 15: $V7, 16: $V8, 17: 16, 18: $V9, 20: $Va, 22: $Vb, 23: $Vc, 24: [1, 67], 25: $Vd, 28: $Ve, 39: $Vf}, {21: [1, 68]}, {4: $V4, 5: $V5, 8: 8, 9: 10, 10: $V6, 11: 22, 14: 13, 15: $V7, 16: $V8, 17: 16, 18: $V9, 20: $Va, 21: [2, 20], 22: $Vb, 23: $Vc, 25: $Vd, 27: [1, 69], 28: $Ve, 39: $Vf}, {11: 70, 39: $Vf}, {11: 71, 39: $Vf}, {19: 72, 46: $Vh}, {19: 73, 46: $Vh}, {19: 74, 46: $Vh}, {33: [1, 75], 46: [2, 27]}, {5: [1, 76]}, o($Vg, [2, 16]), o($Vg, [2, 17]), {13: [1, 77]}, o($Vg, [2, 19]), {13: [1, 78]}, {19: 79, 46: $Vh}, {19: 80, 46: $Vh}, {5: [2, 32]}, {5: [2, 22]}, {5: [2, 23]}, {11: 81, 39: $Vf}, o($Vg, [2, 9]), o($Vi, $V3, {7: 82}), o($Vj, $V3, {7: 57, 26: 83}), {5: [2, 30]}, {5: [2, 31]}, {46: [2, 26]}, {4: $V4, 5: $V5, 8: 8, 9: 10, 10: $V6, 11: 22, 14: 13, 15: $V7, 16: $V8, 17: 16, 18: $V9, 20: $Va, 21: [1, 84], 22: $Vb, 23: $Vc, 25: $Vd, 28: $Ve, 39: $Vf}, {21: [2, 21]}, o($Vg, [2, 18])],
|
||||
defaultActions: {5: [2, 1], 6: [2, 2], 32: [2, 40], 46: [2, 28], 47: [2, 29], 72: [2, 32], 73: [2, 22], 74: [2, 23], 79: [2, 30], 80: [2, 31], 81: [2, 26], 83: [2, 21]},
|
||||
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
|
||||
}
|
||||
var lex = function () {
|
||||
var token
|
||||
token = lexer.lex() || EOF
|
||||
if (typeof token !== 'number') {
|
||||
token = self.symbols_[token] || token
|
||||
}
|
||||
return token
|
||||
}
|
||||
var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected
|
||||
while (true) {
|
||||
state = stack[stack.length - 1]
|
||||
if (this.defaultActions[state]) {
|
||||
action = this.defaultActions[state]
|
||||
} else {
|
||||
if (symbol === null || typeof symbol === 'undefined') {
|
||||
symbol = lex()
|
||||
}
|
||||
action = table[state] && table[state][symbol]
|
||||
}
|
||||
if (typeof action === 'undefined' || !action.length || !action[0]) {
|
||||
var errStr = ''
|
||||
expected = []
|
||||
for (p in table[state]) {
|
||||
if (this.terminals_[p] && p > TERROR) {
|
||||
expected.push('\'' + this.terminals_[p] + '\'')
|
||||
}
|
||||
}
|
||||
if (lexer.showPosition) {
|
||||
errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\''
|
||||
} else {
|
||||
errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'')
|
||||
}
|
||||
this.parseError(errStr, {
|
||||
text: lexer.match,
|
||||
token: this.terminals_[symbol] || symbol,
|
||||
line: lexer.yylineno,
|
||||
loc: yyloc,
|
||||
expected: expected
|
||||
})
|
||||
}
|
||||
if (action[0] instanceof Array && action.length > 1) {
|
||||
throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol)
|
||||
}
|
||||
switch (action[0]) {
|
||||
case 1:
|
||||
stack.push(symbol)
|
||||
vstack.push(lexer.yytext)
|
||||
lstack.push(lexer.yylloc)
|
||||
stack.push(action[1])
|
||||
symbol = null
|
||||
if (!preErrorSymbol) {
|
||||
yyleng = lexer.yyleng
|
||||
yytext = lexer.yytext
|
||||
yylineno = lexer.yylineno
|
||||
yyloc = lexer.yylloc
|
||||
if (recovering > 0) {
|
||||
recovering--
|
||||
}
|
||||
} else {
|
||||
symbol = preErrorSymbol
|
||||
preErrorSymbol = null
|
||||
}
|
||||
break
|
||||
case 2:
|
||||
len = this.productions_[action[1]][1]
|
||||
yyval.$ = vstack[vstack.length - len]
|
||||
yyval._$ = {
|
||||
first_line: lstack[lstack.length - (len || 1)].first_line,
|
||||
last_line: lstack[lstack.length - 1].last_line,
|
||||
first_column: lstack[lstack.length - (len || 1)].first_column,
|
||||
last_column: lstack[lstack.length - 1].last_column
|
||||
}
|
||||
if (ranges) {
|
||||
yyval._$.range = [
|
||||
lstack[lstack.length - (len || 1)].range[0],
|
||||
lstack[lstack.length - 1].range[1]
|
||||
]
|
||||
}
|
||||
r = this.performAction.apply(yyval, [
|
||||
yytext,
|
||||
yyleng,
|
||||
yylineno,
|
||||
sharedState.yy,
|
||||
action[1],
|
||||
vstack,
|
||||
lstack
|
||||
].concat(args))
|
||||
if (typeof r !== 'undefined') {
|
||||
return r
|
||||
}
|
||||
if (len) {
|
||||
stack = stack.slice(0, -1 * len * 2)
|
||||
vstack = vstack.slice(0, -1 * len)
|
||||
lstack = lstack.slice(0, -1 * len)
|
||||
}
|
||||
stack.push(this.productions_[action[1]][0])
|
||||
vstack.push(yyval.$)
|
||||
lstack.push(yyval._$)
|
||||
newState = table[stack[stack.length - 2]][stack[stack.length - 1]]
|
||||
stack.push(newState)
|
||||
break
|
||||
case 3:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
}}
|
||||
|
||||
/* generated by jison-lex 0.3.4 */
|
||||
var lexer = (function () {
|
||||
var lexer = ({
|
||||
|
||||
EOF: 1,
|
||||
|
||||
parseError: function parseError (str, hash) {
|
||||
if (this.yy.parser) {
|
||||
this.yy.parser.parseError(str, hash)
|
||||
} else {
|
||||
throw new Error(str)
|
||||
}
|
||||
},
|
||||
|
||||
// resets the lexer, sets new input
|
||||
setInput: function (input, yy) {
|
||||
this.yy = yy || this.yy || {}
|
||||
this._input = input
|
||||
this._more = this._backtrack = this.done = false
|
||||
this.yylineno = this.yyleng = 0
|
||||
this.yytext = this.matched = this.match = ''
|
||||
this.conditionStack = ['INITIAL']
|
||||
this.yylloc = {
|
||||
first_line: 1,
|
||||
first_column: 0,
|
||||
last_line: 1,
|
||||
last_column: 0
|
||||
}
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range = [0, 0]
|
||||
}
|
||||
this.offset = 0
|
||||
return this
|
||||
},
|
||||
|
||||
// consumes and returns one char from the input
|
||||
input: function () {
|
||||
var ch = this._input[0]
|
||||
this.yytext += ch
|
||||
this.yyleng++
|
||||
this.offset++
|
||||
this.match += ch
|
||||
this.matched += ch
|
||||
var lines = ch.match(/(?:\r\n?|\n).*/g)
|
||||
if (lines) {
|
||||
this.yylineno++
|
||||
this.yylloc.last_line++
|
||||
} else {
|
||||
this.yylloc.last_column++
|
||||
}
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range[1]++
|
||||
}
|
||||
|
||||
this._input = this._input.slice(1)
|
||||
return ch
|
||||
},
|
||||
|
||||
// unshifts one char (or a string) into the input
|
||||
unput: function (ch) {
|
||||
var len = ch.length
|
||||
var lines = ch.split(/(?:\r\n?|\n)/g)
|
||||
|
||||
this._input = ch + this._input
|
||||
this.yytext = this.yytext.substr(0, this.yytext.length - len)
|
||||
// this.yyleng -= len;
|
||||
this.offset -= len
|
||||
var oldLines = this.match.split(/(?:\r\n?|\n)/g)
|
||||
this.match = this.match.substr(0, this.match.length - 1)
|
||||
this.matched = this.matched.substr(0, this.matched.length - 1)
|
||||
|
||||
if (lines.length - 1) {
|
||||
this.yylineno -= lines.length - 1
|
||||
}
|
||||
var r = this.yylloc.range
|
||||
|
||||
this.yylloc = {
|
||||
first_line: this.yylloc.first_line,
|
||||
last_line: this.yylineno + 1,
|
||||
first_column: this.yylloc.first_column,
|
||||
last_column: lines
|
||||
? (lines.length === oldLines.length ? this.yylloc.first_column : 0) +
|
||||
oldLines[oldLines.length - lines.length].length - lines[0].length
|
||||
: this.yylloc.first_column - len
|
||||
}
|
||||
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range = [r[0], r[0] + this.yyleng - len]
|
||||
}
|
||||
this.yyleng = this.yytext.length
|
||||
return this
|
||||
},
|
||||
|
||||
// When called from action, caches matched text and appends it on next action
|
||||
more: function () {
|
||||
this._more = true
|
||||
return this
|
||||
},
|
||||
|
||||
// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
|
||||
reject: function () {
|
||||
if (this.options.backtrack_lexer) {
|
||||
this._backtrack = true
|
||||
} else {
|
||||
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), {
|
||||
text: '',
|
||||
token: null,
|
||||
line: this.yylineno
|
||||
})
|
||||
}
|
||||
return this
|
||||
},
|
||||
|
||||
// retain first n characters of the match
|
||||
less: function (n) {
|
||||
this.unput(this.match.slice(n))
|
||||
},
|
||||
|
||||
// displays already matched input, i.e. for error messages
|
||||
pastInput: function () {
|
||||
var past = this.matched.substr(0, this.matched.length - this.match.length)
|
||||
return (past.length > 20 ? '...' : '') + past.substr(-20).replace(/\n/g, '')
|
||||
},
|
||||
|
||||
// displays upcoming input, i.e. for error messages
|
||||
upcomingInput: function () {
|
||||
var next = this.match
|
||||
if (next.length < 20) {
|
||||
next += this._input.substr(0, 20 - next.length)
|
||||
}
|
||||
return (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace(/\n/g, '')
|
||||
},
|
||||
|
||||
// displays the character position where the lexing error occurred, i.e. for error messages
|
||||
showPosition: function () {
|
||||
var pre = this.pastInput()
|
||||
var c = new Array(pre.length + 1).join('-')
|
||||
return pre + this.upcomingInput() + '\n' + c + '^'
|
||||
},
|
||||
|
||||
// test the lexed token: return FALSE when not a match, otherwise return token
|
||||
test_match: function (match, indexed_rule) {
|
||||
var token,
|
||||
lines,
|
||||
backup
|
||||
|
||||
if (this.options.backtrack_lexer) {
|
||||
// save context
|
||||
backup = {
|
||||
yylineno: this.yylineno,
|
||||
yylloc: {
|
||||
first_line: this.yylloc.first_line,
|
||||
last_line: this.last_line,
|
||||
first_column: this.yylloc.first_column,
|
||||
last_column: this.yylloc.last_column
|
||||
},
|
||||
yytext: this.yytext,
|
||||
match: this.match,
|
||||
matches: this.matches,
|
||||
matched: this.matched,
|
||||
yyleng: this.yyleng,
|
||||
offset: this.offset,
|
||||
_more: this._more,
|
||||
_input: this._input,
|
||||
yy: this.yy,
|
||||
conditionStack: this.conditionStack.slice(0),
|
||||
done: this.done
|
||||
}
|
||||
if (this.options.ranges) {
|
||||
backup.yylloc.range = this.yylloc.range.slice(0)
|
||||
}
|
||||
}
|
||||
|
||||
lines = match[0].match(/(?:\r\n?|\n).*/g)
|
||||
if (lines) {
|
||||
this.yylineno += lines.length
|
||||
}
|
||||
this.yylloc = {
|
||||
first_line: this.yylloc.last_line,
|
||||
last_line: this.yylineno + 1,
|
||||
first_column: this.yylloc.last_column,
|
||||
last_column: lines
|
||||
? lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length
|
||||
: this.yylloc.last_column + match[0].length
|
||||
}
|
||||
this.yytext += match[0]
|
||||
this.match += match[0]
|
||||
this.matches = match
|
||||
this.yyleng = this.yytext.length
|
||||
if (this.options.ranges) {
|
||||
this.yylloc.range = [this.offset, this.offset += this.yyleng]
|
||||
}
|
||||
this._more = false
|
||||
this._backtrack = false
|
||||
this._input = this._input.slice(match[0].length)
|
||||
this.matched += match[0]
|
||||
token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1])
|
||||
if (this.done && this._input) {
|
||||
this.done = false
|
||||
}
|
||||
if (token) {
|
||||
return token
|
||||
} else if (this._backtrack) {
|
||||
// recover context
|
||||
for (var k in backup) {
|
||||
this[k] = backup[k]
|
||||
}
|
||||
return false // rule action called reject() implying the next rule should be tested instead.
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
// return next match in input
|
||||
next: function () {
|
||||
if (this.done) {
|
||||
return this.EOF
|
||||
}
|
||||
if (!this._input) {
|
||||
this.done = true
|
||||
}
|
||||
|
||||
var token,
|
||||
match,
|
||||
tempMatch,
|
||||
index
|
||||
if (!this._more) {
|
||||
this.yytext = ''
|
||||
this.match = ''
|
||||
}
|
||||
var rules = this._currentRules()
|
||||
for (var i = 0; i < rules.length; i++) {
|
||||
tempMatch = this._input.match(this.rules[rules[i]])
|
||||
if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
|
||||
match = tempMatch
|
||||
index = i
|
||||
if (this.options.backtrack_lexer) {
|
||||
token = this.test_match(tempMatch, rules[i])
|
||||
if (token !== false) {
|
||||
return token
|
||||
} else if (this._backtrack) {
|
||||
match = false
|
||||
continue // rule action called reject() implying a rule MISmatch.
|
||||
} else {
|
||||
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
|
||||
return false
|
||||
}
|
||||
} else if (!this.options.flex) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
token = this.test_match(match, rules[index])
|
||||
if (token !== false) {
|
||||
return token
|
||||
}
|
||||
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
|
||||
return false
|
||||
}
|
||||
if (this._input === '') {
|
||||
return this.EOF
|
||||
} else {
|
||||
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
|
||||
text: '',
|
||||
token: null,
|
||||
line: this.yylineno
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// return next match that has a token
|
||||
lex: function lex () {
|
||||
var r = this.next()
|
||||
if (r) {
|
||||
return r
|
||||
} else {
|
||||
return this.lex()
|
||||
}
|
||||
},
|
||||
|
||||
// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
|
||||
begin: function begin (condition) {
|
||||
this.conditionStack.push(condition)
|
||||
},
|
||||
|
||||
// pop the previously active lexer condition state off the condition stack
|
||||
popState: function popState () {
|
||||
var n = this.conditionStack.length - 1
|
||||
if (n > 0) {
|
||||
return this.conditionStack.pop()
|
||||
} else {
|
||||
return this.conditionStack[0]
|
||||
}
|
||||
},
|
||||
|
||||
// produce the lexer rule set which is active for the currently active lexer condition state
|
||||
_currentRules: function _currentRules () {
|
||||
if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
|
||||
return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules
|
||||
} else {
|
||||
return this.conditions['INITIAL'].rules
|
||||
}
|
||||
},
|
||||
|
||||
// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
|
||||
topState: function topState (n) {
|
||||
n = this.conditionStack.length - 1 - Math.abs(n || 0)
|
||||
if (n >= 0) {
|
||||
return this.conditionStack[n]
|
||||
} else {
|
||||
return 'INITIAL'
|
||||
}
|
||||
},
|
||||
|
||||
// alias for begin(condition)
|
||||
pushState: function pushState (condition) {
|
||||
this.begin(condition)
|
||||
},
|
||||
|
||||
// return the number of states currently on the stack
|
||||
stateStackSize: function stateStackSize () {
|
||||
return this.conditionStack.length
|
||||
},
|
||||
options: {'case-insensitive': true},
|
||||
performAction: function anonymous (yy, yy_, $avoiding_name_collisions, YY_START) {
|
||||
var YYSTATE = YY_START
|
||||
switch ($avoiding_name_collisions) {
|
||||
case 0:return 5
|
||||
break
|
||||
case 1:/* skip all whitespace */
|
||||
break
|
||||
case 2:/* skip same-line whitespace */
|
||||
break
|
||||
case 3:/* skip comments */
|
||||
break
|
||||
case 4:/* skip comments */
|
||||
break
|
||||
case 5: this.begin('ID'); return 10
|
||||
break
|
||||
case 6: this.begin('ALIAS'); return 39
|
||||
break
|
||||
case 7: this.popState(); this.popState(); this.begin('LINE'); return 12
|
||||
break
|
||||
case 8: this.popState(); this.popState(); return 5
|
||||
break
|
||||
case 9: this.begin('LINE'); return 20
|
||||
break
|
||||
case 10: this.begin('LINE'); return 22
|
||||
break
|
||||
case 11: this.begin('LINE'); return 23
|
||||
break
|
||||
case 12: this.begin('LINE'); return 24
|
||||
break
|
||||
case 13: this.begin('LINE'); return 25
|
||||
break
|
||||
case 14: this.begin('LINE'); return 27
|
||||
break
|
||||
case 15: this.popState(); return 13
|
||||
break
|
||||
case 16:return 21
|
||||
break
|
||||
case 17:return 34
|
||||
break
|
||||
case 18:return 35
|
||||
break
|
||||
case 19:return 30
|
||||
break
|
||||
case 20:return 28
|
||||
break
|
||||
case 21: this.begin('ID'); return 15
|
||||
break
|
||||
case 22: this.begin('ID'); return 16
|
||||
break
|
||||
case 23:return 18
|
||||
break
|
||||
case 24:return 6
|
||||
break
|
||||
case 25:return 33
|
||||
break
|
||||
case 26:return 5
|
||||
break
|
||||
case 27: yy_.yytext = yy_.yytext.trim(); return 39
|
||||
break
|
||||
case 28:return 42
|
||||
break
|
||||
case 29:return 43
|
||||
break
|
||||
case 30:return 40
|
||||
break
|
||||
case 31:return 41
|
||||
break
|
||||
case 32:return 44
|
||||
break
|
||||
case 33:return 45
|
||||
break
|
||||
case 34:return 46
|
||||
break
|
||||
case 35:return 37
|
||||
break
|
||||
case 36:return 38
|
||||
break
|
||||
case 37:return 5
|
||||
break
|
||||
case 38:return 'INVALID'
|
||||
break
|
||||
}
|
||||
},
|
||||
rules: [/^(?:[\n]+)/i, /^(?:\s+)/i, /^(?:((?!\n)\s)+)/i, /^(?:#[^\n]*)/i, /^(?:%[^\n]*)/i, /^(?:participant\b)/i, /^(?:[^\->:\n,;]+?(?=((?!\n)\s)+as(?!\n)\s|[#\n;]|$))/i, /^(?:as\b)/i, /^(?:(?:))/i, /^(?:loop\b)/i, /^(?:opt\b)/i, /^(?:alt\b)/i, /^(?:else\b)/i, /^(?:par\b)/i, /^(?:and\b)/i, /^(?:[^#\n;]*)/i, /^(?:end\b)/i, /^(?:left of\b)/i, /^(?:right of\b)/i, /^(?:over\b)/i, /^(?:note\b)/i, /^(?:activate\b)/i, /^(?:deactivate\b)/i, /^(?:title\b)/i, /^(?:sequenceDiagram\b)/i, /^(?:,)/i, /^(?:;)/i, /^(?:[^\+\->:\n,;]+)/i, /^(?:->>)/i, /^(?:-->>)/i, /^(?:->)/i, /^(?:-->)/i, /^(?:-[x])/i, /^(?:--[x])/i, /^(?::[^#\n;]+)/i, /^(?:\+)/i, /^(?:-)/i, /^(?:$)/i, /^(?:.)/i],
|
||||
conditions: {'LINE': {'rules': [2, 3, 15], 'inclusive': false}, 'ALIAS': {'rules': [2, 3, 7, 8], 'inclusive': false}, 'ID': {'rules': [2, 3, 6], 'inclusive': false}, 'INITIAL': {'rules': [0, 1, 3, 4, 5, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38], '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))
|
||||
}
|
||||
}
|
||||
161
src/diagrams/sequenceDiagram/sequenceDb.js
Normal file
161
src/diagrams/sequenceDiagram/sequenceDb.js
Normal file
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* Created by knut on 14-11-19.
|
||||
*/
|
||||
var actors = {}
|
||||
var messages = []
|
||||
var notes = []
|
||||
var title = ''
|
||||
var Logger = require('../../logger')
|
||||
var log = Logger.Log
|
||||
|
||||
exports.addActor = function (id, name, description) {
|
||||
// Don't allow description nulling
|
||||
var old = actors[id]
|
||||
if (old && name === old.name && description == null) return
|
||||
|
||||
// Don't allow null descriptions, either
|
||||
if (description == null) description = name
|
||||
|
||||
actors[id] = { name: name, description: description }
|
||||
}
|
||||
|
||||
exports.addMessage = function (idFrom, idTo, message, answer) {
|
||||
messages.push({ from: idFrom, to: idTo, message: message, answer: answer })
|
||||
}
|
||||
|
||||
exports.addSignal = function (idFrom, idTo, message, messageType) {
|
||||
log.debug('Adding message from=' + idFrom + ' to=' + idTo + ' message=' + message + ' type=' + messageType)
|
||||
messages.push({ from: idFrom, to: idTo, message: message, type: messageType })
|
||||
}
|
||||
|
||||
exports.getMessages = function () {
|
||||
return messages
|
||||
}
|
||||
|
||||
exports.getActors = function () {
|
||||
return actors
|
||||
}
|
||||
exports.getActor = function (id) {
|
||||
return actors[id]
|
||||
}
|
||||
exports.getActorKeys = function () {
|
||||
return Object.keys(actors)
|
||||
}
|
||||
exports.getTitle = function () {
|
||||
return title
|
||||
}
|
||||
|
||||
exports.clear = function () {
|
||||
actors = {}
|
||||
messages = []
|
||||
}
|
||||
|
||||
exports.LINETYPE = {
|
||||
SOLID: 0,
|
||||
DOTTED: 1,
|
||||
NOTE: 2,
|
||||
SOLID_CROSS: 3,
|
||||
DOTTED_CROSS: 4,
|
||||
SOLID_OPEN: 5,
|
||||
DOTTED_OPEN: 6,
|
||||
LOOP_START: 10,
|
||||
LOOP_END: 11,
|
||||
ALT_START: 12,
|
||||
ALT_ELSE: 13,
|
||||
ALT_END: 14,
|
||||
OPT_START: 15,
|
||||
OPT_END: 16,
|
||||
ACTIVE_START: 17,
|
||||
ACTIVE_END: 18,
|
||||
PAR_START: 19,
|
||||
PAR_AND: 20,
|
||||
PAR_END: 21
|
||||
}
|
||||
|
||||
exports.ARROWTYPE = {
|
||||
FILLED: 0,
|
||||
OPEN: 1
|
||||
}
|
||||
|
||||
exports.PLACEMENT = {
|
||||
LEFTOF: 0,
|
||||
RIGHTOF: 1,
|
||||
OVER: 2
|
||||
}
|
||||
|
||||
exports.addNote = function (actor, placement, message) {
|
||||
var note = { actor: actor, placement: placement, message: message }
|
||||
|
||||
// Coerce actor into a [to, from, ...] array
|
||||
var actors = [].concat(actor, actor)
|
||||
|
||||
notes.push(note)
|
||||
messages.push({ from: actors[0], to: actors[1], message: message, type: exports.LINETYPE.NOTE, placement: placement })
|
||||
}
|
||||
|
||||
exports.setTitle = function (titleText) {
|
||||
title = titleText
|
||||
}
|
||||
|
||||
exports.parseError = function (err, hash) {
|
||||
global.mermaidAPI.parseError(err, hash)
|
||||
}
|
||||
|
||||
exports.apply = function (param) {
|
||||
if (param instanceof Array) {
|
||||
param.forEach(function (item) {
|
||||
exports.apply(item)
|
||||
})
|
||||
} else {
|
||||
switch (param.type) {
|
||||
case 'addActor':
|
||||
exports.addActor(param.actor, param.actor, param.description)
|
||||
break
|
||||
case 'activeStart':
|
||||
exports.addSignal(param.actor, undefined, undefined, param.signalType)
|
||||
break
|
||||
case 'activeEnd':
|
||||
exports.addSignal(param.actor, undefined, undefined, param.signalType)
|
||||
break
|
||||
case 'addNote':
|
||||
exports.addNote(param.actor, param.placement, param.text)
|
||||
break
|
||||
case 'addMessage':
|
||||
exports.addSignal(param.from, param.to, param.msg, param.signalType)
|
||||
break
|
||||
case 'loopStart':
|
||||
exports.addSignal(undefined, undefined, param.loopText, param.signalType)
|
||||
break
|
||||
case 'loopEnd':
|
||||
exports.addSignal(undefined, undefined, undefined, param.signalType)
|
||||
break
|
||||
case 'optStart':
|
||||
exports.addSignal(undefined, undefined, param.optText, param.signalType)
|
||||
break
|
||||
case 'optEnd':
|
||||
exports.addSignal(undefined, undefined, undefined, param.signalType)
|
||||
break
|
||||
case 'altStart':
|
||||
exports.addSignal(undefined, undefined, param.altText, param.signalType)
|
||||
break
|
||||
case 'else':
|
||||
exports.addSignal(undefined, undefined, param.altText, param.signalType)
|
||||
break
|
||||
case 'altEnd':
|
||||
exports.addSignal(undefined, undefined, undefined, param.signalType)
|
||||
break
|
||||
case 'setTitle':
|
||||
exports.setTitle(param.text)
|
||||
break
|
||||
case 'parStart':
|
||||
exports.addSignal(undefined, undefined, param.parText, param.signalType)
|
||||
break
|
||||
case 'and':
|
||||
exports.addSignal(undefined, undefined, param.parText, param.signalType)
|
||||
break
|
||||
case 'parEnd':
|
||||
exports.addSignal(undefined, undefined, undefined, param.signalType)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
1027
src/diagrams/sequenceDiagram/sequenceDiagram.spec.js
Normal file
1027
src/diagrams/sequenceDiagram/sequenceDiagram.spec.js
Normal file
File diff suppressed because it is too large
Load Diff
506
src/diagrams/sequenceDiagram/sequenceRenderer.js
Normal file
506
src/diagrams/sequenceDiagram/sequenceRenderer.js
Normal file
@@ -0,0 +1,506 @@
|
||||
/**
|
||||
* Created by knut on 14-11-23.
|
||||
*/
|
||||
|
||||
var sq = require('./parser/sequenceDiagram').parser
|
||||
sq.yy = require('./sequenceDb')
|
||||
var svgDraw = require('./svgDraw')
|
||||
var d3 = require('../../d3')
|
||||
var Logger = require('../../logger')
|
||||
var log = Logger.Log
|
||||
|
||||
var conf = {
|
||||
|
||||
diagramMarginX: 50,
|
||||
diagramMarginY: 30,
|
||||
// Margin between actors
|
||||
actorMargin: 50,
|
||||
// Width of actor boxes
|
||||
width: 150,
|
||||
// Height of actor boxes
|
||||
height: 65,
|
||||
// Margin around loop boxes
|
||||
boxMargin: 10,
|
||||
boxTextMargin: 5,
|
||||
noteMargin: 10,
|
||||
// Space between messages
|
||||
messageMargin: 35,
|
||||
// mirror actors under diagram
|
||||
mirrorActors: false,
|
||||
// Depending on css styling this might need adjustment
|
||||
// Prolongs the edge of the diagram downwards
|
||||
bottomMarginAdj: 1,
|
||||
|
||||
// width of activation box
|
||||
activationWidth: 10,
|
||||
|
||||
// text placement as: tspan | fo | old only text as before
|
||||
textPlacement: 'tspan'
|
||||
}
|
||||
|
||||
exports.bounds = {
|
||||
data: {
|
||||
startx: undefined,
|
||||
stopx: undefined,
|
||||
starty: undefined,
|
||||
stopy: undefined
|
||||
},
|
||||
verticalPos: 0,
|
||||
|
||||
sequenceItems: [],
|
||||
activations: [],
|
||||
init: function () {
|
||||
this.sequenceItems = []
|
||||
this.activations = []
|
||||
this.data = {
|
||||
startx: undefined,
|
||||
stopx: undefined,
|
||||
starty: undefined,
|
||||
stopy: undefined
|
||||
}
|
||||
this.verticalPos = 0
|
||||
},
|
||||
updateVal: function (obj, key, val, fun) {
|
||||
if (typeof obj[key] === 'undefined') {
|
||||
obj[key] = val
|
||||
} else {
|
||||
obj[key] = fun(val, obj[key])
|
||||
}
|
||||
},
|
||||
updateBounds: function (startx, starty, stopx, stopy) {
|
||||
var _self = this
|
||||
var cnt = 0
|
||||
function updateFn (type) {
|
||||
return function updateItemBounds (item) {
|
||||
cnt++
|
||||
// The loop sequenceItems is a stack so the biggest margins in the beginning of the sequenceItems
|
||||
var n = _self.sequenceItems.length - cnt + 1
|
||||
|
||||
_self.updateVal(item, 'starty', starty - n * conf.boxMargin, Math.min)
|
||||
_self.updateVal(item, 'stopy', stopy + n * conf.boxMargin, Math.max)
|
||||
|
||||
_self.updateVal(exports.bounds.data, 'startx', startx - n * conf.boxMargin, Math.min)
|
||||
_self.updateVal(exports.bounds.data, 'stopx', stopx + n * conf.boxMargin, Math.max)
|
||||
|
||||
if (!(type === 'activation')) {
|
||||
_self.updateVal(item, 'startx', startx - n * conf.boxMargin, Math.min)
|
||||
_self.updateVal(item, 'stopx', stopx + n * conf.boxMargin, Math.max)
|
||||
|
||||
_self.updateVal(exports.bounds.data, 'starty', starty - n * conf.boxMargin, Math.min)
|
||||
_self.updateVal(exports.bounds.data, 'stopy', stopy + n * conf.boxMargin, Math.max)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.sequenceItems.forEach(updateFn())
|
||||
this.activations.forEach(updateFn('activation'))
|
||||
},
|
||||
insert: function (startx, starty, stopx, stopy) {
|
||||
var _startx, _starty, _stopx, _stopy
|
||||
|
||||
_startx = Math.min(startx, stopx)
|
||||
_stopx = Math.max(startx, stopx)
|
||||
_starty = Math.min(starty, stopy)
|
||||
_stopy = Math.max(starty, stopy)
|
||||
|
||||
this.updateVal(exports.bounds.data, 'startx', _startx, Math.min)
|
||||
this.updateVal(exports.bounds.data, 'starty', _starty, Math.min)
|
||||
this.updateVal(exports.bounds.data, 'stopx', _stopx, Math.max)
|
||||
this.updateVal(exports.bounds.data, 'stopy', _stopy, Math.max)
|
||||
|
||||
this.updateBounds(_startx, _starty, _stopx, _stopy)
|
||||
},
|
||||
newActivation: function (message, diagram) {
|
||||
var actorRect = sq.yy.getActors()[message.from.actor]
|
||||
var stackedSize = actorActivations(message.from.actor).length
|
||||
var x = actorRect.x + conf.width / 2 + (stackedSize - 1) * conf.activationWidth / 2
|
||||
this.activations.push({
|
||||
startx: x,
|
||||
starty: this.verticalPos + 2,
|
||||
stopx: x + conf.activationWidth,
|
||||
stopy: undefined,
|
||||
actor: message.from.actor,
|
||||
anchored: svgDraw.anchorElement(diagram)
|
||||
})
|
||||
},
|
||||
endActivation: function (message) {
|
||||
// find most recent activation for given actor
|
||||
var lastActorActivationIdx = this.activations
|
||||
.map(function (activation) { return activation.actor })
|
||||
.lastIndexOf(message.from.actor)
|
||||
var activation = this.activations.splice(lastActorActivationIdx, 1)[0]
|
||||
return activation
|
||||
},
|
||||
newLoop: function (title) {
|
||||
this.sequenceItems.push({ startx: undefined, starty: this.verticalPos, stopx: undefined, stopy: undefined, title: title })
|
||||
},
|
||||
endLoop: function () {
|
||||
var loop = this.sequenceItems.pop()
|
||||
return loop
|
||||
},
|
||||
addSectionToLoop: function (message) {
|
||||
var loop = this.sequenceItems.pop()
|
||||
loop.sections = loop.sections || []
|
||||
loop.sectionTitles = loop.sectionTitles || []
|
||||
loop.sections.push(exports.bounds.getVerticalPos())
|
||||
loop.sectionTitles.push(message)
|
||||
this.sequenceItems.push(loop)
|
||||
},
|
||||
bumpVerticalPos: function (bump) {
|
||||
this.verticalPos = this.verticalPos + bump
|
||||
this.data.stopy = this.verticalPos
|
||||
},
|
||||
getVerticalPos: function () {
|
||||
return this.verticalPos
|
||||
},
|
||||
getBounds: function () {
|
||||
return this.data
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws an actor in the diagram with the attaced line
|
||||
* @param center - The center of the the actor
|
||||
* @param pos The position if the actor in the liost of actors
|
||||
* @param description The text in the box
|
||||
*/
|
||||
var drawNote = function (elem, startx, verticalPos, msg, forceWidth) {
|
||||
var rect = svgDraw.getNoteRect()
|
||||
rect.x = startx
|
||||
rect.y = verticalPos
|
||||
rect.width = forceWidth || conf.width
|
||||
rect.class = 'note'
|
||||
|
||||
var g = elem.append('g')
|
||||
var rectElem = svgDraw.drawRect(g, rect)
|
||||
|
||||
var textObj = svgDraw.getTextObj()
|
||||
textObj.x = startx - 4
|
||||
textObj.y = verticalPos - 13
|
||||
textObj.textMargin = conf.noteMargin
|
||||
textObj.dy = '1em'
|
||||
textObj.text = msg.message
|
||||
textObj.class = 'noteText'
|
||||
|
||||
var textElem = svgDraw.drawText(g, textObj, rect.width - conf.noteMargin)
|
||||
|
||||
var textHeight = textElem[0][0].getBBox().height
|
||||
if (!forceWidth && textHeight > conf.width) {
|
||||
textElem.remove()
|
||||
g = elem.append('g')
|
||||
|
||||
textElem = svgDraw.drawText(g, textObj, 2 * rect.width - conf.noteMargin)
|
||||
textHeight = textElem[0][0].getBBox().height
|
||||
rectElem.attr('width', 2 * rect.width)
|
||||
exports.bounds.insert(startx, verticalPos, startx + 2 * rect.width, verticalPos + 2 * conf.noteMargin + textHeight)
|
||||
} else {
|
||||
exports.bounds.insert(startx, verticalPos, startx + rect.width, verticalPos + 2 * conf.noteMargin + textHeight)
|
||||
}
|
||||
|
||||
rectElem.attr('height', textHeight + 2 * conf.noteMargin)
|
||||
exports.bounds.bumpVerticalPos(textHeight + 2 * conf.noteMargin)
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a message
|
||||
* @param elem
|
||||
* @param startx
|
||||
* @param stopx
|
||||
* @param verticalPos
|
||||
* @param txtCenter
|
||||
* @param msg
|
||||
*/
|
||||
var drawMessage = function (elem, startx, stopx, verticalPos, msg) {
|
||||
var g = elem.append('g')
|
||||
var txtCenter = startx + (stopx - startx) / 2
|
||||
|
||||
var textElem = g.append('text') // text label for the x axis
|
||||
.attr('x', txtCenter)
|
||||
.attr('y', verticalPos - 7)
|
||||
.style('text-anchor', 'middle')
|
||||
.attr('class', 'messageText')
|
||||
.text(msg.message)
|
||||
|
||||
var textWidth
|
||||
|
||||
if (typeof textElem[0][0].getBBox !== 'undefined') {
|
||||
textWidth = textElem[0][0].getBBox().width
|
||||
} else {
|
||||
textWidth = textElem[0][0].getBoundingClientRect()
|
||||
}
|
||||
|
||||
var 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))
|
||||
|
||||
exports.bounds.bumpVerticalPos(30)
|
||||
var dx = Math.max(textWidth / 2, 100)
|
||||
exports.bounds.insert(startx - dx, exports.bounds.getVerticalPos() - 10, stopx + dx, exports.bounds.getVerticalPos())
|
||||
} else {
|
||||
line = g.append('line')
|
||||
line.attr('x1', startx)
|
||||
line.attr('y1', verticalPos)
|
||||
line.attr('x2', stopx)
|
||||
line.attr('y2', verticalPos)
|
||||
exports.bounds.insert(startx, exports.bounds.getVerticalPos() - 10, stopx, exports.bounds.getVerticalPos())
|
||||
}
|
||||
// Make an SVG Container
|
||||
// Draw the line
|
||||
if (msg.type === sq.yy.LINETYPE.DOTTED || msg.type === sq.yy.LINETYPE.DOTTED_CROSS || msg.type === sq.yy.LINETYPE.DOTTED_OPEN) {
|
||||
line.style('stroke-dasharray', ('3, 3'))
|
||||
line.attr('class', 'messageLine1')
|
||||
} else {
|
||||
line.attr('class', 'messageLine0')
|
||||
}
|
||||
|
||||
var url = ''
|
||||
if (conf.arrowMarkerAbsolute) {
|
||||
url = window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search
|
||||
url = url.replace(/\(/g, '\\(')
|
||||
url = url.replace(/\)/g, '\\)')
|
||||
}
|
||||
|
||||
line.attr('stroke-width', 2)
|
||||
line.attr('stroke', 'black')
|
||||
line.style('fill', 'none') // remove any fill colour
|
||||
if (msg.type === sq.yy.LINETYPE.SOLID || msg.type === sq.yy.LINETYPE.DOTTED) {
|
||||
line.attr('marker-end', 'url(' + url + '#arrowhead)')
|
||||
}
|
||||
|
||||
if (msg.type === sq.yy.LINETYPE.SOLID_CROSS || msg.type === sq.yy.LINETYPE.DOTTED_CROSS) {
|
||||
line.attr('marker-end', 'url(' + url + '#crosshead)')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.drawActors = function (diagram, actors, actorKeys, verticalPos) {
|
||||
var i
|
||||
// Draw the actors
|
||||
for (i = 0; i < actorKeys.length; i++) {
|
||||
var key = actorKeys[i]
|
||||
|
||||
// Add some rendering data to the object
|
||||
actors[key].x = i * conf.actorMargin + i * conf.width
|
||||
actors[key].y = verticalPos
|
||||
actors[key].width = conf.diagramMarginX
|
||||
actors[key].height = conf.diagramMarginY
|
||||
|
||||
// Draw the box with the attached line
|
||||
svgDraw.drawActor(diagram, actors[key].x, verticalPos, actors[key].description, conf)
|
||||
exports.bounds.insert(actors[key].x, verticalPos, actors[key].x + conf.width, conf.height)
|
||||
}
|
||||
|
||||
// Add a margin between the actor boxes and the first arrow
|
||||
exports.bounds.bumpVerticalPos(conf.height)
|
||||
}
|
||||
|
||||
module.exports.setConf = function (cnf) {
|
||||
var keys = Object.keys(cnf)
|
||||
|
||||
keys.forEach(function (key) {
|
||||
conf[key] = cnf[key]
|
||||
})
|
||||
}
|
||||
|
||||
var actorActivations = function (actor) {
|
||||
return module.exports.bounds.activations.filter(function (activation) {
|
||||
return activation.actor === actor
|
||||
})
|
||||
}
|
||||
|
||||
var actorFlowVerticaBounds = function (actor) {
|
||||
// handle multiple stacked activations for same actor
|
||||
var actors = sq.yy.getActors()
|
||||
var activations = actorActivations(actor)
|
||||
|
||||
var left = activations.reduce(function (acc, activation) { return Math.min(acc, activation.startx) }, actors[actor].x + conf.width / 2)
|
||||
var right = activations.reduce(function (acc, activation) { return Math.max(acc, activation.stopx) }, actors[actor].x + conf.width / 2)
|
||||
return [left, right]
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a flowchart in the tag with id: id based on the graph definition in text.
|
||||
* @param text
|
||||
* @param id
|
||||
*/
|
||||
module.exports.draw = function (text, id) {
|
||||
sq.yy.clear()
|
||||
sq.parse(text + '\n')
|
||||
|
||||
exports.bounds.init()
|
||||
var diagram = d3.select('#' + id)
|
||||
|
||||
var startx
|
||||
var stopx
|
||||
var forceWidth
|
||||
|
||||
// Fetch data from the parsing
|
||||
var actors = sq.yy.getActors()
|
||||
var actorKeys = sq.yy.getActorKeys()
|
||||
var messages = sq.yy.getMessages()
|
||||
var title = sq.yy.getTitle()
|
||||
module.exports.drawActors(diagram, actors, actorKeys, 0)
|
||||
|
||||
// The arrow head definition is attached to the svg once
|
||||
svgDraw.insertArrowHead(diagram)
|
||||
svgDraw.insertArrowCrossHead(diagram)
|
||||
|
||||
function activeEnd (msg, verticalPos) {
|
||||
var activationData = exports.bounds.endActivation(msg)
|
||||
if (activationData.starty + 18 > verticalPos) {
|
||||
activationData.starty = verticalPos - 6
|
||||
verticalPos += 12
|
||||
}
|
||||
svgDraw.drawActivation(diagram, activationData, verticalPos, conf)
|
||||
|
||||
exports.bounds.insert(activationData.startx, verticalPos - 10, activationData.stopx, verticalPos)
|
||||
}
|
||||
|
||||
// var lastMsg
|
||||
|
||||
// Draw the messages/signals
|
||||
messages.forEach(function (msg) {
|
||||
var loopData
|
||||
|
||||
switch (msg.type) {
|
||||
case sq.yy.LINETYPE.NOTE:
|
||||
exports.bounds.bumpVerticalPos(conf.boxMargin)
|
||||
|
||||
startx = actors[msg.from].x
|
||||
stopx = actors[msg.to].x
|
||||
|
||||
if (msg.placement === sq.yy.PLACEMENT.RIGHTOF) {
|
||||
drawNote(diagram, startx + (conf.width + conf.actorMargin) / 2, exports.bounds.getVerticalPos(), msg)
|
||||
} else if (msg.placement === sq.yy.PLACEMENT.LEFTOF) {
|
||||
drawNote(diagram, startx - (conf.width + conf.actorMargin) / 2, exports.bounds.getVerticalPos(), msg)
|
||||
} else if (msg.to === msg.from) {
|
||||
// Single-actor over
|
||||
drawNote(diagram, startx, exports.bounds.getVerticalPos(), msg)
|
||||
} else {
|
||||
// Multi-actor over
|
||||
forceWidth = Math.abs(startx - stopx) + conf.actorMargin
|
||||
drawNote(diagram, (startx + stopx + conf.width - forceWidth) / 2, exports.bounds.getVerticalPos(), msg,
|
||||
forceWidth)
|
||||
}
|
||||
break
|
||||
case sq.yy.LINETYPE.ACTIVE_START:
|
||||
exports.bounds.newActivation(msg, diagram)
|
||||
break
|
||||
case sq.yy.LINETYPE.ACTIVE_END:
|
||||
activeEnd(msg, exports.bounds.getVerticalPos())
|
||||
break
|
||||
case sq.yy.LINETYPE.LOOP_START:
|
||||
exports.bounds.bumpVerticalPos(conf.boxMargin)
|
||||
exports.bounds.newLoop(msg.message)
|
||||
exports.bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin)
|
||||
break
|
||||
case sq.yy.LINETYPE.LOOP_END:
|
||||
loopData = exports.bounds.endLoop()
|
||||
|
||||
svgDraw.drawLoop(diagram, loopData, 'loop', conf)
|
||||
exports.bounds.bumpVerticalPos(conf.boxMargin)
|
||||
break
|
||||
case sq.yy.LINETYPE.OPT_START:
|
||||
exports.bounds.bumpVerticalPos(conf.boxMargin)
|
||||
exports.bounds.newLoop(msg.message)
|
||||
exports.bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin)
|
||||
break
|
||||
case sq.yy.LINETYPE.OPT_END:
|
||||
loopData = exports.bounds.endLoop()
|
||||
|
||||
svgDraw.drawLoop(diagram, loopData, 'opt', conf)
|
||||
exports.bounds.bumpVerticalPos(conf.boxMargin)
|
||||
break
|
||||
case sq.yy.LINETYPE.ALT_START:
|
||||
exports.bounds.bumpVerticalPos(conf.boxMargin)
|
||||
exports.bounds.newLoop(msg.message)
|
||||
exports.bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin)
|
||||
break
|
||||
case sq.yy.LINETYPE.ALT_ELSE:
|
||||
exports.bounds.bumpVerticalPos(conf.boxMargin)
|
||||
loopData = exports.bounds.addSectionToLoop(msg.message)
|
||||
exports.bounds.bumpVerticalPos(conf.boxMargin)
|
||||
break
|
||||
case sq.yy.LINETYPE.ALT_END:
|
||||
loopData = exports.bounds.endLoop()
|
||||
|
||||
svgDraw.drawLoop(diagram, loopData, 'alt', conf)
|
||||
exports.bounds.bumpVerticalPos(conf.boxMargin)
|
||||
break
|
||||
case sq.yy.LINETYPE.PAR_START:
|
||||
exports.bounds.bumpVerticalPos(conf.boxMargin)
|
||||
exports.bounds.newLoop(msg.message)
|
||||
exports.bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin)
|
||||
break
|
||||
case sq.yy.LINETYPE.PAR_AND:
|
||||
exports.bounds.bumpVerticalPos(conf.boxMargin)
|
||||
loopData = exports.bounds.addSectionToLoop(msg.message)
|
||||
exports.bounds.bumpVerticalPos(conf.boxMargin)
|
||||
break
|
||||
case sq.yy.LINETYPE.PAR_END:
|
||||
loopData = exports.bounds.endLoop()
|
||||
svgDraw.drawLoop(diagram, loopData, 'par', conf)
|
||||
exports.bounds.bumpVerticalPos(conf.boxMargin)
|
||||
break
|
||||
default:
|
||||
try {
|
||||
// lastMsg = msg
|
||||
exports.bounds.bumpVerticalPos(conf.messageMargin)
|
||||
var fromBounds = actorFlowVerticaBounds(msg.from)
|
||||
var toBounds = actorFlowVerticaBounds(msg.to)
|
||||
var fromIdx = fromBounds[0] <= toBounds[0] ? 1 : 0
|
||||
var toIdx = fromBounds[0] < toBounds[0] ? 0 : 1
|
||||
startx = fromBounds[fromIdx]
|
||||
stopx = toBounds[toIdx]
|
||||
|
||||
var verticalPos = exports.bounds.getVerticalPos()
|
||||
drawMessage(diagram, startx, stopx, verticalPos, msg)
|
||||
var allBounds = fromBounds.concat(toBounds)
|
||||
exports.bounds.insert(Math.min.apply(null, allBounds), verticalPos, Math.max.apply(null, allBounds), verticalPos)
|
||||
} catch (e) {
|
||||
console.error('error while drawing message', e)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (conf.mirrorActors) {
|
||||
// Draw actors below diagram
|
||||
exports.bounds.bumpVerticalPos(conf.boxMargin * 2)
|
||||
module.exports.drawActors(diagram, actors, actorKeys, exports.bounds.getVerticalPos())
|
||||
}
|
||||
|
||||
var box = exports.bounds.getBounds()
|
||||
|
||||
// Adjust line height of actor lines now that the height of the diagram is known
|
||||
log.debug('For line height fix Querying: #' + id + ' .actor-line')
|
||||
var actorLines = d3.selectAll('#' + id + ' .actor-line')
|
||||
actorLines.attr('y2', box.stopy)
|
||||
|
||||
var height = box.stopy - box.starty + 2 * conf.diagramMarginY
|
||||
|
||||
if (conf.mirrorActors) {
|
||||
height = height - conf.boxMargin + conf.bottomMarginAdj
|
||||
}
|
||||
|
||||
var width = (box.stopx - box.startx) + (2 * conf.diagramMarginX)
|
||||
|
||||
if (title) {
|
||||
diagram.append('text')
|
||||
.text(title)
|
||||
.attr('x', ((box.stopx - box.startx) / 2) - (2 * conf.diagramMarginX))
|
||||
.attr('y', -25)
|
||||
}
|
||||
|
||||
if (conf.useMaxWidth) {
|
||||
diagram.attr('height', '100%')
|
||||
diagram.attr('width', '100%')
|
||||
diagram.attr('style', 'max-width:' + (width) + 'px;')
|
||||
} else {
|
||||
diagram.attr('height', height)
|
||||
diagram.attr('width', width)
|
||||
}
|
||||
var extraVertForTitle = title ? 40 : 0
|
||||
diagram.attr('viewBox', (box.startx - conf.diagramMarginX) + ' -' + (conf.diagramMarginY + extraVertForTitle) + ' ' + width + ' ' + (height + extraVertForTitle))
|
||||
}
|
||||
319
src/diagrams/sequenceDiagram/svgDraw.js
Normal file
319
src/diagrams/sequenceDiagram/svgDraw.js
Normal file
@@ -0,0 +1,319 @@
|
||||
/**
|
||||
* Created by knut on 14-12-20.
|
||||
*/
|
||||
exports.drawRect = function (elem, rectData) {
|
||||
var rectElem = elem.append('rect')
|
||||
rectElem.attr('x', rectData.x)
|
||||
rectElem.attr('y', rectData.y)
|
||||
rectElem.attr('fill', rectData.fill)
|
||||
rectElem.attr('stroke', rectData.stroke)
|
||||
rectElem.attr('width', rectData.width)
|
||||
rectElem.attr('height', rectData.height)
|
||||
rectElem.attr('rx', rectData.rx)
|
||||
rectElem.attr('ry', rectData.ry)
|
||||
|
||||
if (typeof rectData.class !== 'undefined') {
|
||||
rectElem.attr('class', rectData.class)
|
||||
}
|
||||
|
||||
return rectElem
|
||||
}
|
||||
|
||||
exports.drawText = function (elem, textData, width) {
|
||||
// Remove and ignore br:s
|
||||
var nText = textData.text.replace(/<br\/?>/ig, ' ')
|
||||
|
||||
var textElem = elem.append('text')
|
||||
textElem.attr('x', textData.x)
|
||||
textElem.attr('y', textData.y)
|
||||
textElem.style('text-anchor', textData.anchor)
|
||||
textElem.attr('fill', textData.fill)
|
||||
if (typeof textData.class !== 'undefined') {
|
||||
textElem.attr('class', textData.class)
|
||||
}
|
||||
|
||||
var span = textElem.append('tspan')
|
||||
span.attr('x', textData.x + textData.textMargin * 2)
|
||||
span.attr('fill', textData.fill)
|
||||
span.text(nText)
|
||||
if (typeof textElem.textwrap !== 'undefined') {
|
||||
textElem.textwrap({
|
||||
x: textData.x, // bounding box is 300 pixels from the left
|
||||
y: textData.y, // bounding box is 400 pixels from the top
|
||||
width: width, // bounding box is 500 pixels across
|
||||
height: 1800 // bounding box is 600 pixels tall
|
||||
}, textData.textMargin)
|
||||
}
|
||||
|
||||
return textElem
|
||||
}
|
||||
|
||||
exports.drawLabel = function (elem, txtObject) {
|
||||
function genPoints (x, y, width, height, cut) {
|
||||
return x + ',' + y + ' ' +
|
||||
(x + width) + ',' + y + ' ' +
|
||||
(x + width) + ',' + (y + height - cut) + ' ' +
|
||||
(x + width - cut * 1.2) + ',' + (y + height) + ' ' +
|
||||
(x) + ',' + (y + height)
|
||||
}
|
||||
var polygon = elem.append('polygon')
|
||||
polygon.attr('points', genPoints(txtObject.x, txtObject.y, 50, 20, 7))
|
||||
polygon.attr('class', 'labelBox')
|
||||
|
||||
txtObject.y = txtObject.y + txtObject.labelMargin
|
||||
txtObject.x = txtObject.x + 0.5 * txtObject.labelMargin
|
||||
exports.drawText(elem, txtObject)
|
||||
}
|
||||
var actorCnt = -1
|
||||
/**
|
||||
* Draws an actor in the diagram with the attaced line
|
||||
* @param center - The center of the the actor
|
||||
* @param pos The position if the actor in the liost of actors
|
||||
* @param description The text in the box
|
||||
*/
|
||||
exports.drawActor = function (elem, left, verticalPos, description, conf) {
|
||||
var center = left + (conf.width / 2)
|
||||
var g = elem.append('g')
|
||||
if (verticalPos === 0) {
|
||||
actorCnt++
|
||||
g.append('line')
|
||||
.attr('id', 'actor' + actorCnt)
|
||||
.attr('x1', center)
|
||||
.attr('y1', 5)
|
||||
.attr('x2', center)
|
||||
.attr('y2', 2000)
|
||||
.attr('class', 'actor-line')
|
||||
.attr('stroke-width', '0.5px')
|
||||
.attr('stroke', '#999')
|
||||
}
|
||||
|
||||
var rect = exports.getNoteRect()
|
||||
rect.x = left
|
||||
rect.y = verticalPos
|
||||
rect.fill = '#eaeaea'
|
||||
rect.width = conf.width
|
||||
rect.height = conf.height
|
||||
rect.class = 'actor'
|
||||
rect.rx = 3
|
||||
rect.ry = 3
|
||||
exports.drawRect(g, rect)
|
||||
|
||||
_drawTextCandidateFunc(conf)(description, g,
|
||||
rect.x, rect.y, rect.width, rect.height, { 'class': 'actor' })
|
||||
}
|
||||
|
||||
exports.anchorElement = function (elem) {
|
||||
return elem.append('g')
|
||||
}
|
||||
/**
|
||||
* Draws an actor in the diagram with the attaced line
|
||||
* @param elem - element to append activation rect
|
||||
* @param bounds - activation box bounds
|
||||
* @param verticalPos - precise y cooridnate of bottom activation box edge
|
||||
*/
|
||||
exports.drawActivation = function (elem, bounds, verticalPos) {
|
||||
var rect = exports.getNoteRect()
|
||||
var g = bounds.anchored
|
||||
rect.x = bounds.startx
|
||||
rect.y = bounds.starty
|
||||
rect.fill = '#f4f4f4'
|
||||
rect.width = bounds.stopx - bounds.startx
|
||||
rect.height = verticalPos - bounds.starty
|
||||
exports.drawRect(g, rect)
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws an actor in the diagram with the attaced line
|
||||
* @param center - The center of the the actor
|
||||
* @param pos The position if the actor in the list of actors
|
||||
* @param description The text in the box
|
||||
*/
|
||||
exports.drawLoop = function (elem, bounds, labelText, conf) {
|
||||
var g = elem.append('g')
|
||||
var drawLoopLine = function (startx, starty, stopx, stopy) {
|
||||
return g.append('line')
|
||||
.attr('x1', startx)
|
||||
.attr('y1', starty)
|
||||
.attr('x2', stopx)
|
||||
.attr('y2', stopy)
|
||||
.attr('class', 'loopLine')
|
||||
}
|
||||
drawLoopLine(bounds.startx, bounds.starty, bounds.stopx, bounds.starty)
|
||||
drawLoopLine(bounds.stopx, bounds.starty, bounds.stopx, bounds.stopy)
|
||||
drawLoopLine(bounds.startx, bounds.stopy, bounds.stopx, bounds.stopy)
|
||||
drawLoopLine(bounds.startx, bounds.starty, bounds.startx, bounds.stopy)
|
||||
if (typeof bounds.sections !== 'undefined') {
|
||||
bounds.sections.forEach(function (item) {
|
||||
drawLoopLine(bounds.startx, item, bounds.stopx, item).style('stroke-dasharray', '3, 3')
|
||||
})
|
||||
}
|
||||
|
||||
var txt = exports.getTextObj()
|
||||
txt.text = labelText
|
||||
txt.x = bounds.startx
|
||||
txt.y = bounds.starty
|
||||
txt.labelMargin = 1.5 * 10 // This is the small box that says "loop"
|
||||
txt.class = 'labelText' // Its size & position are fixed.
|
||||
|
||||
exports.drawLabel(g, txt)
|
||||
|
||||
txt = exports.getTextObj()
|
||||
txt.text = '[ ' + bounds.title + ' ]'
|
||||
txt.x = bounds.startx + (bounds.stopx - bounds.startx) / 2
|
||||
txt.y = bounds.starty + 1.5 * conf.boxMargin
|
||||
txt.anchor = 'middle'
|
||||
txt.class = 'loopText'
|
||||
|
||||
exports.drawText(g, txt)
|
||||
|
||||
if (typeof bounds.sectionTitles !== 'undefined') {
|
||||
bounds.sectionTitles.forEach(function (item, idx) {
|
||||
if (item !== '') {
|
||||
txt.text = '[ ' + item + ' ]'
|
||||
txt.y = bounds.sections[idx] + 1.5 * conf.boxMargin
|
||||
exports.drawText(g, txt)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup arrow head and define the marker. The result is appended to the svg.
|
||||
*/
|
||||
exports.insertArrowHead = function (elem) {
|
||||
elem.append('defs').append('marker')
|
||||
.attr('id', 'arrowhead')
|
||||
.attr('refX', 5)
|
||||
.attr('refY', 2)
|
||||
.attr('markerWidth', 6)
|
||||
.attr('markerHeight', 4)
|
||||
.attr('orient', 'auto')
|
||||
.append('path')
|
||||
.attr('d', 'M 0,0 V 4 L6,2 Z') // this is actual shape for arrowhead
|
||||
}
|
||||
/**
|
||||
* Setup arrow head and define the marker. The result is appended to the svg.
|
||||
*/
|
||||
exports.insertArrowCrossHead = function (elem) {
|
||||
var defs = elem.append('defs')
|
||||
var marker = defs.append('marker')
|
||||
.attr('id', 'crosshead')
|
||||
.attr('markerWidth', 15)
|
||||
.attr('markerHeight', 8)
|
||||
.attr('orient', 'auto')
|
||||
.attr('refX', 16)
|
||||
.attr('refY', 4)
|
||||
|
||||
// The arrow
|
||||
marker.append('path')
|
||||
.attr('fill', 'black')
|
||||
.attr('stroke', '#000000')
|
||||
.style('stroke-dasharray', ('0, 0'))
|
||||
.attr('stroke-width', '1px')
|
||||
.attr('d', 'M 9,2 V 6 L16,4 Z')
|
||||
|
||||
// The cross
|
||||
marker.append('path')
|
||||
.attr('fill', 'none')
|
||||
.attr('stroke', '#000000')
|
||||
.style('stroke-dasharray', ('0, 0'))
|
||||
.attr('stroke-width', '1px')
|
||||
.attr('d', 'M 0,1 L 6,7 M 6,1 L 0,7')
|
||||
// this is actual shape for arrowhead
|
||||
}
|
||||
|
||||
exports.getTextObj = function () {
|
||||
var txt = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
'fill': 'black',
|
||||
'text-anchor': 'start',
|
||||
style: '#666',
|
||||
width: 100,
|
||||
height: 100,
|
||||
textMargin: 0,
|
||||
rx: 0,
|
||||
ry: 0
|
||||
}
|
||||
return txt
|
||||
}
|
||||
|
||||
exports.getNoteRect = function () {
|
||||
var rect = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
fill: '#EDF2AE',
|
||||
stroke: '#666',
|
||||
width: 100,
|
||||
anchor: 'start',
|
||||
height: 100,
|
||||
rx: 0,
|
||||
ry: 0
|
||||
}
|
||||
return rect
|
||||
}
|
||||
|
||||
var _drawTextCandidateFunc = (function () {
|
||||
function byText (content, g, x, y, width, height, textAttrs) {
|
||||
var text = g.append('text')
|
||||
.attr('x', x + width / 2).attr('y', y + height / 2 + 5)
|
||||
.style('text-anchor', 'middle')
|
||||
.text(content)
|
||||
_setTextAttrs(text, textAttrs)
|
||||
}
|
||||
|
||||
function byTspan (content, g, x, y, width, height, textAttrs) {
|
||||
var 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)
|
||||
|
||||
if (typeof (text.textwrap) !== 'undefined') {
|
||||
text.textwrap({ // d3textwrap
|
||||
x: x + width / 2, y: y, width: width, height: height
|
||||
}, 0)
|
||||
// vertical aligment after d3textwrap expans tspan to multiple tspans
|
||||
var tspans = text.selectAll('tspan')
|
||||
if (tspans.length > 0 && tspans[0].length > 0) {
|
||||
tspans = tspans[0]
|
||||
// set y of <text> to the mid y of the first line
|
||||
text.attr('y', y + (height / 2.0 - text[0][0].getBBox().height * (1 - 1.0 / tspans.length) / 2.0))
|
||||
.attr('dominant-baseline', 'central')
|
||||
.attr('alignment-baseline', 'central')
|
||||
}
|
||||
}
|
||||
_setTextAttrs(text, textAttrs)
|
||||
}
|
||||
|
||||
function byFo (content, g, x, y, width, height, textAttrs) {
|
||||
var s = g.append('switch')
|
||||
var f = s.append('foreignObject')
|
||||
.attr('x', x).attr('y', y)
|
||||
.attr('width', width).attr('height', height)
|
||||
|
||||
var text = f.append('div').style('display', 'table')
|
||||
.style('height', '100%').style('width', '100%')
|
||||
|
||||
text.append('div').style('display', 'table-cell')
|
||||
.style('text-align', 'center').style('vertical-align', 'middle')
|
||||
.text(content)
|
||||
|
||||
byTspan(content, s, x, y, width, height, textAttrs)
|
||||
_setTextAttrs(text, textAttrs)
|
||||
}
|
||||
|
||||
function _setTextAttrs (toText, fromTextAttrsDict) {
|
||||
for (var key in fromTextAttrsDict) {
|
||||
if (fromTextAttrsDict.hasOwnProperty(key)) {
|
||||
toText.attr(key, fromTextAttrsDict[key])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return function (conf) {
|
||||
return conf.textPlacement === 'fo' ? byFo : (
|
||||
conf.textPlacement === 'old' ? byText : byTspan)
|
||||
}
|
||||
})()
|
||||
198
src/graphDb.js
198
src/graphDb.js
@@ -1,198 +0,0 @@
|
||||
/**
|
||||
* Created by knut on 14-11-03.
|
||||
*/
|
||||
|
||||
var vertices = {};
|
||||
var edges = [];
|
||||
var classes = [];
|
||||
var direction;
|
||||
// Functions to be run after graph rendering
|
||||
var funs = [];
|
||||
/**
|
||||
* Function called by parser when a node definition has been found
|
||||
* @param id
|
||||
* @param text
|
||||
* @param type
|
||||
* @param style
|
||||
*/
|
||||
exports.addVertex = function (id, text, type, style) {
|
||||
//console.log('Got node ' + id + ' ' + type + ' ' + text + ' styles: ' + JSON.stringify(style));
|
||||
if (typeof vertices[id] === 'undefined') {
|
||||
vertices[id] = {id: id, styles: [], classes:[]};
|
||||
}
|
||||
if (typeof text !== 'undefined') {
|
||||
vertices[id].text = text;
|
||||
}
|
||||
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) {
|
||||
vertices[id].styles.push(s);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Function called by parser when a link/edge definition has been found
|
||||
* @param start
|
||||
* @param end
|
||||
* @param type
|
||||
* @param linktext
|
||||
*/
|
||||
exports.addLink = function (start, end, type, linktext) {
|
||||
var edge = {start: start, end: end, type: undefined, text: ''};
|
||||
var linktext = type.text;
|
||||
if (typeof linktext !== 'undefined') {
|
||||
edge.text = linktext;
|
||||
}
|
||||
|
||||
if (typeof type !== 'undefined') {
|
||||
edge.type = type.type;
|
||||
}
|
||||
edges.push(edge);
|
||||
};
|
||||
/**
|
||||
* Updates a link with a style
|
||||
* @param pos
|
||||
* @param style
|
||||
*/
|
||||
exports.updateLink = function (pos, style) {
|
||||
var position = pos.substr(1);
|
||||
edges[pos].style = style;
|
||||
};
|
||||
|
||||
exports.addClass = function (id, style) {
|
||||
if (typeof classes[id] === 'undefined') {
|
||||
classes[id] = {id: id, styles: []};
|
||||
}
|
||||
|
||||
if (typeof style !== 'undefined') {
|
||||
if (style !== null) {
|
||||
style.forEach(function (s) {
|
||||
classes[id].styles.push(s);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by parser when a graph definition is found, stores the direction of the chart.
|
||||
* @param dir
|
||||
*/
|
||||
exports.setDirection = function (dir) {
|
||||
direction = dir;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by parser when a graph definition is found, stores the direction of the chart.
|
||||
* @param dir
|
||||
*/
|
||||
exports.setClass = function (id,className) {
|
||||
if(id.indexOf(',')>0){
|
||||
id.split(',').forEach(function(id2){
|
||||
if(typeof vertices[id2] !== 'undefined'){
|
||||
vertices[id2].classes.push(className);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
if(typeof vertices[id] !== 'undefined'){
|
||||
vertices[id].classes.push(className);
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Called by parser when a graph definition is found, stores the direction of the chart.
|
||||
* @param dir
|
||||
*/
|
||||
exports.setClickEvent = function (id,functionName) {
|
||||
|
||||
|
||||
if(id.indexOf(',')>0){
|
||||
id.split(',').forEach(function(id2) {
|
||||
if (typeof vertices[id2] !== 'undefined') {
|
||||
funs.push(function () {
|
||||
var elem = document.getElementById(id2);
|
||||
if (elem !== null) {
|
||||
elem.onclick = function () {
|
||||
eval(functionName + '(\'' + id2 + '\')');
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}else{
|
||||
//console.log('Checking now for ::'+id);
|
||||
if(typeof vertices[id] !== 'undefined'){
|
||||
funs.push(function(){
|
||||
var elem = document.getElementById(id);
|
||||
if(elem !== null){
|
||||
//console.log('id was NOT null: '+id);
|
||||
elem.onclick = function(){eval(functionName+'(\'' + id + '\')');};
|
||||
}
|
||||
else{
|
||||
//console.log('id was null: '+id);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
exports.bindFunctions = function(){
|
||||
//setTimeout(function(){
|
||||
funs.forEach(function(fun){
|
||||
fun();
|
||||
});
|
||||
//},1000);
|
||||
|
||||
};
|
||||
exports.getDirection = function () {
|
||||
return direction;
|
||||
};
|
||||
/**
|
||||
* Retrieval function for fetching the found nodes after parsing has completed.
|
||||
* @returns {{}|*|vertices}
|
||||
*/
|
||||
exports.getVertices = function () {
|
||||
return vertices;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieval function for fetching the found links after parsing has completed.
|
||||
* @returns {{}|*|edges}
|
||||
*/
|
||||
exports.getEdges = function () {
|
||||
return edges;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieval function for fetching the found class definitions after parsing has completed.
|
||||
* @returns {{}|*|classes}
|
||||
*/
|
||||
exports.getClasses = function () {
|
||||
return classes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears the internal graph db so that a new graph can be parsed.
|
||||
*/
|
||||
exports.clear = function () {
|
||||
vertices = {};
|
||||
classes = {};
|
||||
edges = [];
|
||||
funs = [];
|
||||
};
|
||||
/**
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
exports.defaultStyle = function () {
|
||||
return "fill:#ffa;stroke: #f66; stroke-width: 3px; stroke-dasharray: 5, 5;fill:#ffa;stroke: #666;";
|
||||
};
|
||||
|
||||
78
src/less/dark/classDiagram.less
Normal file
78
src/less/dark/classDiagram.less
Normal file
@@ -0,0 +1,78 @@
|
||||
g.classGroup text {
|
||||
fill: @nodeBorder;
|
||||
stroke: none;
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
g.classGroup rect {
|
||||
fill: @nodeBkg;
|
||||
stroke: @nodeBorder;
|
||||
}
|
||||
|
||||
g.classGroup line {
|
||||
stroke: @nodeBorder;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
svg .classLabel .box {
|
||||
stroke: none;
|
||||
stroke-width: 0;
|
||||
fill: @nodeBkg;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
svg .classLabel .label {
|
||||
fill: @nodeBorder;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.relation {
|
||||
stroke: @nodeBorder;
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.composition {
|
||||
fill: @nodeBorder;
|
||||
stroke: @nodeBorder;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
#compositionStart {
|
||||
.composition;
|
||||
}
|
||||
|
||||
#compositionEnd {
|
||||
.composition;
|
||||
}
|
||||
|
||||
.aggregation {
|
||||
fill: @nodeBkg;
|
||||
stroke: @nodeBorder;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
#aggregationStart {
|
||||
.aggregation;
|
||||
}
|
||||
|
||||
#aggregationEnd {
|
||||
.aggregation;
|
||||
}
|
||||
|
||||
#dependencyStart {
|
||||
.composition;
|
||||
}
|
||||
|
||||
#dependencyEnd {
|
||||
.composition;
|
||||
}
|
||||
|
||||
#extensionStart {
|
||||
.composition;
|
||||
}
|
||||
|
||||
#extensionEnd {
|
||||
.composition;
|
||||
}
|
||||
35
src/less/dark/flow.less
Normal file
35
src/less/dark/flow.less
Normal file
@@ -0,0 +1,35 @@
|
||||
.mermaid .label {
|
||||
color: @darkTextColor
|
||||
}
|
||||
|
||||
.node rect,
|
||||
.node circle,
|
||||
.node ellipse,
|
||||
.node polygon {
|
||||
fill: @mainBkg;
|
||||
stroke: @border1;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.arrowheadPath {
|
||||
fill: @arrowheadColor;
|
||||
}
|
||||
|
||||
.edgePath .path {
|
||||
stroke: @lineColor;
|
||||
}
|
||||
|
||||
.edgeLabel {
|
||||
background-color: @edgeLabelBackground;
|
||||
}
|
||||
|
||||
.cluster rect {
|
||||
fill: @secondBkg !important;
|
||||
rx: 4 !important;
|
||||
stroke: @clusterBorder !important;
|
||||
stroke-width: 1px !important;
|
||||
}
|
||||
|
||||
.cluster text {
|
||||
fill: @titleColor;
|
||||
}
|
||||
212
src/less/dark/gantt.less
Normal file
212
src/less/dark/gantt.less
Normal file
@@ -0,0 +1,212 @@
|
||||
/** Section styling */
|
||||
|
||||
.section {
|
||||
stroke: none;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.section0 {
|
||||
fill: @sectionBkgColor;
|
||||
}
|
||||
|
||||
.section2 {
|
||||
fill: @sectionBkgColor2;
|
||||
}
|
||||
|
||||
.section1,
|
||||
.section3 {
|
||||
fill: @altSectionBkgColor;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.sectionTitle0 {
|
||||
fill: @titleColor;
|
||||
}
|
||||
|
||||
.sectionTitle1 {
|
||||
fill: @titleColor;
|
||||
}
|
||||
|
||||
.sectionTitle2 {
|
||||
fill: @titleColor;
|
||||
}
|
||||
|
||||
.sectionTitle3 {
|
||||
fill: @titleColor;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
text-anchor: start;
|
||||
font-size: 11px;
|
||||
text-height: 14px;
|
||||
}
|
||||
|
||||
|
||||
/* Grid and axis */
|
||||
|
||||
.grid .tick {
|
||||
stroke: @sectionBkgColor;
|
||||
opacity: 0.3;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.grid .tick text {
|
||||
fill: @taskTextLightColor;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.grid path {
|
||||
stroke-width: 0;
|
||||
}
|
||||
|
||||
|
||||
/* Today line */
|
||||
|
||||
.today {
|
||||
fill: none;
|
||||
stroke: @todayLineColor;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
|
||||
/* Task styling */
|
||||
|
||||
|
||||
/* Default task */
|
||||
|
||||
.task {
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.taskText {
|
||||
text-anchor: middle;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.taskTextOutsideRight {
|
||||
fill: @taskTextDarkColor;
|
||||
text-anchor: start;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.taskTextOutsideLeft {
|
||||
fill: @taskTextDarkColor;
|
||||
text-anchor: end;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
|
||||
/* Specific task settings for the sections*/
|
||||
|
||||
.taskText0,
|
||||
.taskText1,
|
||||
.taskText2,
|
||||
.taskText3 {
|
||||
fill: @taskTextColor;
|
||||
}
|
||||
|
||||
.task0,
|
||||
.task1,
|
||||
.task2,
|
||||
.task3 {
|
||||
fill: @taskBkgColor;
|
||||
stroke: @taskBorderColor;
|
||||
}
|
||||
|
||||
.taskTextOutside0,
|
||||
.taskTextOutside2,
|
||||
{
|
||||
fill: @taskTextOutsideColor;
|
||||
}
|
||||
|
||||
.taskTextOutside1,
|
||||
.taskTextOutside3 {
|
||||
fill: @taskTextOutsideColor;
|
||||
}
|
||||
|
||||
|
||||
/* Active task */
|
||||
|
||||
.active0,
|
||||
.active1,
|
||||
.active2,
|
||||
.active3 {
|
||||
fill: @activeTaskBkgColor;
|
||||
stroke: @activeTaskBorderColor;
|
||||
}
|
||||
|
||||
.activeText0,
|
||||
.activeText1,
|
||||
.activeText2,
|
||||
.activeText3 {
|
||||
fill: @taskTextDarkColor !important;
|
||||
}
|
||||
|
||||
|
||||
/* Completed task */
|
||||
|
||||
.done0,
|
||||
.done1,
|
||||
.done2,
|
||||
.done3 {
|
||||
fill: @doneTaskBkgColor;
|
||||
}
|
||||
|
||||
.doneText0,
|
||||
.doneText1,
|
||||
.doneText2,
|
||||
.doneText3 {
|
||||
fill: @taskTextDarkColor !important;
|
||||
}
|
||||
|
||||
|
||||
/* Tasks on the critical line */
|
||||
|
||||
.crit0,
|
||||
.crit1,
|
||||
.crit2,
|
||||
.crit3 {
|
||||
stroke: @critBorderColor;
|
||||
fill: @critBkgColor;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.activeCrit0,
|
||||
.activeCrit1,
|
||||
.activeCrit2,
|
||||
.activeCrit3 {
|
||||
stroke: @critBorderColor;
|
||||
fill: @activeTaskBkgColor;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.doneCrit0,
|
||||
.doneCrit1,
|
||||
.doneCrit2,
|
||||
.doneCrit3 {
|
||||
stroke: @critBorderColor;
|
||||
fill: @doneTaskBkgColor;
|
||||
stroke-width: 1;
|
||||
cursor: pointer;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.doneCritText0,
|
||||
.doneCritText1,
|
||||
.doneCritText2,
|
||||
.doneCritText3 {
|
||||
fill: @taskTextLightColor !important;
|
||||
}
|
||||
|
||||
.activeCritText0,
|
||||
.activeCritText1,
|
||||
.activeCritText2,
|
||||
.activeCritText3 {
|
||||
fill: @taskTextDarkColor !important;
|
||||
}
|
||||
|
||||
.titleText {
|
||||
text-anchor: middle;
|
||||
font-size: 18px;
|
||||
fill: @taskTextLightColor;
|
||||
}
|
||||
6
src/less/dark/gitGraph.less
Normal file
6
src/less/dark/gitGraph.less
Normal file
@@ -0,0 +1,6 @@
|
||||
.commit-id,
|
||||
.commit-msg,
|
||||
.branch-label {
|
||||
fill: lightgrey;
|
||||
color: lightgrey;
|
||||
}
|
||||
24
src/less/dark/mermaid.less
Normal file
24
src/less/dark/mermaid.less
Normal file
@@ -0,0 +1,24 @@
|
||||
@import "variables";
|
||||
@import "flow";
|
||||
@import "sequenceDiagram";
|
||||
@import "gantt";
|
||||
@import "classDiagram";
|
||||
@import "gitGraph";
|
||||
.node text {
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
div.mermaidTooltip {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
max-width: 200px;
|
||||
padding: 2px;
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
font-size: 12px;
|
||||
background: @secondBkg;
|
||||
border: 1px solid @border2;
|
||||
border-radius: 2px;
|
||||
pointer-events: none;
|
||||
z-index: 100;
|
||||
}
|
||||
75
src/less/dark/sequenceDiagram.less
Normal file
75
src/less/dark/sequenceDiagram.less
Normal file
@@ -0,0 +1,75 @@
|
||||
.actor {
|
||||
stroke: @actorBorder;
|
||||
fill: @actorBkg;
|
||||
}
|
||||
|
||||
text.actor {
|
||||
fill: @actorTextColor;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.actor-line {
|
||||
stroke: @actorLineColor;
|
||||
}
|
||||
|
||||
.messageLine0 {
|
||||
stroke-width: 1.5;
|
||||
stroke-dasharray: "2 2";
|
||||
marker-end: "url(#arrowhead)";
|
||||
stroke: @signalColor;
|
||||
}
|
||||
|
||||
.messageLine1 {
|
||||
stroke-width: 1.5;
|
||||
stroke-dasharray: "2 2";
|
||||
stroke: @signalColor;
|
||||
}
|
||||
|
||||
#arrowhead {
|
||||
fill: @signalColor !important;
|
||||
}
|
||||
|
||||
#crosshead path {
|
||||
fill: @signalColor !important;
|
||||
stroke: @signalColor !important;
|
||||
}
|
||||
|
||||
.messageText {
|
||||
fill: @signalTextColor;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.labelBox {
|
||||
stroke: @labelBoxBorderColor;
|
||||
fill: @labelBoxBkgColor;
|
||||
}
|
||||
|
||||
.labelText {
|
||||
fill: @darkTextColor;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.loopText {
|
||||
fill: @labelTextColor;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.loopLine {
|
||||
stroke-width: 2;
|
||||
stroke-dasharray: "2 2";
|
||||
marker-end: "url(#arrowhead)";
|
||||
stroke: @labelBoxBorderColor;
|
||||
}
|
||||
|
||||
.note {
|
||||
//stroke: #decc93;
|
||||
stroke: @noteBorderColor;
|
||||
fill: @noteBkgColor;
|
||||
}
|
||||
|
||||
.noteText {
|
||||
fill: black;
|
||||
stroke: none;
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
font-size: 14px;
|
||||
}
|
||||
52
src/less/dark/variables.less
Normal file
52
src/less/dark/variables.less
Normal file
@@ -0,0 +1,52 @@
|
||||
@mainBkg: #BDD5EA;
|
||||
@secondBkg: #6D6D65;
|
||||
@mainContrastColor: lightgrey;
|
||||
@darkTextColor: #323D47;
|
||||
@lineColor: @mainContrastColor;
|
||||
@border1: #81B1DB;
|
||||
@border2: rgba(255, 255, 255, 0.25);
|
||||
@arrowheadColor: @mainContrastColor;
|
||||
|
||||
/* Flowchart variables */
|
||||
|
||||
@nodeBkg: @mainBkg;
|
||||
@nodeBorder: purple;
|
||||
@clusterBkg: @secondBkg;
|
||||
@clusterBorder: @border2;
|
||||
@defaultLinkColor: @lineColor;
|
||||
@titleColor: #F9FFFE;
|
||||
@edgeLabelBackground: #e8e8e8;
|
||||
|
||||
/* Sequence Diagram variables */
|
||||
|
||||
@actorBorder: @border1;
|
||||
@actorBkg: @mainBkg;
|
||||
@actorTextColor: black;
|
||||
@actorLineColor: @mainContrastColor;
|
||||
@signalColor: @mainContrastColor;
|
||||
@signalTextColor: @mainContrastColor;
|
||||
@labelBoxBkgColor: @actorBkg;
|
||||
@labelBoxBorderColor: @actorBorder;
|
||||
@labelTextColor: @mainContrastColor;
|
||||
@noteBorderColor: @border2;
|
||||
@noteBkgColor: #fff5ad;
|
||||
|
||||
/* Gantt chart variables */
|
||||
|
||||
@sectionBkgColor: rgba(255, 255, 255, 0.3);
|
||||
@altSectionBkgColor: white;
|
||||
@sectionBkgColor2: #EAE8B9;
|
||||
@taskBorderColor: rgba(255, 255, 255, 0.5);
|
||||
@taskBkgColor: @mainBkg;
|
||||
@taskTextColor: @darkTextColor;
|
||||
@taskTextOutsideColor: @taskTextLightColor;
|
||||
@activeTaskBorderColor: rgba(255, 255, 255, 0.5);
|
||||
@activeTaskBkgColor: #81B1DB;
|
||||
@gridColor: @mainContrastColor;
|
||||
@doneTaskBkgColor: @mainContrastColor;
|
||||
@doneTaskBorderColor: grey;
|
||||
@critBorderColor: #E83737;
|
||||
@critBkgColor: #E83737;
|
||||
@taskTextLightColor: @mainContrastColor;
|
||||
@taskTextDarkColor: @darkTextColor;
|
||||
@todayLineColor: #DB5757;
|
||||
78
src/less/default/classDiagram.less
Normal file
78
src/less/default/classDiagram.less
Normal file
@@ -0,0 +1,78 @@
|
||||
g.classGroup text {
|
||||
fill: @nodeBorder;
|
||||
stroke: none;
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
g.classGroup rect {
|
||||
fill: @nodeBkg;
|
||||
stroke: @nodeBorder;
|
||||
}
|
||||
|
||||
g.classGroup line {
|
||||
stroke: @nodeBorder;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
svg .classLabel .box {
|
||||
stroke: none;
|
||||
stroke-width: 0;
|
||||
fill: @nodeBkg;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
svg .classLabel .label {
|
||||
fill: @nodeBorder;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.relation {
|
||||
stroke: @nodeBorder;
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.composition {
|
||||
fill: @nodeBorder;
|
||||
stroke: @nodeBorder;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
#compositionStart {
|
||||
.composition;
|
||||
}
|
||||
|
||||
#compositionEnd {
|
||||
.composition;
|
||||
}
|
||||
|
||||
.aggregation {
|
||||
fill: @nodeBkg;
|
||||
stroke: @nodeBorder;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
#aggregationStart {
|
||||
.aggregation;
|
||||
}
|
||||
|
||||
#aggregationEnd {
|
||||
.aggregation;
|
||||
}
|
||||
|
||||
#dependencyStart {
|
||||
.composition;
|
||||
}
|
||||
|
||||
#dependencyEnd {
|
||||
.composition;
|
||||
}
|
||||
|
||||
#extensionStart {
|
||||
.composition;
|
||||
}
|
||||
|
||||
#extensionEnd {
|
||||
.composition;
|
||||
}
|
||||
35
src/less/default/flow.less
Normal file
35
src/less/default/flow.less
Normal file
@@ -0,0 +1,35 @@
|
||||
.mermaid .label {
|
||||
color: #333
|
||||
}
|
||||
|
||||
.node rect,
|
||||
.node circle,
|
||||
.node ellipse,
|
||||
.node polygon {
|
||||
fill: @mainBkg;
|
||||
stroke: @border1;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.arrowheadPath {
|
||||
fill: @arrowheadColor;
|
||||
}
|
||||
|
||||
.edgePath .path {
|
||||
stroke: @lineColor;
|
||||
}
|
||||
|
||||
.edgeLabel {
|
||||
background-color: @edgeLabelBackground;
|
||||
}
|
||||
|
||||
.cluster rect {
|
||||
fill: @secondBkg !important;
|
||||
rx: 4 !important;
|
||||
stroke: @clusterBorder !important;
|
||||
stroke-width: 1px !important;
|
||||
}
|
||||
|
||||
.cluster text {
|
||||
fill: @titleColor;
|
||||
}
|
||||
209
src/less/default/gantt.less
Normal file
209
src/less/default/gantt.less
Normal file
@@ -0,0 +1,209 @@
|
||||
/** Section styling */
|
||||
|
||||
.section {
|
||||
stroke: none;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.section0 {
|
||||
fill: @sectionBkgColor;
|
||||
}
|
||||
|
||||
.section2 {
|
||||
fill: @sectionBkgColor2;
|
||||
}
|
||||
|
||||
.section1,
|
||||
.section3 {
|
||||
fill: @altSectionBkgColor;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.sectionTitle0 {
|
||||
fill: @titleColor;
|
||||
}
|
||||
|
||||
.sectionTitle1 {
|
||||
fill: @titleColor;
|
||||
}
|
||||
|
||||
.sectionTitle2 {
|
||||
fill: @titleColor;
|
||||
}
|
||||
|
||||
.sectionTitle3 {
|
||||
fill: @titleColor;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
text-anchor: start;
|
||||
font-size: 11px;
|
||||
text-height: 14px;
|
||||
}
|
||||
|
||||
|
||||
/* Grid and axis */
|
||||
|
||||
.grid .tick {
|
||||
stroke: @gridColor;
|
||||
opacity: 0.3;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.grid path {
|
||||
stroke-width: 0;
|
||||
}
|
||||
|
||||
|
||||
/* Today line */
|
||||
|
||||
.today {
|
||||
fill: none;
|
||||
stroke: @todayLineColor;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
|
||||
/* Task styling */
|
||||
|
||||
|
||||
/* Default task */
|
||||
|
||||
.task {
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.taskText {
|
||||
text-anchor: middle;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.taskTextOutsideRight {
|
||||
fill: @taskTextDarkColor;
|
||||
text-anchor: start;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.taskTextOutsideLeft {
|
||||
fill: @taskTextDarkColor;
|
||||
text-anchor: end;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
|
||||
/* Specific task settings for the sections*/
|
||||
|
||||
.taskText0,
|
||||
.taskText1,
|
||||
.taskText2,
|
||||
.taskText3 {
|
||||
fill: @taskTextColor;
|
||||
}
|
||||
|
||||
.task0,
|
||||
.task1,
|
||||
.task2,
|
||||
.task3 {
|
||||
fill: @taskBkgColor;
|
||||
stroke: @taskBorderColor;
|
||||
}
|
||||
|
||||
.taskTextOutside0,
|
||||
.taskTextOutside2,
|
||||
{
|
||||
fill: @taskTextOutsideColor;
|
||||
}
|
||||
|
||||
.taskTextOutside1,
|
||||
.taskTextOutside3 {
|
||||
fill: @taskTextOutsideColor;
|
||||
}
|
||||
|
||||
|
||||
/* Active task */
|
||||
|
||||
.active0,
|
||||
.active1,
|
||||
.active2,
|
||||
.active3 {
|
||||
fill: @activeTaskBkgColor;
|
||||
stroke: @activeTaskBorderColor;
|
||||
}
|
||||
|
||||
.activeText0,
|
||||
.activeText1,
|
||||
.activeText2,
|
||||
.activeText3 {
|
||||
fill: @taskTextDarkColor !important;
|
||||
}
|
||||
|
||||
|
||||
/* Completed task */
|
||||
|
||||
.done0,
|
||||
.done1,
|
||||
.done2,
|
||||
.done3 {
|
||||
stroke: @doneTaskBorderColor;
|
||||
fill: @doneTaskBkgColor;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.doneText0,
|
||||
.doneText1,
|
||||
.doneText2,
|
||||
.doneText3 {
|
||||
fill: @taskTextDarkColor !important;
|
||||
}
|
||||
|
||||
|
||||
/* Tasks on the critical line */
|
||||
|
||||
.crit0,
|
||||
.crit1,
|
||||
.crit2,
|
||||
.crit3 {
|
||||
stroke: @critBorderColor;
|
||||
fill: @critBkgColor;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.activeCrit0,
|
||||
.activeCrit1,
|
||||
.activeCrit2,
|
||||
.activeCrit3 {
|
||||
stroke: @critBorderColor;
|
||||
fill: @activeTaskBkgColor;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.doneCrit0,
|
||||
.doneCrit1,
|
||||
.doneCrit2,
|
||||
.doneCrit3 {
|
||||
stroke: @critBorderColor;
|
||||
fill: @doneTaskBkgColor;
|
||||
stroke-width: 2;
|
||||
cursor: pointer;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.doneCritText0,
|
||||
.doneCritText1,
|
||||
.doneCritText2,
|
||||
.doneCritText3 {
|
||||
fill: @taskTextDarkColor !important;
|
||||
}
|
||||
|
||||
.activeCritText0,
|
||||
.activeCritText1,
|
||||
.activeCritText2,
|
||||
.activeCritText3 {
|
||||
fill: @taskTextDarkColor !important;
|
||||
}
|
||||
|
||||
.titleText {
|
||||
text-anchor: middle;
|
||||
font-size: 18px;
|
||||
fill: @taskTextDarkColor;
|
||||
}
|
||||
23
src/less/default/mermaid.less
Normal file
23
src/less/default/mermaid.less
Normal file
@@ -0,0 +1,23 @@
|
||||
@import "variables";
|
||||
@import "flow";
|
||||
@import "sequenceDiagram";
|
||||
@import "gantt";
|
||||
@import "classDiagram";
|
||||
.node text {
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
div.mermaidTooltip {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
max-width: 200px;
|
||||
padding: 2px;
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
font-size: 12px;
|
||||
background: @secondBkg;
|
||||
border: 1px solid @border2;
|
||||
border-radius: 2px;
|
||||
pointer-events: none;
|
||||
z-index: 100;
|
||||
}
|
||||
75
src/less/default/sequenceDiagram.less
Normal file
75
src/less/default/sequenceDiagram.less
Normal file
@@ -0,0 +1,75 @@
|
||||
.actor {
|
||||
stroke: @actorBorder;
|
||||
fill: @actorBkg;
|
||||
}
|
||||
|
||||
text.actor {
|
||||
fill: @actorTextColor;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.actor-line {
|
||||
stroke: @actorLineColor;
|
||||
}
|
||||
|
||||
.messageLine0 {
|
||||
stroke-width: 1.5;
|
||||
stroke-dasharray: "2 2";
|
||||
marker-end: "url(#arrowhead)";
|
||||
stroke: @signalColor;
|
||||
}
|
||||
|
||||
.messageLine1 {
|
||||
stroke-width: 1.5;
|
||||
stroke-dasharray: "2 2";
|
||||
stroke: @signalColor;
|
||||
}
|
||||
|
||||
#arrowhead {
|
||||
fill: @signalColor;
|
||||
}
|
||||
|
||||
#crosshead path {
|
||||
fill: @signalColor !important;
|
||||
stroke: @signalColor !important;
|
||||
}
|
||||
|
||||
.messageText {
|
||||
fill: @signalTextColor;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.labelBox {
|
||||
stroke: @labelBoxBorderColor;
|
||||
fill: @labelBoxBkgColor;
|
||||
}
|
||||
|
||||
.labelText {
|
||||
fill: @labelTextColor;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.loopText {
|
||||
fill: @labelTextColor;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.loopLine {
|
||||
stroke-width: 2;
|
||||
stroke-dasharray: "2 2";
|
||||
marker-end: "url(#arrowhead)";
|
||||
stroke: @labelBoxBorderColor;
|
||||
}
|
||||
|
||||
.note {
|
||||
//stroke: #decc93;
|
||||
stroke: @noteBorderColor;
|
||||
fill: @noteBkgColor;
|
||||
}
|
||||
|
||||
.noteText {
|
||||
fill: black;
|
||||
stroke: none;
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
font-size: 14px;
|
||||
}
|
||||
50
src/less/default/variables.less
Normal file
50
src/less/default/variables.less
Normal file
@@ -0,0 +1,50 @@
|
||||
@mainBkg: #ECECFF;
|
||||
@secondBkg: #ffffde;
|
||||
@lineColor: #333333;
|
||||
@border1: #CCCCFF;
|
||||
@border2: #aaaa33;
|
||||
@arrowheadColor: #333333;
|
||||
|
||||
/* Flowchart variables */
|
||||
|
||||
@nodeBkg: @mainBkg;
|
||||
@nodeBorder: #9370DB;
|
||||
@clusterBkg: @secondBkg;
|
||||
@clusterBorder: @border2;
|
||||
@defaultLinkColor: @lineColor;
|
||||
@titleColor: #333;
|
||||
@edgeLabelBackground: #e8e8e8;
|
||||
|
||||
/* Sequence Diagram variables */
|
||||
|
||||
@actorBorder: @border1;
|
||||
@actorBkg: @mainBkg;
|
||||
@actorTextColor: black;
|
||||
@actorLineColor: grey;
|
||||
@signalColor: #333;
|
||||
@signalTextColor: #333;
|
||||
@labelBoxBkgColor: @actorBkg;
|
||||
@labelBoxBorderColor: @actorBorder;
|
||||
@labelTextColor: @actorTextColor;
|
||||
@noteBorderColor: @border2;
|
||||
@noteBkgColor: #fff5ad;
|
||||
|
||||
/* Gantt chart variables */
|
||||
|
||||
@sectionBkgColor: rgba(102, 102, 255, 0.49);
|
||||
@altSectionBkgColor: white;
|
||||
@sectionBkgColor2: #fff400;
|
||||
@taskBorderColor: #534fbc;
|
||||
@taskBkgColor: #8a90dd;
|
||||
@taskTextColor: @taskTextLightColor;
|
||||
@taskTextOutsideColor: @taskTextDarkColor;
|
||||
@activeTaskBorderColor: #534fbc;
|
||||
@activeTaskBkgColor: #bfc7ff;
|
||||
@gridColor: lightgrey;
|
||||
@doneTaskBkgColor: lightgrey;
|
||||
@doneTaskBorderColor: grey;
|
||||
@critBorderColor: #ff8888;
|
||||
@critBkgColor: red;
|
||||
@taskTextLightColor: white;
|
||||
@taskTextDarkColor: black;
|
||||
@todayLineColor: red;
|
||||
78
src/less/forest/classDiagram.less
Normal file
78
src/less/forest/classDiagram.less
Normal file
@@ -0,0 +1,78 @@
|
||||
g.classGroup text {
|
||||
fill: @nodeBorder;
|
||||
stroke: none;
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
g.classGroup rect {
|
||||
fill: @nodeBkg;
|
||||
stroke: @nodeBorder;
|
||||
}
|
||||
|
||||
g.classGroup line {
|
||||
stroke: @nodeBorder;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
svg .classLabel .box {
|
||||
stroke: none;
|
||||
stroke-width: 0;
|
||||
fill: @nodeBkg;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
svg .classLabel .label {
|
||||
fill: @nodeBorder;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.relation {
|
||||
stroke: @nodeBorder;
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.composition {
|
||||
fill: @nodeBorder;
|
||||
stroke: @nodeBorder;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
#compositionStart {
|
||||
.composition;
|
||||
}
|
||||
|
||||
#compositionEnd {
|
||||
.composition;
|
||||
}
|
||||
|
||||
.aggregation {
|
||||
fill: @nodeBkg;
|
||||
stroke: @nodeBorder;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
#aggregationStart {
|
||||
.aggregation;
|
||||
}
|
||||
|
||||
#aggregationEnd {
|
||||
.aggregation;
|
||||
}
|
||||
|
||||
#dependencyStart {
|
||||
.composition;
|
||||
}
|
||||
|
||||
#dependencyEnd {
|
||||
.composition;
|
||||
}
|
||||
|
||||
#extensionStart {
|
||||
.composition;
|
||||
}
|
||||
|
||||
#extensionEnd {
|
||||
.composition;
|
||||
}
|
||||
37
src/less/forest/flow.less
Normal file
37
src/less/forest/flow.less
Normal file
@@ -0,0 +1,37 @@
|
||||
.mermaid .label {
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
color: #333
|
||||
}
|
||||
|
||||
.node rect,
|
||||
.node circle,
|
||||
.node ellipse,
|
||||
.node polygon {
|
||||
fill: @mainBkg;
|
||||
stroke: @nodeBorder;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.arrowheadPath {
|
||||
fill: @arrowheadColor;
|
||||
}
|
||||
|
||||
.edgePath .path {
|
||||
stroke: @lineColor;
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
|
||||
.edgeLabel {
|
||||
background-color: @edgeLabelBackground;
|
||||
}
|
||||
|
||||
.cluster rect {
|
||||
fill: @secondBkg !important;
|
||||
rx: 4 !important;
|
||||
stroke: @clusterBorder !important;
|
||||
stroke-width: 1px !important;
|
||||
}
|
||||
|
||||
.cluster text {
|
||||
fill: @titleColor;
|
||||
}
|
||||
209
src/less/forest/gantt.less
Normal file
209
src/less/forest/gantt.less
Normal file
@@ -0,0 +1,209 @@
|
||||
/** Section styling */
|
||||
|
||||
.section {
|
||||
stroke: none;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.section0 {
|
||||
fill: @sectionBkgColor;
|
||||
}
|
||||
|
||||
.section2 {
|
||||
fill: @sectionBkgColor2;
|
||||
}
|
||||
|
||||
.section1,
|
||||
.section3 {
|
||||
fill: @altSectionBkgColor;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.sectionTitle0 {
|
||||
fill: @titleColor;
|
||||
}
|
||||
|
||||
.sectionTitle1 {
|
||||
fill: @titleColor;
|
||||
}
|
||||
|
||||
.sectionTitle2 {
|
||||
fill: @titleColor;
|
||||
}
|
||||
|
||||
.sectionTitle3 {
|
||||
fill: @titleColor;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
text-anchor: start;
|
||||
font-size: 11px;
|
||||
text-height: 14px;
|
||||
}
|
||||
|
||||
|
||||
/* Grid and axis */
|
||||
|
||||
.grid .tick {
|
||||
stroke: @gridColor;
|
||||
opacity: 0.3;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.grid path {
|
||||
stroke-width: 0;
|
||||
}
|
||||
|
||||
|
||||
/* Today line */
|
||||
|
||||
.today {
|
||||
fill: none;
|
||||
stroke: @todayLineColor;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
|
||||
/* Task styling */
|
||||
|
||||
|
||||
/* Default task */
|
||||
|
||||
.task {
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.taskText {
|
||||
text-anchor: middle;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.taskTextOutsideRight {
|
||||
fill: @taskTextDarkColor;
|
||||
text-anchor: start;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.taskTextOutsideLeft {
|
||||
fill: @taskTextDarkColor;
|
||||
text-anchor: end;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
|
||||
/* Specific task settings for the sections*/
|
||||
|
||||
.taskText0,
|
||||
.taskText1,
|
||||
.taskText2,
|
||||
.taskText3 {
|
||||
fill: @taskTextColor;
|
||||
}
|
||||
|
||||
.task0,
|
||||
.task1,
|
||||
.task2,
|
||||
.task3 {
|
||||
fill: @taskBkgColor;
|
||||
stroke: @taskBorderColor;
|
||||
}
|
||||
|
||||
.taskTextOutside0,
|
||||
.taskTextOutside2,
|
||||
{
|
||||
fill: @taskTextOutsideColor;
|
||||
}
|
||||
|
||||
.taskTextOutside1,
|
||||
.taskTextOutside3 {
|
||||
fill: @taskTextOutsideColor;
|
||||
}
|
||||
|
||||
|
||||
/* Active task */
|
||||
|
||||
.active0,
|
||||
.active1,
|
||||
.active2,
|
||||
.active3 {
|
||||
fill: @activeTaskBkgColor;
|
||||
stroke: @activeTaskBorderColor;
|
||||
}
|
||||
|
||||
.activeText0,
|
||||
.activeText1,
|
||||
.activeText2,
|
||||
.activeText3 {
|
||||
fill: @taskTextDarkColor !important;
|
||||
}
|
||||
|
||||
|
||||
/* Completed task */
|
||||
|
||||
.done0,
|
||||
.done1,
|
||||
.done2,
|
||||
.done3 {
|
||||
stroke: @doneTaskBorderColor;
|
||||
fill: @doneTaskBkgColor;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.doneText0,
|
||||
.doneText1,
|
||||
.doneText2,
|
||||
.doneText3 {
|
||||
fill: @taskTextDarkColor !important;
|
||||
}
|
||||
|
||||
|
||||
/* Tasks on the critical line */
|
||||
|
||||
.crit0,
|
||||
.crit1,
|
||||
.crit2,
|
||||
.crit3 {
|
||||
stroke: @critBorderColor;
|
||||
fill: @critBkgColor;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.activeCrit0,
|
||||
.activeCrit1,
|
||||
.activeCrit2,
|
||||
.activeCrit3 {
|
||||
stroke: @critBorderColor;
|
||||
fill: @activeTaskBkgColor;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.doneCrit0,
|
||||
.doneCrit1,
|
||||
.doneCrit2,
|
||||
.doneCrit3 {
|
||||
stroke: @critBorderColor;
|
||||
fill: @doneTaskBkgColor;
|
||||
stroke-width: 2;
|
||||
cursor: pointer;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.doneCritText0,
|
||||
.doneCritText1,
|
||||
.doneCritText2,
|
||||
.doneCritText3 {
|
||||
fill: @taskTextDarkColor !important;
|
||||
}
|
||||
|
||||
.activeCritText0,
|
||||
.activeCritText1,
|
||||
.activeCritText2,
|
||||
.activeCritText3 {
|
||||
fill: @taskTextDarkColor !important;
|
||||
}
|
||||
|
||||
.titleText {
|
||||
text-anchor: middle;
|
||||
font-size: 18px;
|
||||
fill: @taskTextDarkColor;
|
||||
}
|
||||
23
src/less/forest/mermaid.less
Normal file
23
src/less/forest/mermaid.less
Normal file
@@ -0,0 +1,23 @@
|
||||
@import "variables";
|
||||
@import "flow";
|
||||
@import "sequenceDiagram";
|
||||
@import "gantt";
|
||||
@import "classDiagram";
|
||||
.node text {
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
div.mermaidTooltip {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
max-width: 200px;
|
||||
padding: 2px;
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
font-size: 12px;
|
||||
background: @secondBkg;
|
||||
border: 1px solid @border2;
|
||||
border-radius: 2px;
|
||||
pointer-events: none;
|
||||
z-index: 100;
|
||||
}
|
||||
75
src/less/forest/sequenceDiagram.less
Normal file
75
src/less/forest/sequenceDiagram.less
Normal file
@@ -0,0 +1,75 @@
|
||||
.actor {
|
||||
stroke: @actorBorder;
|
||||
fill: @actorBkg;
|
||||
}
|
||||
|
||||
text.actor {
|
||||
fill: @actorTextColor;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.actor-line {
|
||||
stroke: @actorLineColor;
|
||||
}
|
||||
|
||||
.messageLine0 {
|
||||
stroke-width: 1.5;
|
||||
stroke-dasharray: "2 2";
|
||||
marker-end: "url(#arrowhead)";
|
||||
stroke: @signalColor;
|
||||
}
|
||||
|
||||
.messageLine1 {
|
||||
stroke-width: 1.5;
|
||||
stroke-dasharray: "2 2";
|
||||
stroke: @signalColor;
|
||||
}
|
||||
|
||||
#arrowhead {
|
||||
fill: @signalColor;
|
||||
}
|
||||
|
||||
#crosshead path {
|
||||
fill: @signalColor !important;
|
||||
stroke: @signalColor !important;
|
||||
}
|
||||
|
||||
.messageText {
|
||||
fill: @signalTextColor;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.labelBox {
|
||||
stroke: @labelBoxBorderColor;
|
||||
fill: @labelBoxBkgColor;
|
||||
}
|
||||
|
||||
.labelText {
|
||||
fill: @labelTextColor;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.loopText {
|
||||
fill: @labelTextColor;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.loopLine {
|
||||
stroke-width: 2;
|
||||
stroke-dasharray: "2 2";
|
||||
marker-end: "url(#arrowhead)";
|
||||
stroke: @labelBoxBorderColor;
|
||||
}
|
||||
|
||||
.note {
|
||||
//stroke: #decc93;
|
||||
stroke: @noteBorderColor;
|
||||
fill: @noteBkgColor;
|
||||
}
|
||||
|
||||
.noteText {
|
||||
fill: black;
|
||||
stroke: none;
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
font-size: 14px;
|
||||
}
|
||||
52
src/less/forest/variables.less
Normal file
52
src/less/forest/variables.less
Normal file
@@ -0,0 +1,52 @@
|
||||
@mainBkg: #cde498;
|
||||
@secondBkg: #cdffb2;
|
||||
@lineColor: #1a3318;
|
||||
@lineColor: green;
|
||||
@border1: #13540c;
|
||||
@border2: #6eaa49;
|
||||
@arrowheadColor: green;
|
||||
|
||||
/* Flowchart variables */
|
||||
|
||||
@nodeBkg: @mainBkg;
|
||||
@nodeBorder: @border1;
|
||||
@clusterBkg: @secondBkg;
|
||||
@clusterBorder: @border2;
|
||||
@defaultLinkColor: @lineColor;
|
||||
@titleColor: #333;
|
||||
@edgeLabelBackground: #e8e8e8;
|
||||
|
||||
/* Sequence Diagram variables */
|
||||
|
||||
@actorBorder: @border1;
|
||||
@actorBkg: @mainBkg;
|
||||
@actorTextColor: black;
|
||||
@actorLineColor: grey;
|
||||
@signalColor: #333;
|
||||
@signalTextColor: #333;
|
||||
@labelBoxBkgColor: @actorBkg;
|
||||
@labelBoxBorderColor: #326932;
|
||||
@labelTextColor: @actorTextColor;
|
||||
@noteBorderColor: @border2;
|
||||
@noteBkgColor: #fff5ad;
|
||||
|
||||
/* Gantt chart variables */
|
||||
|
||||
@sectionBkgColor: #6eaa49;
|
||||
;
|
||||
@altSectionBkgColor: white;
|
||||
@sectionBkgColor2: #6eaa49;
|
||||
@taskBorderColor: @border1;
|
||||
@taskBkgColor: #487e3a;
|
||||
@taskTextColor: @taskTextLightColor;
|
||||
@taskTextOutsideColor: @taskTextDarkColor;
|
||||
@activeTaskBorderColor: @taskBorderColor;
|
||||
@activeTaskBkgColor: @mainBkg;
|
||||
@gridColor: lightgrey;
|
||||
@doneTaskBkgColor: lightgrey;
|
||||
@doneTaskBorderColor: grey;
|
||||
@critBorderColor: #ff8888;
|
||||
@critBkgColor: red;
|
||||
@taskTextLightColor: white;
|
||||
@taskTextDarkColor: black;
|
||||
@todayLineColor: red;
|
||||
78
src/less/neutral/classDiagram.less
Normal file
78
src/less/neutral/classDiagram.less
Normal file
@@ -0,0 +1,78 @@
|
||||
g.classGroup text {
|
||||
fill: @nodeBorder;
|
||||
stroke: none;
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
g.classGroup rect {
|
||||
fill: @nodeBkg;
|
||||
stroke: @nodeBorder;
|
||||
}
|
||||
|
||||
g.classGroup line {
|
||||
stroke: @nodeBorder;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
svg .classLabel .box {
|
||||
stroke: none;
|
||||
stroke-width: 0;
|
||||
fill: @nodeBkg;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
svg .classLabel .label {
|
||||
fill: @nodeBorder;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.relation {
|
||||
stroke: @nodeBorder;
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.composition {
|
||||
fill: @nodeBorder;
|
||||
stroke: @nodeBorder;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
#compositionStart {
|
||||
.composition;
|
||||
}
|
||||
|
||||
#compositionEnd {
|
||||
.composition;
|
||||
}
|
||||
|
||||
.aggregation {
|
||||
fill: @nodeBkg;
|
||||
stroke: @nodeBorder;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
#aggregationStart {
|
||||
.aggregation;
|
||||
}
|
||||
|
||||
#aggregationEnd {
|
||||
.aggregation;
|
||||
}
|
||||
|
||||
#dependencyStart {
|
||||
.composition;
|
||||
}
|
||||
|
||||
#dependencyEnd {
|
||||
.composition;
|
||||
}
|
||||
|
||||
#extensionStart {
|
||||
.composition;
|
||||
}
|
||||
|
||||
#extensionEnd {
|
||||
.composition;
|
||||
}
|
||||
32
src/less/neutral/flow.less
Normal file
32
src/less/neutral/flow.less
Normal file
@@ -0,0 +1,32 @@
|
||||
.mermaid .label {
|
||||
color: @text
|
||||
}
|
||||
|
||||
.node rect,
|
||||
.node circle,
|
||||
.node ellipse,
|
||||
.node polygon {
|
||||
fill: @mainBkg;
|
||||
stroke: @nodeBorder;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.edgePath .path {
|
||||
stroke: @lineColor;
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
|
||||
.edgeLabel {
|
||||
background-color: @edgeLabelBackground;
|
||||
}
|
||||
|
||||
.cluster rect {
|
||||
fill: @secondBkg !important;
|
||||
rx: 4 !important;
|
||||
stroke: @clusterBorder !important;
|
||||
stroke-width: 1px !important;
|
||||
}
|
||||
|
||||
.cluster text {
|
||||
fill: @titleColor;
|
||||
}
|
||||
208
src/less/neutral/gantt.less
Normal file
208
src/less/neutral/gantt.less
Normal file
@@ -0,0 +1,208 @@
|
||||
/** Section styling */
|
||||
|
||||
.section {
|
||||
stroke: none;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.section0 {
|
||||
fill: @sectionBkgColor;
|
||||
}
|
||||
|
||||
.section2 {
|
||||
fill: @sectionBkgColor2;
|
||||
}
|
||||
|
||||
.section1,
|
||||
.section3 {
|
||||
fill: @altSectionBkgColor;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.sectionTitle0 {
|
||||
fill: @titleColor;
|
||||
}
|
||||
|
||||
.sectionTitle1 {
|
||||
fill: @titleColor;
|
||||
}
|
||||
|
||||
.sectionTitle2 {
|
||||
fill: @titleColor;
|
||||
}
|
||||
|
||||
.sectionTitle3 {
|
||||
fill: @titleColor;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
text-anchor: start;
|
||||
font-size: 11px;
|
||||
text-height: 14px;
|
||||
}
|
||||
|
||||
|
||||
/* Grid and axis */
|
||||
|
||||
.grid .tick {
|
||||
stroke: @gridColor;
|
||||
opacity: 0.3;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.grid path {
|
||||
stroke-width: 0;
|
||||
}
|
||||
|
||||
|
||||
/* Today line */
|
||||
|
||||
.today {
|
||||
fill: none;
|
||||
stroke: @todayLineColor;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
|
||||
/* Task styling */
|
||||
|
||||
|
||||
/* Default task */
|
||||
|
||||
.task {
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.taskText {
|
||||
text-anchor: middle;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.taskTextOutsideRight {
|
||||
fill: @taskTextDarkColor;
|
||||
text-anchor: start;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.taskTextOutsideLeft {
|
||||
fill: @taskTextDarkColor;
|
||||
text-anchor: end;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
|
||||
/* Specific task settings for the sections*/
|
||||
|
||||
.taskText0,
|
||||
.taskText1,
|
||||
.taskText2,
|
||||
.taskText3 {
|
||||
fill: @taskTextColor;
|
||||
}
|
||||
|
||||
.task0,
|
||||
.task1,
|
||||
.task2,
|
||||
.task3 {
|
||||
fill: @taskBkgColor;
|
||||
stroke: @taskBorderColor;
|
||||
}
|
||||
|
||||
.taskTextOutside0,
|
||||
.taskTextOutside2,
|
||||
{
|
||||
fill: @taskTextOutsideColor;
|
||||
}
|
||||
|
||||
.taskTextOutside1,
|
||||
.taskTextOutside3 {
|
||||
fill: @taskTextOutsideColor;
|
||||
}
|
||||
|
||||
|
||||
/* Active task */
|
||||
|
||||
.active0,
|
||||
.active1,
|
||||
.active2,
|
||||
.active3 {
|
||||
fill: @activeTaskBkgColor;
|
||||
stroke: @activeTaskBorderColor;
|
||||
}
|
||||
|
||||
.activeText0,
|
||||
.activeText1,
|
||||
.activeText2,
|
||||
.activeText3 {
|
||||
fill: @taskTextDarkColor !important;
|
||||
}
|
||||
|
||||
|
||||
/* Completed task */
|
||||
|
||||
.done0,
|
||||
.done1,
|
||||
.done2,
|
||||
.done3 {
|
||||
stroke: @doneTaskBorderColor;
|
||||
fill: @doneTaskBkgColor;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.doneText0,
|
||||
.doneText1,
|
||||
.doneText2,
|
||||
.doneText3 {
|
||||
fill: @taskTextDarkColor !important;
|
||||
}
|
||||
|
||||
|
||||
/* Tasks on the critical line */
|
||||
|
||||
.crit0,
|
||||
.crit1,
|
||||
.crit2,
|
||||
.crit3 {
|
||||
stroke: @critBorderColor;
|
||||
fill: @critBkgColor;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.activeCrit0,
|
||||
.activeCrit1,
|
||||
.activeCrit2,
|
||||
.activeCrit3 {
|
||||
stroke: @critBorderColor;
|
||||
fill: @activeTaskBkgColor;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.doneCrit0,
|
||||
.doneCrit1,
|
||||
.doneCrit2,
|
||||
.doneCrit3 {
|
||||
stroke: @critBorderColor;
|
||||
fill: @doneTaskBkgColor;
|
||||
stroke-width: 2;
|
||||
cursor: pointer; //shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.doneCritText0,
|
||||
.doneCritText1,
|
||||
.doneCritText2,
|
||||
.doneCritText3 {
|
||||
fill: @taskTextDarkColor !important;
|
||||
}
|
||||
|
||||
.activeCritText0,
|
||||
.activeCritText1,
|
||||
.activeCritText2,
|
||||
.activeCritText3 {
|
||||
fill: @taskTextDarkColor !important;
|
||||
}
|
||||
|
||||
.titleText {
|
||||
text-anchor: middle;
|
||||
font-size: 18px;
|
||||
fill: @taskTextDarkColor;
|
||||
}
|
||||
23
src/less/neutral/mermaid.less
Normal file
23
src/less/neutral/mermaid.less
Normal file
@@ -0,0 +1,23 @@
|
||||
@import "variables";
|
||||
@import "flow";
|
||||
@import "sequenceDiagram";
|
||||
@import "gantt";
|
||||
@import "classDiagram";
|
||||
.node text {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
div.mermaidTooltip {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
max-width: 200px;
|
||||
padding: 2px;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 12px;
|
||||
background: @secondBkg;
|
||||
border: 1px solid @border2;
|
||||
border-radius: 2px;
|
||||
pointer-events: none;
|
||||
z-index: 100;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user