Merge branch 'develop' into feature/1460_add_link_target_to_flowchart_click

This commit is contained in:
Knut Sveidqvist 2020-07-28 13:21:31 +02:00 committed by GitHub
commit cf70678188
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 1627 additions and 903 deletions

View File

@ -224,6 +224,7 @@ erDiagram
it('should render a user journey diagram', () => {
imgSnapshotTest(
`
%%{init: { 'logLevel': 0, 'theme': '${theme}'} }%%
journey
title My working day
section Go to work
@ -232,7 +233,8 @@ erDiagram
Do work: 1: Me, Cat
section Go home
Go downstairs: 5: Me
Sit down: 5: Me `,
Sit down: 5: Me
`,
{theme}
);
cy.get('svg');

880
dist/mermaid.core.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

880
dist/mermaid.js vendored

File diff suppressed because one or more lines are too long

2
dist/mermaid.js.map vendored

File diff suppressed because one or more lines are too long

10
dist/mermaid.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -526,7 +526,7 @@ This might need adjustment to match your locale and preferences
## journey
The object containing configurations specific for sequence diagrams
The object containing configurations specific for journey diagrams
### diagramMarginX

View File

@ -593,7 +593,7 @@ const config = {
axisFormat: '%Y-%m-%d'
},
/**
* The object containing configurations specific for sequence diagrams
* The object containing configurations specific for journey diagrams
*/
journey: {
/**

View File

@ -113,8 +113,8 @@ Required edgeData for proper rendering:
| label | overlap between label and labelText? |
| labelPos | |
| labelType | overlap between label and labelText? |
| thickness | Sets the thinkess of the edge. Can be ['normal', 'thick'] |
| pattern | Sets the pattern of the edge. Can be ['solid', 'dotted', 'dashed'] |
| thickness | Sets the thinkess of the edge. Can be \['normal', 'thick'\] |
| pattern | Sets the pattern of the edge. Can be \['solid', 'dotted', 'dashed'\] |
# Markers
@ -122,7 +122,7 @@ Required edgeData for proper rendering:
Define what markers that should be included in the diagram with the insert markers function. The function takes two arguments, first the element in which the markers should be included and a list of the markers that should be added.
Ex:
insertMarkers(el, ['point', 'circle'])
insertMarkers(el, \['point', 'circle'\])
The example above adds the markers point and cross. This means that edges with the arrowTypes arrow_cross, double_arrow_cross, arrow_point and double_arrow_cross will get the corresponding markers but arrowType arrow_cross will have no impact.
@ -136,4 +136,4 @@ Current markers:
# Common functions used by the renderer to be implemented by the Db
getDirection
getClasses
getClasses

View File

@ -240,7 +240,7 @@ const barb = (elem, type) => {
.attr('refY', 7)
.attr('markerWidth', 20)
.attr('markerHeight', 14)
.attr('markerUnits', 0)
.attr('markerUnits', 'strokeWidth')
.attr('orient', 'auto')
.append('path')
.attr('d', 'M 19,7 L9,13 L14,7 L9,1 Z');

View File

@ -1,8 +1,9 @@
import { select } from 'd3';
import { logger } from '../../logger';
import { getConfig } from '../../config';
import configApi, { getConfig } from '../../config';
import common from '../common/common';
import utils from '../../utils';
import mermaidAPI from '../../mermaidAPI';
const MERMAID_DOM_ID_PREFIX = 'classid-';
@ -14,6 +15,10 @@ let classCounter = 0;
let funs = [];
export const parseDirective = function(statement, context, type) {
mermaidAPI.parseDirective(this, statement, context, type);
};
const splitClassNameAndType = function(id) {
let genericType = '';
let className = id;
@ -288,6 +293,8 @@ const setupToolTips = function(element) {
funs.push(setupToolTips);
export default {
parseDirective,
getConfig: () => configApi.getConfig().class,
addClass,
bindFunctions,
clear,

View File

@ -242,69 +242,74 @@ describe('class diagram, ', function () {
it('should handle comments at the start', function () {
const str =
'%% Comment\n' +
'classDiagram\n' +
'class Class1 {\n' +
'int : test\n' +
'string : foo\n' +
'test()\n' +
'foo()\n' +
'}';
`%% Comment
classDiagram
class Class1 {
int : test
string : foo
test()
foo()
}`;
parser.parse(str);
});
it('should handle comments at the end', function () {
const str =
'classDiagram\n' +
'class Class1 {\n' +
'int : test\n' +
'string : foo\n' +
'test()\n' +
'foo()\n' +
'\n}' +
'%% Comment\n';
`classDiagram
class Class1 {
int : test
string : foo
test()
foo()
}
%% Comment
`;
parser.parse(str);
});
it('should handle comments at the end no trailing newline', function () {
const str =
'classDiagram\n' +
'class Class1 {\n' +
'int : test\n' +
'string : foo\n' +
'test()\n' +
'foo()\n' +
'}\n' +
'%% Comment';
`classDiagram
class Class1 {
int : test
string : foo
test()
foo()
}
%% Comment`;
parser.parse(str);
});
it('should handle a comment with multiple line feeds', function () {
const str =
'classDiagram\n\n\n' +
'%% Comment\n\n' +
'class Class1 {\n' +
'int : test\n' +
'string : foo\n' +
'test()\n' +
'foo()\n' +
'}';
`classDiagram
%% Comment
class Class1 {
int : test
string : foo
test()
foo()
}`;
parser.parse(str);
});
it('should handle a comment with mermaid class diagram code in them', function () {
const str =
'classDiagram\n' +
'%% Comment Class01 <|-- Class02\n' +
'class Class1 {\n' +
'int : test\n' +
'string : foo\n' +
'test()\n' +
'foo()\n' +
'}';
`classDiagram
%% Comment Class01 <|-- Class02
class Class1 {
int : test
string : foo
test()
foo()
}`;
parser.parse(str);
});
@ -640,7 +645,7 @@ describe('class diagram, ', function () {
expect(testClass.cssClasses.length).toBe(1);
expect(testClass.cssClasses[0]).toBe('clickable');
});
it('should associate link with tooltip', function () {
const str = 'classDiagram\n' + 'class Class1\n' + 'Class1 : someMethod()\n' + 'link Class1 "google.com" "A tooltip"';
parser.parse(str);

View File

@ -6,25 +6,31 @@
/* lexical grammar */
%lex
%x string generic struct
%x string generic struct open_directive type_directive arg_directive
%%
\%\%[^\n]*\n* /* do nothing */
\n+ return 'NEWLINE';
\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
<open_directive>((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
\%\%(?!\{)*[^\n]*(\r?\n)+ /* skip comments */
\%\%[^\n]*(\r?\n)* /* skip comments */
(\r?\n)+ return 'NEWLINE';
\s+ /* skip whitespace */
"classDiagram-v2" return 'CLASS_DIAGRAM';
"classDiagram" return 'CLASS_DIAGRAM';
[\{] { this.begin("struct"); /*console.log('Starting struct');*/return 'STRUCT_START';}
[{] { this.begin("struct"); /*console.log('Starting struct');*/ return 'STRUCT_START';}
<struct><<EOF>> return "EOF_IN_STRUCT";
<struct>[\{] return "OPEN_IN_STRUCT";
<struct>\} { /*console.log('Ending struct');*/this.popState(); return 'STRUCT_STOP';}}
<struct>[\n] /* nothing */
<struct>[^\{\}\n]* { /*console.log('lex-member: ' + yytext);*/ return "MEMBER";}
<struct>[{] return "OPEN_IN_STRUCT";
<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';
//"click" return 'CLICK';
//"click" return 'CLICK';
"callback" return 'CALLBACK';
"link" return 'LINK';
"<<" return 'ANNOTATION_START';
@ -126,11 +132,39 @@
%left '^'
%start mermaidDoc
%start start
%% /* language grammar */
mermaidDoc: graphConfig;
start
: mermaidDoc
| directive start
;
mermaidDoc
: graphConfig
;
directive
: openDirective typeDirective closeDirective NEWLINE
| openDirective typeDirective ':' argDirective closeDirective NEWLINE
;
openDirective
: open_directive { yy.parseDirective('%%{', 'open_directive'); }
;
typeDirective
: type_directive { yy.parseDirective($1, 'type_directive'); }
;
argDirective
: arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
;
closeDirective
: close_directive { yy.parseDirective('}%%', 'close_directive', 'class'); }
;
graphConfig
: CLASS_DIAGRAM NEWLINE statements EOF
@ -156,6 +190,7 @@ statement
| methodStatement
| annotationStatement
| clickStatement
| directive
;
classStatement

View File

@ -35,13 +35,14 @@ export const sanitizeText = (text, config) => {
if (
config.flowchart &&
(config.flowchart.htmlLabels === false || config.flowchart.htmlLabels === 'false')
)
) {
htmlLabels = false;
}
if (htmlLabels) {
var level = config.securityLevel;
const level = config.securityLevel;
if (level == 'antiscript') {
if (level === 'antiscript') {
txt = removeScript(txt);
} else if (level !== 'loose') {
// eslint-disable-line

View File

@ -2,6 +2,8 @@
*
*/
import { logger } from '../../logger';
import mermaidAPI from '../../mermaidAPI';
import configApi from '../../config';
let entities = {};
let relationships = [];
@ -19,6 +21,10 @@ const Identification = {
IDENTIFYING: 'IDENTIFYING'
};
export const parseDirective = function(statement, context, type) {
mermaidAPI.parseDirective(this, statement, context, type);
};
const addEntity = function(name) {
if (typeof entities[name] === 'undefined') {
entities[name] = name;
@ -67,6 +73,8 @@ const clear = function() {
export default {
Cardinality,
Identification,
parseDirective,
getConfig: () => configApi.getConfig().er,
addEntity,
getEntities,
addRelationship,

View File

@ -1,8 +1,17 @@
%lex
%options case-insensitive
%x open_directive type_directive arg_directive
%%
\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
<open_directive>((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
\%%(?!\{)[^\n]* /* skip comments */
[^\}]\%\%[^\n]* /* skip comments */
[\n]+ return 'NEWLINE';
\s+ /* skip whitespace */
[\s]+ return 'SPACE';
\"[^"]*\" return 'WORD';
@ -29,21 +38,36 @@ o\{ return 'ZERO_OR_MORE';
start
: 'ER_DIAGRAM' document 'EOF' { /*console.log('finished parsing');*/ }
| directive start
;
document
: /* empty */
| document statement
;
: /* empty */ { $$ = [] }
| document line {$1.push($2);$$ = $1}
;
line
: SPACE statement { $$ = $2 }
| statement { $$ = $1 }
| NEWLINE { $$=[];}
| EOF { $$=[];}
;
directive
: openDirective typeDirective closeDirective 'NEWLINE'
| openDirective typeDirective ':' argDirective closeDirective 'NEWLINE'
;
statement
: entityName relSpec entityName ':' role
: directive
| entityName relSpec entityName ':' role
{
yy.addEntity($1);
yy.addEntity($3);
yy.addEntity($1);
yy.addEntity($3);
yy.addRelationship($1, $5, $3, $2);
/*console.log($1 + $2 + $3 + ':' + $5);*/
};
}
;
entityName
: 'ALPHANUM' { $$ = $1; /*console.log('Entity: ' + $1);*/ }
@ -62,7 +86,7 @@ cardinality
| 'ZERO_OR_MORE' { $$ = yy.Cardinality.ZERO_OR_MORE; }
| 'ONE_OR_MORE' { $$ = yy.Cardinality.ONE_OR_MORE; }
| 'ONLY_ONE' { $$ = yy.Cardinality.ONLY_ONE; }
;
;
relType
: 'NON_IDENTIFYING' { $$ = yy.Identification.NON_IDENTIFYING; }
@ -73,4 +97,21 @@ role
: 'WORD' { $$ = $1.replace(/"/g, ''); }
| 'ALPHANUM' { $$ = $1; }
;
openDirective
: open_directive { yy.parseDirective('%%{', 'open_directive'); }
;
typeDirective
: type_directive { yy.parseDirective($1, 'type_directive'); }
;
argDirective
: arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
;
closeDirective
: close_directive { yy.parseDirective('}%%', 'close_directive', 'er'); }
;
%%

View File

@ -1,8 +1,8 @@
import { select } from 'd3';
import { logger } from '../../logger'; // eslint-disable-line
import utils from '../../utils';
import { getConfig } from '../../config';
import configApi, { getConfig } from '../../config';
import common from '../common/common';
import mermaidAPI from '../../mermaidAPI';
// const MERMAID_DOM_ID_PREFIX = 'mermaid-dom-id-';
const MERMAID_DOM_ID_PREFIX = '';
@ -20,6 +20,10 @@ let direction;
// Functions to be run after graph rendering
let funs = [];
export const parseDirective = function(statement, context, type) {
mermaidAPI.parseDirective(this, statement, context, type);
};
/**
* Function called by parser when a node definition has been found
* @param id
@ -618,6 +622,8 @@ const destructLink = (_str, _startStr) => {
};
export default {
parseDirective,
getConfig: () => configApi.getConfig().flowchart,
addVertex,
addLink,
updateLinkInterpolate,

View File

@ -9,8 +9,19 @@
%x string
%x dir
%x vertex
%x open_directive
%x type_directive
%x arg_directive
%x close_directive
%%
\%\%[^\n]*\n* /* do nothing */
\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
<open_directive>((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
\%\%(?!\{)[^\n]* /* skip comments */
[^\}]\%\%[^\n]* /* skip comments */
["] this.begin("string");
<string>["] this.popState();
<string>[^"]* return "STR";
@ -25,10 +36,13 @@
"flowchart" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';}
"subgraph" return 'subgraph';
"end"\b\s* return 'end';
"_self" return 'LINK_TARGET';
"_blank" return 'LINK_TARGET';
"_parent" return 'LINK_TARGET';
"_top" return 'LINK_TARGET';
<dir>(\r?\n)*\s*\n { this.popState(); return 'NODIR'; }
<dir>\s*"LR" { this.popState(); return 'DIR'; }
<dir>\s*"RL" { this.popState(); return 'DIR'; }
<dir>\s*"TB" { this.popState(); return 'DIR'; }
@ -183,7 +197,7 @@
"{" return 'DIAMOND_START'
"}" return 'DIAMOND_STOP'
"\"" return 'QUOTE';
(\r|\n|\r\n)+ return 'NEWLINE';
(\r?\n)+ return 'NEWLINE';
\s return 'SPACE';
<<EOF>> return 'EOF';
@ -193,11 +207,39 @@
%left '^'
%start mermaidDoc
%start start
%% /* language grammar */
mermaidDoc: graphConfig document;
start
: mermaidDoc
| directive start
;
directive
: openDirective typeDirective closeDirective separator
| openDirective typeDirective ':' argDirective closeDirective separator
;
openDirective
: open_directive { yy.parseDirective('%%{', 'open_directive'); }
;
typeDirective
: type_directive { yy.parseDirective($1, 'type_directive'); }
;
argDirective
: arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
;
closeDirective
: close_directive { yy.parseDirective('}%%', 'close_directive', 'flowchart'); }
;
mermaidDoc
: graphConfig document
;
document
: /* empty */
@ -222,6 +264,8 @@ line
graphConfig
: SPACE graphConfig
| NEWLINE graphConfig
| GRAPH NODIR
{ yy.setDirection('TB');$$ = 'TB';}
| GRAPH DIR FirstStmtSeperator
{ yy.setDirection($2);$$ = $2;}
// | GRAPH SPACE TAGEND FirstStmtSeperator

View File

@ -1,8 +1,9 @@
import moment from 'moment-mini';
import { sanitizeUrl } from '@braintree/sanitize-url';
import { logger } from '../../logger';
import { getConfig } from '../../config';
import configApi, { getConfig } from '../../config';
import utils from '../../utils';
import mermaidAPI from '../../mermaidAPI';
let dateFormat = '';
let axisFormat = '';
@ -19,6 +20,10 @@ let inclusiveEndDates = false;
// The serial order of the task in the script
let lastOrder = 0;
export const parseDirective = function(statement, context, type) {
mermaidAPI.parseDirective(this, statement, context, type);
};
export const clear = function() {
sections = [];
tasks = [];
@ -582,6 +587,8 @@ export const bindFunctions = function(element) {
};
export default {
parseDirective,
getConfig: () => configApi.getConfig().gantt,
clear,
setDateFormat,
getDateFormat,

View File

@ -11,7 +11,19 @@
%x href
%x callbackname
%x callbackargs
%x open_directive
%x type_directive
%x arg_directive
%x close_directive
%%
\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
<open_directive>((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
\%\%(?!\{)*[^\n]* /* skip comments */
[^\}]\%\%*[^\n]* /* skip comments */
\%\%*[^\n]*[\n]* /* do nothing */
[\n]+ return 'NL';
\s+ /* skip whitespace */
@ -77,7 +89,8 @@ that id.
%% /* language grammar */
start
: gantt document 'EOF' { return $2; }
: directive start
| gantt document 'EOF' { return $2; }
;
document
@ -102,8 +115,14 @@ statement
| section {yy.addSection($1.substr(8));$$=$1.substr(8);}
| clickStatement
| taskTxt taskData {yy.addTask($1,$2);$$='task';}
| directive
;
directive
: openDirective typeDirective closeDirective 'NL'
| openDirective typeDirective ':' argDirective closeDirective 'NL'
;
/*
click allows any combination of href and call.
*/
@ -131,4 +150,22 @@ clickStatementDebug
| click href callbackname callbackargs {$$=$1 + ' ' + $2 + ' ' + $3 + ' ' + $4;}
| click href {$$=$1 + ' ' + $2;}
;%%
;
openDirective
: open_directive { yy.parseDirective('%%{', 'open_directive'); }
;
typeDirective
: type_directive { yy.parseDirective($1, 'type_directive'); }
;
argDirective
: arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
;
closeDirective
: close_directive { yy.parseDirective('}%%', 'close_directive', 'gantt'); }
;
%%

View File

@ -4,26 +4,34 @@
* MIT license.
*/
%lex
%x string
%options case-insensitive
%{
// Pre-lexer code can go here
%}
%x string
%x title
%x open_directive
%x type_directive
%x arg_directive
%x close_directive
%%
\%\%[^\n]* /* do nothing */
\s+ /* skip whitespace */
"pie" return 'pie' ;
[\s\n\r]+ return 'NL' ;
[\s]+ return 'space';
"title"\s[^#\n;]+ return 'title';
["] {/*console.log('begin str');*/this.begin("string");}
<string>["] {/*console.log('pop-state');*/this.popState();}
<string>[^"]* {/*console.log('ending string')*/return "STR";}
":"[\s]*[\d]+(?:\.[\d]+)? return "VALUE";
<<EOF>> return 'EOF' ;
\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
<open_directive>((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
\%\%(?!\{)[^\n]* /* skip comments */
[^\}]\%\%[^\n]* /* skip comments */{ console.log('Crap after close'); }
[\n\r]+ return 'NEWLINE';
\%\%[^\n]* /* do nothing */
[\s]+ /* ignore */
title { this.begin("title");return 'title'; }
<title>(?!\n|;|#)*[^\n]* { this.popState(); return "title_value"; }
["] { this.begin("string"); }
<string>["] { this.popState(); }
<string>[^"]* { return "txt"; }
"pie" return 'PIE';
":"[\s]*[\d]+(?:\.[\d]+)? return "value";
<<EOF>> return 'EOF';
/lex
@ -33,8 +41,9 @@
%% /* language grammar */
start
// %{ : info document 'EOF' { return yy; } }
: pie document 'EOF'
: eol start
| directive start
| PIE document
;
document
@ -43,15 +52,41 @@ document
;
line
: statement { }
| 'NL'
: statement eol { $$ = $1 }
;
statement
: STR VALUE {
/*console.log('str:'+$1+' value: '+$2)*/
yy.addSection($1,yy.cleanupValue($2)); }
| title {yy.setTitle($1.substr(6));$$=$1.substr(6);}
:
| txt value { yy.addSection($1,yy.cleanupValue($2)); }
| title title_value { $$=$2.trim();yy.setTitle($$); }
| directive
;
directive
: openDirective typeDirective closeDirective
| openDirective typeDirective ':' argDirective closeDirective
;
eol
: NEWLINE
| ';'
| EOF
;
openDirective
: open_directive { yy.parseDirective('%%{', 'open_directive'); }
;
typeDirective
: type_directive { yy.parseDirective($1, 'type_directive'); }
;
argDirective
: arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
;
closeDirective
: close_directive { yy.parseDirective('}%%', 'close_directive', 'pie'); }
;
%%

View File

@ -12,16 +12,67 @@ describe('when parsing pie', function() {
pie.parser.yy = pieDb;
pie.parser.yy.clear();
});
it('should handle very simple pie', function() {
const res = pie.parser.parse(`pie
"ash" : 100
`);
const sections = pieDb.getSections();
console.log('sections: ', sections);
const section1 = sections['ash'];
expect(section1).toBe(100);
});
it('should handle simple pie', function() {
const res = pie.parser.parse('pie \n"ash" : 60\n"bat" : 40\n');
const res = pie.parser.parse(`pie
"ash" : 60
"bat" : 40
`);
const sections = pieDb.getSections();
console.log('sections: ', sections);
const section1 = sections['ash'];
expect(section1).toBe(60);
});
it('should handle simple pie with comments', function() {
const res = pie.parser.parse(`pie
%% comments
"ash" : 60
"bat" : 40
`);
const sections = pieDb.getSections();
console.log('sections: ', sections);
const section1 = sections['ash'];
expect(section1).toBe(60);
});
it('should handle simple pie with a directive', function() {
const res = pie.parser.parse(`%%{init: {'logLevel':0}}%%
pie
"ash" : 60
"bat" : 40
`);
const sections = pieDb.getSections();
console.log('sections: ', sections);
const section1 = sections['ash'];
expect(section1).toBe(60);
});
it('should handle simple pie with a title', function() {
const res = pie.parser.parse(`pie title a 60/40 pie
"ash" : 60
"bat" : 40
`);
const sections = pieDb.getSections();
const title = pieDb.getTitle();
console.log('sections: ', sections);
const section1 = sections['ash'];
expect(section1).toBe(60);
expect(title).toBe('a 60/40 pie');
});
it('should handle simple pie with positive decimal', function() {
const res = pie.parser.parse('pie \n"ash" : 60.67\n"bat" : 40\n');
const res = pie.parser.parse(`pie
"ash" : 60.67
"bat" : 40
`);
const sections = pieDb.getSections();
console.log('sections: ', sections);
const section1 = sections['ash'];
@ -30,7 +81,10 @@ describe('when parsing pie', function() {
it('should handle simple pie with negative decimal', function() {
expect(() => {
pie.parser.parse('pie \n"ash" : 60.67\n"bat" : 40..12\n');
pie.parser.parse(`pie
"ash" : 60.67
"bat" : 40..12
`);
}).toThrowError();
});
});

View File

@ -2,10 +2,16 @@
*
*/
import { logger } from '../../logger';
import mermaidAPI from '../../mermaidAPI';
import configApi from '../../config';
let sections = {};
let title = '';
export const parseDirective = function(statement, context, type) {
mermaidAPI.parseDirective(this, statement, context, type);
};
const addSection = function(id, value) {
if (typeof sections[id] === 'undefined') {
sections[id] = value;
@ -39,6 +45,8 @@ const clear = function() {
// }
export default {
parseDirective,
getConfig: () => configApi.getConfig().pie,
addSection,
getSections,
cleanupValue,

View File

@ -153,7 +153,7 @@ export const draw = (txt, id) => {
});
} catch (e) {
logger.error('Error while rendering info diagram');
logger.error(e.message);
logger.error(e);
}
};

View File

@ -13,34 +13,29 @@
%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 ID ALIAS LINE
// Directive states
%x OPEN_DIRECTIVE
%x TYPE_DIRECTIVE
%x ARG_DIRECTIVE
// A special state for grabbing text up to the first comment/newline
%x LINE
%x open_directive type_directive arg_directive
%%
\%\%\{ { this.begin('OPEN_DIRECTIVE'); return 'open_directive'; }
<OPEN_DIRECTIVE>((?:(?!\}\%\%)[^:.])*) { this.begin('TYPE_DIRECTIVE'); return 'type_directive'; }
<TYPE_DIRECTIVE>":" { this.popState(); this.begin('ARG_DIRECTIVE'); return ':'; }
<TYPE_DIRECTIVE,ARG_DIRECTIVE>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
<ARG_DIRECTIVE>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
[\n]+ return 'NL';
\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
<open_directive>((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
[\n]+ return 'NEWLINE';
\s+ /* skip all whitespace */
<ID,ALIAS,LINE>((?!\n)\s)+ /* skip same-line whitespace */
<INITIAL,ID,ALIAS,LINE,ARG_DIRECTIVE,TYPE_DIRECTIVE,OPEN_DIRECTIVE>\#[^\n]* /* skip comments */
<INITIAL,ID,ALIAS,LINE,arg_directive,type_directive,open_directive>\#[^\n]* /* skip comments */
\%%(?!\{)[^\n]* /* skip comments */
[^\}]\%\%[^\n]* /* skip comments */
"participant" { this.begin('ID'); return 'participant'; }
<ID>[^\->:\n,;]+?(?=((?!\n)\s)+"as"(?!\n)\s|[#\n;]|$) { yytext = yytext.trim(); this.begin('ALIAS'); return 'ACTOR'; }
<ALIAS>"as" { this.popState(); this.popState(); this.begin('LINE'); return 'AS'; }
<ALIAS>(?:) { this.popState(); this.popState(); return 'NL'; }
<ALIAS>(?:) { this.popState(); this.popState(); return 'NEWLINE'; }
"loop" { this.begin('LINE'); return 'loop'; }
"rect" { this.begin('LINE'); return 'rect'; }
"opt" { this.begin('LINE'); return 'opt'; }
@ -58,10 +53,10 @@
"deactivate" { this.begin('ID'); return 'deactivate'; }
"title" return 'title';
"sequenceDiagram" return 'SD';
"autonumber" return 'autonumber';
"autonumber" return 'autonumber';
"," return ',';
";" return 'NL';
[^\+\->:\n,;]+ { yytext = yytext.trim(); return 'ACTOR'; }
";" return 'NEWLINE';
[^\+\->:\n,;]+((?!(\-x|\-\-x))[\-]*[^\+\->:\n,;]+)* { yytext = yytext.trim(); return 'ACTOR'; }
"->>" return 'SOLID_ARROW';
"-->>" return 'DOTTED_ARROW';
"->" return 'SOLID_OPEN_ARROW';
@ -71,7 +66,7 @@
":"(?:(?:no)?wrap:)?[^#\n;]+ return 'TXT';
"+" return '+';
"-" return '-';
<<EOF>> return 'NL';
<<EOF>> return 'NEWLINE';
. return 'INVALID';
/lex
@ -84,7 +79,7 @@
start
: SPACE start
| NL start
| NEWLINE start
| directive start
| SD document { yy.apply($2);return $2; }
;
@ -97,23 +92,23 @@ document
line
: SPACE statement { $$ = $2 }
| statement { $$ = $1 }
| NL { $$=[]; }
| NEWLINE { $$=[]; }
;
directive
: openDirective typeDirective closeDirective 'NL'
| openDirective typeDirective ':' argDirective closeDirective 'NL'
: openDirective typeDirective closeDirective 'NEWLINE'
| openDirective typeDirective ':' argDirective closeDirective 'NEWLINE'
;
statement
: 'participant' actor 'AS' restOfLine 'NL' {$2.description=yy.parseMessage($4); $$=$2;}
| 'participant' actor 'NL' {$$=$2;}
| signal 'NL'
: 'participant' actor 'AS' restOfLine 'NEWLINE' {$2.description=yy.parseMessage($4); $$=$2;}
| 'participant' actor 'NEWLINE' {$$=$2;}
| signal 'NEWLINE'
| autonumber {yy.enableSequenceNumbers()}
| '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}]}
| 'activate' actor 'NEWLINE' {$$={type: 'activeStart', signalType: yy.LINETYPE.ACTIVE_START, actor: $2};}
| 'deactivate' actor 'NEWLINE' {$$={type: 'activeEnd', signalType: yy.LINETYPE.ACTIVE_END, actor: $2};}
| note_statement 'NEWLINE'
| title text2 'NEWLINE' {$$=[{type:'setTitle', text:$2}]}
| 'loop' restOfLine document end
{
$3.unshift({type: 'loopStart', loopText:yy.parseMessage($2), signalType: yy.LINETYPE.LOOP_START});

View File

@ -13,7 +13,7 @@ let sequenceNumbersEnabled = false;
let wrapEnabled = false;
export const parseDirective = function(statement, context, type) {
mermaidAPI.parseDirective(statement, context, type);
mermaidAPI.parseDirective(this, statement, context, type);
};
export const addActor = function(id, name, description) {
@ -140,12 +140,12 @@ export const parseMessage = function(str) {
text: _str.replace(/^[:]?(?:no)?wrap:/, '').trim(),
wrap:
_str.match(/^[:]?(?:no)?wrap:/) === null
? common.hasBreaks(_str) || autoWrap()
? common.hasBreaks(_str) || undefined
: _str.match(/^[:]?wrap:/) !== null
? true
: _str.match(/^[:]?nowrap:/) !== null
? false
: autoWrap()
: undefined
};
logger.debug('parseMessage:', message);
return message;

View File

@ -93,6 +93,23 @@ Bob-->Alice: I am good thanks!`;
expect(messages[0].from).toBe('Alice');
expect(messages[1].from).toBe('Bob');
});
it('it should handle dashes in actor names', function() {
const str = `
sequenceDiagram
Alice-in-Wonderland->Bob:Hello Bob, how are - you?
Bob-->Alice-in-Wonderland:I am good thanks!`;
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors["Alice-in-Wonderland"].description).toBe('Alice-in-Wonderland');
actors.Bob.description = 'Bob';
const messages = parser.yy.getMessages();
expect(messages.length).toBe(2);
expect(messages[0].from).toBe('Alice-in-Wonderland');
expect(messages[1].from).toBe('Bob');
});
it('it should alias participants', function() {
const str = `
sequenceDiagram
@ -932,7 +949,6 @@ describe('when rendering a sequenceDiagram', function() {
parser.yy = sequenceDb;
parser.yy.clear();
conf = parser.yy.getConfig();
renderer.bounds.init();
});
['tspan', 'fo', 'old', undefined].forEach(function(textPlacement) {
it(`

View File

@ -26,16 +26,27 @@
%x FLOATING_NOTE
%x FLOATING_NOTE_ID
%x struct
%x open_directive
%x type_directive
%x arg_directive
%x close_directive
// A special state for grabbing text up to the first comment/newline
%x LINE
%%
\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
<open_directive>((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
\%\%(?!\{)[^\n]* /* skip comments */
[^\}]\%\%[^\n]* /* skip comments */{ console.log('Crap after close'); }
[\n]+ return 'NL';
\s+ /* skip all whitespace */
<ID,STATE,struct,LINE>((?!\n)\s)+ /* skip same-line whitespace */
<INITIAL,ID,STATE,struct,LINE>\#[^\n]* /* skip comments */
[\s]+ /* skip all whitespace */
<ID,STATE,struct,LINE,open_directive,type_directive,arg_directive,close_directive>((?!\n)\s)+ /* skip same-line whitespace */
<INITIAL,ID,STATE,struct,LINE,open_directive,type_directive,arg_directive,close_directive>\#[^\n]* /* skip comments */
\%%[^\n]* /* skip comments */
"scale"\s+ { this.pushState('SCALE'); /* console.log('Got scale', yytext);*/ return 'scale'; }
@ -93,6 +104,7 @@
start
: SPACE start
| NL start
| directive start
| SD document { /*console.warn('Root document', $2);*/ yy.setRootDoc($2);return $2; }
;
@ -165,6 +177,17 @@ statement
$$={ stmt: 'state', id: $3.trim(), note:{position: $2.trim(), text: $4.trim()}};
}
| note NOTE_TEXT AS ID
| directive
;
directive
: openDirective typeDirective closeDirective
| openDirective typeDirective ':' argDirective closeDirective
;
eol
: NL
| ';'
;
idStatement
@ -177,4 +200,20 @@ notePosition
| right_of
;
openDirective
: open_directive { yy.parseDirective('%%{', 'open_directive'); }
;
typeDirective
: type_directive { yy.parseDirective($1, 'type_directive'); }
;
argDirective
: arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
;
closeDirective
: close_directive { yy.parseDirective('}%%', 'close_directive', 'state'); }
;
%%

View File

@ -1,9 +1,16 @@
import { logger } from '../../logger';
import { generateId } from '../../utils';
import mermaidAPI from '../../mermaidAPI';
import configApi from '../../config';
const clone = o => JSON.parse(JSON.stringify(o));
let rootDoc = [];
export const parseDirective = function(statement, context, type) {
mermaidAPI.parseDirective(this, statement, context, type);
};
const setRootDoc = o => {
logger.info('Setting root doc', o);
// rootDoc = { id: 'root', doc: o };
@ -235,6 +242,8 @@ export const relationType = {
const trimColon = str => (str && str[0] === ':' ? str.substr(1).trim() : str.trim());
export default {
parseDirective,
getConfig: () => configApi.getConfig().state,
addState,
clear,
getState,

View File

@ -26,6 +26,16 @@ describe('state diagram, ', function() {
parser.parse(str);
});
it('simple with directive', function() {
const str = `%%{init: {'logLevel': 0 }}%%
stateDiagram\n
State1 : this is another string
[*] --> State1
State1 --> [*]
`;
parser.parse(str);
});
it('should handle relation definitions', function() {
const str = `stateDiagram\n
[*] --> State1
@ -337,6 +347,9 @@ describe('state diagram, ', function() {
parser.parse(str);
});
});
describe('when parsing an ignored info graph it', function() {
xit('should handle if statements', function() {
const str = `stateDiagram\n
[*] --> "Order Submitted"

View File

@ -211,11 +211,11 @@ export const draw = function(text, id) {
parser.yy = stateDb;
// Parse the graph definition
try {
parser.parse(text);
} catch (err) {
logger.debug('Parsing failed');
}
// try {
parser.parse(text);
// } catch (err) {
// logger.error('Parsing failed', err);
// }
// Fetch the default direction, use TD if none was found
let dir = stateDb.getDirection();

View File

@ -1,3 +1,6 @@
import mermaidAPI from '../../mermaidAPI';
import configApi from '../../config';
let title = '';
let currentSection = '';
@ -5,6 +8,10 @@ const sections = [];
const tasks = [];
const rawTasks = [];
export const parseDirective = function(statement, context, type) {
mermaidAPI.parseDirective(this, statement, context, type);
};
export const clear = function() {
sections.length = 0;
tasks.length = 0;
@ -111,6 +118,8 @@ const getActors = function() {
};
export default {
parseDirective,
getConfig: () => configApi.getConfig().journey,
clear,
setTitle,
getTitle,

View File

@ -70,7 +70,8 @@ function drawActorLegend(diagram) {
x: 40,
y: yPos + 7,
fill: '#666',
text: person
text: person,
textMargin: conf.boxTextMargin | 5
};
svgDraw.drawText(diagram, labelData);

View File

@ -5,12 +5,23 @@
*/
%lex
%options case-insensitive
// Directive states
%x open_directive type_directive arg_directive
%%
[\n]+ return 'NL';
\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
<open_directive>((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
\%%(?!\{)[^\n]* /* skip comments */
[^\}]\%\%[^\n]* /* skip comments */
[\n]+ return 'NEWLINE';
\s+ /* skip whitespace */
\#[^\n]* /* skip comments */
\%%[^\n]* /* skip comments */
"journey" return 'journey';
"title"\s[^#\n;]+ return 'title';
@ -31,6 +42,7 @@
start
: journey document 'EOF' { return $2; }
| directive start
;
document
@ -41,12 +53,36 @@ document
line
: SPACE statement { $$ = $2 }
| statement { $$ = $1 }
| NL { $$=[];}
| NEWLINE { $$=[];}
| EOF { $$=[];}
;
directive
: openDirective typeDirective closeDirective 'NEWLINE'
| openDirective typeDirective ':' argDirective closeDirective 'NEWLINE'
;
statement
: title {yy.setTitle($1.substr(6));$$=$1.substr(6);}
| section {yy.addSection($1.substr(8));$$=$1.substr(8);}
| taskName taskData {yy.addTask($1, $2);$$='task';}
| directive
;
openDirective
: open_directive { yy.parseDirective('%%{', 'open_directive'); }
;
typeDirective
: type_directive { yy.parseDirective($1, 'type_directive'); }
;
argDirective
: arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
;
closeDirective
: close_directive { yy.parseDirective('}%%', 'close_directive', 'journey'); }
;
%%

View File

@ -205,7 +205,7 @@ const render = function(id, _txt, cb, container) {
} else {
configApi.reset();
const siteConfig = getSiteConfig();
updateRendererConfigs(siteConfig);
reinitialize(siteConfig);
}
const cnf = getConfig();
@ -438,7 +438,7 @@ const render = function(id, _txt, cb, container) {
let currentDirective = {};
const parseDirective = function(statement, context, type) {
const parseDirective = function(p, statement, context, type) {
try {
if (statement !== undefined) {
statement = statement.trim();
@ -453,7 +453,7 @@ const parseDirective = function(statement, context, type) {
currentDirective.args = JSON.parse(statement);
break;
case 'close_directive':
handleDirective(currentDirective, type);
handleDirective(p, currentDirective, type);
currentDirective = null;
break;
}
@ -466,7 +466,7 @@ const parseDirective = function(statement, context, type) {
}
};
const handleDirective = function(directive, type) {
const handleDirective = function(p, directive, type) {
logger.debug(`Directive type=${directive.type} with args:`, directive.args);
switch (directive.type) {
case 'init':
@ -486,17 +486,9 @@ const handleDirective = function(directive, type) {
}
case 'wrap':
case 'nowrap':
directive.args = { config: { wrap: directive.type === 'wrap' } };
['config'].forEach(prop => {
if (typeof directive.args[prop] !== 'undefined') {
if (type === 'flowchart-v2') {
type = 'flowchart';
}
directive.args[type] = directive.args[prop];
delete directive.args[prop];
}
});
reinitialize(directive.args);
if (p && p['setWrap']) {
p.setWrap(directive.type === 'wrap');
}
break;
default:
logger.warn(

View File

@ -14,8 +14,9 @@ const themes = {
'flowchart-v2': flowchart,
sequence,
gantt,
class: classDiagram,
classDiagram,
'classDiagram-v2': classDiagram,
class: classDiagram,
stateDiagram,
state: stateDiagram,
git,

View File

@ -155,5 +155,6 @@ class Theme {
export const getThemeVariables = userOverrides => {
const theme = new Theme();
theme.calculate(userOverrides);
console.info('Theme(dark)', { userOverrides, theme });
return theme;
};

View File

@ -161,5 +161,6 @@ class Theme {
export const getThemeVariables = userOverrides => {
const theme = new Theme();
theme.calculate(userOverrides);
console.info('Theme(default)', { userOverrides, theme });
return theme;
};

View File

@ -136,5 +136,6 @@ class Theme {
export const getThemeVariables = userOverrides => {
const theme = new Theme();
theme.calculate(userOverrides);
console.info('Theme(forest)', { userOverrides, theme });
return theme;
};

View File

@ -168,6 +168,6 @@ class Theme {
export const getThemeVariables = userOverrides => {
const theme = new Theme();
theme.calculate(userOverrides);
console.info('Theme', userOverrides, theme);
console.info('Theme(neutral)', { userOverrides, theme });
return theme;
};

View File

@ -116,13 +116,13 @@ Alice->Bob: hi`;
});
it('should handle an init definition with config converted to the proper diagram configuration', function() {
const str = `
%%{init: { 'logLevel': 0, 'theme': 'dark', 'config': {'wrapEnabled': true} } }%%
%%{init: { 'logLevel': 0, 'theme': 'dark', 'config': {'wrap': true} } }%%
sequenceDiagram
Alice->Bob: hi`;
const type = utils.detectType(str);
const init = utils.detectInit(str);
expect(type).toBe('sequence');
expect(init).toEqual({logLevel:0, theme:"dark", sequence: { wrapEnabled: true }});
expect(init).toEqual({logLevel:0, theme:"dark", sequence: { wrap: true }});
});
it('should handle a multiline init definition', function() {
const str = `