mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-02-04 07:13:25 +08:00
Merge pull request #932 from knsv/i931_code_standard
I931 code standard
This commit is contained in:
commit
4d1a34661e
18
.eslintrc.json
Normal file
18
.eslintrc.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"experimentalObjectRestSpread": true,
|
||||||
|
"jsx": true
|
||||||
|
},
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"extends": ["prettier"],
|
||||||
|
"plugins": ["prettier"],
|
||||||
|
"rules": {
|
||||||
|
"prettier/prettier": ["error"]
|
||||||
|
}
|
||||||
|
}
|
4
.prettierrc
Normal file
4
.prettierrc
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 100,
|
||||||
|
"singleQuote": true
|
||||||
|
}
|
@ -18,7 +18,7 @@
|
|||||||
"build:watch": "yarn build --watch",
|
"build:watch": "yarn build --watch",
|
||||||
"minify": "minify ./dist/mermaid.js > ./dist/mermaid.min.js",
|
"minify": "minify ./dist/mermaid.js > ./dist/mermaid.min.js",
|
||||||
"release": "yarn build -p --config webpack.config.prod.babel.js",
|
"release": "yarn build -p --config webpack.config.prod.babel.js",
|
||||||
"lint": "standard",
|
"lint": "eslint src",
|
||||||
"e2e:depr": "yarn lint && jest e2e --config e2e/jest.config.js",
|
"e2e:depr": "yarn lint && jest e2e --config e2e/jest.config.js",
|
||||||
"cypress": "percy exec -- cypress run",
|
"cypress": "percy exec -- cypress run",
|
||||||
"e2e": "start-server-and-test dev http://localhost:9000/ cypress",
|
"e2e": "start-server-and-test dev http://localhost:9000/ cypress",
|
||||||
@ -51,11 +51,15 @@
|
|||||||
"dagre-d3-renderer": "^0.5.8",
|
"dagre-d3-renderer": "^0.5.8",
|
||||||
"dagre-layout": "^0.8.8",
|
"dagre-layout": "^0.8.8",
|
||||||
"documentation": "^12.0.1",
|
"documentation": "^12.0.1",
|
||||||
|
"eslint": "^6.3.0",
|
||||||
|
"eslint-config-prettier": "^6.3.0",
|
||||||
|
"eslint-plugin-prettier": "^3.1.0",
|
||||||
"graphlibrary": "^2.2.0",
|
"graphlibrary": "^2.2.0",
|
||||||
"he": "^1.2.0",
|
"he": "^1.2.0",
|
||||||
"lodash": "^4.17.11",
|
"lodash": "^4.17.11",
|
||||||
"minify": "^4.1.1",
|
"minify": "^4.1.1",
|
||||||
"moment-mini": "^2.22.1",
|
"moment-mini": "^2.22.1",
|
||||||
|
"prettier": "^1.18.2",
|
||||||
"scope-css": "^1.2.1"
|
"scope-css": "^1.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -1,28 +1,27 @@
|
|||||||
let config = {
|
let config = {};
|
||||||
}
|
|
||||||
|
|
||||||
const setConf = function (cnf) {
|
const setConf = function(cnf) {
|
||||||
// Top level initially mermaid, gflow, sequenceDiagram and gantt
|
// Top level initially mermaid, gflow, sequenceDiagram and gantt
|
||||||
const lvl1Keys = Object.keys(cnf)
|
const lvl1Keys = Object.keys(cnf);
|
||||||
for (let i = 0; i < lvl1Keys.length; i++) {
|
for (let i = 0; i < lvl1Keys.length; i++) {
|
||||||
if (typeof cnf[lvl1Keys[i]] === 'object' && cnf[lvl1Keys[i]] != null) {
|
if (typeof cnf[lvl1Keys[i]] === 'object' && cnf[lvl1Keys[i]] != null) {
|
||||||
const lvl2Keys = Object.keys(cnf[lvl1Keys[i]])
|
const lvl2Keys = Object.keys(cnf[lvl1Keys[i]]);
|
||||||
|
|
||||||
for (let j = 0; j < lvl2Keys.length; j++) {
|
for (let j = 0; j < lvl2Keys.length; j++) {
|
||||||
// logger.debug('Setting conf ', lvl1Keys[i], '-', lvl2Keys[j])
|
// logger.debug('Setting conf ', lvl1Keys[i], '-', lvl2Keys[j])
|
||||||
if (typeof config[lvl1Keys[i]] === 'undefined') {
|
if (typeof config[lvl1Keys[i]] === 'undefined') {
|
||||||
config[lvl1Keys[i]] = {}
|
config[lvl1Keys[i]] = {};
|
||||||
}
|
}
|
||||||
// logger.debug('Setting config: ' + lvl1Keys[i] + ' ' + lvl2Keys[j] + ' to ' + cnf[lvl1Keys[i]][lvl2Keys[j]])
|
// logger.debug('Setting config: ' + lvl1Keys[i] + ' ' + lvl2Keys[j] + ' to ' + cnf[lvl1Keys[i]][lvl2Keys[j]])
|
||||||
config[lvl1Keys[i]][lvl2Keys[j]] = cnf[lvl1Keys[i]][lvl2Keys[j]]
|
config[lvl1Keys[i]][lvl2Keys[j]] = cnf[lvl1Keys[i]][lvl2Keys[j]];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
config[lvl1Keys[i]] = cnf[lvl1Keys[i]]
|
config[lvl1Keys[i]] = cnf[lvl1Keys[i]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const setConfig = conf => {
|
export const setConfig = conf => {
|
||||||
setConf(conf)
|
setConf(conf);
|
||||||
}
|
};
|
||||||
export const getConfig = () => config
|
export const getConfig = () => config;
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
|
import { logger } from '../../logger';
|
||||||
|
|
||||||
import { logger } from '../../logger'
|
let relations = [];
|
||||||
|
let classes = {};
|
||||||
let relations = []
|
|
||||||
let classes = {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function called by parser when a node definition has been found.
|
* Function called by parser when a node definition has been found.
|
||||||
@ -11,75 +10,75 @@ let classes = {}
|
|||||||
* @param type
|
* @param type
|
||||||
* @param style
|
* @param style
|
||||||
*/
|
*/
|
||||||
export const addClass = function (id) {
|
export const addClass = function(id) {
|
||||||
if (typeof classes[id] === 'undefined') {
|
if (typeof classes[id] === 'undefined') {
|
||||||
classes[id] = {
|
classes[id] = {
|
||||||
id: id,
|
id: id,
|
||||||
methods: [],
|
methods: [],
|
||||||
members: []
|
members: []
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const clear = function () {
|
export const clear = function() {
|
||||||
relations = []
|
relations = [];
|
||||||
classes = {}
|
classes = {};
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getClass = function (id) {
|
export const getClass = function(id) {
|
||||||
return classes[id]
|
return classes[id];
|
||||||
}
|
};
|
||||||
export const getClasses = function () {
|
export const getClasses = function() {
|
||||||
return classes
|
return classes;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getRelations = function () {
|
export const getRelations = function() {
|
||||||
return relations
|
return relations;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const addRelation = function (relation) {
|
export const addRelation = function(relation) {
|
||||||
logger.debug('Adding relation: ' + JSON.stringify(relation))
|
logger.debug('Adding relation: ' + JSON.stringify(relation));
|
||||||
addClass(relation.id1)
|
addClass(relation.id1);
|
||||||
addClass(relation.id2)
|
addClass(relation.id2);
|
||||||
relations.push(relation)
|
relations.push(relation);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const addMember = function (className, member) {
|
export const addMember = function(className, member) {
|
||||||
const theClass = classes[className]
|
const theClass = classes[className];
|
||||||
if (typeof member === 'string') {
|
if (typeof member === 'string') {
|
||||||
if (member.substr(-1) === ')') {
|
if (member.substr(-1) === ')') {
|
||||||
theClass.methods.push(member)
|
theClass.methods.push(member);
|
||||||
} else {
|
} else {
|
||||||
theClass.members.push(member)
|
theClass.members.push(member);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const addMembers = function (className, MembersArr) {
|
export const addMembers = function(className, MembersArr) {
|
||||||
if (Array.isArray(MembersArr)) {
|
if (Array.isArray(MembersArr)) {
|
||||||
MembersArr.forEach(member => addMember(className, member))
|
MembersArr.forEach(member => addMember(className, member));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const cleanupLabel = function (label) {
|
export const cleanupLabel = function(label) {
|
||||||
if (label.substring(0, 1) === ':') {
|
if (label.substring(0, 1) === ':') {
|
||||||
return label.substr(2).trim()
|
return label.substr(2).trim();
|
||||||
} else {
|
} else {
|
||||||
return label.trim()
|
return label.trim();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const lineType = {
|
export const lineType = {
|
||||||
LINE: 0,
|
LINE: 0,
|
||||||
DOTTED_LINE: 1
|
DOTTED_LINE: 1
|
||||||
}
|
};
|
||||||
|
|
||||||
export const relationType = {
|
export const relationType = {
|
||||||
AGGREGATION: 0,
|
AGGREGATION: 0,
|
||||||
EXTENSION: 1,
|
EXTENSION: 1,
|
||||||
COMPOSITION: 2,
|
COMPOSITION: 2,
|
||||||
DEPENDENCY: 3
|
DEPENDENCY: 3
|
||||||
}
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
addClass,
|
addClass,
|
||||||
@ -93,4 +92,4 @@ export default {
|
|||||||
cleanupLabel,
|
cleanupLabel,
|
||||||
lineType,
|
lineType,
|
||||||
relationType
|
relationType
|
||||||
}
|
};
|
||||||
|
@ -1,208 +1,211 @@
|
|||||||
/* eslint-env jasmine */
|
/* eslint-env jasmine */
|
||||||
import { parser } from './parser/classDiagram'
|
import { parser } from './parser/classDiagram';
|
||||||
import classDb from './classDb'
|
import classDb from './classDb';
|
||||||
|
|
||||||
describe('class diagram, ', function () {
|
describe('class diagram, ', function() {
|
||||||
describe('when parsing an info graph it', function () {
|
describe('when parsing an info graph it', function() {
|
||||||
beforeEach(function () {
|
beforeEach(function() {
|
||||||
parser.yy = classDb
|
parser.yy = classDb;
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle relation definitions', function () {
|
it('should handle relation definitions', function() {
|
||||||
const str = 'classDiagram\n' +
|
const str =
|
||||||
'Class01 <|-- Class02\n' +
|
'classDiagram\n' +
|
||||||
'Class03 *-- Class04\n' +
|
'Class01 <|-- Class02\n' +
|
||||||
'Class05 o-- Class06\n' +
|
'Class03 *-- Class04\n' +
|
||||||
'Class07 .. Class08\n' +
|
'Class05 o-- Class06\n' +
|
||||||
'Class09 -- Class1'
|
'Class07 .. Class08\n' +
|
||||||
|
'Class09 -- Class1';
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
})
|
});
|
||||||
it('should handle relation definition of different types and directions', function () {
|
it('should handle relation definition of different types and directions', function() {
|
||||||
const str = 'classDiagram\n' +
|
const str =
|
||||||
'Class11 <|.. Class12\n' +
|
'classDiagram\n' +
|
||||||
'Class13 --> Class14\n' +
|
'Class11 <|.. Class12\n' +
|
||||||
'Class15 ..> Class16\n' +
|
'Class13 --> Class14\n' +
|
||||||
'Class17 ..|> Class18\n' +
|
'Class15 ..> Class16\n' +
|
||||||
'Class19 <--* Class20'
|
'Class17 ..|> Class18\n' +
|
||||||
|
'Class19 <--* Class20';
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle cardinality and labels', function () {
|
it('should handle cardinality and labels', function() {
|
||||||
const str = 'classDiagram\n' +
|
const str =
|
||||||
'Class01 "1" *-- "many" Class02 : contains\n' +
|
'classDiagram\n' +
|
||||||
'Class03 o-- Class04 : aggregation\n' +
|
'Class01 "1" *-- "many" Class02 : contains\n' +
|
||||||
'Class05 --> "1" Class06'
|
'Class03 o-- Class04 : aggregation\n' +
|
||||||
|
'Class05 --> "1" Class06';
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
})
|
});
|
||||||
it('should handle class definitions', function () {
|
it('should handle class definitions', function() {
|
||||||
const str = 'classDiagram\n' +
|
const str =
|
||||||
'class Car\n' +
|
'classDiagram\n' +
|
||||||
'Driver -- Car : drives >\n' +
|
'class Car\n' +
|
||||||
'Car *-- Wheel : have 4 >\n' +
|
'Driver -- Car : drives >\n' +
|
||||||
'Car -- Person : < owns'
|
'Car *-- Wheel : have 4 >\n' +
|
||||||
|
'Car -- Person : < owns';
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle method statements', function () {
|
it('should handle method statements', function() {
|
||||||
const str = 'classDiagram\n' +
|
const str =
|
||||||
'Object <|-- ArrayList\n' +
|
'classDiagram\n' +
|
||||||
'Object : equals()\n' +
|
'Object <|-- ArrayList\n' +
|
||||||
'ArrayList : Object[] elementData\n' +
|
'Object : equals()\n' +
|
||||||
'ArrayList : size()'
|
'ArrayList : Object[] elementData\n' +
|
||||||
|
'ArrayList : size()';
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
})
|
});
|
||||||
it('should handle parsing of method statements grouped by brackets', function () {
|
it('should handle parsing of method statements grouped by brackets', function() {
|
||||||
const str = 'classDiagram\n' +
|
const str =
|
||||||
'class Dummy {\n' +
|
'classDiagram\n' +
|
||||||
'String data\n' +
|
'class Dummy {\n' +
|
||||||
' void methods()\n' +
|
'String data\n' +
|
||||||
'}\n' +
|
' void methods()\n' +
|
||||||
'\n' +
|
'}\n' +
|
||||||
'class Flight {\n' +
|
'\n' +
|
||||||
' flightNumber : Integer\n' +
|
'class Flight {\n' +
|
||||||
' departureTime : Date\n' +
|
' flightNumber : Integer\n' +
|
||||||
'}'
|
' departureTime : Date\n' +
|
||||||
|
'}';
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle parsing of separators', function () {
|
it('should handle parsing of separators', function() {
|
||||||
const str = 'classDiagram\n' +
|
const str =
|
||||||
'class Foo1 {\n' +
|
'classDiagram\n' +
|
||||||
' You can use\n' +
|
'class Foo1 {\n' +
|
||||||
' several lines\n' +
|
' You can use\n' +
|
||||||
'..\n' +
|
' several lines\n' +
|
||||||
'as you want\n' +
|
'..\n' +
|
||||||
'and group\n' +
|
'as you want\n' +
|
||||||
'==\n' +
|
'and group\n' +
|
||||||
'things together.\n' +
|
'==\n' +
|
||||||
'__\n' +
|
'things together.\n' +
|
||||||
'You can have as many groups\n' +
|
'__\n' +
|
||||||
'as you want\n' +
|
'You can have as many groups\n' +
|
||||||
'--\n' +
|
'as you want\n' +
|
||||||
'End of class\n' +
|
'--\n' +
|
||||||
'}\n' +
|
'End of class\n' +
|
||||||
'\n' +
|
'}\n' +
|
||||||
'class User {\n' +
|
'\n' +
|
||||||
'.. Simple Getter ..\n' +
|
'class User {\n' +
|
||||||
'+ getName()\n' +
|
'.. Simple Getter ..\n' +
|
||||||
'+ getAddress()\n' +
|
'+ getName()\n' +
|
||||||
'.. Some setter ..\n' +
|
'+ getAddress()\n' +
|
||||||
'+ setName()\n' +
|
'.. Some setter ..\n' +
|
||||||
'__ private data __\n' +
|
'+ setName()\n' +
|
||||||
'int age\n' +
|
'__ private data __\n' +
|
||||||
'-- encrypted --\n' +
|
'int age\n' +
|
||||||
'String password\n' +
|
'-- encrypted --\n' +
|
||||||
'}'
|
'String password\n' +
|
||||||
|
'}';
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
describe('when fetching data from an classDiagram graph it', function () {
|
describe('when fetching data from an classDiagram graph it', function() {
|
||||||
beforeEach(function () {
|
beforeEach(function() {
|
||||||
parser.yy = classDb
|
parser.yy = classDb;
|
||||||
parser.yy.clear()
|
parser.yy.clear();
|
||||||
})
|
});
|
||||||
it('should handle relation definitions EXTENSION', function () {
|
it('should handle relation definitions EXTENSION', function() {
|
||||||
const str = 'classDiagram\n' +
|
const str = 'classDiagram\n' + 'Class01 <|-- Class02';
|
||||||
'Class01 <|-- Class02'
|
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
|
|
||||||
const relations = parser.yy.getRelations()
|
const relations = parser.yy.getRelations();
|
||||||
|
|
||||||
expect(parser.yy.getClass('Class01').id).toBe('Class01')
|
expect(parser.yy.getClass('Class01').id).toBe('Class01');
|
||||||
expect(parser.yy.getClass('Class02').id).toBe('Class02')
|
expect(parser.yy.getClass('Class02').id).toBe('Class02');
|
||||||
expect(relations[0].relation.type1).toBe(classDb.relationType.EXTENSION)
|
expect(relations[0].relation.type1).toBe(classDb.relationType.EXTENSION);
|
||||||
expect(relations[0].relation.type2).toBe('none')
|
expect(relations[0].relation.type2).toBe('none');
|
||||||
expect(relations[0].relation.lineType).toBe(classDb.lineType.LINE)
|
expect(relations[0].relation.lineType).toBe(classDb.lineType.LINE);
|
||||||
})
|
});
|
||||||
it('should handle relation definitions AGGREGATION and dotted line', function () {
|
it('should handle relation definitions AGGREGATION and dotted line', function() {
|
||||||
const str = 'classDiagram\n' +
|
const str = 'classDiagram\n' + 'Class01 o.. Class02';
|
||||||
'Class01 o.. Class02'
|
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
|
|
||||||
const relations = parser.yy.getRelations()
|
const relations = parser.yy.getRelations();
|
||||||
|
|
||||||
expect(parser.yy.getClass('Class01').id).toBe('Class01')
|
expect(parser.yy.getClass('Class01').id).toBe('Class01');
|
||||||
expect(parser.yy.getClass('Class02').id).toBe('Class02')
|
expect(parser.yy.getClass('Class02').id).toBe('Class02');
|
||||||
expect(relations[0].relation.type1).toBe(classDb.relationType.AGGREGATION)
|
expect(relations[0].relation.type1).toBe(classDb.relationType.AGGREGATION);
|
||||||
expect(relations[0].relation.type2).toBe('none')
|
expect(relations[0].relation.type2).toBe('none');
|
||||||
expect(relations[0].relation.lineType).toBe(classDb.lineType.DOTTED_LINE)
|
expect(relations[0].relation.lineType).toBe(classDb.lineType.DOTTED_LINE);
|
||||||
})
|
});
|
||||||
it('should handle relation definitions COMPOSITION on both sides', function () {
|
it('should handle relation definitions COMPOSITION on both sides', function() {
|
||||||
const str = 'classDiagram\n' +
|
const str = 'classDiagram\n' + 'Class01 *--* Class02';
|
||||||
'Class01 *--* Class02'
|
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
|
|
||||||
const relations = parser.yy.getRelations()
|
const relations = parser.yy.getRelations();
|
||||||
|
|
||||||
expect(parser.yy.getClass('Class01').id).toBe('Class01')
|
expect(parser.yy.getClass('Class01').id).toBe('Class01');
|
||||||
expect(parser.yy.getClass('Class02').id).toBe('Class02')
|
expect(parser.yy.getClass('Class02').id).toBe('Class02');
|
||||||
expect(relations[0].relation.type1).toBe(classDb.relationType.COMPOSITION)
|
expect(relations[0].relation.type1).toBe(classDb.relationType.COMPOSITION);
|
||||||
expect(relations[0].relation.type2).toBe(classDb.relationType.COMPOSITION)
|
expect(relations[0].relation.type2).toBe(classDb.relationType.COMPOSITION);
|
||||||
expect(relations[0].relation.lineType).toBe(classDb.lineType.LINE)
|
expect(relations[0].relation.lineType).toBe(classDb.lineType.LINE);
|
||||||
})
|
});
|
||||||
it('should handle relation definitions no types', function () {
|
it('should handle relation definitions no types', function() {
|
||||||
const str = 'classDiagram\n' +
|
const str = 'classDiagram\n' + 'Class01 -- Class02';
|
||||||
'Class01 -- Class02'
|
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
|
|
||||||
const relations = parser.yy.getRelations()
|
const relations = parser.yy.getRelations();
|
||||||
|
|
||||||
expect(parser.yy.getClass('Class01').id).toBe('Class01')
|
expect(parser.yy.getClass('Class01').id).toBe('Class01');
|
||||||
expect(parser.yy.getClass('Class02').id).toBe('Class02')
|
expect(parser.yy.getClass('Class02').id).toBe('Class02');
|
||||||
expect(relations[0].relation.type1).toBe('none')
|
expect(relations[0].relation.type1).toBe('none');
|
||||||
expect(relations[0].relation.type2).toBe('none')
|
expect(relations[0].relation.type2).toBe('none');
|
||||||
expect(relations[0].relation.lineType).toBe(classDb.lineType.LINE)
|
expect(relations[0].relation.lineType).toBe(classDb.lineType.LINE);
|
||||||
})
|
});
|
||||||
it('should handle relation definitions with type only on right side', function () {
|
it('should handle relation definitions with type only on right side', function() {
|
||||||
const str = 'classDiagram\n' +
|
const str = 'classDiagram\n' + 'Class01 --|> Class02';
|
||||||
'Class01 --|> Class02'
|
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
|
|
||||||
const relations = parser.yy.getRelations()
|
const relations = parser.yy.getRelations();
|
||||||
|
|
||||||
expect(parser.yy.getClass('Class01').id).toBe('Class01')
|
expect(parser.yy.getClass('Class01').id).toBe('Class01');
|
||||||
expect(parser.yy.getClass('Class02').id).toBe('Class02')
|
expect(parser.yy.getClass('Class02').id).toBe('Class02');
|
||||||
expect(relations[0].relation.type1).toBe('none')
|
expect(relations[0].relation.type1).toBe('none');
|
||||||
expect(relations[0].relation.type2).toBe(classDb.relationType.EXTENSION)
|
expect(relations[0].relation.type2).toBe(classDb.relationType.EXTENSION);
|
||||||
expect(relations[0].relation.lineType).toBe(classDb.lineType.LINE)
|
expect(relations[0].relation.lineType).toBe(classDb.lineType.LINE);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle multiple classes and relation definitions', function () {
|
it('should handle multiple classes and relation definitions', function() {
|
||||||
const str = 'classDiagram\n' +
|
const str =
|
||||||
'Class01 <|-- Class02\n' +
|
'classDiagram\n' +
|
||||||
'Class03 *-- Class04\n' +
|
'Class01 <|-- Class02\n' +
|
||||||
'Class05 o-- Class06\n' +
|
'Class03 *-- Class04\n' +
|
||||||
'Class07 .. Class08\n' +
|
'Class05 o-- Class06\n' +
|
||||||
'Class09 -- Class10'
|
'Class07 .. Class08\n' +
|
||||||
|
'Class09 -- Class10';
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
|
|
||||||
const relations = parser.yy.getRelations()
|
const relations = parser.yy.getRelations();
|
||||||
|
|
||||||
expect(parser.yy.getClass('Class01').id).toBe('Class01')
|
expect(parser.yy.getClass('Class01').id).toBe('Class01');
|
||||||
expect(parser.yy.getClass('Class10').id).toBe('Class10')
|
expect(parser.yy.getClass('Class10').id).toBe('Class10');
|
||||||
|
|
||||||
expect(relations.length).toBe(5)
|
expect(relations.length).toBe(5);
|
||||||
|
|
||||||
expect(relations[0].relation.type1).toBe(classDb.relationType.EXTENSION)
|
expect(relations[0].relation.type1).toBe(classDb.relationType.EXTENSION);
|
||||||
expect(relations[0].relation.type2).toBe('none')
|
expect(relations[0].relation.type2).toBe('none');
|
||||||
expect(relations[0].relation.lineType).toBe(classDb.lineType.LINE)
|
expect(relations[0].relation.lineType).toBe(classDb.lineType.LINE);
|
||||||
expect(relations[3].relation.type1).toBe('none')
|
expect(relations[3].relation.type1).toBe('none');
|
||||||
expect(relations[3].relation.type2).toBe('none')
|
expect(relations[3].relation.type2).toBe('none');
|
||||||
expect(relations[3].relation.lineType).toBe(classDb.lineType.DOTTED_LINE)
|
expect(relations[3].relation.lineType).toBe(classDb.lineType.DOTTED_LINE);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
@ -1,38 +1,38 @@
|
|||||||
import * as d3 from 'd3'
|
import * as d3 from 'd3';
|
||||||
import dagre from 'dagre-layout'
|
import dagre from 'dagre-layout';
|
||||||
import graphlib from 'graphlibrary'
|
import graphlib from 'graphlibrary';
|
||||||
import { logger } from '../../logger'
|
import { logger } from '../../logger';
|
||||||
import classDb from './classDb'
|
import classDb from './classDb';
|
||||||
import { parser } from './parser/classDiagram'
|
import { parser } from './parser/classDiagram';
|
||||||
|
|
||||||
parser.yy = classDb
|
parser.yy = classDb;
|
||||||
|
|
||||||
const idCache = {}
|
const idCache = {};
|
||||||
|
|
||||||
let classCnt = 0
|
let classCnt = 0;
|
||||||
const conf = {
|
const conf = {
|
||||||
dividerMargin: 10,
|
dividerMargin: 10,
|
||||||
padding: 5,
|
padding: 5,
|
||||||
textHeight: 10
|
textHeight: 10
|
||||||
}
|
};
|
||||||
|
|
||||||
// Todo optimize
|
// Todo optimize
|
||||||
const getGraphId = function (label) {
|
const getGraphId = function(label) {
|
||||||
const keys = Object.keys(idCache)
|
const keys = Object.keys(idCache);
|
||||||
|
|
||||||
for (let i = 0; i < keys.length; i++) {
|
for (let i = 0; i < keys.length; i++) {
|
||||||
if (idCache[keys[i]].label === label) {
|
if (idCache[keys[i]].label === label) {
|
||||||
return keys[i]
|
return keys[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined
|
return undefined;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup arrow head and define the marker. The result is appended to the svg.
|
* Setup arrow head and define the marker. The result is appended to the svg.
|
||||||
*/
|
*/
|
||||||
const insertMarkers = function (elem) {
|
const insertMarkers = function(elem) {
|
||||||
elem
|
elem
|
||||||
.append('defs')
|
.append('defs')
|
||||||
.append('marker')
|
.append('marker')
|
||||||
@ -44,7 +44,7 @@ const insertMarkers = function (elem) {
|
|||||||
.attr('markerHeight', 240)
|
.attr('markerHeight', 240)
|
||||||
.attr('orient', 'auto')
|
.attr('orient', 'auto')
|
||||||
.append('path')
|
.append('path')
|
||||||
.attr('d', 'M 1,7 L18,13 V 1 Z')
|
.attr('d', 'M 1,7 L18,13 V 1 Z');
|
||||||
|
|
||||||
elem
|
elem
|
||||||
.append('defs')
|
.append('defs')
|
||||||
@ -56,7 +56,7 @@ const insertMarkers = function (elem) {
|
|||||||
.attr('markerHeight', 28)
|
.attr('markerHeight', 28)
|
||||||
.attr('orient', 'auto')
|
.attr('orient', 'auto')
|
||||||
.append('path')
|
.append('path')
|
||||||
.attr('d', 'M 1,1 V 13 L18,7 Z') // this is actual shape for arrowhead
|
.attr('d', 'M 1,1 V 13 L18,7 Z'); // this is actual shape for arrowhead
|
||||||
|
|
||||||
elem
|
elem
|
||||||
.append('defs')
|
.append('defs')
|
||||||
@ -69,7 +69,7 @@ const insertMarkers = function (elem) {
|
|||||||
.attr('markerHeight', 240)
|
.attr('markerHeight', 240)
|
||||||
.attr('orient', 'auto')
|
.attr('orient', 'auto')
|
||||||
.append('path')
|
.append('path')
|
||||||
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z')
|
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z');
|
||||||
|
|
||||||
elem
|
elem
|
||||||
.append('defs')
|
.append('defs')
|
||||||
@ -81,7 +81,7 @@ const insertMarkers = function (elem) {
|
|||||||
.attr('markerHeight', 28)
|
.attr('markerHeight', 28)
|
||||||
.attr('orient', 'auto')
|
.attr('orient', 'auto')
|
||||||
.append('path')
|
.append('path')
|
||||||
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z')
|
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z');
|
||||||
|
|
||||||
elem
|
elem
|
||||||
.append('defs')
|
.append('defs')
|
||||||
@ -94,7 +94,7 @@ const insertMarkers = function (elem) {
|
|||||||
.attr('markerHeight', 240)
|
.attr('markerHeight', 240)
|
||||||
.attr('orient', 'auto')
|
.attr('orient', 'auto')
|
||||||
.append('path')
|
.append('path')
|
||||||
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z')
|
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z');
|
||||||
|
|
||||||
elem
|
elem
|
||||||
.append('defs')
|
.append('defs')
|
||||||
@ -106,7 +106,7 @@ const insertMarkers = function (elem) {
|
|||||||
.attr('markerHeight', 28)
|
.attr('markerHeight', 28)
|
||||||
.attr('orient', 'auto')
|
.attr('orient', 'auto')
|
||||||
.append('path')
|
.append('path')
|
||||||
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z')
|
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z');
|
||||||
|
|
||||||
elem
|
elem
|
||||||
.append('defs')
|
.append('defs')
|
||||||
@ -119,7 +119,7 @@ const insertMarkers = function (elem) {
|
|||||||
.attr('markerHeight', 240)
|
.attr('markerHeight', 240)
|
||||||
.attr('orient', 'auto')
|
.attr('orient', 'auto')
|
||||||
.append('path')
|
.append('path')
|
||||||
.attr('d', 'M 5,7 L9,13 L1,7 L9,1 Z')
|
.attr('d', 'M 5,7 L9,13 L1,7 L9,1 Z');
|
||||||
|
|
||||||
elem
|
elem
|
||||||
.append('defs')
|
.append('defs')
|
||||||
@ -131,96 +131,86 @@ const insertMarkers = function (elem) {
|
|||||||
.attr('markerHeight', 28)
|
.attr('markerHeight', 28)
|
||||||
.attr('orient', 'auto')
|
.attr('orient', 'auto')
|
||||||
.append('path')
|
.append('path')
|
||||||
.attr('d', 'M 18,7 L9,13 L14,7 L9,1 Z')
|
.attr('d', 'M 18,7 L9,13 L14,7 L9,1 Z');
|
||||||
}
|
};
|
||||||
|
|
||||||
let edgeCount = 0
|
let edgeCount = 0;
|
||||||
let total = 0
|
let total = 0;
|
||||||
const drawEdge = function (elem, path, relation) {
|
const drawEdge = function(elem, path, relation) {
|
||||||
const getRelationType = function (type) {
|
const getRelationType = function(type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case classDb.relationType.AGGREGATION:
|
case classDb.relationType.AGGREGATION:
|
||||||
return 'aggregation'
|
return 'aggregation';
|
||||||
case classDb.relationType.EXTENSION:
|
case classDb.relationType.EXTENSION:
|
||||||
return 'extension'
|
return 'extension';
|
||||||
case classDb.relationType.COMPOSITION:
|
case classDb.relationType.COMPOSITION:
|
||||||
return 'composition'
|
return 'composition';
|
||||||
case classDb.relationType.DEPENDENCY:
|
case classDb.relationType.DEPENDENCY:
|
||||||
return 'dependency'
|
return 'dependency';
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
path.points = path.points.filter(p => !Number.isNaN(p.y))
|
path.points = path.points.filter(p => !Number.isNaN(p.y));
|
||||||
|
|
||||||
// The data for our line
|
// The data for our line
|
||||||
const lineData = path.points
|
const lineData = path.points;
|
||||||
|
|
||||||
// This is the accessor function we talked about above
|
// This is the accessor function we talked about above
|
||||||
const lineFunction = d3
|
const lineFunction = d3
|
||||||
.line()
|
.line()
|
||||||
.x(function (d) {
|
.x(function(d) {
|
||||||
return d.x
|
return d.x;
|
||||||
})
|
})
|
||||||
.y(function (d) {
|
.y(function(d) {
|
||||||
return d.y
|
return d.y;
|
||||||
})
|
})
|
||||||
.curve(d3.curveBasis)
|
.curve(d3.curveBasis);
|
||||||
|
|
||||||
const svgPath = elem
|
const svgPath = elem
|
||||||
.append('path')
|
.append('path')
|
||||||
.attr('d', lineFunction(lineData))
|
.attr('d', lineFunction(lineData))
|
||||||
.attr('id', 'edge' + edgeCount)
|
.attr('id', 'edge' + edgeCount)
|
||||||
.attr('class', 'relation')
|
.attr('class', 'relation');
|
||||||
let url = ''
|
let url = '';
|
||||||
if (conf.arrowMarkerAbsolute) {
|
if (conf.arrowMarkerAbsolute) {
|
||||||
url =
|
url =
|
||||||
window.location.protocol +
|
window.location.protocol +
|
||||||
'//' +
|
'//' +
|
||||||
window.location.host +
|
window.location.host +
|
||||||
window.location.pathname +
|
window.location.pathname +
|
||||||
window.location.search
|
window.location.search;
|
||||||
url = url.replace(/\(/g, '\\(')
|
url = url.replace(/\(/g, '\\(');
|
||||||
url = url.replace(/\)/g, '\\)')
|
url = url.replace(/\)/g, '\\)');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relation.relation.type1 !== 'none') {
|
if (relation.relation.type1 !== 'none') {
|
||||||
svgPath.attr(
|
svgPath.attr(
|
||||||
'marker-start',
|
'marker-start',
|
||||||
'url(' +
|
'url(' + url + '#' + getRelationType(relation.relation.type1) + 'Start' + ')'
|
||||||
url +
|
);
|
||||||
'#' +
|
|
||||||
getRelationType(relation.relation.type1) +
|
|
||||||
'Start' +
|
|
||||||
')'
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
if (relation.relation.type2 !== 'none') {
|
if (relation.relation.type2 !== 'none') {
|
||||||
svgPath.attr(
|
svgPath.attr(
|
||||||
'marker-end',
|
'marker-end',
|
||||||
'url(' +
|
'url(' + url + '#' + getRelationType(relation.relation.type2) + 'End' + ')'
|
||||||
url +
|
);
|
||||||
'#' +
|
|
||||||
getRelationType(relation.relation.type2) +
|
|
||||||
'End' +
|
|
||||||
')'
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let x, y
|
let x, y;
|
||||||
const l = path.points.length
|
const l = path.points.length;
|
||||||
if (l % 2 !== 0 && l > 1) {
|
if (l % 2 !== 0 && l > 1) {
|
||||||
const p1 = path.points[Math.floor(l / 2)]
|
const p1 = path.points[Math.floor(l / 2)];
|
||||||
const p2 = path.points[Math.ceil(l / 2)]
|
const p2 = path.points[Math.ceil(l / 2)];
|
||||||
x = (p1.x + p2.x) / 2
|
x = (p1.x + p2.x) / 2;
|
||||||
y = (p1.y + p2.y) / 2
|
y = (p1.y + p2.y) / 2;
|
||||||
} else {
|
} else {
|
||||||
const p = path.points[Math.floor(l / 2)]
|
const p = path.points[Math.floor(l / 2)];
|
||||||
x = p.x
|
x = p.x;
|
||||||
y = p.y
|
y = p.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof relation.title !== 'undefined') {
|
if (typeof relation.title !== 'undefined') {
|
||||||
const g = elem.append('g').attr('class', 'classLabel')
|
const g = elem.append('g').attr('class', 'classLabel');
|
||||||
const label = g
|
const label = g
|
||||||
.append('text')
|
.append('text')
|
||||||
.attr('class', 'label')
|
.attr('class', 'label')
|
||||||
@ -228,189 +218,177 @@ const drawEdge = function (elem, path, relation) {
|
|||||||
.attr('y', y)
|
.attr('y', y)
|
||||||
.attr('fill', 'red')
|
.attr('fill', 'red')
|
||||||
.attr('text-anchor', 'middle')
|
.attr('text-anchor', 'middle')
|
||||||
.text(relation.title)
|
.text(relation.title);
|
||||||
|
|
||||||
window.label = label
|
window.label = label;
|
||||||
const bounds = label.node().getBBox()
|
const bounds = label.node().getBBox();
|
||||||
|
|
||||||
g.insert('rect', ':first-child')
|
g.insert('rect', ':first-child')
|
||||||
.attr('class', 'box')
|
.attr('class', 'box')
|
||||||
.attr('x', bounds.x - conf.padding / 2)
|
.attr('x', bounds.x - conf.padding / 2)
|
||||||
.attr('y', bounds.y - conf.padding / 2)
|
.attr('y', bounds.y - conf.padding / 2)
|
||||||
.attr('width', bounds.width + conf.padding)
|
.attr('width', bounds.width + conf.padding)
|
||||||
.attr('height', bounds.height + conf.padding)
|
.attr('height', bounds.height + conf.padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
edgeCount++
|
edgeCount++;
|
||||||
}
|
};
|
||||||
|
|
||||||
const drawClass = function (elem, classDef) {
|
const drawClass = function(elem, classDef) {
|
||||||
logger.info('Rendering class ' + classDef)
|
logger.info('Rendering class ' + classDef);
|
||||||
|
|
||||||
const addTspan = function (textEl, txt, isFirst) {
|
const addTspan = function(textEl, txt, isFirst) {
|
||||||
const tSpan = textEl
|
const tSpan = textEl
|
||||||
.append('tspan')
|
.append('tspan')
|
||||||
.attr('x', conf.padding)
|
.attr('x', conf.padding)
|
||||||
.text(txt)
|
.text(txt);
|
||||||
if (!isFirst) {
|
if (!isFirst) {
|
||||||
tSpan.attr('dy', conf.textHeight)
|
tSpan.attr('dy', conf.textHeight);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const id = 'classId' + (classCnt % total)
|
const id = 'classId' + (classCnt % total);
|
||||||
const classInfo = {
|
const classInfo = {
|
||||||
id: id,
|
id: id,
|
||||||
label: classDef.id,
|
label: classDef.id,
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0
|
height: 0
|
||||||
}
|
};
|
||||||
|
|
||||||
const g = elem
|
const g = elem
|
||||||
.append('g')
|
.append('g')
|
||||||
.attr('id', id)
|
.attr('id', id)
|
||||||
.attr('class', 'classGroup')
|
.attr('class', 'classGroup');
|
||||||
const title = g
|
const title = g
|
||||||
.append('text')
|
.append('text')
|
||||||
.attr('x', conf.padding)
|
.attr('x', conf.padding)
|
||||||
.attr('y', conf.textHeight + conf.padding)
|
.attr('y', conf.textHeight + conf.padding)
|
||||||
.text(classDef.id)
|
.text(classDef.id);
|
||||||
|
|
||||||
const titleHeight = title.node().getBBox().height
|
const titleHeight = title.node().getBBox().height;
|
||||||
|
|
||||||
const membersLine = g
|
const membersLine = g
|
||||||
.append('line') // text label for the x axis
|
.append('line') // text label for the x axis
|
||||||
.attr('x1', 0)
|
.attr('x1', 0)
|
||||||
.attr('y1', conf.padding + titleHeight + conf.dividerMargin / 2)
|
.attr('y1', conf.padding + titleHeight + conf.dividerMargin / 2)
|
||||||
.attr('y2', conf.padding + titleHeight + conf.dividerMargin / 2)
|
.attr('y2', conf.padding + titleHeight + conf.dividerMargin / 2);
|
||||||
|
|
||||||
const members = g
|
const members = g
|
||||||
.append('text') // text label for the x axis
|
.append('text') // text label for the x axis
|
||||||
.attr('x', conf.padding)
|
.attr('x', conf.padding)
|
||||||
.attr('y', titleHeight + conf.dividerMargin + conf.textHeight)
|
.attr('y', titleHeight + conf.dividerMargin + conf.textHeight)
|
||||||
.attr('fill', 'white')
|
.attr('fill', 'white')
|
||||||
.attr('class', 'classText')
|
.attr('class', 'classText');
|
||||||
|
|
||||||
let isFirst = true
|
let isFirst = true;
|
||||||
classDef.members.forEach(function (member) {
|
classDef.members.forEach(function(member) {
|
||||||
addTspan(members, member, isFirst)
|
addTspan(members, member, isFirst);
|
||||||
isFirst = false
|
isFirst = false;
|
||||||
})
|
});
|
||||||
|
|
||||||
const membersBox = members.node().getBBox()
|
const membersBox = members.node().getBBox();
|
||||||
|
|
||||||
const methodsLine = g
|
const methodsLine = g
|
||||||
.append('line') // text label for the x axis
|
.append('line') // text label for the x axis
|
||||||
.attr('x1', 0)
|
.attr('x1', 0)
|
||||||
.attr(
|
.attr('y1', conf.padding + titleHeight + conf.dividerMargin + membersBox.height)
|
||||||
'y1',
|
.attr('y2', conf.padding + titleHeight + conf.dividerMargin + membersBox.height);
|
||||||
conf.padding + titleHeight + conf.dividerMargin + membersBox.height
|
|
||||||
)
|
|
||||||
.attr(
|
|
||||||
'y2',
|
|
||||||
conf.padding + titleHeight + conf.dividerMargin + membersBox.height
|
|
||||||
)
|
|
||||||
|
|
||||||
const methods = g
|
const methods = g
|
||||||
.append('text') // text label for the x axis
|
.append('text') // text label for the x axis
|
||||||
.attr('x', conf.padding)
|
.attr('x', conf.padding)
|
||||||
.attr(
|
.attr('y', titleHeight + 2 * conf.dividerMargin + membersBox.height + conf.textHeight)
|
||||||
'y',
|
|
||||||
titleHeight + 2 * conf.dividerMargin + membersBox.height + conf.textHeight
|
|
||||||
)
|
|
||||||
.attr('fill', 'white')
|
.attr('fill', 'white')
|
||||||
.attr('class', 'classText')
|
.attr('class', 'classText');
|
||||||
|
|
||||||
isFirst = true
|
isFirst = true;
|
||||||
|
|
||||||
classDef.methods.forEach(function (method) {
|
classDef.methods.forEach(function(method) {
|
||||||
addTspan(methods, method, isFirst)
|
addTspan(methods, method, isFirst);
|
||||||
isFirst = false
|
isFirst = false;
|
||||||
})
|
});
|
||||||
|
|
||||||
const classBox = g.node().getBBox()
|
const classBox = g.node().getBBox();
|
||||||
g.insert('rect', ':first-child')
|
g.insert('rect', ':first-child')
|
||||||
.attr('x', 0)
|
.attr('x', 0)
|
||||||
.attr('y', 0)
|
.attr('y', 0)
|
||||||
.attr('width', classBox.width + 2 * conf.padding)
|
.attr('width', classBox.width + 2 * conf.padding)
|
||||||
.attr('height', classBox.height + conf.padding + 0.5 * conf.dividerMargin)
|
.attr('height', classBox.height + conf.padding + 0.5 * conf.dividerMargin);
|
||||||
|
|
||||||
membersLine.attr('x2', classBox.width + 2 * conf.padding)
|
membersLine.attr('x2', classBox.width + 2 * conf.padding);
|
||||||
methodsLine.attr('x2', classBox.width + 2 * conf.padding)
|
methodsLine.attr('x2', classBox.width + 2 * conf.padding);
|
||||||
|
|
||||||
classInfo.width = classBox.width + 2 * conf.padding
|
classInfo.width = classBox.width + 2 * conf.padding;
|
||||||
classInfo.height = classBox.height + conf.padding + 0.5 * conf.dividerMargin
|
classInfo.height = classBox.height + conf.padding + 0.5 * conf.dividerMargin;
|
||||||
|
|
||||||
idCache[id] = classInfo
|
idCache[id] = classInfo;
|
||||||
classCnt++
|
classCnt++;
|
||||||
return classInfo
|
return classInfo;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const setConf = function (cnf) {
|
export const setConf = function(cnf) {
|
||||||
const keys = Object.keys(cnf)
|
const keys = Object.keys(cnf);
|
||||||
|
|
||||||
keys.forEach(function (key) {
|
keys.forEach(function(key) {
|
||||||
conf[key] = cnf[key]
|
conf[key] = cnf[key];
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
/**
|
/**
|
||||||
* Draws a flowchart in the tag with id: id based on the graph definition in text.
|
* Draws a flowchart in the tag with id: id based on the graph definition in text.
|
||||||
* @param text
|
* @param text
|
||||||
* @param id
|
* @param id
|
||||||
*/
|
*/
|
||||||
export const draw = function (text, id) {
|
export const draw = function(text, id) {
|
||||||
parser.yy.clear()
|
parser.yy.clear();
|
||||||
parser.parse(text)
|
parser.parse(text);
|
||||||
|
|
||||||
logger.info('Rendering diagram ' + text)
|
logger.info('Rendering diagram ' + text);
|
||||||
|
|
||||||
/// / Fetch the default direction, use TD if none was found
|
/// / Fetch the default direction, use TD if none was found
|
||||||
const diagram = d3.select(`[id='${id}']`)
|
const diagram = d3.select(`[id='${id}']`);
|
||||||
insertMarkers(diagram)
|
insertMarkers(diagram);
|
||||||
|
|
||||||
// Layout graph, Create a new directed graph
|
// Layout graph, Create a new directed graph
|
||||||
const g = new graphlib.Graph({
|
const g = new graphlib.Graph({
|
||||||
multigraph: true
|
multigraph: true
|
||||||
})
|
});
|
||||||
|
|
||||||
// Set an object for the graph label
|
// Set an object for the graph label
|
||||||
g.setGraph({
|
g.setGraph({
|
||||||
isMultiGraph: true
|
isMultiGraph: true
|
||||||
})
|
});
|
||||||
|
|
||||||
// Default to assigning a new object as a label for each new edge.
|
// Default to assigning a new object as a label for each new edge.
|
||||||
g.setDefaultEdgeLabel(function () {
|
g.setDefaultEdgeLabel(function() {
|
||||||
return {}
|
return {};
|
||||||
})
|
});
|
||||||
|
|
||||||
const classes = classDb.getClasses()
|
const classes = classDb.getClasses();
|
||||||
const keys = Object.keys(classes)
|
const keys = Object.keys(classes);
|
||||||
total = keys.length
|
total = keys.length;
|
||||||
for (let i = 0; i < keys.length; i++) {
|
for (let i = 0; i < keys.length; i++) {
|
||||||
const classDef = classes[keys[i]]
|
const classDef = classes[keys[i]];
|
||||||
const node = drawClass(diagram, classDef)
|
const node = drawClass(diagram, classDef);
|
||||||
// Add nodes to the graph. The first argument is the node id. The second is
|
// 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
|
// metadata about the node. In this case we're going to add labels to each of
|
||||||
// our nodes.
|
// our nodes.
|
||||||
g.setNode(node.id, node)
|
g.setNode(node.id, node);
|
||||||
logger.info('Org height: ' + node.height)
|
logger.info('Org height: ' + node.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
const relations = classDb.getRelations()
|
const relations = classDb.getRelations();
|
||||||
relations.forEach(function (relation) {
|
relations.forEach(function(relation) {
|
||||||
logger.info(
|
logger.info(
|
||||||
'tjoho' +
|
'tjoho' + getGraphId(relation.id1) + getGraphId(relation.id2) + JSON.stringify(relation)
|
||||||
getGraphId(relation.id1) +
|
);
|
||||||
getGraphId(relation.id2) +
|
|
||||||
JSON.stringify(relation)
|
|
||||||
)
|
|
||||||
g.setEdge(getGraphId(relation.id1), getGraphId(relation.id2), {
|
g.setEdge(getGraphId(relation.id1), getGraphId(relation.id2), {
|
||||||
relation: relation
|
relation: relation
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
dagre.layout(g)
|
dagre.layout(g);
|
||||||
g.nodes().forEach(function (v) {
|
g.nodes().forEach(function(v) {
|
||||||
if (typeof v !== 'undefined' && typeof g.node(v) !== 'undefined') {
|
if (typeof v !== 'undefined' && typeof g.node(v) !== 'undefined') {
|
||||||
logger.debug('Node ' + v + ': ' + JSON.stringify(g.node(v)))
|
logger.debug('Node ' + v + ': ' + JSON.stringify(g.node(v)));
|
||||||
d3.select('#' + v).attr(
|
d3.select('#' + v).attr(
|
||||||
'transform',
|
'transform',
|
||||||
'translate(' +
|
'translate(' +
|
||||||
@ -418,27 +396,22 @@ export const draw = function (text, id) {
|
|||||||
',' +
|
',' +
|
||||||
(g.node(v).y - g.node(v).height / 2) +
|
(g.node(v).y - g.node(v).height / 2) +
|
||||||
' )'
|
' )'
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
g.edges().forEach(function (e) {
|
g.edges().forEach(function(e) {
|
||||||
if (typeof e !== 'undefined' && typeof g.edge(e) !== 'undefined') {
|
if (typeof e !== 'undefined' && typeof g.edge(e) !== 'undefined') {
|
||||||
logger.debug(
|
logger.debug('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(g.edge(e)));
|
||||||
'Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(g.edge(e))
|
drawEdge(diagram, g.edge(e), g.edge(e).relation);
|
||||||
)
|
|
||||||
drawEdge(diagram, g.edge(e), g.edge(e).relation)
|
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
diagram.attr('height', '100%')
|
diagram.attr('height', '100%');
|
||||||
diagram.attr('width', '100%')
|
diagram.attr('width', '100%');
|
||||||
diagram.attr(
|
diagram.attr('viewBox', '0 0 ' + (g.graph().width + 20) + ' ' + (g.graph().height + 20));
|
||||||
'viewBox',
|
};
|
||||||
'0 0 ' + (g.graph().width + 20) + ' ' + (g.graph().height + 20)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setConf,
|
setConf,
|
||||||
draw
|
draw
|
||||||
}
|
};
|
||||||
|
@ -1,33 +1,33 @@
|
|||||||
import * as d3 from 'd3'
|
import * as d3 from 'd3';
|
||||||
import { sanitizeUrl } from '@braintree/sanitize-url'
|
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||||
import { logger } from '../../logger'
|
import { logger } from '../../logger';
|
||||||
import utils from '../../utils'
|
import utils from '../../utils';
|
||||||
import { getConfig } from '../../config'
|
import { getConfig } from '../../config';
|
||||||
|
|
||||||
const config = getConfig()
|
const config = getConfig();
|
||||||
let vertices = {}
|
let vertices = {};
|
||||||
let edges = []
|
let edges = [];
|
||||||
let classes = []
|
let classes = [];
|
||||||
let subGraphs = []
|
let subGraphs = [];
|
||||||
let subGraphLookup = {}
|
let subGraphLookup = {};
|
||||||
let tooltips = {}
|
let tooltips = {};
|
||||||
let subCount = 0
|
let subCount = 0;
|
||||||
let direction
|
let direction;
|
||||||
// Functions to be run after graph rendering
|
// Functions to be run after graph rendering
|
||||||
let funs = []
|
let funs = [];
|
||||||
|
|
||||||
const sanitize = text => {
|
const sanitize = text => {
|
||||||
let txt = text
|
let txt = text;
|
||||||
if (config.securityLevel !== 'loose') {
|
if (config.securityLevel !== 'loose') {
|
||||||
txt = txt.replace(/<br>/g, '#br#')
|
txt = txt.replace(/<br>/g, '#br#');
|
||||||
txt = txt.replace(/<br\S*?\/>/g, '#br#')
|
txt = txt.replace(/<br\S*?\/>/g, '#br#');
|
||||||
txt = txt.replace(/</g, '<').replace(/>/g, '>')
|
txt = txt.replace(/</g, '<').replace(/>/g, '>');
|
||||||
txt = txt.replace(/=/g, '=')
|
txt = txt.replace(/=/g, '=');
|
||||||
txt = txt.replace(/#br#/g, '<br/>')
|
txt = txt.replace(/#br#/g, '<br/>');
|
||||||
}
|
}
|
||||||
|
|
||||||
return txt
|
return txt;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function called by parser when a node definition has been found
|
* Function called by parser when a node definition has been found
|
||||||
@ -37,53 +37,53 @@ const sanitize = text => {
|
|||||||
* @param style
|
* @param style
|
||||||
* @param classes
|
* @param classes
|
||||||
*/
|
*/
|
||||||
export const addVertex = function (_id, text, type, style, classes) {
|
export const addVertex = function(_id, text, type, style, classes) {
|
||||||
let txt
|
let txt;
|
||||||
let id = _id
|
let id = _id;
|
||||||
if (typeof id === 'undefined') {
|
if (typeof id === 'undefined') {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
if (id.trim().length === 0) {
|
if (id.trim().length === 0) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id[0].match(/\d/)) id = 's' + id
|
if (id[0].match(/\d/)) id = 's' + id;
|
||||||
|
|
||||||
if (typeof vertices[id] === 'undefined') {
|
if (typeof vertices[id] === 'undefined') {
|
||||||
vertices[id] = { id: id, styles: [], classes: [] }
|
vertices[id] = { id: id, styles: [], classes: [] };
|
||||||
}
|
}
|
||||||
if (typeof text !== 'undefined') {
|
if (typeof text !== 'undefined') {
|
||||||
txt = sanitize(text.trim())
|
txt = sanitize(text.trim());
|
||||||
|
|
||||||
// strip quotes if string starts and exnds with a quote
|
// strip quotes if string starts and exnds with a quote
|
||||||
if (txt[0] === '"' && txt[txt.length - 1] === '"') {
|
if (txt[0] === '"' && txt[txt.length - 1] === '"') {
|
||||||
txt = txt.substring(1, txt.length - 1)
|
txt = txt.substring(1, txt.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
vertices[id].text = txt
|
vertices[id].text = txt;
|
||||||
} else {
|
} else {
|
||||||
if (!vertices[id].text) {
|
if (!vertices[id].text) {
|
||||||
vertices[id].text = _id
|
vertices[id].text = _id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeof type !== 'undefined') {
|
if (typeof type !== 'undefined') {
|
||||||
vertices[id].type = type
|
vertices[id].type = type;
|
||||||
}
|
}
|
||||||
if (typeof style !== 'undefined') {
|
if (typeof style !== 'undefined') {
|
||||||
if (style !== null) {
|
if (style !== null) {
|
||||||
style.forEach(function (s) {
|
style.forEach(function(s) {
|
||||||
vertices[id].styles.push(s)
|
vertices[id].styles.push(s);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeof classes !== 'undefined') {
|
if (typeof classes !== 'undefined') {
|
||||||
if (classes !== null) {
|
if (classes !== null) {
|
||||||
classes.forEach(function (s) {
|
classes.forEach(function(s) {
|
||||||
vertices[id].classes.push(s)
|
vertices[id].classes.push(s);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function called by parser when a link/edge definition has been found
|
* Function called by parser when a link/edge definition has been found
|
||||||
@ -92,134 +92,138 @@ export const addVertex = function (_id, text, type, style, classes) {
|
|||||||
* @param type
|
* @param type
|
||||||
* @param linktext
|
* @param linktext
|
||||||
*/
|
*/
|
||||||
export const addLink = function (_start, _end, type, linktext) {
|
export const addLink = function(_start, _end, type, linktext) {
|
||||||
let start = _start
|
let start = _start;
|
||||||
let end = _end
|
let end = _end;
|
||||||
if (start[0].match(/\d/)) start = 's' + start
|
if (start[0].match(/\d/)) start = 's' + start;
|
||||||
if (end[0].match(/\d/)) end = 's' + end
|
if (end[0].match(/\d/)) end = 's' + end;
|
||||||
logger.info('Got edge...', start, end)
|
logger.info('Got edge...', start, end);
|
||||||
|
|
||||||
const edge = { start: start, end: end, type: undefined, text: '' }
|
const edge = { start: start, end: end, type: undefined, text: '' };
|
||||||
linktext = type.text
|
linktext = type.text;
|
||||||
|
|
||||||
if (typeof linktext !== 'undefined') {
|
if (typeof linktext !== 'undefined') {
|
||||||
edge.text = sanitize(linktext.trim())
|
edge.text = sanitize(linktext.trim());
|
||||||
|
|
||||||
// strip quotes if string starts and exnds with a quote
|
// strip quotes if string starts and exnds with a quote
|
||||||
if (edge.text[0] === '"' && edge.text[edge.text.length - 1] === '"') {
|
if (edge.text[0] === '"' && edge.text[edge.text.length - 1] === '"') {
|
||||||
edge.text = edge.text.substring(1, edge.text.length - 1)
|
edge.text = edge.text.substring(1, edge.text.length - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof type !== 'undefined') {
|
if (typeof type !== 'undefined') {
|
||||||
edge.type = type.type
|
edge.type = type.type;
|
||||||
edge.stroke = type.stroke
|
edge.stroke = type.stroke;
|
||||||
}
|
}
|
||||||
edges.push(edge)
|
edges.push(edge);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates a link's line interpolation algorithm
|
* Updates a link's line interpolation algorithm
|
||||||
* @param pos
|
* @param pos
|
||||||
* @param interpolate
|
* @param interpolate
|
||||||
*/
|
*/
|
||||||
export const updateLinkInterpolate = function (positions, interp) {
|
export const updateLinkInterpolate = function(positions, interp) {
|
||||||
positions.forEach(function (pos) {
|
positions.forEach(function(pos) {
|
||||||
if (pos === 'default') {
|
if (pos === 'default') {
|
||||||
edges.defaultInterpolate = interp
|
edges.defaultInterpolate = interp;
|
||||||
} else {
|
} else {
|
||||||
edges[pos].interpolate = interp
|
edges[pos].interpolate = interp;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates a link with a style
|
* Updates a link with a style
|
||||||
* @param pos
|
* @param pos
|
||||||
* @param style
|
* @param style
|
||||||
*/
|
*/
|
||||||
export const updateLink = function (positions, style) {
|
export const updateLink = function(positions, style) {
|
||||||
positions.forEach(function (pos) {
|
positions.forEach(function(pos) {
|
||||||
if (pos === 'default') {
|
if (pos === 'default') {
|
||||||
edges.defaultStyle = style
|
edges.defaultStyle = style;
|
||||||
} else {
|
} else {
|
||||||
if (utils.isSubstringInArray('fill', style) === -1) {
|
if (utils.isSubstringInArray('fill', style) === -1) {
|
||||||
style.push('fill:none')
|
style.push('fill:none');
|
||||||
}
|
}
|
||||||
edges[pos].style = style
|
edges[pos].style = style;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
export const addClass = function (id, style) {
|
export const addClass = function(id, style) {
|
||||||
if (typeof classes[id] === 'undefined') {
|
if (typeof classes[id] === 'undefined') {
|
||||||
classes[id] = { id: id, styles: [] }
|
classes[id] = { id: id, styles: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof style !== 'undefined') {
|
if (typeof style !== 'undefined') {
|
||||||
if (style !== null) {
|
if (style !== null) {
|
||||||
style.forEach(function (s) {
|
style.forEach(function(s) {
|
||||||
classes[id].styles.push(s)
|
classes[id].styles.push(s);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by parser when a graph definition is found, stores the direction of the chart.
|
* Called by parser when a graph definition is found, stores the direction of the chart.
|
||||||
* @param dir
|
* @param dir
|
||||||
*/
|
*/
|
||||||
export const setDirection = function (dir) {
|
export const setDirection = function(dir) {
|
||||||
direction = dir
|
direction = dir;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by parser when a special node is found, e.g. a clickable element.
|
* Called by parser when a special node is found, e.g. a clickable element.
|
||||||
* @param ids Comma separated list of ids
|
* @param ids Comma separated list of ids
|
||||||
* @param className Class to add
|
* @param className Class to add
|
||||||
*/
|
*/
|
||||||
export const setClass = function (ids, className) {
|
export const setClass = function(ids, className) {
|
||||||
ids.split(',').forEach(function (_id) {
|
ids.split(',').forEach(function(_id) {
|
||||||
let id = _id
|
let id = _id;
|
||||||
if (_id[0].match(/\d/)) id = 's' + id
|
if (_id[0].match(/\d/)) id = 's' + id;
|
||||||
if (typeof vertices[id] !== 'undefined') {
|
if (typeof vertices[id] !== 'undefined') {
|
||||||
vertices[id].classes.push(className)
|
vertices[id].classes.push(className);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof subGraphLookup[id] !== 'undefined') {
|
if (typeof subGraphLookup[id] !== 'undefined') {
|
||||||
subGraphLookup[id].classes.push(className)
|
subGraphLookup[id].classes.push(className);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
const setTooltip = function (ids, tooltip) {
|
const setTooltip = function(ids, tooltip) {
|
||||||
ids.split(',').forEach(function (id) {
|
ids.split(',').forEach(function(id) {
|
||||||
if (typeof tooltip !== 'undefined') {
|
if (typeof tooltip !== 'undefined') {
|
||||||
tooltips[id] = sanitize(tooltip)
|
tooltips[id] = sanitize(tooltip);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
const setClickFun = function (_id, functionName) {
|
const setClickFun = function(_id, functionName) {
|
||||||
let id = _id
|
let id = _id;
|
||||||
if (_id[0].match(/\d/)) id = 's' + id
|
if (_id[0].match(/\d/)) id = 's' + id;
|
||||||
if (config.securityLevel !== 'loose') {
|
if (config.securityLevel !== 'loose') {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
if (typeof functionName === 'undefined') {
|
if (typeof functionName === 'undefined') {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
if (typeof vertices[id] !== 'undefined') {
|
if (typeof vertices[id] !== 'undefined') {
|
||||||
funs.push(function (element) {
|
funs.push(function(element) {
|
||||||
const elem = document.querySelector(`[id="${id}"]`)
|
const elem = document.querySelector(`[id="${id}"]`);
|
||||||
if (elem !== null) {
|
if (elem !== null) {
|
||||||
elem.addEventListener('click', function () {
|
elem.addEventListener(
|
||||||
window[functionName](id)
|
'click',
|
||||||
}, false)
|
function() {
|
||||||
|
window[functionName](id);
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by parser when a link is found. Adds the URL to the vertex data.
|
* Called by parser when a link is found. Adds the URL to the vertex data.
|
||||||
@ -227,24 +231,24 @@ const setClickFun = function (_id, functionName) {
|
|||||||
* @param linkStr URL to create a link for
|
* @param linkStr URL to create a link for
|
||||||
* @param tooltip Tooltip for the clickable element
|
* @param tooltip Tooltip for the clickable element
|
||||||
*/
|
*/
|
||||||
export const setLink = function (ids, linkStr, tooltip) {
|
export const setLink = function(ids, linkStr, tooltip) {
|
||||||
ids.split(',').forEach(function (_id) {
|
ids.split(',').forEach(function(_id) {
|
||||||
let id = _id
|
let id = _id;
|
||||||
if (_id[0].match(/\d/)) id = 's' + id
|
if (_id[0].match(/\d/)) id = 's' + id;
|
||||||
if (typeof vertices[id] !== 'undefined') {
|
if (typeof vertices[id] !== 'undefined') {
|
||||||
if (config.securityLevel !== 'loose') {
|
if (config.securityLevel !== 'loose') {
|
||||||
vertices[id].link = sanitizeUrl(linkStr) // .replace(/javascript:.*/g, '')
|
vertices[id].link = sanitizeUrl(linkStr); // .replace(/javascript:.*/g, '')
|
||||||
} else {
|
} else {
|
||||||
vertices[id].link = linkStr
|
vertices[id].link = linkStr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
setTooltip(ids, tooltip)
|
setTooltip(ids, tooltip);
|
||||||
setClass(ids, 'clickable')
|
setClass(ids, 'clickable');
|
||||||
}
|
};
|
||||||
export const getTooltip = function (id) {
|
export const getTooltip = function(id) {
|
||||||
return tooltips[id]
|
return tooltips[id];
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by parser when a click definition is found. Registers an event handler.
|
* Called by parser when a click definition is found. Registers an event handler.
|
||||||
@ -252,209 +256,219 @@ export const getTooltip = function (id) {
|
|||||||
* @param functionName Function to be called on click
|
* @param functionName Function to be called on click
|
||||||
* @param tooltip Tooltip for the clickable element
|
* @param tooltip Tooltip for the clickable element
|
||||||
*/
|
*/
|
||||||
export const setClickEvent = function (ids, functionName, tooltip) {
|
export const setClickEvent = function(ids, functionName, tooltip) {
|
||||||
ids.split(',').forEach(function (id) { setClickFun(id, functionName) })
|
ids.split(',').forEach(function(id) {
|
||||||
setTooltip(ids, tooltip)
|
setClickFun(id, functionName);
|
||||||
setClass(ids, 'clickable')
|
});
|
||||||
}
|
setTooltip(ids, tooltip);
|
||||||
|
setClass(ids, 'clickable');
|
||||||
|
};
|
||||||
|
|
||||||
export const bindFunctions = function (element) {
|
export const bindFunctions = function(element) {
|
||||||
funs.forEach(function (fun) {
|
funs.forEach(function(fun) {
|
||||||
fun(element)
|
fun(element);
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
export const getDirection = function () {
|
export const getDirection = function() {
|
||||||
return direction
|
return direction;
|
||||||
}
|
};
|
||||||
/**
|
/**
|
||||||
* Retrieval function for fetching the found nodes after parsing has completed.
|
* Retrieval function for fetching the found nodes after parsing has completed.
|
||||||
* @returns {{}|*|vertices}
|
* @returns {{}|*|vertices}
|
||||||
*/
|
*/
|
||||||
export const getVertices = function () {
|
export const getVertices = function() {
|
||||||
return vertices
|
return vertices;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieval function for fetching the found links after parsing has completed.
|
* Retrieval function for fetching the found links after parsing has completed.
|
||||||
* @returns {{}|*|edges}
|
* @returns {{}|*|edges}
|
||||||
*/
|
*/
|
||||||
export const getEdges = function () {
|
export const getEdges = function() {
|
||||||
return edges
|
return edges;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieval function for fetching the found class definitions after parsing has completed.
|
* Retrieval function for fetching the found class definitions after parsing has completed.
|
||||||
* @returns {{}|*|classes}
|
* @returns {{}|*|classes}
|
||||||
*/
|
*/
|
||||||
export const getClasses = function () {
|
export const getClasses = function() {
|
||||||
return classes
|
return classes;
|
||||||
}
|
};
|
||||||
|
|
||||||
const setupToolTips = function (element) {
|
const setupToolTips = function(element) {
|
||||||
let tooltipElem = d3.select('.mermaidTooltip')
|
let tooltipElem = d3.select('.mermaidTooltip');
|
||||||
if ((tooltipElem._groups || tooltipElem)[0][0] === null) {
|
if ((tooltipElem._groups || tooltipElem)[0][0] === null) {
|
||||||
tooltipElem = d3.select('body')
|
tooltipElem = d3
|
||||||
|
.select('body')
|
||||||
.append('div')
|
.append('div')
|
||||||
.attr('class', 'mermaidTooltip')
|
.attr('class', 'mermaidTooltip')
|
||||||
.style('opacity', 0)
|
.style('opacity', 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const svg = d3.select(element).select('svg')
|
const svg = d3.select(element).select('svg');
|
||||||
|
|
||||||
const nodes = svg.selectAll('g.node')
|
const nodes = svg.selectAll('g.node');
|
||||||
nodes
|
nodes
|
||||||
.on('mouseover', function () {
|
.on('mouseover', function() {
|
||||||
const el = d3.select(this)
|
const el = d3.select(this);
|
||||||
const title = el.attr('title')
|
const title = el.attr('title');
|
||||||
// Dont try to draw a tooltip if no data is provided
|
// Dont try to draw a tooltip if no data is provided
|
||||||
if (title === null) {
|
if (title === null) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const rect = this.getBoundingClientRect()
|
const rect = this.getBoundingClientRect();
|
||||||
|
|
||||||
tooltipElem.transition()
|
tooltipElem
|
||||||
|
.transition()
|
||||||
.duration(200)
|
.duration(200)
|
||||||
.style('opacity', '.9')
|
.style('opacity', '.9');
|
||||||
tooltipElem.html(el.attr('title'))
|
tooltipElem
|
||||||
.style('left', (rect.left + (rect.right - rect.left) / 2) + 'px')
|
.html(el.attr('title'))
|
||||||
.style('top', (rect.top - 14 + document.body.scrollTop) + 'px')
|
.style('left', rect.left + (rect.right - rect.left) / 2 + 'px')
|
||||||
el.classed('hover', true)
|
.style('top', rect.top - 14 + document.body.scrollTop + 'px');
|
||||||
|
el.classed('hover', true);
|
||||||
})
|
})
|
||||||
.on('mouseout', function () {
|
.on('mouseout', function() {
|
||||||
tooltipElem.transition()
|
tooltipElem
|
||||||
|
.transition()
|
||||||
.duration(500)
|
.duration(500)
|
||||||
.style('opacity', 0)
|
.style('opacity', 0);
|
||||||
const el = d3.select(this)
|
const el = d3.select(this);
|
||||||
el.classed('hover', false)
|
el.classed('hover', false);
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
funs.push(setupToolTips)
|
funs.push(setupToolTips);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the internal graph db so that a new graph can be parsed.
|
* Clears the internal graph db so that a new graph can be parsed.
|
||||||
*/
|
*/
|
||||||
export const clear = function () {
|
export const clear = function() {
|
||||||
vertices = {}
|
vertices = {};
|
||||||
classes = {}
|
classes = {};
|
||||||
edges = []
|
edges = [];
|
||||||
funs = []
|
funs = [];
|
||||||
funs.push(setupToolTips)
|
funs.push(setupToolTips);
|
||||||
subGraphs = []
|
subGraphs = [];
|
||||||
subGraphLookup = {}
|
subGraphLookup = {};
|
||||||
subCount = 0
|
subCount = 0;
|
||||||
tooltips = []
|
tooltips = [];
|
||||||
}
|
};
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
export const defaultStyle = function () {
|
export const defaultStyle = function() {
|
||||||
return 'fill:#ffa;stroke: #f66; stroke-width: 3px; stroke-dasharray: 5, 5;fill:#ffa;stroke: #666;'
|
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.
|
* Clears the internal graph db so that a new graph can be parsed.
|
||||||
*/
|
*/
|
||||||
export const addSubGraph = function (_id, list, _title) {
|
export const addSubGraph = function(_id, list, _title) {
|
||||||
let id = _id
|
let id = _id;
|
||||||
let title = _title
|
let title = _title;
|
||||||
if (_id === _title && _title.match(/\s/)) {
|
if (_id === _title && _title.match(/\s/)) {
|
||||||
id = undefined
|
id = undefined;
|
||||||
}
|
}
|
||||||
function uniq (a) {
|
function uniq(a) {
|
||||||
const prims = { 'boolean': {}, 'number': {}, 'string': {} }
|
const prims = { boolean: {}, number: {}, string: {} };
|
||||||
const objs = []
|
const objs = [];
|
||||||
|
|
||||||
return a.filter(function (item) {
|
return a.filter(function(item) {
|
||||||
const type = typeof item
|
const type = typeof item;
|
||||||
if (item.trim() === '') {
|
if (item.trim() === '') {
|
||||||
return false
|
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) }
|
if (type in prims) {
|
||||||
})
|
return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true);
|
||||||
|
} else {
|
||||||
|
return objs.indexOf(item) >= 0 ? false : objs.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let nodeList = []
|
let nodeList = [];
|
||||||
|
|
||||||
nodeList = uniq(nodeList.concat.apply(nodeList, list))
|
nodeList = uniq(nodeList.concat.apply(nodeList, list));
|
||||||
for (let i = 0; i < nodeList.length; i++) {
|
for (let i = 0; i < nodeList.length; i++) {
|
||||||
if (nodeList[i][0].match(/\d/)) nodeList[i] = 's' + nodeList[i]
|
if (nodeList[i][0].match(/\d/)) nodeList[i] = 's' + nodeList[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
id = id || ('subGraph' + subCount)
|
id = id || 'subGraph' + subCount;
|
||||||
if (id[0].match(/\d/)) id = 's' + id
|
if (id[0].match(/\d/)) id = 's' + id;
|
||||||
title = title || ''
|
title = title || '';
|
||||||
title = sanitize(title)
|
title = sanitize(title);
|
||||||
subCount = subCount + 1
|
subCount = subCount + 1;
|
||||||
const subGraph = { id: id, nodes: nodeList, title: title.trim(), classes: [] }
|
const subGraph = { id: id, nodes: nodeList, title: title.trim(), classes: [] };
|
||||||
subGraphs.push(subGraph)
|
subGraphs.push(subGraph);
|
||||||
subGraphLookup[id] = subGraph
|
subGraphLookup[id] = subGraph;
|
||||||
return id
|
return id;
|
||||||
}
|
};
|
||||||
|
|
||||||
const getPosForId = function (id) {
|
const getPosForId = function(id) {
|
||||||
for (let i = 0; i < subGraphs.length; i++) {
|
for (let i = 0; i < subGraphs.length; i++) {
|
||||||
if (subGraphs[i].id === id) {
|
if (subGraphs[i].id === id) {
|
||||||
return i
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1
|
return -1;
|
||||||
}
|
};
|
||||||
let secCount = -1
|
let secCount = -1;
|
||||||
const posCrossRef = []
|
const posCrossRef = [];
|
||||||
const indexNodes2 = function (id, pos) {
|
const indexNodes2 = function(id, pos) {
|
||||||
const nodes = subGraphs[pos].nodes
|
const nodes = subGraphs[pos].nodes;
|
||||||
secCount = secCount + 1
|
secCount = secCount + 1;
|
||||||
if (secCount > 2000) {
|
if (secCount > 2000) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
posCrossRef[secCount] = pos
|
posCrossRef[secCount] = pos;
|
||||||
// Check if match
|
// Check if match
|
||||||
if (subGraphs[pos].id === id) {
|
if (subGraphs[pos].id === id) {
|
||||||
return {
|
return {
|
||||||
result: true,
|
result: true,
|
||||||
count: 0
|
count: 0
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let count = 0
|
let count = 0;
|
||||||
let posCount = 1
|
let posCount = 1;
|
||||||
while (count < nodes.length) {
|
while (count < nodes.length) {
|
||||||
const childPos = getPosForId(nodes[count])
|
const childPos = getPosForId(nodes[count]);
|
||||||
// Ignore regular nodes (pos will be -1)
|
// Ignore regular nodes (pos will be -1)
|
||||||
if (childPos >= 0) {
|
if (childPos >= 0) {
|
||||||
const res = indexNodes2(id, childPos)
|
const res = indexNodes2(id, childPos);
|
||||||
if (res.result) {
|
if (res.result) {
|
||||||
return {
|
return {
|
||||||
result: true,
|
result: true,
|
||||||
count: posCount + res.count
|
count: posCount + res.count
|
||||||
}
|
};
|
||||||
} else {
|
} else {
|
||||||
posCount = posCount + res.count
|
posCount = posCount + res.count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
count = count + 1
|
count = count + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
result: false,
|
result: false,
|
||||||
count: posCount
|
count: posCount
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getDepthFirstPos = function (pos) {
|
export const getDepthFirstPos = function(pos) {
|
||||||
return posCrossRef[pos]
|
return posCrossRef[pos];
|
||||||
}
|
};
|
||||||
export const indexNodes = function () {
|
export const indexNodes = function() {
|
||||||
secCount = -1
|
secCount = -1;
|
||||||
if (subGraphs.length > 0) {
|
if (subGraphs.length > 0) {
|
||||||
indexNodes2('none', subGraphs.length - 1, 0)
|
indexNodes2('none', subGraphs.length - 1, 0);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getSubGraphs = function () {
|
export const getSubGraphs = function() {
|
||||||
return subGraphs
|
return subGraphs;
|
||||||
}
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
addVertex,
|
addVertex,
|
||||||
@ -478,4 +492,4 @@ export default {
|
|||||||
getDepthFirstPos,
|
getDepthFirstPos,
|
||||||
indexNodes,
|
indexNodes,
|
||||||
getSubGraphs
|
getSubGraphs
|
||||||
}
|
};
|
||||||
|
@ -1,274 +1,288 @@
|
|||||||
import graphlib from 'graphlibrary'
|
import graphlib from 'graphlibrary';
|
||||||
import * as d3 from 'd3'
|
import * as d3 from 'd3';
|
||||||
|
|
||||||
import flowDb from './flowDb'
|
import flowDb from './flowDb';
|
||||||
import flow from './parser/flow'
|
import flow from './parser/flow';
|
||||||
import { getConfig } from '../../config'
|
import { getConfig } from '../../config';
|
||||||
import dagreD3 from 'dagre-d3-renderer'
|
import dagreD3 from 'dagre-d3-renderer';
|
||||||
import addHtmlLabel from 'dagre-d3-renderer/lib/label/add-html-label.js'
|
import addHtmlLabel from 'dagre-d3-renderer/lib/label/add-html-label.js';
|
||||||
import { logger } from '../../logger'
|
import { logger } from '../../logger';
|
||||||
import { interpolateToCurve } from '../../utils'
|
import { interpolateToCurve } from '../../utils';
|
||||||
|
|
||||||
const conf = {
|
const conf = {};
|
||||||
}
|
export const setConf = function(cnf) {
|
||||||
export const setConf = function (cnf) {
|
const keys = Object.keys(cnf);
|
||||||
const keys = Object.keys(cnf)
|
|
||||||
for (let i = 0; i < keys.length; i++) {
|
for (let i = 0; i < keys.length; i++) {
|
||||||
conf[keys[i]] = cnf[keys[i]]
|
conf[keys[i]] = cnf[keys[i]];
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function that adds the vertices found in the graph definition to the graph to be rendered.
|
* Function that adds the vertices found in the graph definition to the graph to be rendered.
|
||||||
* @param vert Object containing the vertices.
|
* @param vert Object containing the vertices.
|
||||||
* @param g The graph that is to be drawn.
|
* @param g The graph that is to be drawn.
|
||||||
*/
|
*/
|
||||||
export const addVertices = function (vert, g, svgId) {
|
export const addVertices = function(vert, g, svgId) {
|
||||||
const svg = d3.select(`[id="${svgId}"]`)
|
const svg = d3.select(`[id="${svgId}"]`);
|
||||||
const keys = Object.keys(vert)
|
const keys = Object.keys(vert);
|
||||||
|
|
||||||
const styleFromStyleArr = function (styleStr, arr, { label }) {
|
const styleFromStyleArr = function(styleStr, arr, { label }) {
|
||||||
if (!label) {
|
if (!label) {
|
||||||
// Create a compound style definition from the style definitions found for the node in the graph definition
|
// Create a compound style definition from the style definitions found for the node in the graph definition
|
||||||
for (let i = 0; i < arr.length; i++) {
|
for (let i = 0; i < arr.length; i++) {
|
||||||
if (typeof arr[i] !== 'undefined') {
|
if (typeof arr[i] !== 'undefined') {
|
||||||
styleStr = styleStr + arr[i] + ';'
|
styleStr = styleStr + arr[i] + ';';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let i = 0; i < arr.length; i++) {
|
for (let i = 0; i < arr.length; i++) {
|
||||||
if (typeof arr[i] !== 'undefined') {
|
if (typeof arr[i] !== 'undefined') {
|
||||||
if (arr[i].match('^color:')) styleStr = styleStr + arr[i] + ';'
|
if (arr[i].match('^color:')) styleStr = styleStr + arr[i] + ';';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return styleStr
|
return styleStr;
|
||||||
}
|
};
|
||||||
|
|
||||||
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
|
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
|
||||||
keys.forEach(function (id) {
|
keys.forEach(function(id) {
|
||||||
const vertex = vert[id]
|
const vertex = vert[id];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable for storing the classes for the vertex
|
* Variable for storing the classes for the vertex
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
let classStr = ''
|
let classStr = '';
|
||||||
if (vertex.classes.length > 0) {
|
if (vertex.classes.length > 0) {
|
||||||
classStr = vertex.classes.join(' ')
|
classStr = vertex.classes.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable for storing the extracted style for the vertex
|
* Variable for storing the extracted style for the vertex
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
let style = ''
|
let style = '';
|
||||||
// Create a compound style definition from the style definitions found for the node in the graph definition
|
// Create a compound style definition from the style definitions found for the node in the graph definition
|
||||||
style = styleFromStyleArr(style, vertex.styles, { label: false })
|
style = styleFromStyleArr(style, vertex.styles, { label: false });
|
||||||
let labelStyle = ''
|
let labelStyle = '';
|
||||||
labelStyle = styleFromStyleArr(labelStyle, vertex.styles, { label: true })
|
labelStyle = styleFromStyleArr(labelStyle, vertex.styles, { label: true });
|
||||||
|
|
||||||
// Use vertex id as text in the box if no text is provided by the graph definition
|
// Use vertex id as text in the box if no text is provided by the graph definition
|
||||||
let vertexText = vertex.text !== undefined ? vertex.text : vertex.id
|
let vertexText = vertex.text !== undefined ? vertex.text : vertex.id;
|
||||||
|
|
||||||
// We create a SVG label, either by delegating to addHtmlLabel or manually
|
// We create a SVG label, either by delegating to addHtmlLabel or manually
|
||||||
let vertexNode
|
let vertexNode;
|
||||||
if (getConfig().flowchart.htmlLabels) {
|
if (getConfig().flowchart.htmlLabels) {
|
||||||
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
||||||
const node = { label: vertexText.replace(/fa[lrsb]?:fa-[\w-]+/g, s => `<i class='${s.replace(':', ' ')}'></i>`) }
|
const node = {
|
||||||
vertexNode = addHtmlLabel(svg, node).node()
|
label: vertexText.replace(
|
||||||
vertexNode.parentNode.removeChild(vertexNode)
|
/fa[lrsb]?:fa-[\w-]+/g,
|
||||||
|
s => `<i class='${s.replace(':', ' ')}'></i>`
|
||||||
|
)
|
||||||
|
};
|
||||||
|
vertexNode = addHtmlLabel(svg, node).node();
|
||||||
|
vertexNode.parentNode.removeChild(vertexNode);
|
||||||
} else {
|
} else {
|
||||||
const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text')
|
const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||||
|
|
||||||
const rows = vertexText.split(/<br[/]{0,1}>/)
|
const rows = vertexText.split(/<br[/]{0,1}>/);
|
||||||
|
|
||||||
for (let j = 0; j < rows.length; j++) {
|
for (let j = 0; j < rows.length; j++) {
|
||||||
const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan')
|
const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
|
||||||
tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve')
|
tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
|
||||||
tspan.setAttribute('dy', '1em')
|
tspan.setAttribute('dy', '1em');
|
||||||
tspan.setAttribute('x', '1')
|
tspan.setAttribute('x', '1');
|
||||||
tspan.textContent = rows[j]
|
tspan.textContent = rows[j];
|
||||||
svgLabel.appendChild(tspan)
|
svgLabel.appendChild(tspan);
|
||||||
}
|
}
|
||||||
vertexNode = svgLabel
|
vertexNode = svgLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the node has a link, we wrap it in a SVG link
|
// If the node has a link, we wrap it in a SVG link
|
||||||
if (vertex.link) {
|
if (vertex.link) {
|
||||||
const link = document.createElementNS('http://www.w3.org/2000/svg', 'a')
|
const link = document.createElementNS('http://www.w3.org/2000/svg', 'a');
|
||||||
link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link)
|
link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link);
|
||||||
link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener')
|
link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener');
|
||||||
link.appendChild(vertexNode)
|
link.appendChild(vertexNode);
|
||||||
vertexNode = link
|
vertexNode = link;
|
||||||
}
|
}
|
||||||
|
|
||||||
let radious = 0
|
let radious = 0;
|
||||||
let _shape = ''
|
let _shape = '';
|
||||||
// Set the shape based parameters
|
// Set the shape based parameters
|
||||||
switch (vertex.type) {
|
switch (vertex.type) {
|
||||||
case 'round':
|
case 'round':
|
||||||
radious = 5
|
radious = 5;
|
||||||
_shape = 'rect'
|
_shape = 'rect';
|
||||||
break
|
break;
|
||||||
case 'square':
|
case 'square':
|
||||||
_shape = 'rect'
|
_shape = 'rect';
|
||||||
break
|
break;
|
||||||
case 'diamond':
|
case 'diamond':
|
||||||
_shape = 'question'
|
_shape = 'question';
|
||||||
break
|
break;
|
||||||
case 'odd':
|
case 'odd':
|
||||||
_shape = 'rect_left_inv_arrow'
|
_shape = 'rect_left_inv_arrow';
|
||||||
break
|
break;
|
||||||
case 'lean_right':
|
case 'lean_right':
|
||||||
_shape = 'lean_right'
|
_shape = 'lean_right';
|
||||||
break
|
break;
|
||||||
case 'lean_left':
|
case 'lean_left':
|
||||||
_shape = 'lean_left'
|
_shape = 'lean_left';
|
||||||
break
|
break;
|
||||||
case 'trapezoid':
|
case 'trapezoid':
|
||||||
_shape = 'trapezoid'
|
_shape = 'trapezoid';
|
||||||
break
|
break;
|
||||||
case 'inv_trapezoid':
|
case 'inv_trapezoid':
|
||||||
_shape = 'inv_trapezoid'
|
_shape = 'inv_trapezoid';
|
||||||
break
|
break;
|
||||||
case 'odd_right':
|
case 'odd_right':
|
||||||
_shape = 'rect_left_inv_arrow'
|
_shape = 'rect_left_inv_arrow';
|
||||||
break
|
break;
|
||||||
case 'circle':
|
case 'circle':
|
||||||
_shape = 'circle'
|
_shape = 'circle';
|
||||||
break
|
break;
|
||||||
case 'ellipse':
|
case 'ellipse':
|
||||||
_shape = 'ellipse'
|
_shape = 'ellipse';
|
||||||
break
|
break;
|
||||||
case 'group':
|
case 'group':
|
||||||
_shape = 'rect'
|
_shape = 'rect';
|
||||||
break
|
break;
|
||||||
default:
|
default:
|
||||||
_shape = 'rect'
|
_shape = 'rect';
|
||||||
}
|
}
|
||||||
// Add the node
|
// Add the node
|
||||||
g.setNode(vertex.id, { labelType: 'svg', labelStyle: labelStyle, shape: _shape, label: vertexNode, rx: radious, ry: radious, 'class': classStr, style: style, id: vertex.id })
|
g.setNode(vertex.id, {
|
||||||
})
|
labelType: 'svg',
|
||||||
}
|
labelStyle: labelStyle,
|
||||||
|
shape: _shape,
|
||||||
|
label: vertexNode,
|
||||||
|
rx: radious,
|
||||||
|
ry: radious,
|
||||||
|
class: classStr,
|
||||||
|
style: style,
|
||||||
|
id: vertex.id
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add edges to graph based on parsed graph defninition
|
* Add edges to graph based on parsed graph defninition
|
||||||
* @param {Object} edges The edges to add to the graph
|
* @param {Object} edges The edges to add to the graph
|
||||||
* @param {Object} g The graph object
|
* @param {Object} g The graph object
|
||||||
*/
|
*/
|
||||||
export const addEdges = function (edges, g) {
|
export const addEdges = function(edges, g) {
|
||||||
let cnt = 0
|
let cnt = 0;
|
||||||
|
|
||||||
let defaultStyle
|
let defaultStyle;
|
||||||
if (typeof edges.defaultStyle !== 'undefined') {
|
if (typeof edges.defaultStyle !== 'undefined') {
|
||||||
defaultStyle = edges.defaultStyle.toString().replace(/,/g, ';')
|
defaultStyle = edges.defaultStyle.toString().replace(/,/g, ';');
|
||||||
}
|
}
|
||||||
|
|
||||||
edges.forEach(function (edge) {
|
edges.forEach(function(edge) {
|
||||||
cnt++
|
cnt++;
|
||||||
const edgeData = {}
|
const edgeData = {};
|
||||||
|
|
||||||
// Set link type for rendering
|
// Set link type for rendering
|
||||||
if (edge.type === 'arrow_open') {
|
if (edge.type === 'arrow_open') {
|
||||||
edgeData.arrowhead = 'none'
|
edgeData.arrowhead = 'none';
|
||||||
} else {
|
} else {
|
||||||
edgeData.arrowhead = 'normal'
|
edgeData.arrowhead = 'normal';
|
||||||
}
|
}
|
||||||
|
|
||||||
let style = ''
|
let style = '';
|
||||||
if (typeof edge.style !== 'undefined') {
|
if (typeof edge.style !== 'undefined') {
|
||||||
edge.style.forEach(function (s) {
|
edge.style.forEach(function(s) {
|
||||||
style = style + s + ';'
|
style = style + s + ';';
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
switch (edge.stroke) {
|
switch (edge.stroke) {
|
||||||
case 'normal':
|
case 'normal':
|
||||||
style = 'fill:none'
|
style = 'fill:none';
|
||||||
if (typeof defaultStyle !== 'undefined') {
|
if (typeof defaultStyle !== 'undefined') {
|
||||||
style = defaultStyle
|
style = defaultStyle;
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
case 'dotted':
|
case 'dotted':
|
||||||
style = 'stroke: #333; fill:none;stroke-width:2px;stroke-dasharray:3;'
|
style = 'stroke: #333; fill:none;stroke-width:2px;stroke-dasharray:3;';
|
||||||
break
|
break;
|
||||||
case 'thick':
|
case 'thick':
|
||||||
style = 'stroke: #333; stroke-width: 3.5px;fill:none'
|
style = 'stroke: #333; stroke-width: 3.5px;fill:none';
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
edgeData.style = style
|
edgeData.style = style;
|
||||||
|
|
||||||
if (typeof edge.interpolate !== 'undefined') {
|
if (typeof edge.interpolate !== 'undefined') {
|
||||||
edgeData.curve = interpolateToCurve(edge.interpolate, d3.curveLinear)
|
edgeData.curve = interpolateToCurve(edge.interpolate, d3.curveLinear);
|
||||||
} else if (typeof edges.defaultInterpolate !== 'undefined') {
|
} else if (typeof edges.defaultInterpolate !== 'undefined') {
|
||||||
edgeData.curve = interpolateToCurve(edges.defaultInterpolate, d3.curveLinear)
|
edgeData.curve = interpolateToCurve(edges.defaultInterpolate, d3.curveLinear);
|
||||||
} else {
|
} else {
|
||||||
edgeData.curve = interpolateToCurve(conf.curve, d3.curveLinear)
|
edgeData.curve = interpolateToCurve(conf.curve, d3.curveLinear);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof edge.text === 'undefined') {
|
if (typeof edge.text === 'undefined') {
|
||||||
if (typeof edge.style !== 'undefined') {
|
if (typeof edge.style !== 'undefined') {
|
||||||
edgeData.arrowheadStyle = 'fill: #333'
|
edgeData.arrowheadStyle = 'fill: #333';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
edgeData.arrowheadStyle = 'fill: #333'
|
edgeData.arrowheadStyle = 'fill: #333';
|
||||||
if (typeof edge.style === 'undefined') {
|
if (typeof edge.style === 'undefined') {
|
||||||
edgeData.labelpos = 'c'
|
edgeData.labelpos = 'c';
|
||||||
if (getConfig().flowchart.htmlLabels) {
|
if (getConfig().flowchart.htmlLabels) {
|
||||||
edgeData.labelType = 'html'
|
edgeData.labelType = 'html';
|
||||||
edgeData.label = '<span class="edgeLabel">' + edge.text + '</span>'
|
edgeData.label = '<span class="edgeLabel">' + edge.text + '</span>';
|
||||||
} else {
|
} else {
|
||||||
edgeData.labelType = 'text'
|
edgeData.labelType = 'text';
|
||||||
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none'
|
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none';
|
||||||
edgeData.label = edge.text.replace(/<br>/g, '\n')
|
edgeData.label = edge.text.replace(/<br>/g, '\n');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
edgeData.label = edge.text.replace(/<br>/g, '\n')
|
edgeData.label = edge.text.replace(/<br>/g, '\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add the edge to the graph
|
// Add the edge to the graph
|
||||||
g.setEdge(edge.start, edge.end, edgeData, cnt)
|
g.setEdge(edge.start, edge.end, edgeData, cnt);
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the all the styles from classDef statements in the graph definition.
|
* Returns the all the styles from classDef statements in the graph definition.
|
||||||
* @returns {object} classDef styles
|
* @returns {object} classDef styles
|
||||||
*/
|
*/
|
||||||
export const getClasses = function (text) {
|
export const getClasses = function(text) {
|
||||||
logger.info('Extracting classes')
|
logger.info('Extracting classes');
|
||||||
flowDb.clear()
|
flowDb.clear();
|
||||||
const parser = flow.parser
|
const parser = flow.parser;
|
||||||
parser.yy = flowDb
|
parser.yy = flowDb;
|
||||||
|
|
||||||
// Parse the graph definition
|
// Parse the graph definition
|
||||||
parser.parse(text)
|
parser.parse(text);
|
||||||
return flowDb.getClasses()
|
return flowDb.getClasses();
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a flowchart in the tag with id: id based on the graph definition in text.
|
* Draws a flowchart in the tag with id: id based on the graph definition in text.
|
||||||
* @param text
|
* @param text
|
||||||
* @param id
|
* @param id
|
||||||
*/
|
*/
|
||||||
export const draw = function (text, id) {
|
export const draw = function(text, id) {
|
||||||
logger.info('Drawing flowchart')
|
logger.info('Drawing flowchart');
|
||||||
flowDb.clear()
|
flowDb.clear();
|
||||||
const parser = flow.parser
|
const parser = flow.parser;
|
||||||
parser.yy = flowDb
|
parser.yy = flowDb;
|
||||||
|
|
||||||
// Parse the graph definition
|
// Parse the graph definition
|
||||||
try {
|
try {
|
||||||
parser.parse(text)
|
parser.parse(text);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.debug('Parsing failed')
|
logger.debug('Parsing failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the default direction, use TD if none was found
|
// Fetch the default direction, use TD if none was found
|
||||||
let dir = flowDb.getDirection()
|
let dir = flowDb.getDirection();
|
||||||
if (typeof dir === 'undefined') {
|
if (typeof dir === 'undefined') {
|
||||||
dir = 'TD'
|
dir = 'TD';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the input mermaid.graph
|
// Create the input mermaid.graph
|
||||||
@ -280,196 +294,238 @@ export const draw = function (text, id) {
|
|||||||
rankdir: dir,
|
rankdir: dir,
|
||||||
marginx: 20,
|
marginx: 20,
|
||||||
marginy: 20
|
marginy: 20
|
||||||
|
|
||||||
})
|
|
||||||
.setDefaultEdgeLabel(function () {
|
|
||||||
return {}
|
|
||||||
})
|
})
|
||||||
|
.setDefaultEdgeLabel(function() {
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
|
||||||
let subG
|
let subG;
|
||||||
const subGraphs = flowDb.getSubGraphs()
|
const subGraphs = flowDb.getSubGraphs();
|
||||||
for (let i = subGraphs.length - 1; i >= 0; i--) {
|
for (let i = subGraphs.length - 1; i >= 0; i--) {
|
||||||
subG = subGraphs[i]
|
subG = subGraphs[i];
|
||||||
flowDb.addVertex(subG.id, subG.title, 'group', undefined, subG.classes)
|
flowDb.addVertex(subG.id, subG.title, 'group', undefined, subG.classes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the verices/nodes and edges/links from the parsed graph definition
|
// Fetch the verices/nodes and edges/links from the parsed graph definition
|
||||||
const vert = flowDb.getVertices()
|
const vert = flowDb.getVertices();
|
||||||
|
|
||||||
const edges = flowDb.getEdges()
|
const edges = flowDb.getEdges();
|
||||||
|
|
||||||
let i = 0
|
let i = 0;
|
||||||
for (i = subGraphs.length - 1; i >= 0; i--) {
|
for (i = subGraphs.length - 1; i >= 0; i--) {
|
||||||
subG = subGraphs[i]
|
subG = subGraphs[i];
|
||||||
|
|
||||||
d3.selectAll('cluster').append('text')
|
d3.selectAll('cluster').append('text');
|
||||||
|
|
||||||
for (let j = 0; j < subG.nodes.length; j++) {
|
for (let j = 0; j < subG.nodes.length; j++) {
|
||||||
g.setParent(subG.nodes[j], subG.id)
|
g.setParent(subG.nodes[j], subG.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addVertices(vert, g, id)
|
addVertices(vert, g, id);
|
||||||
addEdges(edges, g)
|
addEdges(edges, g);
|
||||||
|
|
||||||
// Create the renderer
|
// Create the renderer
|
||||||
const Render = dagreD3.render
|
const Render = dagreD3.render;
|
||||||
const render = new Render()
|
const render = new Render();
|
||||||
|
|
||||||
// Add custom shape for rhombus type of boc (decision)
|
// Add custom shape for rhombus type of boc (decision)
|
||||||
render.shapes().question = function (parent, bbox, node) {
|
render.shapes().question = function(parent, bbox, node) {
|
||||||
const w = bbox.width
|
const w = bbox.width;
|
||||||
const h = bbox.height
|
const h = bbox.height;
|
||||||
const s = (w + h) * 0.9
|
const s = (w + h) * 0.9;
|
||||||
const points = [
|
const points = [
|
||||||
{ x: s / 2, y: 0 },
|
{ x: s / 2, y: 0 },
|
||||||
{ x: s, y: -s / 2 },
|
{ x: s, y: -s / 2 },
|
||||||
{ x: s / 2, y: -s },
|
{ x: s / 2, y: -s },
|
||||||
{ x: 0, y: -s / 2 }
|
{ x: 0, y: -s / 2 }
|
||||||
]
|
];
|
||||||
const shapeSvg = parent.insert('polygon', ':first-child')
|
const shapeSvg = parent
|
||||||
.attr('points', points.map(function (d) {
|
.insert('polygon', ':first-child')
|
||||||
return d.x + ',' + d.y
|
.attr(
|
||||||
}).join(' '))
|
'points',
|
||||||
|
points
|
||||||
|
.map(function(d) {
|
||||||
|
return d.x + ',' + d.y;
|
||||||
|
})
|
||||||
|
.join(' ')
|
||||||
|
)
|
||||||
.attr('rx', 5)
|
.attr('rx', 5)
|
||||||
.attr('ry', 5)
|
.attr('ry', 5)
|
||||||
.attr('transform', 'translate(' + (-s / 2) + ',' + (s * 2 / 4) + ')')
|
.attr('transform', 'translate(' + -s / 2 + ',' + (s * 2) / 4 + ')');
|
||||||
node.intersect = function (point) {
|
node.intersect = function(point) {
|
||||||
return dagreD3.intersect.polygon(node, points, point)
|
return dagreD3.intersect.polygon(node, points, point);
|
||||||
}
|
};
|
||||||
return shapeSvg
|
return shapeSvg;
|
||||||
}
|
};
|
||||||
|
|
||||||
// Add custom shape for box with inverted arrow on left side
|
// Add custom shape for box with inverted arrow on left side
|
||||||
render.shapes().rect_left_inv_arrow = function (parent, bbox, node) {
|
render.shapes().rect_left_inv_arrow = function(parent, bbox, node) {
|
||||||
const w = bbox.width
|
const w = bbox.width;
|
||||||
const h = bbox.height
|
const h = bbox.height;
|
||||||
const points = [
|
const points = [
|
||||||
{ x: -h / 2, y: 0 },
|
{ x: -h / 2, y: 0 },
|
||||||
{ x: w, y: 0 },
|
{ x: w, y: 0 },
|
||||||
{ x: w, y: -h },
|
{ x: w, y: -h },
|
||||||
{ x: -h / 2, y: -h },
|
{ x: -h / 2, y: -h },
|
||||||
{ x: 0, y: -h / 2 }
|
{ x: 0, y: -h / 2 }
|
||||||
]
|
];
|
||||||
const shapeSvg = parent.insert('polygon', ':first-child')
|
const shapeSvg = parent
|
||||||
.attr('points', points.map(function (d) {
|
.insert('polygon', ':first-child')
|
||||||
return d.x + ',' + d.y
|
.attr(
|
||||||
}).join(' '))
|
'points',
|
||||||
.attr('transform', 'translate(' + (-w / 2) + ',' + (h * 2 / 4) + ')')
|
points
|
||||||
node.intersect = function (point) {
|
.map(function(d) {
|
||||||
return dagreD3.intersect.polygon(node, points, point)
|
return d.x + ',' + d.y;
|
||||||
}
|
})
|
||||||
return shapeSvg
|
.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 left side
|
// Add custom shape for box with inverted arrow on left side
|
||||||
render.shapes().lean_right = function (parent, bbox, node) {
|
render.shapes().lean_right = function(parent, bbox, node) {
|
||||||
const w = bbox.width
|
const w = bbox.width;
|
||||||
const h = bbox.height
|
const h = bbox.height;
|
||||||
const points = [
|
const points = [
|
||||||
{ x: -2 * h / 6, y: 0 },
|
{ x: (-2 * h) / 6, y: 0 },
|
||||||
{ x: w - h / 6, y: 0 },
|
{ x: w - h / 6, y: 0 },
|
||||||
{ x: w + 2 * h / 6, y: -h },
|
{ x: w + (2 * h) / 6, y: -h },
|
||||||
{ x: h / 6, y: -h }
|
{ x: h / 6, y: -h }
|
||||||
]
|
];
|
||||||
const shapeSvg = parent.insert('polygon', ':first-child')
|
const shapeSvg = parent
|
||||||
.attr('points', points.map(function (d) {
|
.insert('polygon', ':first-child')
|
||||||
return d.x + ',' + d.y
|
.attr(
|
||||||
}).join(' '))
|
'points',
|
||||||
.attr('transform', 'translate(' + (-w / 2) + ',' + (h * 2 / 4) + ')')
|
points
|
||||||
node.intersect = function (point) {
|
.map(function(d) {
|
||||||
return dagreD3.intersect.polygon(node, points, point)
|
return d.x + ',' + d.y;
|
||||||
}
|
})
|
||||||
return shapeSvg
|
.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 left side
|
// Add custom shape for box with inverted arrow on left side
|
||||||
render.shapes().lean_left = function (parent, bbox, node) {
|
render.shapes().lean_left = function(parent, bbox, node) {
|
||||||
const w = bbox.width
|
const w = bbox.width;
|
||||||
const h = bbox.height
|
const h = bbox.height;
|
||||||
const points = [
|
const points = [
|
||||||
{ x: 2 * h / 6, y: 0 },
|
{ x: (2 * h) / 6, y: 0 },
|
||||||
{ x: w + h / 6, y: 0 },
|
{ x: w + h / 6, y: 0 },
|
||||||
{ x: w - 2 * h / 6, y: -h },
|
{ x: w - (2 * h) / 6, y: -h },
|
||||||
{ x: -h / 6, y: -h }
|
{ x: -h / 6, y: -h }
|
||||||
]
|
];
|
||||||
const shapeSvg = parent.insert('polygon', ':first-child')
|
const shapeSvg = parent
|
||||||
.attr('points', points.map(function (d) {
|
.insert('polygon', ':first-child')
|
||||||
return d.x + ',' + d.y
|
.attr(
|
||||||
}).join(' '))
|
'points',
|
||||||
.attr('transform', 'translate(' + (-w / 2) + ',' + (h * 2 / 4) + ')')
|
points
|
||||||
node.intersect = function (point) {
|
.map(function(d) {
|
||||||
return dagreD3.intersect.polygon(node, points, point)
|
return d.x + ',' + d.y;
|
||||||
}
|
})
|
||||||
return shapeSvg
|
.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 left side
|
// Add custom shape for box with inverted arrow on left side
|
||||||
render.shapes().trapezoid = function (parent, bbox, node) {
|
render.shapes().trapezoid = function(parent, bbox, node) {
|
||||||
const w = bbox.width
|
const w = bbox.width;
|
||||||
const h = bbox.height
|
const h = bbox.height;
|
||||||
const points = [
|
const points = [
|
||||||
{ x: -2 * h / 6, y: 0 },
|
{ x: (-2 * h) / 6, y: 0 },
|
||||||
{ x: w + 2 * h / 6, y: 0 },
|
{ x: w + (2 * h) / 6, y: 0 },
|
||||||
{ x: w - h / 6, y: -h },
|
{ x: w - h / 6, y: -h },
|
||||||
{ x: h / 6, y: -h }
|
{ x: h / 6, y: -h }
|
||||||
]
|
];
|
||||||
const shapeSvg = parent.insert('polygon', ':first-child')
|
const shapeSvg = parent
|
||||||
.attr('points', points.map(function (d) {
|
.insert('polygon', ':first-child')
|
||||||
return d.x + ',' + d.y
|
.attr(
|
||||||
}).join(' '))
|
'points',
|
||||||
.attr('transform', 'translate(' + (-w / 2) + ',' + (h * 2 / 4) + ')')
|
points
|
||||||
node.intersect = function (point) {
|
.map(function(d) {
|
||||||
return dagreD3.intersect.polygon(node, points, point)
|
return d.x + ',' + d.y;
|
||||||
}
|
})
|
||||||
return shapeSvg
|
.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 left side
|
// Add custom shape for box with inverted arrow on left side
|
||||||
render.shapes().inv_trapezoid = function (parent, bbox, node) {
|
render.shapes().inv_trapezoid = function(parent, bbox, node) {
|
||||||
const w = bbox.width
|
const w = bbox.width;
|
||||||
const h = bbox.height
|
const h = bbox.height;
|
||||||
const points = [
|
const points = [
|
||||||
{ x: h / 6, y: 0 },
|
{ x: h / 6, y: 0 },
|
||||||
{ x: w - h / 6, y: 0 },
|
{ x: w - h / 6, y: 0 },
|
||||||
{ x: w + 2 * h / 6, y: -h },
|
{ x: w + (2 * h) / 6, y: -h },
|
||||||
{ x: -2 * h / 6, y: -h }
|
{ x: (-2 * h) / 6, y: -h }
|
||||||
]
|
];
|
||||||
const shapeSvg = parent.insert('polygon', ':first-child')
|
const shapeSvg = parent
|
||||||
.attr('points', points.map(function (d) {
|
.insert('polygon', ':first-child')
|
||||||
return d.x + ',' + d.y
|
.attr(
|
||||||
}).join(' '))
|
'points',
|
||||||
.attr('transform', 'translate(' + (-w / 2) + ',' + (h * 2 / 4) + ')')
|
points
|
||||||
node.intersect = function (point) {
|
.map(function(d) {
|
||||||
return dagreD3.intersect.polygon(node, points, point)
|
return d.x + ',' + d.y;
|
||||||
}
|
})
|
||||||
return shapeSvg
|
.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
|
// Add custom shape for box with inverted arrow on right side
|
||||||
render.shapes().rect_right_inv_arrow = function (parent, bbox, node) {
|
render.shapes().rect_right_inv_arrow = function(parent, bbox, node) {
|
||||||
const w = bbox.width
|
const w = bbox.width;
|
||||||
const h = bbox.height
|
const h = bbox.height;
|
||||||
const points = [
|
const points = [
|
||||||
{ x: 0, y: 0 },
|
{ x: 0, y: 0 },
|
||||||
{ x: w + h / 2, y: 0 },
|
{ x: w + h / 2, y: 0 },
|
||||||
{ x: w, y: -h / 2 },
|
{ x: w, y: -h / 2 },
|
||||||
{ x: w + h / 2, y: -h },
|
{ x: w + h / 2, y: -h },
|
||||||
{ x: 0, y: -h }
|
{ x: 0, y: -h }
|
||||||
]
|
];
|
||||||
const shapeSvg = parent.insert('polygon', ':first-child')
|
const shapeSvg = parent
|
||||||
.attr('points', points.map(function (d) {
|
.insert('polygon', ':first-child')
|
||||||
return d.x + ',' + d.y
|
.attr(
|
||||||
}).join(' '))
|
'points',
|
||||||
.attr('transform', 'translate(' + (-w / 2) + ',' + (h * 2 / 4) + ')')
|
points
|
||||||
node.intersect = function (point) {
|
.map(function(d) {
|
||||||
return dagreD3.intersect.polygon(node, points, point)
|
return d.x + ',' + d.y;
|
||||||
}
|
})
|
||||||
return shapeSvg
|
.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
|
// Add our custom arrow - an empty arrowhead
|
||||||
render.arrows().none = function normal (parent, id, edge, type) {
|
render.arrows().none = function normal(parent, id, edge, type) {
|
||||||
const marker = parent.append('marker')
|
const marker = parent
|
||||||
|
.append('marker')
|
||||||
.attr('id', id)
|
.attr('id', id)
|
||||||
.attr('viewBox', '0 0 10 10')
|
.attr('viewBox', '0 0 10 10')
|
||||||
.attr('refX', 9)
|
.attr('refX', 9)
|
||||||
@ -477,16 +533,16 @@ export const draw = function (text, id) {
|
|||||||
.attr('markerUnits', 'strokeWidth')
|
.attr('markerUnits', 'strokeWidth')
|
||||||
.attr('markerWidth', 8)
|
.attr('markerWidth', 8)
|
||||||
.attr('markerHeight', 6)
|
.attr('markerHeight', 6)
|
||||||
.attr('orient', 'auto')
|
.attr('orient', 'auto');
|
||||||
|
|
||||||
const path = marker.append('path')
|
const path = marker.append('path').attr('d', 'M 0 0 L 0 0 L 0 0 z');
|
||||||
.attr('d', 'M 0 0 L 0 0 L 0 0 z')
|
dagreD3.util.applyStyle(path, edge[type + 'Style']);
|
||||||
dagreD3.util.applyStyle(path, edge[type + 'Style'])
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// Override normal arrowhead defined in d3. Remove style & add class to allow css styling.
|
// Override normal arrowhead defined in d3. Remove style & add class to allow css styling.
|
||||||
render.arrows().normal = function normal (parent, id, edge, type) {
|
render.arrows().normal = function normal(parent, id, edge, type) {
|
||||||
const marker = parent.append('marker')
|
const marker = parent
|
||||||
|
.append('marker')
|
||||||
.attr('id', id)
|
.attr('id', id)
|
||||||
.attr('viewBox', '0 0 10 10')
|
.attr('viewBox', '0 0 10 10')
|
||||||
.attr('refX', 9)
|
.attr('refX', 9)
|
||||||
@ -494,76 +550,76 @@ export const draw = function (text, id) {
|
|||||||
.attr('markerUnits', 'strokeWidth')
|
.attr('markerUnits', 'strokeWidth')
|
||||||
.attr('markerWidth', 8)
|
.attr('markerWidth', 8)
|
||||||
.attr('markerHeight', 6)
|
.attr('markerHeight', 6)
|
||||||
.attr('orient', 'auto')
|
.attr('orient', 'auto');
|
||||||
|
|
||||||
marker.append('path')
|
marker
|
||||||
|
.append('path')
|
||||||
.attr('d', 'M 0 0 L 10 5 L 0 10 z')
|
.attr('d', 'M 0 0 L 10 5 L 0 10 z')
|
||||||
.attr('class', 'arrowheadPath')
|
.attr('class', 'arrowheadPath')
|
||||||
.style('stroke-width', 1)
|
.style('stroke-width', 1)
|
||||||
.style('stroke-dasharray', '1,0')
|
.style('stroke-dasharray', '1,0');
|
||||||
}
|
};
|
||||||
|
|
||||||
// Set up an SVG group so that we can translate the final graph.
|
// Set up an SVG group so that we can translate the final graph.
|
||||||
const svg = d3.select(`[id="${id}"]`)
|
const svg = d3.select(`[id="${id}"]`);
|
||||||
|
|
||||||
// Run the renderer. This is what draws the final graph.
|
// Run the renderer. This is what draws the final graph.
|
||||||
const element = d3.select('#' + id + ' g')
|
const element = d3.select('#' + id + ' g');
|
||||||
render(element, g)
|
render(element, g);
|
||||||
|
|
||||||
element.selectAll('g.node')
|
element.selectAll('g.node').attr('title', function() {
|
||||||
.attr('title', function () {
|
return flowDb.getTooltip(this.id);
|
||||||
return flowDb.getTooltip(this.id)
|
});
|
||||||
})
|
|
||||||
|
|
||||||
const padding = 8
|
const padding = 8;
|
||||||
const width = g.maxX - g.minX + padding * 2
|
const width = g.maxX - g.minX + padding * 2;
|
||||||
const height = g.maxY - g.minY + padding * 2
|
const height = g.maxY - g.minY + padding * 2;
|
||||||
svg.attr('width', '100%')
|
svg.attr('width', '100%');
|
||||||
svg.attr('style', `max-width: ${width}px;`)
|
svg.attr('style', `max-width: ${width}px;`);
|
||||||
svg.attr('viewBox', `0 0 ${width} ${height}`)
|
svg.attr('viewBox', `0 0 ${width} ${height}`);
|
||||||
svg.select('g').attr('transform', `translate(${padding - g.minX}, ${padding - g.minY})`)
|
svg.select('g').attr('transform', `translate(${padding - g.minX}, ${padding - g.minY})`);
|
||||||
|
|
||||||
// Index nodes
|
// Index nodes
|
||||||
flowDb.indexNodes('subGraph' + i)
|
flowDb.indexNodes('subGraph' + i);
|
||||||
|
|
||||||
// reposition labels
|
// reposition labels
|
||||||
for (i = 0; i < subGraphs.length; i++) {
|
for (i = 0; i < subGraphs.length; i++) {
|
||||||
subG = subGraphs[i]
|
subG = subGraphs[i];
|
||||||
|
|
||||||
if (subG.title !== 'undefined') {
|
if (subG.title !== 'undefined') {
|
||||||
const clusterRects = document.querySelectorAll('#' + id + ' #' + subG.id + ' rect')
|
const clusterRects = document.querySelectorAll('#' + id + ' #' + subG.id + ' rect');
|
||||||
const clusterEl = document.querySelectorAll('#' + id + ' #' + subG.id)
|
const clusterEl = document.querySelectorAll('#' + id + ' #' + subG.id);
|
||||||
|
|
||||||
const xPos = clusterRects[0].x.baseVal.value
|
const xPos = clusterRects[0].x.baseVal.value;
|
||||||
const yPos = clusterRects[0].y.baseVal.value
|
const yPos = clusterRects[0].y.baseVal.value;
|
||||||
const width = clusterRects[0].width.baseVal.value
|
const width = clusterRects[0].width.baseVal.value;
|
||||||
const cluster = d3.select(clusterEl[0])
|
const cluster = d3.select(clusterEl[0]);
|
||||||
const te = cluster.select('.label')
|
const te = cluster.select('.label');
|
||||||
te.attr('transform', `translate(${xPos + width / 2}, ${yPos + 14})`)
|
te.attr('transform', `translate(${xPos + width / 2}, ${yPos + 14})`);
|
||||||
te.attr('id', id + 'Text')
|
te.attr('id', id + 'Text');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add label rects for non html labels
|
// Add label rects for non html labels
|
||||||
if (!getConfig().flowchart.htmlLabels) {
|
if (!getConfig().flowchart.htmlLabels) {
|
||||||
const labels = document.querySelectorAll('#' + id + ' .edgeLabel .label')
|
const labels = document.querySelectorAll('#' + id + ' .edgeLabel .label');
|
||||||
for (let k = 0; k < labels.length; k++) {
|
for (let k = 0; k < labels.length; k++) {
|
||||||
const label = labels[k]
|
const label = labels[k];
|
||||||
|
|
||||||
// Get dimensions of label
|
// Get dimensions of label
|
||||||
const dim = label.getBBox()
|
const dim = label.getBBox();
|
||||||
|
|
||||||
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
|
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
||||||
rect.setAttribute('rx', 0)
|
rect.setAttribute('rx', 0);
|
||||||
rect.setAttribute('ry', 0)
|
rect.setAttribute('ry', 0);
|
||||||
rect.setAttribute('width', dim.width)
|
rect.setAttribute('width', dim.width);
|
||||||
rect.setAttribute('height', dim.height)
|
rect.setAttribute('height', dim.height);
|
||||||
rect.setAttribute('style', 'fill:#e8e8e8;')
|
rect.setAttribute('style', 'fill:#e8e8e8;');
|
||||||
|
|
||||||
label.insertBefore(rect, label.firstChild)
|
label.insertBefore(rect, label.firstChild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setConf,
|
setConf,
|
||||||
@ -571,4 +627,4 @@ export default {
|
|||||||
addEdges,
|
addEdges,
|
||||||
getClasses,
|
getClasses,
|
||||||
draw
|
draw
|
||||||
}
|
};
|
||||||
|
@ -1,38 +1,37 @@
|
|||||||
import flowDb from '../flowDb'
|
import flowDb from '../flowDb';
|
||||||
import flow from './flow'
|
import flow from './flow';
|
||||||
import { setConfig } from '../../../config'
|
import { setConfig } from '../../../config';
|
||||||
|
|
||||||
setConfig({
|
setConfig({
|
||||||
securityLevel: 'strict',
|
securityLevel: 'strict'
|
||||||
})
|
});
|
||||||
|
|
||||||
describe('when parsing flowcharts', function () {
|
describe('when parsing flowcharts', function() {
|
||||||
beforeEach(function () {
|
beforeEach(function() {
|
||||||
flow.parser.yy = flowDb
|
flow.parser.yy = flowDb;
|
||||||
flow.parser.yy.clear()
|
flow.parser.yy.clear();
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle chaining of vertices', function () {
|
it('should handle chaining of vertices', function() {
|
||||||
const res = flow.parser.parse(`
|
const res = flow.parser.parse(`
|
||||||
graph TD
|
graph TD
|
||||||
A-->B-->C;
|
A-->B-->C;
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices()
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges()
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
expect(vert['A'].id).toBe('A')
|
expect(vert['A'].id).toBe('A');
|
||||||
expect(vert['B'].id).toBe('B')
|
expect(vert['B'].id).toBe('B');
|
||||||
expect(vert['C'].id).toBe('C')
|
expect(vert['C'].id).toBe('C');
|
||||||
expect(edges.length).toBe(2)
|
expect(edges.length).toBe(2);
|
||||||
expect(edges[0].start).toBe('A')
|
expect(edges[0].start).toBe('A');
|
||||||
expect(edges[0].end).toBe('B')
|
expect(edges[0].end).toBe('B');
|
||||||
expect(edges[0].type).toBe('arrow')
|
expect(edges[0].type).toBe('arrow');
|
||||||
expect(edges[0].text).toBe('')
|
expect(edges[0].text).toBe('');
|
||||||
expect(edges[1].start).toBe('B')
|
expect(edges[1].start).toBe('B');
|
||||||
expect(edges[1].end).toBe('C')
|
expect(edges[1].end).toBe('C');
|
||||||
expect(edges[1].type).toBe('arrow')
|
expect(edges[1].type).toBe('arrow');
|
||||||
expect(edges[1].text).toBe('')
|
expect(edges[1].text).toBe('');
|
||||||
})
|
});
|
||||||
|
});
|
||||||
})
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,223 +1,223 @@
|
|||||||
import flowDb from '../flowDb'
|
import flowDb from '../flowDb';
|
||||||
import flow from './flow'
|
import flow from './flow';
|
||||||
import { setConfig } from '../../../config'
|
import { setConfig } from '../../../config';
|
||||||
|
|
||||||
setConfig({
|
setConfig({
|
||||||
securityLevel: 'strict',
|
securityLevel: 'strict'
|
||||||
})
|
});
|
||||||
|
|
||||||
describe('when parsing subgraphs', function () {
|
describe('when parsing subgraphs', function() {
|
||||||
beforeEach(function () {
|
beforeEach(function() {
|
||||||
flow.parser.yy = flowDb
|
flow.parser.yy = flowDb;
|
||||||
flow.parser.yy.clear()
|
flow.parser.yy.clear();
|
||||||
})
|
});
|
||||||
it('should handle subgraph with tab indentation', function () {
|
it('should handle subgraph with tab indentation', function() {
|
||||||
const res = flow.parser.parse('graph TB\nsubgraph One\n\ta1-->a2\nend')
|
const res = flow.parser.parse('graph TB\nsubgraph One\n\ta1-->a2\nend');
|
||||||
const subgraphs = flow.parser.yy.getSubGraphs()
|
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||||
expect(subgraphs.length).toBe(1)
|
expect(subgraphs.length).toBe(1);
|
||||||
const subgraph = subgraphs[0]
|
const subgraph = subgraphs[0];
|
||||||
expect(subgraph.nodes.length).toBe(2)
|
expect(subgraph.nodes.length).toBe(2);
|
||||||
expect(subgraph.nodes[0]).toBe('a2')
|
expect(subgraph.nodes[0]).toBe('a2');
|
||||||
expect(subgraph.nodes[1]).toBe('a1')
|
expect(subgraph.nodes[1]).toBe('a1');
|
||||||
expect(subgraph.title).toBe('One')
|
expect(subgraph.title).toBe('One');
|
||||||
expect(subgraph.id).toBe('One')
|
expect(subgraph.id).toBe('One');
|
||||||
})
|
});
|
||||||
it('should handle subgraph with chaining nodes indentation', function () {
|
it('should handle subgraph with chaining nodes indentation', function() {
|
||||||
const res = flow.parser.parse('graph TB\nsubgraph One\n\ta1-->a2-->a3\nend')
|
const res = flow.parser.parse('graph TB\nsubgraph One\n\ta1-->a2-->a3\nend');
|
||||||
const subgraphs = flow.parser.yy.getSubGraphs()
|
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||||
expect(subgraphs.length).toBe(1)
|
expect(subgraphs.length).toBe(1);
|
||||||
const subgraph = subgraphs[0]
|
const subgraph = subgraphs[0];
|
||||||
expect(subgraph.nodes.length).toBe(3)
|
expect(subgraph.nodes.length).toBe(3);
|
||||||
expect(subgraph.nodes[0]).toBe('a3')
|
expect(subgraph.nodes[0]).toBe('a3');
|
||||||
expect(subgraph.nodes[1]).toBe('a2')
|
expect(subgraph.nodes[1]).toBe('a2');
|
||||||
expect(subgraph.nodes[2]).toBe('a1')
|
expect(subgraph.nodes[2]).toBe('a1');
|
||||||
expect(subgraph.title).toBe('One')
|
expect(subgraph.title).toBe('One');
|
||||||
expect(subgraph.id).toBe('One')
|
expect(subgraph.id).toBe('One');
|
||||||
})
|
|
||||||
|
|
||||||
it('should handle subgraph with multiple words in title', function () {
|
|
||||||
const res = flow.parser.parse('graph TB\nsubgraph "Some Title"\n\ta1-->a2\nend')
|
|
||||||
const subgraphs = flow.parser.yy.getSubGraphs()
|
|
||||||
expect(subgraphs.length).toBe(1)
|
|
||||||
const subgraph = subgraphs[0]
|
|
||||||
expect(subgraph.nodes.length).toBe(2)
|
|
||||||
expect(subgraph.nodes[0]).toBe('a2')
|
|
||||||
expect(subgraph.nodes[1]).toBe('a1')
|
|
||||||
expect(subgraph.title).toBe('Some Title')
|
|
||||||
expect(subgraph.id).toBe('subGraph0')
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle subgraph with id and title notation', function () {
|
it('should handle subgraph with multiple words in title', function() {
|
||||||
const res = flow.parser.parse('graph TB\nsubgraph some-id[Some Title]\n\ta1-->a2\nend')
|
const res = flow.parser.parse('graph TB\nsubgraph "Some Title"\n\ta1-->a2\nend');
|
||||||
const subgraphs = flow.parser.yy.getSubGraphs()
|
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||||
expect(subgraphs.length).toBe(1)
|
expect(subgraphs.length).toBe(1);
|
||||||
const subgraph = subgraphs[0]
|
const subgraph = subgraphs[0];
|
||||||
expect(subgraph.nodes.length).toBe(2)
|
expect(subgraph.nodes.length).toBe(2);
|
||||||
expect(subgraph.nodes[0]).toBe('a2')
|
expect(subgraph.nodes[0]).toBe('a2');
|
||||||
expect(subgraph.nodes[1]).toBe('a1')
|
expect(subgraph.nodes[1]).toBe('a1');
|
||||||
expect(subgraph.title).toBe('Some Title')
|
expect(subgraph.title).toBe('Some Title');
|
||||||
expect(subgraph.id).toBe('some-id')
|
expect(subgraph.id).toBe('subGraph0');
|
||||||
});
|
});
|
||||||
|
|
||||||
xit('should handle subgraph without id and space in title', function () {
|
it('should handle subgraph with id and title notation', function() {
|
||||||
const res = flow.parser.parse('graph TB\nsubgraph Some Title\n\ta1-->a2\nend')
|
const res = flow.parser.parse('graph TB\nsubgraph some-id[Some Title]\n\ta1-->a2\nend');
|
||||||
const subgraphs = flow.parser.yy.getSubGraphs()
|
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||||
expect(subgraphs.length).toBe(1)
|
expect(subgraphs.length).toBe(1);
|
||||||
const subgraph = subgraphs[0]
|
const subgraph = subgraphs[0];
|
||||||
expect(subgraph.nodes.length).toBe(2)
|
expect(subgraph.nodes.length).toBe(2);
|
||||||
expect(subgraph.nodes[0]).toBe('a1')
|
expect(subgraph.nodes[0]).toBe('a2');
|
||||||
expect(subgraph.nodes[1]).toBe('a2')
|
expect(subgraph.nodes[1]).toBe('a1');
|
||||||
expect(subgraph.title).toBe('Some Title')
|
expect(subgraph.title).toBe('Some Title');
|
||||||
expect(subgraph.id).toBe('some-id')
|
expect(subgraph.id).toBe('some-id');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle subgraph id starting with a number', function () {
|
xit('should handle subgraph without id and space in title', function() {
|
||||||
|
const res = flow.parser.parse('graph TB\nsubgraph Some Title\n\ta1-->a2\nend');
|
||||||
|
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||||
|
expect(subgraphs.length).toBe(1);
|
||||||
|
const subgraph = subgraphs[0];
|
||||||
|
expect(subgraph.nodes.length).toBe(2);
|
||||||
|
expect(subgraph.nodes[0]).toBe('a1');
|
||||||
|
expect(subgraph.nodes[1]).toBe('a2');
|
||||||
|
expect(subgraph.title).toBe('Some Title');
|
||||||
|
expect(subgraph.id).toBe('some-id');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle subgraph id starting with a number', function() {
|
||||||
const res = flow.parser.parse(`graph TD
|
const res = flow.parser.parse(`graph TD
|
||||||
A[Christmas] -->|Get money| B(Go shopping)
|
A[Christmas] -->|Get money| B(Go shopping)
|
||||||
subgraph 1test
|
subgraph 1test
|
||||||
A
|
A
|
||||||
end`)
|
end`);
|
||||||
|
|
||||||
const subgraphs = flow.parser.yy.getSubGraphs()
|
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||||
expect(subgraphs.length).toBe(1)
|
expect(subgraphs.length).toBe(1);
|
||||||
const subgraph = subgraphs[0]
|
const subgraph = subgraphs[0];
|
||||||
expect(subgraph.nodes.length).toBe(1)
|
expect(subgraph.nodes.length).toBe(1);
|
||||||
expect(subgraph.nodes[0]).toBe('A')
|
expect(subgraph.nodes[0]).toBe('A');
|
||||||
expect(subgraph.id).toBe('s1test')
|
expect(subgraph.id).toBe('s1test');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle subgraphs1', function () {
|
it('should handle subgraphs1', function() {
|
||||||
const res = flow.parser.parse('graph TD;A-->B;subgraph myTitle;c-->d;end;')
|
const res = flow.parser.parse('graph TD;A-->B;subgraph myTitle;c-->d;end;');
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices()
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges()
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
expect(edges[0].type).toBe('arrow')
|
expect(edges[0].type).toBe('arrow');
|
||||||
})
|
});
|
||||||
it('should handle subgraphs with title in quotes', function () {
|
it('should handle subgraphs with title in quotes', function() {
|
||||||
const res = flow.parser.parse('graph TD;A-->B;subgraph "title in quotes";c-->d;end;')
|
const res = flow.parser.parse('graph TD;A-->B;subgraph "title in quotes";c-->d;end;');
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices()
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges()
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
const subgraphs = flow.parser.yy.getSubGraphs()
|
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||||
expect(subgraphs.length).toBe(1)
|
expect(subgraphs.length).toBe(1);
|
||||||
const subgraph = subgraphs[0]
|
const subgraph = subgraphs[0];
|
||||||
|
|
||||||
expect(subgraph.title).toBe('title in quotes')
|
expect(subgraph.title).toBe('title in quotes');
|
||||||
|
|
||||||
expect(edges[0].type).toBe('arrow')
|
expect(edges[0].type).toBe('arrow');
|
||||||
})
|
});
|
||||||
it('should handle subgraphs in old style that was broken', function () {
|
it('should handle subgraphs in old style that was broken', function() {
|
||||||
const res = flow.parser.parse('graph TD;A-->B;subgraph old style that is broken;c-->d;end;')
|
const res = flow.parser.parse('graph TD;A-->B;subgraph old style that is broken;c-->d;end;');
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices()
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges()
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
const subgraphs = flow.parser.yy.getSubGraphs()
|
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||||
expect(subgraphs.length).toBe(1)
|
expect(subgraphs.length).toBe(1);
|
||||||
const subgraph = subgraphs[0]
|
const subgraph = subgraphs[0];
|
||||||
|
|
||||||
expect(subgraph.title).toBe('old style that is broken')
|
expect(subgraph.title).toBe('old style that is broken');
|
||||||
|
|
||||||
expect(edges[0].type).toBe('arrow')
|
expect(edges[0].type).toBe('arrow');
|
||||||
})
|
});
|
||||||
it('should handle subgraphs with dashes in the title', function () {
|
it('should handle subgraphs with dashes in the title', function() {
|
||||||
const res = flow.parser.parse('graph TD;A-->B;subgraph a-b-c;c-->d;end;')
|
const res = flow.parser.parse('graph TD;A-->B;subgraph a-b-c;c-->d;end;');
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices()
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges()
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
const subgraphs = flow.parser.yy.getSubGraphs()
|
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||||
expect(subgraphs.length).toBe(1)
|
expect(subgraphs.length).toBe(1);
|
||||||
const subgraph = subgraphs[0]
|
const subgraph = subgraphs[0];
|
||||||
|
|
||||||
expect(subgraph.title).toBe('a-b-c')
|
expect(subgraph.title).toBe('a-b-c');
|
||||||
|
|
||||||
expect(edges[0].type).toBe('arrow')
|
expect(edges[0].type).toBe('arrow');
|
||||||
})
|
});
|
||||||
it('should handle subgraphs with id and title in brackets', function () {
|
it('should handle subgraphs with id and title in brackets', function() {
|
||||||
const res = flow.parser.parse('graph TD;A-->B;subgraph uid1[text of doom];c-->d;end;')
|
const res = flow.parser.parse('graph TD;A-->B;subgraph uid1[text of doom];c-->d;end;');
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices()
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges()
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
const subgraphs = flow.parser.yy.getSubGraphs()
|
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||||
expect(subgraphs.length).toBe(1)
|
expect(subgraphs.length).toBe(1);
|
||||||
const subgraph = subgraphs[0]
|
const subgraph = subgraphs[0];
|
||||||
|
|
||||||
expect(subgraph.title).toBe('text of doom')
|
expect(subgraph.title).toBe('text of doom');
|
||||||
expect(subgraph.id).toBe('uid1')
|
expect(subgraph.id).toBe('uid1');
|
||||||
|
|
||||||
expect(edges[0].type).toBe('arrow')
|
expect(edges[0].type).toBe('arrow');
|
||||||
})
|
});
|
||||||
it('should handle subgraphs with id and title in brackets and quotes', function () {
|
it('should handle subgraphs with id and title in brackets and quotes', function() {
|
||||||
const res = flow.parser.parse('graph TD;A-->B;subgraph uid2["text of doom"];c-->d;end;')
|
const res = flow.parser.parse('graph TD;A-->B;subgraph uid2["text of doom"];c-->d;end;');
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices()
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges()
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
const subgraphs = flow.parser.yy.getSubGraphs()
|
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||||
expect(subgraphs.length).toBe(1)
|
expect(subgraphs.length).toBe(1);
|
||||||
const subgraph = subgraphs[0]
|
const subgraph = subgraphs[0];
|
||||||
|
|
||||||
expect(subgraph.title).toBe('text of doom')
|
expect(subgraph.title).toBe('text of doom');
|
||||||
expect(subgraph.id).toBe('uid2')
|
expect(subgraph.id).toBe('uid2');
|
||||||
|
|
||||||
expect(edges[0].type).toBe('arrow')
|
expect(edges[0].type).toBe('arrow');
|
||||||
})
|
});
|
||||||
it('should handle subgraphs with id and title in brackets without spaces', function () {
|
it('should handle subgraphs with id and title in brackets without spaces', function() {
|
||||||
const res = flow.parser.parse('graph TD;A-->B;subgraph uid2[textofdoom];c-->d;end;')
|
const res = flow.parser.parse('graph TD;A-->B;subgraph uid2[textofdoom];c-->d;end;');
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices()
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges()
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
const subgraphs = flow.parser.yy.getSubGraphs()
|
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||||
expect(subgraphs.length).toBe(1)
|
expect(subgraphs.length).toBe(1);
|
||||||
const subgraph = subgraphs[0]
|
const subgraph = subgraphs[0];
|
||||||
|
|
||||||
expect(subgraph.title).toBe('textofdoom')
|
expect(subgraph.title).toBe('textofdoom');
|
||||||
expect(subgraph.id).toBe('uid2')
|
expect(subgraph.id).toBe('uid2');
|
||||||
|
|
||||||
expect(edges[0].type).toBe('arrow')
|
expect(edges[0].type).toBe('arrow');
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle subgraphs2', function () {
|
it('should handle subgraphs2', function() {
|
||||||
const res = flow.parser.parse('graph TD\nA-->B\nsubgraph myTitle\n\n c-->d \nend\n')
|
const res = flow.parser.parse('graph TD\nA-->B\nsubgraph myTitle\n\n c-->d \nend\n');
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices()
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges()
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
expect(edges[0].type).toBe('arrow')
|
expect(edges[0].type).toBe('arrow');
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle nested subgraphs', function () {
|
it('should handle nested subgraphs', function() {
|
||||||
const str = 'graph TD\n' +
|
const str =
|
||||||
'A-->B\n' +
|
'graph TD\n' +
|
||||||
'subgraph myTitle\n\n' +
|
'A-->B\n' +
|
||||||
' c-->d \n\n' +
|
'subgraph myTitle\n\n' +
|
||||||
' subgraph inner\n\n e-->f \n end \n\n' +
|
' c-->d \n\n' +
|
||||||
' subgraph inner\n\n h-->i \n end \n\n' +
|
' subgraph inner\n\n e-->f \n end \n\n' +
|
||||||
'end\n'
|
' subgraph inner\n\n h-->i \n end \n\n' +
|
||||||
const res = flow.parser.parse(str)
|
'end\n';
|
||||||
})
|
const res = flow.parser.parse(str);
|
||||||
|
});
|
||||||
|
|
||||||
it('should handle subgraphs4', function () {
|
it('should handle subgraphs4', function() {
|
||||||
const res = flow.parser.parse('graph TD\nA-->B\nsubgraph myTitle\nc-->d\nend;')
|
const res = flow.parser.parse('graph TD\nA-->B\nsubgraph myTitle\nc-->d\nend;');
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices()
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges()
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
expect(edges[0].type).toBe('arrow')
|
expect(edges[0].type).toBe('arrow');
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle subgraphs5', function () {
|
it('should handle subgraphs5', function() {
|
||||||
const res = flow.parser.parse('graph TD\nA-->B\nsubgraph myTitle\nc-- text -->d\nd-->e\n end;')
|
const res = flow.parser.parse('graph TD\nA-->B\nsubgraph myTitle\nc-- text -->d\nd-->e\n end;');
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices()
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges()
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
expect(edges[0].type).toBe('arrow')
|
expect(edges[0].type).toBe('arrow');
|
||||||
})
|
});
|
||||||
|
});
|
||||||
})
|
|
||||||
|
@ -1,217 +1,214 @@
|
|||||||
import moment from 'moment-mini'
|
import moment from 'moment-mini';
|
||||||
import { sanitizeUrl } from '@braintree/sanitize-url'
|
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||||
import { logger } from '../../logger'
|
import { logger } from '../../logger';
|
||||||
import { getConfig } from '../../config'
|
import { getConfig } from '../../config';
|
||||||
|
|
||||||
const config = getConfig()
|
const config = getConfig();
|
||||||
let dateFormat = ''
|
let dateFormat = '';
|
||||||
let axisFormat = ''
|
let axisFormat = '';
|
||||||
let excludes = []
|
let excludes = [];
|
||||||
let title = ''
|
let title = '';
|
||||||
let sections = []
|
let sections = [];
|
||||||
let tasks = []
|
let tasks = [];
|
||||||
let currentSection = ''
|
let currentSection = '';
|
||||||
const tags = ['active', 'done', 'crit', 'milestone']
|
const tags = ['active', 'done', 'crit', 'milestone'];
|
||||||
let funs = []
|
let funs = [];
|
||||||
let inclusiveEndDates = false
|
let inclusiveEndDates = false;
|
||||||
|
|
||||||
export const clear = function () {
|
export const clear = function() {
|
||||||
sections = []
|
sections = [];
|
||||||
tasks = []
|
tasks = [];
|
||||||
currentSection = ''
|
currentSection = '';
|
||||||
funs = []
|
funs = [];
|
||||||
title = ''
|
title = '';
|
||||||
taskCnt = 0
|
taskCnt = 0;
|
||||||
lastTask = undefined
|
lastTask = undefined;
|
||||||
lastTaskID = undefined
|
lastTaskID = undefined;
|
||||||
rawTasks = []
|
rawTasks = [];
|
||||||
dateFormat = ''
|
dateFormat = '';
|
||||||
axisFormat = ''
|
axisFormat = '';
|
||||||
excludes = []
|
excludes = [];
|
||||||
inclusiveEndDates = false
|
inclusiveEndDates = false;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const setAxisFormat = function (txt) {
|
export const setAxisFormat = function(txt) {
|
||||||
axisFormat = txt
|
axisFormat = txt;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getAxisFormat = function () {
|
export const getAxisFormat = function() {
|
||||||
return axisFormat
|
return axisFormat;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const setDateFormat = function (txt) {
|
export const setDateFormat = function(txt) {
|
||||||
dateFormat = txt
|
dateFormat = txt;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const enableInclusiveEndDates = function () {
|
export const enableInclusiveEndDates = function() {
|
||||||
inclusiveEndDates = true
|
inclusiveEndDates = true;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const endDatesAreInclusive = function () {
|
export const endDatesAreInclusive = function() {
|
||||||
return inclusiveEndDates
|
return inclusiveEndDates;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getDateFormat = function () {
|
export const getDateFormat = function() {
|
||||||
return dateFormat
|
return dateFormat;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const setExcludes = function (txt) {
|
export const setExcludes = function(txt) {
|
||||||
excludes = txt.toLowerCase().split(/[\s,]+/)
|
excludes = txt.toLowerCase().split(/[\s,]+/);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getExcludes = function () {
|
export const getExcludes = function() {
|
||||||
return excludes
|
return excludes;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const setTitle = function (txt) {
|
export const setTitle = function(txt) {
|
||||||
title = txt
|
title = txt;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getTitle = function () {
|
export const getTitle = function() {
|
||||||
return title
|
return title;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const addSection = function (txt) {
|
export const addSection = function(txt) {
|
||||||
currentSection = txt
|
currentSection = txt;
|
||||||
sections.push(txt)
|
sections.push(txt);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getSections = function () {
|
export const getSections = function() {
|
||||||
return sections
|
return sections;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getTasks = function () {
|
export const getTasks = function() {
|
||||||
let allItemsPricessed = compileTasks()
|
let allItemsPricessed = compileTasks();
|
||||||
const maxDepth = 10
|
const maxDepth = 10;
|
||||||
let iterationCount = 0
|
let iterationCount = 0;
|
||||||
while (!allItemsPricessed && (iterationCount < maxDepth)) {
|
while (!allItemsPricessed && iterationCount < maxDepth) {
|
||||||
allItemsPricessed = compileTasks()
|
allItemsPricessed = compileTasks();
|
||||||
iterationCount++
|
iterationCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks = rawTasks
|
tasks = rawTasks;
|
||||||
|
|
||||||
return tasks
|
return tasks;
|
||||||
}
|
};
|
||||||
|
|
||||||
const isInvalidDate = function (date, dateFormat, excludes) {
|
const isInvalidDate = function(date, dateFormat, excludes) {
|
||||||
if (date.isoWeekday() >= 6 && excludes.indexOf('weekends') >= 0) {
|
if (date.isoWeekday() >= 6 && excludes.indexOf('weekends') >= 0) {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
if (excludes.indexOf(date.format('dddd').toLowerCase()) >= 0) {
|
if (excludes.indexOf(date.format('dddd').toLowerCase()) >= 0) {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
return excludes.indexOf(date.format(dateFormat.trim())) >= 0
|
return excludes.indexOf(date.format(dateFormat.trim())) >= 0;
|
||||||
}
|
};
|
||||||
|
|
||||||
const checkTaskDates = function (task, dateFormat, excludes) {
|
const checkTaskDates = function(task, dateFormat, excludes) {
|
||||||
if (!excludes.length || task.manualEndTime) return
|
if (!excludes.length || task.manualEndTime) return;
|
||||||
let startTime = moment(task.startTime, dateFormat, true)
|
let startTime = moment(task.startTime, dateFormat, true);
|
||||||
startTime.add(1, 'd')
|
startTime.add(1, 'd');
|
||||||
let endTime = moment(task.endTime, dateFormat, true)
|
let endTime = moment(task.endTime, dateFormat, true);
|
||||||
let renderEndTime = fixTaskDates(startTime, endTime, dateFormat, excludes)
|
let renderEndTime = fixTaskDates(startTime, endTime, dateFormat, excludes);
|
||||||
task.endTime = endTime.toDate()
|
task.endTime = endTime.toDate();
|
||||||
task.renderEndTime = renderEndTime
|
task.renderEndTime = renderEndTime;
|
||||||
}
|
};
|
||||||
|
|
||||||
const fixTaskDates = function (startTime, endTime, dateFormat, excludes) {
|
const fixTaskDates = function(startTime, endTime, dateFormat, excludes) {
|
||||||
let invalid = false
|
let invalid = false;
|
||||||
let renderEndTime = null
|
let renderEndTime = null;
|
||||||
while (startTime.date() <= endTime.date()) {
|
while (startTime.date() <= endTime.date()) {
|
||||||
if (!invalid) {
|
if (!invalid) {
|
||||||
renderEndTime = endTime.toDate()
|
renderEndTime = endTime.toDate();
|
||||||
}
|
}
|
||||||
invalid = isInvalidDate(startTime, dateFormat, excludes)
|
invalid = isInvalidDate(startTime, dateFormat, excludes);
|
||||||
if (invalid) {
|
if (invalid) {
|
||||||
endTime.add(1, 'd')
|
endTime.add(1, 'd');
|
||||||
}
|
}
|
||||||
startTime.add(1, 'd')
|
startTime.add(1, 'd');
|
||||||
}
|
}
|
||||||
return renderEndTime
|
return renderEndTime;
|
||||||
}
|
};
|
||||||
|
|
||||||
const getStartDate = function (prevTime, dateFormat, str) {
|
const getStartDate = function(prevTime, dateFormat, str) {
|
||||||
str = str.trim()
|
str = str.trim();
|
||||||
|
|
||||||
// Test for after
|
// Test for after
|
||||||
const re = /^after\s+([\d\w-]+)/
|
const re = /^after\s+([\d\w-]+)/;
|
||||||
const afterStatement = re.exec(str.trim())
|
const afterStatement = re.exec(str.trim());
|
||||||
|
|
||||||
if (afterStatement !== null) {
|
if (afterStatement !== null) {
|
||||||
const task = findTaskById(afterStatement[1])
|
const task = findTaskById(afterStatement[1]);
|
||||||
|
|
||||||
if (typeof task === 'undefined') {
|
if (typeof task === 'undefined') {
|
||||||
const dt = new Date()
|
const dt = new Date();
|
||||||
dt.setHours(0, 0, 0, 0)
|
dt.setHours(0, 0, 0, 0);
|
||||||
return dt
|
return dt;
|
||||||
}
|
}
|
||||||
return task.endTime
|
return task.endTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for actual date set
|
// Check for actual date set
|
||||||
let mDate = moment(str, dateFormat.trim(), true)
|
let mDate = moment(str, dateFormat.trim(), true);
|
||||||
if (mDate.isValid()) {
|
if (mDate.isValid()) {
|
||||||
return mDate.toDate()
|
return mDate.toDate();
|
||||||
} else {
|
} else {
|
||||||
logger.debug('Invalid date:' + str)
|
logger.debug('Invalid date:' + str);
|
||||||
logger.debug('With date format:' + dateFormat.trim())
|
logger.debug('With date format:' + dateFormat.trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default date - now
|
// Default date - now
|
||||||
return new Date()
|
return new Date();
|
||||||
}
|
};
|
||||||
|
|
||||||
const durationToDate = function (durationStatement, relativeTime) {
|
const durationToDate = function(durationStatement, relativeTime) {
|
||||||
if (durationStatement !== null) {
|
if (durationStatement !== null) {
|
||||||
switch (durationStatement[2]) {
|
switch (durationStatement[2]) {
|
||||||
case 's':
|
case 's':
|
||||||
relativeTime.add(durationStatement[1], 'seconds')
|
relativeTime.add(durationStatement[1], 'seconds');
|
||||||
break
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
relativeTime.add(durationStatement[1], 'minutes')
|
relativeTime.add(durationStatement[1], 'minutes');
|
||||||
break
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
relativeTime.add(durationStatement[1], 'hours')
|
relativeTime.add(durationStatement[1], 'hours');
|
||||||
break
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
relativeTime.add(durationStatement[1], 'days')
|
relativeTime.add(durationStatement[1], 'days');
|
||||||
break
|
break;
|
||||||
case 'w':
|
case 'w':
|
||||||
relativeTime.add(durationStatement[1], 'weeks')
|
relativeTime.add(durationStatement[1], 'weeks');
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Default date - now
|
// Default date - now
|
||||||
return relativeTime.toDate()
|
return relativeTime.toDate();
|
||||||
}
|
};
|
||||||
|
|
||||||
const getEndDate = function (prevTime, dateFormat, str, inclusive) {
|
const getEndDate = function(prevTime, dateFormat, str, inclusive) {
|
||||||
inclusive = inclusive || false
|
inclusive = inclusive || false;
|
||||||
str = str.trim()
|
str = str.trim();
|
||||||
|
|
||||||
// Check for actual date
|
// Check for actual date
|
||||||
let mDate = moment(str, dateFormat.trim(), true)
|
let mDate = moment(str, dateFormat.trim(), true);
|
||||||
if (mDate.isValid()) {
|
if (mDate.isValid()) {
|
||||||
if (inclusive) {
|
if (inclusive) {
|
||||||
mDate.add(1, 'd')
|
mDate.add(1, 'd');
|
||||||
}
|
}
|
||||||
return mDate.toDate()
|
return mDate.toDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
return durationToDate(
|
return durationToDate(/^([\d]+)([wdhms])/.exec(str.trim()), moment(prevTime));
|
||||||
/^([\d]+)([wdhms])/.exec(str.trim()),
|
};
|
||||||
moment(prevTime)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let taskCnt = 0
|
let taskCnt = 0;
|
||||||
const parseId = function (idStr) {
|
const parseId = function(idStr) {
|
||||||
if (typeof idStr === 'undefined') {
|
if (typeof idStr === 'undefined') {
|
||||||
taskCnt = taskCnt + 1
|
taskCnt = taskCnt + 1;
|
||||||
return 'task' + taskCnt
|
return 'task' + taskCnt;
|
||||||
}
|
}
|
||||||
return idStr
|
return idStr;
|
||||||
}
|
};
|
||||||
// id, startDate, endDate
|
// id, startDate, endDate
|
||||||
// id, startDate, length
|
// id, startDate, length
|
||||||
// id, after x, endDate
|
// id, after x, endDate
|
||||||
@ -223,116 +220,116 @@ const parseId = function (idStr) {
|
|||||||
// endDate
|
// endDate
|
||||||
// length
|
// length
|
||||||
|
|
||||||
const compileData = function (prevTask, dataStr) {
|
const compileData = function(prevTask, dataStr) {
|
||||||
let ds
|
let ds;
|
||||||
|
|
||||||
if (dataStr.substr(0, 1) === ':') {
|
if (dataStr.substr(0, 1) === ':') {
|
||||||
ds = dataStr.substr(1, dataStr.length)
|
ds = dataStr.substr(1, dataStr.length);
|
||||||
} else {
|
} else {
|
||||||
ds = dataStr
|
ds = dataStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = ds.split(',')
|
const data = ds.split(',');
|
||||||
|
|
||||||
const task = {}
|
const task = {};
|
||||||
|
|
||||||
// Get tags like active, done, crit and milestone
|
// Get tags like active, done, crit and milestone
|
||||||
getTaskTags(data, task, tags)
|
getTaskTags(data, task, tags);
|
||||||
|
|
||||||
for (let i = 0; i < data.length; i++) {
|
for (let i = 0; i < data.length; i++) {
|
||||||
data[i] = data[i].trim()
|
data[i] = data[i].trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
let endTimeData = ''
|
let endTimeData = '';
|
||||||
switch (data.length) {
|
switch (data.length) {
|
||||||
case 1:
|
case 1:
|
||||||
task.id = parseId()
|
task.id = parseId();
|
||||||
task.startTime = prevTask.endTime
|
task.startTime = prevTask.endTime;
|
||||||
endTimeData = data[0]
|
endTimeData = data[0];
|
||||||
break
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
task.id = parseId()
|
task.id = parseId();
|
||||||
task.startTime = getStartDate(undefined, dateFormat, data[0])
|
task.startTime = getStartDate(undefined, dateFormat, data[0]);
|
||||||
endTimeData = data[1]
|
endTimeData = data[1];
|
||||||
break
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
task.id = parseId(data[0])
|
task.id = parseId(data[0]);
|
||||||
task.startTime = getStartDate(undefined, dateFormat, data[1])
|
task.startTime = getStartDate(undefined, dateFormat, data[1]);
|
||||||
endTimeData = data[2]
|
endTimeData = data[2];
|
||||||
break
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (endTimeData) {
|
if (endTimeData) {
|
||||||
task.endTime = getEndDate(task.startTime, dateFormat, endTimeData, inclusiveEndDates)
|
task.endTime = getEndDate(task.startTime, dateFormat, endTimeData, inclusiveEndDates);
|
||||||
task.manualEndTime = moment(endTimeData, 'YYYY-MM-DD', true).isValid()
|
task.manualEndTime = moment(endTimeData, 'YYYY-MM-DD', true).isValid();
|
||||||
checkTaskDates(task, dateFormat, excludes)
|
checkTaskDates(task, dateFormat, excludes);
|
||||||
}
|
}
|
||||||
|
|
||||||
return task
|
return task;
|
||||||
}
|
};
|
||||||
|
|
||||||
const parseData = function (prevTaskId, dataStr) {
|
const parseData = function(prevTaskId, dataStr) {
|
||||||
let ds
|
let ds;
|
||||||
if (dataStr.substr(0, 1) === ':') {
|
if (dataStr.substr(0, 1) === ':') {
|
||||||
ds = dataStr.substr(1, dataStr.length)
|
ds = dataStr.substr(1, dataStr.length);
|
||||||
} else {
|
} else {
|
||||||
ds = dataStr
|
ds = dataStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = ds.split(',')
|
const data = ds.split(',');
|
||||||
|
|
||||||
const task = {}
|
const task = {};
|
||||||
|
|
||||||
// Get tags like active, done, crit and milestone
|
// Get tags like active, done, crit and milestone
|
||||||
getTaskTags(data, task, tags)
|
getTaskTags(data, task, tags);
|
||||||
|
|
||||||
for (let i = 0; i < data.length; i++) {
|
for (let i = 0; i < data.length; i++) {
|
||||||
data[i] = data[i].trim()
|
data[i] = data[i].trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (data.length) {
|
switch (data.length) {
|
||||||
case 1:
|
case 1:
|
||||||
task.id = parseId()
|
task.id = parseId();
|
||||||
task.startTime = {
|
task.startTime = {
|
||||||
type: 'prevTaskEnd',
|
type: 'prevTaskEnd',
|
||||||
id: prevTaskId
|
id: prevTaskId
|
||||||
}
|
};
|
||||||
task.endTime = {
|
task.endTime = {
|
||||||
data: data[0]
|
data: data[0]
|
||||||
}
|
};
|
||||||
break
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
task.id = parseId()
|
task.id = parseId();
|
||||||
task.startTime = {
|
task.startTime = {
|
||||||
type: 'getStartDate',
|
type: 'getStartDate',
|
||||||
startData: data[0]
|
startData: data[0]
|
||||||
}
|
};
|
||||||
task.endTime = {
|
task.endTime = {
|
||||||
data: data[1]
|
data: data[1]
|
||||||
}
|
};
|
||||||
break
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
task.id = parseId(data[0])
|
task.id = parseId(data[0]);
|
||||||
task.startTime = {
|
task.startTime = {
|
||||||
type: 'getStartDate',
|
type: 'getStartDate',
|
||||||
startData: data[1]
|
startData: data[1]
|
||||||
}
|
};
|
||||||
task.endTime = {
|
task.endTime = {
|
||||||
data: data[2]
|
data: data[2]
|
||||||
}
|
};
|
||||||
break
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
return task
|
return task;
|
||||||
}
|
};
|
||||||
|
|
||||||
let lastTask
|
let lastTask;
|
||||||
let lastTaskID
|
let lastTaskID;
|
||||||
let rawTasks = []
|
let rawTasks = [];
|
||||||
const taskDb = {}
|
const taskDb = {};
|
||||||
export const addTask = function (descr, data) {
|
export const addTask = function(descr, data) {
|
||||||
const rawTask = {
|
const rawTask = {
|
||||||
section: currentSection,
|
section: currentSection,
|
||||||
type: currentSection,
|
type: currentSection,
|
||||||
@ -342,174 +339,187 @@ export const addTask = function (descr, data) {
|
|||||||
raw: { data: data },
|
raw: { data: data },
|
||||||
task: descr,
|
task: descr,
|
||||||
classes: []
|
classes: []
|
||||||
}
|
};
|
||||||
const taskInfo = parseData(lastTaskID, data)
|
const taskInfo = parseData(lastTaskID, data);
|
||||||
rawTask.raw.startTime = taskInfo.startTime
|
rawTask.raw.startTime = taskInfo.startTime;
|
||||||
rawTask.raw.endTime = taskInfo.endTime
|
rawTask.raw.endTime = taskInfo.endTime;
|
||||||
rawTask.id = taskInfo.id
|
rawTask.id = taskInfo.id;
|
||||||
rawTask.prevTaskId = lastTaskID
|
rawTask.prevTaskId = lastTaskID;
|
||||||
rawTask.active = taskInfo.active
|
rawTask.active = taskInfo.active;
|
||||||
rawTask.done = taskInfo.done
|
rawTask.done = taskInfo.done;
|
||||||
rawTask.crit = taskInfo.crit
|
rawTask.crit = taskInfo.crit;
|
||||||
rawTask.milestone = taskInfo.milestone
|
rawTask.milestone = taskInfo.milestone;
|
||||||
|
|
||||||
const pos = rawTasks.push(rawTask)
|
const pos = rawTasks.push(rawTask);
|
||||||
|
|
||||||
lastTaskID = rawTask.id
|
lastTaskID = rawTask.id;
|
||||||
// Store cross ref
|
// Store cross ref
|
||||||
taskDb[rawTask.id] = pos - 1
|
taskDb[rawTask.id] = pos - 1;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const findTaskById = function (id) {
|
export const findTaskById = function(id) {
|
||||||
const pos = taskDb[id]
|
const pos = taskDb[id];
|
||||||
return rawTasks[pos]
|
return rawTasks[pos];
|
||||||
}
|
};
|
||||||
|
|
||||||
export const addTaskOrg = function (descr, data) {
|
export const addTaskOrg = function(descr, data) {
|
||||||
const newTask = {
|
const newTask = {
|
||||||
section: currentSection,
|
section: currentSection,
|
||||||
type: currentSection,
|
type: currentSection,
|
||||||
description: descr,
|
description: descr,
|
||||||
task: descr,
|
task: descr,
|
||||||
classes: []
|
classes: []
|
||||||
}
|
};
|
||||||
const taskInfo = compileData(lastTask, data)
|
const taskInfo = compileData(lastTask, data);
|
||||||
newTask.startTime = taskInfo.startTime
|
newTask.startTime = taskInfo.startTime;
|
||||||
newTask.endTime = taskInfo.endTime
|
newTask.endTime = taskInfo.endTime;
|
||||||
newTask.id = taskInfo.id
|
newTask.id = taskInfo.id;
|
||||||
newTask.active = taskInfo.active
|
newTask.active = taskInfo.active;
|
||||||
newTask.done = taskInfo.done
|
newTask.done = taskInfo.done;
|
||||||
newTask.crit = taskInfo.crit
|
newTask.crit = taskInfo.crit;
|
||||||
newTask.milestone = taskInfo.milestone
|
newTask.milestone = taskInfo.milestone;
|
||||||
lastTask = newTask
|
lastTask = newTask;
|
||||||
tasks.push(newTask)
|
tasks.push(newTask);
|
||||||
}
|
};
|
||||||
|
|
||||||
const compileTasks = function () {
|
const compileTasks = function() {
|
||||||
const compileTask = function (pos) {
|
const compileTask = function(pos) {
|
||||||
const task = rawTasks[pos]
|
const task = rawTasks[pos];
|
||||||
let startTime = ''
|
let startTime = '';
|
||||||
switch (rawTasks[pos].raw.startTime.type) {
|
switch (rawTasks[pos].raw.startTime.type) {
|
||||||
case 'prevTaskEnd':
|
case 'prevTaskEnd':
|
||||||
const prevTask = findTaskById(task.prevTaskId)
|
const prevTask = findTaskById(task.prevTaskId);
|
||||||
task.startTime = prevTask.endTime
|
task.startTime = prevTask.endTime;
|
||||||
break
|
break;
|
||||||
case 'getStartDate':
|
case 'getStartDate':
|
||||||
startTime = getStartDate(undefined, dateFormat, rawTasks[pos].raw.startTime.startData)
|
startTime = getStartDate(undefined, dateFormat, rawTasks[pos].raw.startTime.startData);
|
||||||
if (startTime) {
|
if (startTime) {
|
||||||
rawTasks[pos].startTime = startTime
|
rawTasks[pos].startTime = startTime;
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rawTasks[pos].startTime) {
|
if (rawTasks[pos].startTime) {
|
||||||
rawTasks[pos].endTime = getEndDate(rawTasks[pos].startTime, dateFormat, rawTasks[pos].raw.endTime.data, inclusiveEndDates)
|
rawTasks[pos].endTime = getEndDate(
|
||||||
|
rawTasks[pos].startTime,
|
||||||
|
dateFormat,
|
||||||
|
rawTasks[pos].raw.endTime.data,
|
||||||
|
inclusiveEndDates
|
||||||
|
);
|
||||||
if (rawTasks[pos].endTime) {
|
if (rawTasks[pos].endTime) {
|
||||||
rawTasks[pos].processed = true
|
rawTasks[pos].processed = true;
|
||||||
rawTasks[pos].manualEndTime = moment(rawTasks[pos].raw.endTime.data, 'YYYY-MM-DD', true).isValid()
|
rawTasks[pos].manualEndTime = moment(
|
||||||
checkTaskDates(rawTasks[pos], dateFormat, excludes)
|
rawTasks[pos].raw.endTime.data,
|
||||||
|
'YYYY-MM-DD',
|
||||||
|
true
|
||||||
|
).isValid();
|
||||||
|
checkTaskDates(rawTasks[pos], dateFormat, excludes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return rawTasks[pos].processed
|
return rawTasks[pos].processed;
|
||||||
}
|
};
|
||||||
|
|
||||||
let allProcessed = true
|
let allProcessed = true;
|
||||||
for (let i = 0; i < rawTasks.length; i++) {
|
for (let i = 0; i < rawTasks.length; i++) {
|
||||||
compileTask(i)
|
compileTask(i);
|
||||||
|
|
||||||
allProcessed = allProcessed && rawTasks[i].processed
|
allProcessed = allProcessed && rawTasks[i].processed;
|
||||||
}
|
}
|
||||||
return allProcessed
|
return allProcessed;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by parser when a link is found. Adds the URL to the vertex data.
|
* Called by parser when a link is found. Adds the URL to the vertex data.
|
||||||
* @param ids Comma separated list of ids
|
* @param ids Comma separated list of ids
|
||||||
* @param linkStr URL to create a link for
|
* @param linkStr URL to create a link for
|
||||||
*/
|
*/
|
||||||
export const setLink = function (ids, _linkStr) {
|
export const setLink = function(ids, _linkStr) {
|
||||||
let linkStr = _linkStr
|
let linkStr = _linkStr;
|
||||||
if (config.securityLevel !== 'loose') {
|
if (config.securityLevel !== 'loose') {
|
||||||
linkStr = sanitizeUrl(_linkStr)
|
linkStr = sanitizeUrl(_linkStr);
|
||||||
}
|
}
|
||||||
ids.split(',').forEach(function (id) {
|
ids.split(',').forEach(function(id) {
|
||||||
let rawTask = findTaskById(id)
|
let rawTask = findTaskById(id);
|
||||||
if (typeof rawTask !== 'undefined') {
|
if (typeof rawTask !== 'undefined') {
|
||||||
pushFun(id, () => { window.open(linkStr, '_self') })
|
pushFun(id, () => {
|
||||||
|
window.open(linkStr, '_self');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
setClass(ids, 'clickable')
|
setClass(ids, 'clickable');
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by parser when a special node is found, e.g. a clickable element.
|
* Called by parser when a special node is found, e.g. a clickable element.
|
||||||
* @param ids Comma separated list of ids
|
* @param ids Comma separated list of ids
|
||||||
* @param className Class to add
|
* @param className Class to add
|
||||||
*/
|
*/
|
||||||
export const setClass = function (ids, className) {
|
export const setClass = function(ids, className) {
|
||||||
ids.split(',').forEach(function (id) {
|
ids.split(',').forEach(function(id) {
|
||||||
let rawTask = findTaskById(id)
|
let rawTask = findTaskById(id);
|
||||||
if (typeof rawTask !== 'undefined') {
|
if (typeof rawTask !== 'undefined') {
|
||||||
rawTask.classes.push(className)
|
rawTask.classes.push(className);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
const setClickFun = function (id, functionName, functionArgs) {
|
const setClickFun = function(id, functionName, functionArgs) {
|
||||||
if (config.securityLevel !== 'loose') {
|
if (config.securityLevel !== 'loose') {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
if (typeof functionName === 'undefined') {
|
if (typeof functionName === 'undefined') {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let argList = []
|
let argList = [];
|
||||||
if (typeof functionArgs === 'string') {
|
if (typeof functionArgs === 'string') {
|
||||||
/* Splits functionArgs by ',', ignoring all ',' in double quoted strings */
|
/* Splits functionArgs by ',', ignoring all ',' in double quoted strings */
|
||||||
argList = functionArgs.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/)
|
argList = functionArgs.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);
|
||||||
for (let i = 0; i < argList.length; i++) {
|
for (let i = 0; i < argList.length; i++) {
|
||||||
let item = argList[i].trim()
|
let item = argList[i].trim();
|
||||||
/* Removes all double quotes at the start and end of an argument */
|
/* Removes all double quotes at the start and end of an argument */
|
||||||
/* This preserves all starting and ending whitespace inside */
|
/* This preserves all starting and ending whitespace inside */
|
||||||
if (item.charAt(0) === '"' && item.charAt(item.length - 1) === '"') {
|
if (item.charAt(0) === '"' && item.charAt(item.length - 1) === '"') {
|
||||||
item = item.substr(1, item.length - 2)
|
item = item.substr(1, item.length - 2);
|
||||||
}
|
}
|
||||||
argList[i] = item
|
argList[i] = item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let rawTask = findTaskById(id)
|
let rawTask = findTaskById(id);
|
||||||
if (typeof rawTask !== 'undefined') {
|
if (typeof rawTask !== 'undefined') {
|
||||||
pushFun(id, () => { window[functionName](...argList) })
|
pushFun(id, () => {
|
||||||
|
window[functionName](...argList);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The callbackFunction is executed in a click event bound to the task with the specified id or the task's assigned text
|
* The callbackFunction is executed in a click event bound to the task with the specified id or the task's assigned text
|
||||||
* @param id The task's id
|
* @param id The task's id
|
||||||
* @param callbackFunction A function to be executed when clicked on the task or the task's text
|
* @param callbackFunction A function to be executed when clicked on the task or the task's text
|
||||||
*/
|
*/
|
||||||
const pushFun = function (id, callbackFunction) {
|
const pushFun = function(id, callbackFunction) {
|
||||||
funs.push(function (element) {
|
funs.push(function(element) {
|
||||||
// const elem = d3.select(element).select(`[id="${id}"]`)
|
// const elem = d3.select(element).select(`[id="${id}"]`)
|
||||||
const elem = document.querySelector(`[id="${id}"]`)
|
const elem = document.querySelector(`[id="${id}"]`);
|
||||||
if (elem !== null) {
|
if (elem !== null) {
|
||||||
elem.addEventListener('click', function () {
|
elem.addEventListener('click', function() {
|
||||||
callbackFunction()
|
callbackFunction();
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
funs.push(function (element) {
|
funs.push(function(element) {
|
||||||
// const elem = d3.select(element).select(`[id="${id}-text"]`)
|
// const elem = d3.select(element).select(`[id="${id}-text"]`)
|
||||||
const elem = document.querySelector(`[id="${id}-text"]`)
|
const elem = document.querySelector(`[id="${id}-text"]`);
|
||||||
if (elem !== null) {
|
if (elem !== null) {
|
||||||
elem.addEventListener('click', function () {
|
elem.addEventListener('click', function() {
|
||||||
callbackFunction()
|
callbackFunction();
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by parser when a click definition is found. Registers an event handler.
|
* Called by parser when a click definition is found. Registers an event handler.
|
||||||
@ -517,22 +527,22 @@ const pushFun = function (id, callbackFunction) {
|
|||||||
* @param functionName Function to be called on click
|
* @param functionName Function to be called on click
|
||||||
* @param functionArgs Function args the function should be called with
|
* @param functionArgs Function args the function should be called with
|
||||||
*/
|
*/
|
||||||
export const setClickEvent = function (ids, functionName, functionArgs) {
|
export const setClickEvent = function(ids, functionName, functionArgs) {
|
||||||
ids.split(',').forEach(function (id) {
|
ids.split(',').forEach(function(id) {
|
||||||
setClickFun(id, functionName, functionArgs)
|
setClickFun(id, functionName, functionArgs);
|
||||||
})
|
});
|
||||||
setClass(ids, 'clickable')
|
setClass(ids, 'clickable');
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds all functions previously added to fun (specified through click) to the element
|
* Binds all functions previously added to fun (specified through click) to the element
|
||||||
* @param element
|
* @param element
|
||||||
*/
|
*/
|
||||||
export const bindFunctions = function (element) {
|
export const bindFunctions = function(element) {
|
||||||
funs.forEach(function (fun) {
|
funs.forEach(function(fun) {
|
||||||
fun(element)
|
fun(element);
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
clear,
|
clear,
|
||||||
@ -556,20 +566,20 @@ export default {
|
|||||||
setLink,
|
setLink,
|
||||||
bindFunctions,
|
bindFunctions,
|
||||||
durationToDate
|
durationToDate
|
||||||
}
|
};
|
||||||
|
|
||||||
function getTaskTags (data, task, tags) {
|
function getTaskTags(data, task, tags) {
|
||||||
let matchFound = true
|
let matchFound = true;
|
||||||
while (matchFound) {
|
while (matchFound) {
|
||||||
matchFound = false
|
matchFound = false;
|
||||||
tags.forEach(function (t) {
|
tags.forEach(function(t) {
|
||||||
const pattern = '^\\s*' + t + '\\s*$'
|
const pattern = '^\\s*' + t + '\\s*$';
|
||||||
const regex = new RegExp(pattern)
|
const regex = new RegExp(pattern);
|
||||||
if (data[0].match(regex)) {
|
if (data[0].match(regex)) {
|
||||||
task[t] = true
|
task[t] = true;
|
||||||
data.shift(1)
|
data.shift(1);
|
||||||
matchFound = true
|
matchFound = true;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
/* eslint-env jasmine */
|
/* eslint-env jasmine */
|
||||||
import moment from 'moment-mini'
|
import moment from 'moment-mini';
|
||||||
import ganttDb from './ganttDb'
|
import ganttDb from './ganttDb';
|
||||||
|
|
||||||
describe('when using the ganttDb', function () {
|
describe('when using the ganttDb', function() {
|
||||||
beforeEach(function () {
|
beforeEach(function() {
|
||||||
ganttDb.clear()
|
ganttDb.clear();
|
||||||
})
|
});
|
||||||
|
|
||||||
describe('when using relative times', function () {
|
describe('when using relative times', function() {
|
||||||
it.each`
|
it.each`
|
||||||
diff | date | expected
|
diff | date | expected
|
||||||
${' 1d'} | ${moment('2019-01-01')} | ${moment('2019-01-02').toDate()}
|
${' 1d'} | ${moment('2019-01-01')} | ${moment('2019-01-02').toDate()}
|
||||||
${' 1w'} | ${moment('2019-01-01')} | ${moment('2019-01-08').toDate()}
|
${' 1w'} | ${moment('2019-01-01')} | ${moment('2019-01-08').toDate()}
|
||||||
`('should add $diff to $date resulting in $expected', ({ diff, date, expected }) => {
|
`('should add $diff to $date resulting in $expected', ({ diff, date, expected }) => {
|
||||||
expect(ganttDb.durationToDate(diff, date)).toEqual(expected)
|
expect(ganttDb.durationToDate(diff, date)).toEqual(expected);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
describe('when calling the clear function', function () {
|
describe('when calling the clear function', function() {
|
||||||
beforeEach(function () {
|
beforeEach(function() {
|
||||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
ganttDb.setDateFormat('YYYY-MM-DD');
|
||||||
ganttDb.enableInclusiveEndDates()
|
ganttDb.enableInclusiveEndDates();
|
||||||
ganttDb.setExcludes('weekends 2019-02-06,friday')
|
ganttDb.setExcludes('weekends 2019-02-06,friday');
|
||||||
ganttDb.addSection('weekends skip test')
|
ganttDb.addSection('weekends skip test');
|
||||||
ganttDb.addTask('test1', 'id1,2019-02-01,1d')
|
ganttDb.addTask('test1', 'id1,2019-02-01,1d');
|
||||||
ganttDb.addTask('test2', 'id2,after id1,2d')
|
ganttDb.addTask('test2', 'id2,after id1,2d');
|
||||||
ganttDb.clear()
|
ganttDb.clear();
|
||||||
})
|
});
|
||||||
|
|
||||||
it.each`
|
it.each`
|
||||||
fn | expected
|
fn | expected
|
||||||
@ -38,9 +38,9 @@ describe('when using the ganttDb', function () {
|
|||||||
${'getSections'} | ${[]}
|
${'getSections'} | ${[]}
|
||||||
${'endDatesAreInclusive'} | ${false}
|
${'endDatesAreInclusive'} | ${false}
|
||||||
`('should clear $fn', ({ fn, expected }) => {
|
`('should clear $fn', ({ fn, expected }) => {
|
||||||
expect(ganttDb[ fn ]()).toEqual(expected)
|
expect(ganttDb[fn]()).toEqual(expected);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
it.each`
|
it.each`
|
||||||
testName | section | taskName | taskData | expStartDate | expEndDate | expId | expTask
|
testName | section | taskName | taskData | expStartDate | expEndDate | expId | expTask
|
||||||
@ -52,135 +52,148 @@ describe('when using the ganttDb', function () {
|
|||||||
${'should handle duration (weeks) instead of fixed date to determine end date'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2w'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 15)} | ${'id1'} | ${'test1'}
|
${'should handle duration (weeks) instead of fixed date to determine end date'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2w'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 15)} | ${'id1'} | ${'test1'}
|
||||||
${'should handle fixed dates without id'} | ${'testa1'} | ${'test1'} | ${'2013-01-01,2013-01-12'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 12)} | ${'task1'} | ${'test1'}
|
${'should handle fixed dates without id'} | ${'testa1'} | ${'test1'} | ${'2013-01-01,2013-01-12'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 12)} | ${'task1'} | ${'test1'}
|
||||||
${'should handle duration instead of a fixed date to determine end date without id'} | ${'testa1'} | ${'test1'} | ${'2013-01-01,4d'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 5)} | ${'task1'} | ${'test1'}
|
${'should handle duration instead of a fixed date to determine end date without id'} | ${'testa1'} | ${'test1'} | ${'2013-01-01,4d'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 5)} | ${'task1'} | ${'test1'}
|
||||||
`('$testName', ({ section, taskName, taskData, expStartDate, expEndDate, expId, expTask }) => {
|
`('$testName', ({ section, taskName, taskData, expStartDate, expEndDate, expId, expTask }) => {
|
||||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
ganttDb.setDateFormat('YYYY-MM-DD');
|
||||||
ganttDb.addSection(section)
|
ganttDb.addSection(section);
|
||||||
ganttDb.addTask(taskName, taskData)
|
ganttDb.addTask(taskName, taskData);
|
||||||
const tasks = ganttDb.getTasks()
|
const tasks = ganttDb.getTasks();
|
||||||
expect(tasks[0].startTime).toEqual(expStartDate)
|
expect(tasks[0].startTime).toEqual(expStartDate);
|
||||||
expect(tasks[0].endTime).toEqual(expEndDate)
|
expect(tasks[0].endTime).toEqual(expEndDate);
|
||||||
expect(tasks[0].id).toEqual(expId)
|
expect(tasks[0].id).toEqual(expId);
|
||||||
expect(tasks[0].task).toEqual(expTask)
|
expect(tasks[0].task).toEqual(expTask);
|
||||||
})
|
});
|
||||||
|
|
||||||
it.each`
|
it.each`
|
||||||
section | taskName1 | taskName2 | taskData1 | taskData2 | expStartDate2 | expEndDate2 | expId2 | expTask2
|
section | taskName1 | taskName2 | taskData1 | taskData2 | expStartDate2 | expEndDate2 | expId2 | expTask2
|
||||||
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'id2,after id1,1d'} | ${new Date(2013, 0, 15)} | ${undefined} | ${'id2'} | ${'test2'}
|
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'id2,after id1,1d'} | ${new Date(2013, 0, 15)} | ${undefined} | ${'id2'} | ${'test2'}
|
||||||
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'id2,after id3,1d'} | ${new Date((new Date()).setHours(0, 0, 0, 0))} | ${undefined} | ${'id2'} | ${'test2'}
|
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'id2,after id3,1d'} | ${new Date(new Date().setHours(0, 0, 0, 0))} | ${undefined} | ${'id2'} | ${'test2'}
|
||||||
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'after id1,1d'} | ${new Date(2013, 0, 15)} | ${undefined} | ${'task1'} | ${'test2'}
|
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'after id1,1d'} | ${new Date(2013, 0, 15)} | ${undefined} | ${'task1'} | ${'test2'}
|
||||||
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'2013-01-26'} | ${new Date(2013, 0, 15)} | ${new Date(2013, 0, 26)} | ${'task1'} | ${'test2'}
|
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'2013-01-26'} | ${new Date(2013, 0, 15)} | ${new Date(2013, 0, 26)} | ${'task1'} | ${'test2'}
|
||||||
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'2d'} | ${new Date(2013, 0, 15)} | ${new Date(2013, 0, 17)} | ${'task1'} | ${'test2'}
|
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'2d'} | ${new Date(2013, 0, 15)} | ${new Date(2013, 0, 17)} | ${'task1'} | ${'test2'}
|
||||||
`('$testName', ({ section, taskName1, taskName2, taskData1, taskData2, expStartDate2, expEndDate2, expId2, expTask2 }) => {
|
`(
|
||||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
'$testName',
|
||||||
ganttDb.addSection(section)
|
({
|
||||||
ganttDb.addTask(taskName1, taskData1)
|
section,
|
||||||
ganttDb.addTask(taskName2, taskData2)
|
taskName1,
|
||||||
const tasks = ganttDb.getTasks()
|
taskName2,
|
||||||
expect(tasks[1].startTime).toEqual(expStartDate2)
|
taskData1,
|
||||||
if (!expEndDate2 === undefined) {
|
taskData2,
|
||||||
expect(tasks[1].endTime).toEqual(expEndDate2)
|
expStartDate2,
|
||||||
}
|
expEndDate2,
|
||||||
expect(tasks[1].id).toEqual(expId2)
|
expId2,
|
||||||
expect(tasks[1].task).toEqual(expTask2)
|
expTask2
|
||||||
})
|
}) => {
|
||||||
|
ganttDb.setDateFormat('YYYY-MM-DD');
|
||||||
|
ganttDb.addSection(section);
|
||||||
|
ganttDb.addTask(taskName1, taskData1);
|
||||||
|
ganttDb.addTask(taskName2, taskData2);
|
||||||
|
const tasks = ganttDb.getTasks();
|
||||||
|
expect(tasks[1].startTime).toEqual(expStartDate2);
|
||||||
|
if (!expEndDate2 === undefined) {
|
||||||
|
expect(tasks[1].endTime).toEqual(expEndDate2);
|
||||||
|
}
|
||||||
|
expect(tasks[1].id).toEqual(expId2);
|
||||||
|
expect(tasks[1].task).toEqual(expTask2);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
it('should handle relative start date based on id regardless of sections', function () {
|
it('should handle relative start date based on id regardless of sections', function() {
|
||||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
ganttDb.setDateFormat('YYYY-MM-DD');
|
||||||
ganttDb.addSection('testa1')
|
ganttDb.addSection('testa1');
|
||||||
ganttDb.addTask('test1', 'id1,2013-01-01,2w')
|
ganttDb.addTask('test1', 'id1,2013-01-01,2w');
|
||||||
ganttDb.addTask('test2', 'id2,after id3,1d')
|
ganttDb.addTask('test2', 'id2,after id3,1d');
|
||||||
ganttDb.addSection('testa2')
|
ganttDb.addSection('testa2');
|
||||||
ganttDb.addTask('test3', 'id3,after id1,2d')
|
ganttDb.addTask('test3', 'id3,after id1,2d');
|
||||||
|
|
||||||
const tasks = ganttDb.getTasks()
|
const tasks = ganttDb.getTasks();
|
||||||
|
|
||||||
expect(tasks[1].startTime).toEqual(new Date(2013, 0, 17))
|
expect(tasks[1].startTime).toEqual(new Date(2013, 0, 17));
|
||||||
expect(tasks[1].endTime).toEqual(new Date(2013, 0, 18))
|
expect(tasks[1].endTime).toEqual(new Date(2013, 0, 18));
|
||||||
expect(tasks[1].id).toEqual('id2')
|
expect(tasks[1].id).toEqual('id2');
|
||||||
expect(tasks[1].task).toEqual('test2')
|
expect(tasks[1].task).toEqual('test2');
|
||||||
|
|
||||||
expect(tasks[2].id).toEqual('id3')
|
expect(tasks[2].id).toEqual('id3');
|
||||||
expect(tasks[2].task).toEqual('test3')
|
expect(tasks[2].task).toEqual('test3');
|
||||||
expect(tasks[2].startTime).toEqual(new Date(2013, 0, 15))
|
expect(tasks[2].startTime).toEqual(new Date(2013, 0, 15));
|
||||||
expect(tasks[2].endTime).toEqual(new Date(2013, 0, 17))
|
expect(tasks[2].endTime).toEqual(new Date(2013, 0, 17));
|
||||||
})
|
});
|
||||||
it('should ignore weekends', function () {
|
it('should ignore weekends', function() {
|
||||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
ganttDb.setDateFormat('YYYY-MM-DD');
|
||||||
ganttDb.setExcludes('weekends 2019-02-06,friday')
|
ganttDb.setExcludes('weekends 2019-02-06,friday');
|
||||||
ganttDb.addSection('weekends skip test')
|
ganttDb.addSection('weekends skip test');
|
||||||
ganttDb.addTask('test1', 'id1,2019-02-01,1d')
|
ganttDb.addTask('test1', 'id1,2019-02-01,1d');
|
||||||
ganttDb.addTask('test2', 'id2,after id1,2d')
|
ganttDb.addTask('test2', 'id2,after id1,2d');
|
||||||
ganttDb.addTask('test3', 'id3,after id2,7d')
|
ganttDb.addTask('test3', 'id3,after id2,7d');
|
||||||
ganttDb.addTask('test4', 'id4,2019-02-01,2019-02-20') // Fixed endTime
|
ganttDb.addTask('test4', 'id4,2019-02-01,2019-02-20'); // Fixed endTime
|
||||||
ganttDb.addTask('test5', 'id5,after id4,1d')
|
ganttDb.addTask('test5', 'id5,after id4,1d');
|
||||||
ganttDb.addSection('full ending taks on last day')
|
ganttDb.addSection('full ending taks on last day');
|
||||||
ganttDb.addTask('test6', 'id6,2019-02-13,2d')
|
ganttDb.addTask('test6', 'id6,2019-02-13,2d');
|
||||||
ganttDb.addTask('test7', 'id7,after id6,1d')
|
ganttDb.addTask('test7', 'id7,after id6,1d');
|
||||||
|
|
||||||
const tasks = ganttDb.getTasks()
|
const tasks = ganttDb.getTasks();
|
||||||
|
|
||||||
expect(tasks[0].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate())
|
expect(tasks[0].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[0].endTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate())
|
expect(tasks[0].endTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[0].renderEndTime).toEqual(moment('2019-02-02', 'YYYY-MM-DD').toDate())
|
expect(tasks[0].renderEndTime).toEqual(moment('2019-02-02', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[0].id).toEqual('id1')
|
expect(tasks[0].id).toEqual('id1');
|
||||||
expect(tasks[0].task).toEqual('test1')
|
expect(tasks[0].task).toEqual('test1');
|
||||||
|
|
||||||
expect(tasks[1].startTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate())
|
expect(tasks[1].startTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[1].endTime).toEqual(moment('2019-02-07', 'YYYY-MM-DD').toDate())
|
expect(tasks[1].endTime).toEqual(moment('2019-02-07', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[1].renderEndTime).toEqual(moment('2019-02-06', 'YYYY-MM-DD').toDate())
|
expect(tasks[1].renderEndTime).toEqual(moment('2019-02-06', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[1].id).toEqual('id2')
|
expect(tasks[1].id).toEqual('id2');
|
||||||
expect(tasks[1].task).toEqual('test2')
|
expect(tasks[1].task).toEqual('test2');
|
||||||
|
|
||||||
expect(tasks[2].startTime).toEqual(moment('2019-02-07', 'YYYY-MM-DD').toDate())
|
expect(tasks[2].startTime).toEqual(moment('2019-02-07', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[2].endTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate())
|
expect(tasks[2].endTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[2].renderEndTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate())
|
expect(tasks[2].renderEndTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[2].id).toEqual('id3')
|
expect(tasks[2].id).toEqual('id3');
|
||||||
expect(tasks[2].task).toEqual('test3')
|
expect(tasks[2].task).toEqual('test3');
|
||||||
|
|
||||||
expect(tasks[3].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate())
|
expect(tasks[3].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[3].endTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate())
|
expect(tasks[3].endTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[3].renderEndTime).toBeNull() // Fixed end
|
expect(tasks[3].renderEndTime).toBeNull(); // Fixed end
|
||||||
expect(tasks[3].id).toEqual('id4')
|
expect(tasks[3].id).toEqual('id4');
|
||||||
expect(tasks[3].task).toEqual('test4')
|
expect(tasks[3].task).toEqual('test4');
|
||||||
|
|
||||||
expect(tasks[4].startTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate())
|
expect(tasks[4].startTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[4].endTime).toEqual(moment('2019-02-21', 'YYYY-MM-DD').toDate())
|
expect(tasks[4].endTime).toEqual(moment('2019-02-21', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[4].renderEndTime).toEqual(moment('2019-02-21', 'YYYY-MM-DD').toDate())
|
expect(tasks[4].renderEndTime).toEqual(moment('2019-02-21', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[4].id).toEqual('id5')
|
expect(tasks[4].id).toEqual('id5');
|
||||||
expect(tasks[4].task).toEqual('test5')
|
expect(tasks[4].task).toEqual('test5');
|
||||||
|
|
||||||
expect(tasks[5].startTime).toEqual(moment('2019-02-13', 'YYYY-MM-DD').toDate())
|
expect(tasks[5].startTime).toEqual(moment('2019-02-13', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[5].endTime).toEqual(moment('2019-02-18', 'YYYY-MM-DD').toDate())
|
expect(tasks[5].endTime).toEqual(moment('2019-02-18', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[5].renderEndTime).toEqual(moment('2019-02-15', 'YYYY-MM-DD').toDate())
|
expect(tasks[5].renderEndTime).toEqual(moment('2019-02-15', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[5].id).toEqual('id6')
|
expect(tasks[5].id).toEqual('id6');
|
||||||
expect(tasks[5].task).toEqual('test6')
|
expect(tasks[5].task).toEqual('test6');
|
||||||
|
|
||||||
expect(tasks[6].startTime).toEqual(moment('2019-02-18', 'YYYY-MM-DD').toDate())
|
expect(tasks[6].startTime).toEqual(moment('2019-02-18', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[6].endTime).toEqual(moment('2019-02-19', 'YYYY-MM-DD').toDate())
|
expect(tasks[6].endTime).toEqual(moment('2019-02-19', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[6].id).toEqual('id7')
|
expect(tasks[6].id).toEqual('id7');
|
||||||
expect(tasks[6].task).toEqual('test7')
|
expect(tasks[6].task).toEqual('test7');
|
||||||
})
|
});
|
||||||
|
|
||||||
describe('when setting inclusive end dates', function () {
|
describe('when setting inclusive end dates', function() {
|
||||||
beforeEach(function () {
|
beforeEach(function() {
|
||||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
ganttDb.setDateFormat('YYYY-MM-DD');
|
||||||
ganttDb.enableInclusiveEndDates()
|
ganttDb.enableInclusiveEndDates();
|
||||||
ganttDb.addTask('test1', 'id1,2019-02-01,1d')
|
ganttDb.addTask('test1', 'id1,2019-02-01,1d');
|
||||||
ganttDb.addTask('test2', 'id2,2019-02-01,2019-02-03')
|
ganttDb.addTask('test2', 'id2,2019-02-01,2019-02-03');
|
||||||
})
|
});
|
||||||
it('should automatically add one day to all end dates', function () {
|
it('should automatically add one day to all end dates', function() {
|
||||||
const tasks = ganttDb.getTasks()
|
const tasks = ganttDb.getTasks();
|
||||||
expect(tasks[0].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate())
|
expect(tasks[0].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[0].endTime).toEqual(moment('2019-02-02', 'YYYY-MM-DD').toDate())
|
expect(tasks[0].endTime).toEqual(moment('2019-02-02', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[0].id).toEqual('id1')
|
expect(tasks[0].id).toEqual('id1');
|
||||||
expect(tasks[0].task).toEqual('test1')
|
expect(tasks[0].task).toEqual('test1');
|
||||||
|
|
||||||
expect(tasks[1].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate())
|
expect(tasks[1].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[1].endTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate())
|
expect(tasks[1].endTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate());
|
||||||
expect(tasks[1].renderEndTime).toBeNull() // Fixed end
|
expect(tasks[1].renderEndTime).toBeNull(); // Fixed end
|
||||||
expect(tasks[1].manualEndTime).toBeTruthy()
|
expect(tasks[1].manualEndTime).toBeTruthy();
|
||||||
expect(tasks[1].id).toEqual('id2')
|
expect(tasks[1].id).toEqual('id2');
|
||||||
expect(tasks[1].task).toEqual('test2')
|
expect(tasks[1].task).toEqual('test2');
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import * as d3 from 'd3'
|
import * as d3 from 'd3';
|
||||||
|
|
||||||
import { parser } from './parser/gantt'
|
import { parser } from './parser/gantt';
|
||||||
import ganttDb from './ganttDb'
|
import ganttDb from './ganttDb';
|
||||||
|
|
||||||
parser.yy = ganttDb
|
parser.yy = ganttDb;
|
||||||
|
|
||||||
const conf = {
|
const conf = {
|
||||||
titleTopMargin: 25,
|
titleTopMargin: 25,
|
||||||
@ -15,287 +15,316 @@ const conf = {
|
|||||||
gridLineStartPadding: 35,
|
gridLineStartPadding: 35,
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
fontFamily: '"Open-Sans", "sans-serif"'
|
fontFamily: '"Open-Sans", "sans-serif"'
|
||||||
}
|
};
|
||||||
export const setConf = function (cnf) {
|
export const setConf = function(cnf) {
|
||||||
const keys = Object.keys(cnf)
|
const keys = Object.keys(cnf);
|
||||||
|
|
||||||
keys.forEach(function (key) {
|
keys.forEach(function(key) {
|
||||||
conf[key] = cnf[key]
|
conf[key] = cnf[key];
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
let w
|
let w;
|
||||||
export const draw = function (text, id) {
|
export const draw = function(text, id) {
|
||||||
parser.yy.clear()
|
parser.yy.clear();
|
||||||
parser.parse(text)
|
parser.parse(text);
|
||||||
|
|
||||||
const elem = document.getElementById(id)
|
const elem = document.getElementById(id);
|
||||||
w = elem.parentElement.offsetWidth
|
w = elem.parentElement.offsetWidth;
|
||||||
|
|
||||||
if (typeof w === 'undefined') {
|
if (typeof w === 'undefined') {
|
||||||
w = 1200
|
w = 1200;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof conf.useWidth !== 'undefined') {
|
if (typeof conf.useWidth !== 'undefined') {
|
||||||
w = conf.useWidth
|
w = conf.useWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
const taskArray = parser.yy.getTasks()
|
const taskArray = parser.yy.getTasks();
|
||||||
|
|
||||||
// Set height based on number of tasks
|
// Set height based on number of tasks
|
||||||
const h = taskArray.length * (conf.barHeight + conf.barGap) + 2 * conf.topPadding
|
const h = taskArray.length * (conf.barHeight + conf.barGap) + 2 * conf.topPadding;
|
||||||
|
|
||||||
elem.setAttribute('height', '100%')
|
elem.setAttribute('height', '100%');
|
||||||
// Set viewBox
|
// Set viewBox
|
||||||
elem.setAttribute('viewBox', '0 0 ' + w + ' ' + h)
|
elem.setAttribute('viewBox', '0 0 ' + w + ' ' + h);
|
||||||
const svg = d3.select(`[id="${id}"]`)
|
const svg = d3.select(`[id="${id}"]`);
|
||||||
|
|
||||||
// Set timescale
|
// Set timescale
|
||||||
const timeScale = d3.scaleTime()
|
const timeScale = d3
|
||||||
.domain([d3.min(taskArray, function (d) {
|
.scaleTime()
|
||||||
return d.startTime
|
.domain([
|
||||||
}),
|
d3.min(taskArray, function(d) {
|
||||||
d3.max(taskArray, function (d) {
|
return d.startTime;
|
||||||
return d.endTime
|
}),
|
||||||
})])
|
d3.max(taskArray, function(d) {
|
||||||
.rangeRound([0, w - conf.leftPadding - conf.rightPadding])
|
return d.endTime;
|
||||||
|
})
|
||||||
|
])
|
||||||
|
.rangeRound([0, w - conf.leftPadding - conf.rightPadding]);
|
||||||
|
|
||||||
let categories = []
|
let categories = [];
|
||||||
|
|
||||||
for (let i = 0; i < taskArray.length; i++) {
|
for (let i = 0; i < taskArray.length; i++) {
|
||||||
categories.push(taskArray[i].type)
|
categories.push(taskArray[i].type);
|
||||||
}
|
}
|
||||||
|
|
||||||
const catsUnfiltered = categories // for vert labels
|
const catsUnfiltered = categories; // for vert labels
|
||||||
|
|
||||||
categories = checkUnique(categories)
|
categories = checkUnique(categories);
|
||||||
|
|
||||||
makeGant(taskArray, w, h)
|
makeGant(taskArray, w, h);
|
||||||
if (typeof conf.useWidth !== 'undefined') {
|
if (typeof conf.useWidth !== 'undefined') {
|
||||||
elem.setAttribute('width', w)
|
elem.setAttribute('width', w);
|
||||||
}
|
}
|
||||||
|
|
||||||
svg.append('text')
|
svg
|
||||||
|
.append('text')
|
||||||
.text(parser.yy.getTitle())
|
.text(parser.yy.getTitle())
|
||||||
.attr('x', w / 2)
|
.attr('x', w / 2)
|
||||||
.attr('y', conf.titleTopMargin)
|
.attr('y', conf.titleTopMargin)
|
||||||
.attr('class', 'titleText')
|
.attr('class', 'titleText');
|
||||||
|
|
||||||
function makeGant (tasks, pageWidth, pageHeight) {
|
function makeGant(tasks, pageWidth, pageHeight) {
|
||||||
const barHeight = conf.barHeight
|
const barHeight = conf.barHeight;
|
||||||
const gap = barHeight + conf.barGap
|
const gap = barHeight + conf.barGap;
|
||||||
const topPadding = conf.topPadding
|
const topPadding = conf.topPadding;
|
||||||
const leftPadding = conf.leftPadding
|
const leftPadding = conf.leftPadding;
|
||||||
|
|
||||||
const colorScale = d3.scaleLinear()
|
const colorScale = d3
|
||||||
|
.scaleLinear()
|
||||||
.domain([0, categories.length])
|
.domain([0, categories.length])
|
||||||
.range(['#00B9FA', '#F95002'])
|
.range(['#00B9FA', '#F95002'])
|
||||||
.interpolate(d3.interpolateHcl)
|
.interpolate(d3.interpolateHcl);
|
||||||
|
|
||||||
makeGrid(leftPadding, topPadding, pageWidth, pageHeight)
|
makeGrid(leftPadding, topPadding, pageWidth, pageHeight);
|
||||||
drawRects(tasks, gap, topPadding, leftPadding, barHeight, colorScale, pageWidth, pageHeight)
|
drawRects(tasks, gap, topPadding, leftPadding, barHeight, colorScale, pageWidth, pageHeight);
|
||||||
vertLabels(gap, topPadding, leftPadding, barHeight, colorScale)
|
vertLabels(gap, topPadding, leftPadding, barHeight, colorScale);
|
||||||
drawToday(leftPadding, topPadding, pageWidth, pageHeight)
|
drawToday(leftPadding, topPadding, pageWidth, pageHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawRects (theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w, h) {
|
function drawRects(theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w, h) {
|
||||||
// Draw background rects covering the entire width of the graph, these form the section rows.
|
// Draw background rects covering the entire width of the graph, these form the section rows.
|
||||||
svg.append('g')
|
svg
|
||||||
|
.append('g')
|
||||||
.selectAll('rect')
|
.selectAll('rect')
|
||||||
.data(theArray)
|
.data(theArray)
|
||||||
.enter()
|
.enter()
|
||||||
.append('rect')
|
.append('rect')
|
||||||
.attr('x', 0)
|
.attr('x', 0)
|
||||||
.attr('y', function (d, i) {
|
.attr('y', function(d, i) {
|
||||||
return i * theGap + theTopPad - 2
|
return i * theGap + theTopPad - 2;
|
||||||
})
|
})
|
||||||
.attr('width', function () {
|
.attr('width', function() {
|
||||||
return w - conf.rightPadding / 2
|
return w - conf.rightPadding / 2;
|
||||||
})
|
})
|
||||||
.attr('height', theGap)
|
.attr('height', theGap)
|
||||||
.attr('class', function (d) {
|
.attr('class', function(d) {
|
||||||
for (let i = 0; i < categories.length; i++) {
|
for (let i = 0; i < categories.length; i++) {
|
||||||
if (d.type === categories[i]) {
|
if (d.type === categories[i]) {
|
||||||
return 'section section' + (i % conf.numberSectionStyles)
|
return 'section section' + (i % conf.numberSectionStyles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 'section section0'
|
return 'section section0';
|
||||||
})
|
});
|
||||||
|
|
||||||
// Draw the rects representing the tasks
|
// Draw the rects representing the tasks
|
||||||
const rectangles = svg.append('g')
|
const rectangles = svg
|
||||||
|
.append('g')
|
||||||
.selectAll('rect')
|
.selectAll('rect')
|
||||||
.data(theArray)
|
.data(theArray)
|
||||||
.enter()
|
.enter();
|
||||||
|
|
||||||
rectangles.append('rect')
|
rectangles
|
||||||
.attr('id', function (d) { return d.id })
|
.append('rect')
|
||||||
|
.attr('id', function(d) {
|
||||||
|
return d.id;
|
||||||
|
})
|
||||||
.attr('rx', 3)
|
.attr('rx', 3)
|
||||||
.attr('ry', 3)
|
.attr('ry', 3)
|
||||||
.attr('x', function (d) {
|
.attr('x', function(d) {
|
||||||
if (d.milestone) {
|
if (d.milestone) {
|
||||||
return timeScale(d.startTime) + theSidePad + (0.5 * (timeScale(d.endTime) - timeScale(d.startTime))) - (0.5 * theBarHeight)
|
return (
|
||||||
|
timeScale(d.startTime) +
|
||||||
|
theSidePad +
|
||||||
|
0.5 * (timeScale(d.endTime) - timeScale(d.startTime)) -
|
||||||
|
0.5 * theBarHeight
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return timeScale(d.startTime) + theSidePad
|
return timeScale(d.startTime) + theSidePad;
|
||||||
})
|
})
|
||||||
.attr('y', function (d, i) {
|
.attr('y', function(d, i) {
|
||||||
return i * theGap + theTopPad
|
return i * theGap + theTopPad;
|
||||||
})
|
})
|
||||||
.attr('width', function (d) {
|
.attr('width', function(d) {
|
||||||
if (d.milestone) {
|
if (d.milestone) {
|
||||||
return theBarHeight
|
return theBarHeight;
|
||||||
}
|
}
|
||||||
return (timeScale(d.renderEndTime || d.endTime) - timeScale(d.startTime))
|
return timeScale(d.renderEndTime || d.endTime) - timeScale(d.startTime);
|
||||||
})
|
})
|
||||||
.attr('height', theBarHeight)
|
.attr('height', theBarHeight)
|
||||||
.attr('transform-origin', function (d, i) {
|
.attr('transform-origin', function(d, i) {
|
||||||
return (timeScale(d.startTime) + theSidePad + 0.5 * (timeScale(d.endTime) - timeScale(d.startTime))).toString() + 'px ' + (i * theGap + theTopPad + 0.5 * theBarHeight).toString() + 'px'
|
return (
|
||||||
|
(
|
||||||
|
timeScale(d.startTime) +
|
||||||
|
theSidePad +
|
||||||
|
0.5 * (timeScale(d.endTime) - timeScale(d.startTime))
|
||||||
|
).toString() +
|
||||||
|
'px ' +
|
||||||
|
(i * theGap + theTopPad + 0.5 * theBarHeight).toString() +
|
||||||
|
'px'
|
||||||
|
);
|
||||||
})
|
})
|
||||||
.attr('class', function (d) {
|
.attr('class', function(d) {
|
||||||
const res = 'task'
|
const res = 'task';
|
||||||
|
|
||||||
let classStr = ''
|
let classStr = '';
|
||||||
if (d.classes.length > 0) {
|
if (d.classes.length > 0) {
|
||||||
classStr = d.classes.join(' ')
|
classStr = d.classes.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
let secNum = 0
|
let secNum = 0;
|
||||||
for (let i = 0; i < categories.length; i++) {
|
for (let i = 0; i < categories.length; i++) {
|
||||||
if (d.type === categories[i]) {
|
if (d.type === categories[i]) {
|
||||||
secNum = (i % conf.numberSectionStyles)
|
secNum = i % conf.numberSectionStyles;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let taskClass = ''
|
let taskClass = '';
|
||||||
if (d.active) {
|
if (d.active) {
|
||||||
if (d.crit) {
|
if (d.crit) {
|
||||||
taskClass += ' activeCrit'
|
taskClass += ' activeCrit';
|
||||||
} else {
|
} else {
|
||||||
taskClass = ' active'
|
taskClass = ' active';
|
||||||
}
|
}
|
||||||
} else if (d.done) {
|
} else if (d.done) {
|
||||||
if (d.crit) {
|
if (d.crit) {
|
||||||
taskClass = ' doneCrit'
|
taskClass = ' doneCrit';
|
||||||
} else {
|
} else {
|
||||||
taskClass = ' done'
|
taskClass = ' done';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (d.crit) {
|
if (d.crit) {
|
||||||
taskClass += ' crit'
|
taskClass += ' crit';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (taskClass.length === 0) {
|
if (taskClass.length === 0) {
|
||||||
taskClass = ' task'
|
taskClass = ' task';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d.milestone) {
|
if (d.milestone) {
|
||||||
taskClass = ' milestone ' + taskClass
|
taskClass = ' milestone ' + taskClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
taskClass += secNum
|
taskClass += secNum;
|
||||||
|
|
||||||
taskClass += ' ' + classStr
|
taskClass += ' ' + classStr;
|
||||||
|
|
||||||
return res + taskClass
|
return res + taskClass;
|
||||||
})
|
});
|
||||||
|
|
||||||
// Append task labels
|
// Append task labels
|
||||||
rectangles.append('text')
|
rectangles
|
||||||
.attr('id', function (d) { return d.id + '-text' })
|
.append('text')
|
||||||
.text(function (d) {
|
.attr('id', function(d) {
|
||||||
return d.task
|
return d.id + '-text';
|
||||||
|
})
|
||||||
|
.text(function(d) {
|
||||||
|
return d.task;
|
||||||
})
|
})
|
||||||
.attr('font-size', conf.fontSize)
|
.attr('font-size', conf.fontSize)
|
||||||
.attr('x', function (d) {
|
.attr('x', function(d) {
|
||||||
let startX = timeScale(d.startTime)
|
let startX = timeScale(d.startTime);
|
||||||
let endX = timeScale(d.renderEndTime || d.endTime)
|
let endX = timeScale(d.renderEndTime || d.endTime);
|
||||||
if (d.milestone) {
|
if (d.milestone) {
|
||||||
startX += (0.5 * (timeScale(d.endTime) - timeScale(d.startTime))) - (0.5 * theBarHeight)
|
startX += 0.5 * (timeScale(d.endTime) - timeScale(d.startTime)) - 0.5 * theBarHeight;
|
||||||
}
|
}
|
||||||
if (d.milestone) {
|
if (d.milestone) {
|
||||||
endX = startX + theBarHeight
|
endX = startX + theBarHeight;
|
||||||
}
|
}
|
||||||
const textWidth = this.getBBox().width
|
const textWidth = this.getBBox().width;
|
||||||
|
|
||||||
// Check id text width > width of rectangle
|
// Check id text width > width of rectangle
|
||||||
if (textWidth > (endX - startX)) {
|
if (textWidth > endX - startX) {
|
||||||
if (endX + textWidth + 1.5 * conf.leftPadding > w) {
|
if (endX + textWidth + 1.5 * conf.leftPadding > w) {
|
||||||
return startX + theSidePad - 5
|
return startX + theSidePad - 5;
|
||||||
} else {
|
} else {
|
||||||
return endX + theSidePad + 5
|
return endX + theSidePad + 5;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return (endX - startX) / 2 + startX + theSidePad
|
return (endX - startX) / 2 + startX + theSidePad;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.attr('y', function (d, i) {
|
.attr('y', function(d, i) {
|
||||||
return i * theGap + (conf.barHeight / 2) + (conf.fontSize / 2 - 2) + theTopPad
|
return i * theGap + conf.barHeight / 2 + (conf.fontSize / 2 - 2) + theTopPad;
|
||||||
})
|
})
|
||||||
.attr('text-height', theBarHeight)
|
.attr('text-height', theBarHeight)
|
||||||
.attr('class', function (d) {
|
.attr('class', function(d) {
|
||||||
const startX = timeScale(d.startTime)
|
const startX = timeScale(d.startTime);
|
||||||
let endX = timeScale(d.endTime)
|
let endX = timeScale(d.endTime);
|
||||||
if (d.milestone) {
|
if (d.milestone) {
|
||||||
endX = startX + theBarHeight
|
endX = startX + theBarHeight;
|
||||||
}
|
}
|
||||||
const textWidth = this.getBBox().width
|
const textWidth = this.getBBox().width;
|
||||||
|
|
||||||
let classStr = ''
|
let classStr = '';
|
||||||
if (d.classes.length > 0) {
|
if (d.classes.length > 0) {
|
||||||
classStr = d.classes.join(' ')
|
classStr = d.classes.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
let secNum = 0
|
let secNum = 0;
|
||||||
for (let i = 0; i < categories.length; i++) {
|
for (let i = 0; i < categories.length; i++) {
|
||||||
if (d.type === categories[i]) {
|
if (d.type === categories[i]) {
|
||||||
secNum = (i % conf.numberSectionStyles)
|
secNum = i % conf.numberSectionStyles;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let taskType = ''
|
let taskType = '';
|
||||||
if (d.active) {
|
if (d.active) {
|
||||||
if (d.crit) {
|
if (d.crit) {
|
||||||
taskType = 'activeCritText' + secNum
|
taskType = 'activeCritText' + secNum;
|
||||||
} else {
|
} else {
|
||||||
taskType = 'activeText' + secNum
|
taskType = 'activeText' + secNum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d.done) {
|
if (d.done) {
|
||||||
if (d.crit) {
|
if (d.crit) {
|
||||||
taskType = taskType + ' doneCritText' + secNum
|
taskType = taskType + ' doneCritText' + secNum;
|
||||||
} else {
|
} else {
|
||||||
taskType = taskType + ' doneText' + secNum
|
taskType = taskType + ' doneText' + secNum;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (d.crit) {
|
if (d.crit) {
|
||||||
taskType = taskType + ' critText' + secNum
|
taskType = taskType + ' critText' + secNum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d.milestone) {
|
if (d.milestone) {
|
||||||
taskType += ' milestoneText'
|
taskType += ' milestoneText';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check id text width > width of rectangle
|
// Check id text width > width of rectangle
|
||||||
if (textWidth > (endX - startX)) {
|
if (textWidth > endX - startX) {
|
||||||
if (endX + textWidth + 1.5 * conf.leftPadding > w) {
|
if (endX + textWidth + 1.5 * conf.leftPadding > w) {
|
||||||
return classStr + ' taskTextOutsideLeft taskTextOutside' + secNum + ' ' + taskType
|
return classStr + ' taskTextOutsideLeft taskTextOutside' + secNum + ' ' + taskType;
|
||||||
} else {
|
} else {
|
||||||
return classStr + ' taskTextOutsideRight taskTextOutside' + secNum + ' ' + taskType
|
return classStr + ' taskTextOutsideRight taskTextOutside' + secNum + ' ' + taskType;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return classStr + ' taskText taskText' + secNum + ' ' + taskType
|
return classStr + ' taskText taskText' + secNum + ' ' + taskType;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeGrid (theSidePad, theTopPad, w, h) {
|
function makeGrid(theSidePad, theTopPad, w, h) {
|
||||||
let xAxis = d3.axisBottom(timeScale)
|
let xAxis = d3
|
||||||
|
.axisBottom(timeScale)
|
||||||
.tickSize(-h + theTopPad + conf.gridLineStartPadding)
|
.tickSize(-h + theTopPad + conf.gridLineStartPadding)
|
||||||
.tickFormat(d3.timeFormat(parser.yy.getAxisFormat() || conf.axisFormat || '%Y-%m-%d'))
|
.tickFormat(d3.timeFormat(parser.yy.getAxisFormat() || conf.axisFormat || '%Y-%m-%d'));
|
||||||
|
|
||||||
svg.append('g')
|
svg
|
||||||
|
.append('g')
|
||||||
.attr('class', 'grid')
|
.attr('class', 'grid')
|
||||||
.attr('transform', 'translate(' + theSidePad + ', ' + (h - 50) + ')')
|
.attr('transform', 'translate(' + theSidePad + ', ' + (h - 50) + ')')
|
||||||
.call(xAxis)
|
.call(xAxis)
|
||||||
@ -304,90 +333,92 @@ export const draw = function (text, id) {
|
|||||||
.attr('fill', '#000')
|
.attr('fill', '#000')
|
||||||
.attr('stroke', 'none')
|
.attr('stroke', 'none')
|
||||||
.attr('font-size', 10)
|
.attr('font-size', 10)
|
||||||
.attr('dy', '1em')
|
.attr('dy', '1em');
|
||||||
}
|
}
|
||||||
|
|
||||||
function vertLabels (theGap, theTopPad) {
|
function vertLabels(theGap, theTopPad) {
|
||||||
const numOccurances = []
|
const numOccurances = [];
|
||||||
let prevGap = 0
|
let prevGap = 0;
|
||||||
|
|
||||||
for (let i = 0; i < categories.length; i++) {
|
for (let i = 0; i < categories.length; i++) {
|
||||||
numOccurances[i] = [categories[i], getCount(categories[i], catsUnfiltered)]
|
numOccurances[i] = [categories[i], getCount(categories[i], catsUnfiltered)];
|
||||||
}
|
}
|
||||||
|
|
||||||
svg.append('g') // without doing this, impossible to put grid lines behind text
|
svg
|
||||||
|
.append('g') // without doing this, impossible to put grid lines behind text
|
||||||
.selectAll('text')
|
.selectAll('text')
|
||||||
.data(numOccurances)
|
.data(numOccurances)
|
||||||
.enter()
|
.enter()
|
||||||
.append('text')
|
.append('text')
|
||||||
.text(function (d) {
|
.text(function(d) {
|
||||||
return d[0]
|
return d[0];
|
||||||
})
|
})
|
||||||
.attr('x', 10)
|
.attr('x', 10)
|
||||||
.attr('y', function (d, i) {
|
.attr('y', function(d, i) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
for (let j = 0; j < i; j++) {
|
for (let j = 0; j < i; j++) {
|
||||||
prevGap += numOccurances[i - 1][1]
|
prevGap += numOccurances[i - 1][1];
|
||||||
return d[1] * theGap / 2 + prevGap * theGap + theTopPad
|
return (d[1] * theGap) / 2 + prevGap * theGap + theTopPad;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return d[1] * theGap / 2 + theTopPad
|
return (d[1] * theGap) / 2 + theTopPad;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.attr('class', function (d) {
|
.attr('class', function(d) {
|
||||||
for (let i = 0; i < categories.length; i++) {
|
for (let i = 0; i < categories.length; i++) {
|
||||||
if (d[0] === categories[i]) {
|
if (d[0] === categories[i]) {
|
||||||
return 'sectionTitle sectionTitle' + (i % conf.numberSectionStyles)
|
return 'sectionTitle sectionTitle' + (i % conf.numberSectionStyles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 'sectionTitle'
|
return 'sectionTitle';
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawToday (theSidePad, theTopPad, w, h) {
|
function drawToday(theSidePad, theTopPad, w, h) {
|
||||||
const todayG = svg.append('g')
|
const todayG = svg.append('g').attr('class', 'today');
|
||||||
.attr('class', 'today')
|
|
||||||
|
|
||||||
const today = new Date()
|
const today = new Date();
|
||||||
|
|
||||||
todayG.append('line')
|
todayG
|
||||||
|
.append('line')
|
||||||
.attr('x1', timeScale(today) + theSidePad)
|
.attr('x1', timeScale(today) + theSidePad)
|
||||||
.attr('x2', timeScale(today) + theSidePad)
|
.attr('x2', timeScale(today) + theSidePad)
|
||||||
.attr('y1', conf.titleTopMargin)
|
.attr('y1', conf.titleTopMargin)
|
||||||
.attr('y2', h - conf.titleTopMargin)
|
.attr('y2', h - conf.titleTopMargin)
|
||||||
.attr('class', 'today')
|
.attr('class', 'today');
|
||||||
}
|
}
|
||||||
|
|
||||||
// from this stackexchange question: http://stackoverflow.com/questions/1890203/unique-for-arrays-in-javascript
|
// from this stackexchange question: http://stackoverflow.com/questions/1890203/unique-for-arrays-in-javascript
|
||||||
function checkUnique (arr) {
|
function checkUnique(arr) {
|
||||||
const hash = {}
|
const hash = {};
|
||||||
const result = []
|
const result = [];
|
||||||
for (let i = 0, l = arr.length; i < l; ++i) {
|
for (let i = 0, l = arr.length; i < l; ++i) {
|
||||||
if (!hash.hasOwnProperty(arr[i])) { // it works with objects! in FF, at least
|
if (!hash.hasOwnProperty(arr[i])) {
|
||||||
hash[arr[i]] = true
|
// it works with objects! in FF, at least
|
||||||
result.push(arr[i])
|
hash[arr[i]] = true;
|
||||||
|
result.push(arr[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from this stackexchange question: http://stackoverflow.com/questions/14227981/count-how-many-strings-in-an-array-have-duplicates-in-the-same-array
|
// 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) {
|
function getCounts(arr) {
|
||||||
let i = arr.length // const to loop over
|
let i = arr.length; // const to loop over
|
||||||
const obj = {} // obj to store results
|
const obj = {}; // obj to store results
|
||||||
while (i) {
|
while (i) {
|
||||||
obj[arr[--i]] = (obj[arr[i]] || 0) + 1 // count occurrences
|
obj[arr[--i]] = (obj[arr[i]] || 0) + 1; // count occurrences
|
||||||
}
|
}
|
||||||
return obj
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get specific from everything
|
// get specific from everything
|
||||||
function getCount (word, arr) {
|
function getCount(word, arr) {
|
||||||
return getCounts(arr)[word] || 0
|
return getCounts(arr)[word] || 0;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setConf,
|
setConf,
|
||||||
draw
|
draw
|
||||||
}
|
};
|
||||||
|
@ -1,50 +1,52 @@
|
|||||||
/* eslint-env jasmine */
|
/* eslint-env jasmine */
|
||||||
/* eslint-disable no-eval */
|
/* eslint-disable no-eval */
|
||||||
import { parser } from './gantt'
|
import { parser } from './gantt';
|
||||||
import ganttDb from '../ganttDb'
|
import ganttDb from '../ganttDb';
|
||||||
|
|
||||||
const parserFnConstructor = (str) => {
|
const parserFnConstructor = str => {
|
||||||
return () => {
|
return () => {
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
describe('when parsing a gantt diagram it', function () {
|
describe('when parsing a gantt diagram it', function() {
|
||||||
beforeEach(function () {
|
beforeEach(function() {
|
||||||
parser.yy = ganttDb
|
parser.yy = ganttDb;
|
||||||
parser.yy.clear()
|
parser.yy.clear();
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle a dateFormat definition', function () {
|
it('should handle a dateFormat definition', function() {
|
||||||
const str = 'gantt\ndateFormat yyyy-mm-dd'
|
const str = 'gantt\ndateFormat yyyy-mm-dd';
|
||||||
|
|
||||||
expect(parserFnConstructor(str)).not.toThrow()
|
expect(parserFnConstructor(str)).not.toThrow();
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle a inclusive end date definition', function () {
|
it('should handle a inclusive end date definition', function() {
|
||||||
const str = 'gantt\ndateFormat yyyy-mm-dd\ninclusiveEndDates'
|
const str = 'gantt\ndateFormat yyyy-mm-dd\ninclusiveEndDates';
|
||||||
|
|
||||||
expect(parserFnConstructor(str)).not.toThrow()
|
expect(parserFnConstructor(str)).not.toThrow();
|
||||||
})
|
});
|
||||||
it('should handle a title definition', function () {
|
it('should handle a title definition', function() {
|
||||||
const str = 'gantt\ndateFormat yyyy-mm-dd\ntitle Adding gantt diagram functionality to mermaid'
|
const str = 'gantt\ndateFormat yyyy-mm-dd\ntitle Adding gantt diagram functionality to mermaid';
|
||||||
|
|
||||||
expect(parserFnConstructor(str)).not.toThrow()
|
expect(parserFnConstructor(str)).not.toThrow();
|
||||||
})
|
});
|
||||||
it('should handle an excludes definition', function () {
|
it('should handle an excludes definition', function() {
|
||||||
const str = 'gantt\ndateFormat yyyy-mm-dd\ntitle Adding gantt diagram functionality to mermaid\nexcludes weekdays 2019-02-01'
|
const str =
|
||||||
|
'gantt\ndateFormat yyyy-mm-dd\ntitle Adding gantt diagram functionality to mermaid\nexcludes weekdays 2019-02-01';
|
||||||
|
|
||||||
expect(parserFnConstructor(str)).not.toThrow()
|
expect(parserFnConstructor(str)).not.toThrow();
|
||||||
})
|
});
|
||||||
it('should handle a section definition', function () {
|
it('should handle a section definition', function() {
|
||||||
const str = 'gantt\n' +
|
const str =
|
||||||
|
'gantt\n' +
|
||||||
'dateFormat yyyy-mm-dd\n' +
|
'dateFormat yyyy-mm-dd\n' +
|
||||||
'title Adding gantt diagram functionality to mermaid\n' +
|
'title Adding gantt diagram functionality to mermaid\n' +
|
||||||
'excludes weekdays 2019-02-01\n' +
|
'excludes weekdays 2019-02-01\n' +
|
||||||
'section Documentation'
|
'section Documentation';
|
||||||
|
|
||||||
expect(parserFnConstructor(str)).not.toThrow()
|
expect(parserFnConstructor(str)).not.toThrow();
|
||||||
})
|
});
|
||||||
/**
|
/**
|
||||||
* Beslutsflöde inligt nedan. Obs bla bla bla
|
* Beslutsflöde inligt nedan. Obs bla bla bla
|
||||||
* ```
|
* ```
|
||||||
@ -56,22 +58,23 @@ describe('when parsing a gantt diagram it', function () {
|
|||||||
```
|
```
|
||||||
* params bapa - a unique bapap
|
* params bapa - a unique bapap
|
||||||
*/
|
*/
|
||||||
it('should handle a task definition', function () {
|
it('should handle a task definition', function() {
|
||||||
const str = 'gantt\n' +
|
const str =
|
||||||
|
'gantt\n' +
|
||||||
'dateFormat YYYY-MM-DD\n' +
|
'dateFormat YYYY-MM-DD\n' +
|
||||||
'title Adding gantt diagram functionality to mermaid\n' +
|
'title Adding gantt diagram functionality to mermaid\n' +
|
||||||
'section Documentation\n' +
|
'section Documentation\n' +
|
||||||
'Design jison grammar:des1, 2014-01-01, 2014-01-04'
|
'Design jison grammar:des1, 2014-01-01, 2014-01-04';
|
||||||
|
|
||||||
expect(parserFnConstructor(str)).not.toThrow()
|
expect(parserFnConstructor(str)).not.toThrow();
|
||||||
|
|
||||||
const tasks = parser.yy.getTasks()
|
const tasks = parser.yy.getTasks();
|
||||||
|
|
||||||
expect(tasks[0].startTime).toEqual(new Date(2014, 0, 1))
|
expect(tasks[0].startTime).toEqual(new Date(2014, 0, 1));
|
||||||
expect(tasks[0].endTime).toEqual(new Date(2014, 0, 4))
|
expect(tasks[0].endTime).toEqual(new Date(2014, 0, 4));
|
||||||
expect(tasks[0].id).toEqual('des1')
|
expect(tasks[0].id).toEqual('des1');
|
||||||
expect(tasks[0].task).toEqual('Design jison grammar')
|
expect(tasks[0].task).toEqual('Design jison grammar');
|
||||||
})
|
});
|
||||||
it.each`
|
it.each`
|
||||||
tags | milestone | done | crit | active
|
tags | milestone | done | crit | active
|
||||||
${'milestone'} | ${true} | ${false} | ${false} | ${false}
|
${'milestone'} | ${true} | ${false} | ${false} | ${false}
|
||||||
@ -79,25 +82,28 @@ describe('when parsing a gantt diagram it', function () {
|
|||||||
${'crit'} | ${false} | ${false} | ${true} | ${false}
|
${'crit'} | ${false} | ${false} | ${true} | ${false}
|
||||||
${'active'} | ${false} | ${false} | ${false} | ${true}
|
${'active'} | ${false} | ${false} | ${false} | ${true}
|
||||||
${'crit,milestone,done'} | ${true} | ${true} | ${true} | ${false}
|
${'crit,milestone,done'} | ${true} | ${true} | ${true} | ${false}
|
||||||
`('should handle a task with tags $tags', ({ tags, milestone, done, crit, active }) => {
|
`('should handle a task with tags $tags', ({ tags, milestone, done, crit, active }) => {
|
||||||
const str = 'gantt\n' +
|
const str =
|
||||||
'dateFormat YYYY-MM-DD\n' +
|
'gantt\n' +
|
||||||
'title Adding gantt diagram functionality to mermaid\n' +
|
'dateFormat YYYY-MM-DD\n' +
|
||||||
'section Documentation\n' +
|
'title Adding gantt diagram functionality to mermaid\n' +
|
||||||
'test task:' + tags + ', 2014-01-01, 2014-01-04'
|
'section Documentation\n' +
|
||||||
|
'test task:' +
|
||||||
|
tags +
|
||||||
|
', 2014-01-01, 2014-01-04';
|
||||||
|
|
||||||
const allowedTags = ['active', 'done', 'crit', 'milestone']
|
const allowedTags = ['active', 'done', 'crit', 'milestone'];
|
||||||
|
|
||||||
expect(parserFnConstructor(str)).not.toThrow()
|
expect(parserFnConstructor(str)).not.toThrow();
|
||||||
|
|
||||||
const tasks = parser.yy.getTasks()
|
const tasks = parser.yy.getTasks();
|
||||||
|
|
||||||
allowedTags.forEach(function (t) {
|
allowedTags.forEach(function(t) {
|
||||||
if (eval(t)) {
|
if (eval(t)) {
|
||||||
expect(tasks[0][t]).toBeTruthy()
|
expect(tasks[0][t]).toBeTruthy();
|
||||||
} else {
|
} else {
|
||||||
expect(tasks[0][t]).toBeFalsy()
|
expect(tasks[0][t]).toBeFalsy();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
@ -1,98 +1,100 @@
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash';
|
||||||
|
|
||||||
import { logger } from '../../logger'
|
import { logger } from '../../logger';
|
||||||
|
|
||||||
let commits = {}
|
let commits = {};
|
||||||
let head = null
|
let head = null;
|
||||||
let branches = { 'master': head }
|
let branches = { master: head };
|
||||||
let curBranch = 'master'
|
let curBranch = 'master';
|
||||||
let direction = 'LR'
|
let direction = 'LR';
|
||||||
let seq = 0
|
let seq = 0;
|
||||||
|
|
||||||
function getRandomInt (min, max) {
|
function getRandomInt(min, max) {
|
||||||
return Math.floor(Math.random() * (max - min)) + min
|
return Math.floor(Math.random() * (max - min)) + min;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getId () {
|
function getId() {
|
||||||
const pool = '0123456789abcdef'
|
const pool = '0123456789abcdef';
|
||||||
let id = ''
|
let id = '';
|
||||||
for (let i = 0; i < 7; i++) {
|
for (let i = 0; i < 7; i++) {
|
||||||
id += pool[getRandomInt(0, 16)]
|
id += pool[getRandomInt(0, 16)];
|
||||||
}
|
}
|
||||||
return id
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isfastforwardable (currentCommit, otherCommit) {
|
function isfastforwardable(currentCommit, otherCommit) {
|
||||||
logger.debug('Entering isfastforwardable:', currentCommit.id, otherCommit.id)
|
logger.debug('Entering isfastforwardable:', currentCommit.id, otherCommit.id);
|
||||||
while (currentCommit.seq <= otherCommit.seq && currentCommit !== otherCommit) {
|
while (currentCommit.seq <= otherCommit.seq && currentCommit !== otherCommit) {
|
||||||
// only if other branch has more commits
|
// only if other branch has more commits
|
||||||
if (otherCommit.parent == null) break
|
if (otherCommit.parent == null) break;
|
||||||
if (Array.isArray(otherCommit.parent)) {
|
if (Array.isArray(otherCommit.parent)) {
|
||||||
logger.debug('In merge commit:', otherCommit.parent)
|
logger.debug('In merge commit:', otherCommit.parent);
|
||||||
return isfastforwardable(currentCommit, commits[otherCommit.parent[0]]) ||
|
return (
|
||||||
|
isfastforwardable(currentCommit, commits[otherCommit.parent[0]]) ||
|
||||||
isfastforwardable(currentCommit, commits[otherCommit.parent[1]])
|
isfastforwardable(currentCommit, commits[otherCommit.parent[1]])
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
otherCommit = commits[otherCommit.parent]
|
otherCommit = commits[otherCommit.parent];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.debug(currentCommit.id, otherCommit.id)
|
logger.debug(currentCommit.id, otherCommit.id);
|
||||||
return currentCommit.id === otherCommit.id
|
return currentCommit.id === otherCommit.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isReachableFrom (currentCommit, otherCommit) {
|
function isReachableFrom(currentCommit, otherCommit) {
|
||||||
const currentSeq = currentCommit.seq
|
const currentSeq = currentCommit.seq;
|
||||||
const otherSeq = otherCommit.seq
|
const otherSeq = otherCommit.seq;
|
||||||
if (currentSeq > otherSeq) return isfastforwardable(otherCommit, currentCommit)
|
if (currentSeq > otherSeq) return isfastforwardable(otherCommit, currentCommit);
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setDirection = function (dir) {
|
export const setDirection = function(dir) {
|
||||||
direction = dir
|
direction = dir;
|
||||||
}
|
};
|
||||||
let options = {}
|
let options = {};
|
||||||
export const setOptions = function (rawOptString) {
|
export const setOptions = function(rawOptString) {
|
||||||
logger.debug('options str', rawOptString)
|
logger.debug('options str', rawOptString);
|
||||||
rawOptString = rawOptString && rawOptString.trim()
|
rawOptString = rawOptString && rawOptString.trim();
|
||||||
rawOptString = rawOptString || '{}'
|
rawOptString = rawOptString || '{}';
|
||||||
try {
|
try {
|
||||||
options = JSON.parse(rawOptString)
|
options = JSON.parse(rawOptString);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('error while parsing gitGraph options', e.message)
|
logger.error('error while parsing gitGraph options', e.message);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getOptions = function () {
|
export const getOptions = function() {
|
||||||
return options
|
return options;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const commit = function (msg) {
|
export const commit = function(msg) {
|
||||||
const commit = {
|
const commit = {
|
||||||
id: getId(),
|
id: getId(),
|
||||||
message: msg,
|
message: msg,
|
||||||
seq: seq++,
|
seq: seq++,
|
||||||
parent: head == null ? null : head.id
|
parent: head == null ? null : head.id
|
||||||
}
|
};
|
||||||
head = commit
|
head = commit;
|
||||||
commits[commit.id] = commit
|
commits[commit.id] = commit;
|
||||||
branches[curBranch] = commit.id
|
branches[curBranch] = commit.id;
|
||||||
logger.debug('in pushCommit ' + commit.id)
|
logger.debug('in pushCommit ' + commit.id);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const branch = function (name) {
|
export const branch = function(name) {
|
||||||
branches[name] = head != null ? head.id : null
|
branches[name] = head != null ? head.id : null;
|
||||||
logger.debug('in createBranch')
|
logger.debug('in createBranch');
|
||||||
}
|
};
|
||||||
|
|
||||||
export const merge = function (otherBranch) {
|
export const merge = function(otherBranch) {
|
||||||
const currentCommit = commits[branches[curBranch]]
|
const currentCommit = commits[branches[curBranch]];
|
||||||
const otherCommit = commits[branches[otherBranch]]
|
const otherCommit = commits[branches[otherBranch]];
|
||||||
if (isReachableFrom(currentCommit, otherCommit)) {
|
if (isReachableFrom(currentCommit, otherCommit)) {
|
||||||
logger.debug('Already merged')
|
logger.debug('Already merged');
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
if (isfastforwardable(currentCommit, otherCommit)) {
|
if (isfastforwardable(currentCommit, otherCommit)) {
|
||||||
branches[curBranch] = branches[otherBranch]
|
branches[curBranch] = branches[otherBranch];
|
||||||
head = commits[branches[curBranch]]
|
head = commits[branches[curBranch]];
|
||||||
} else {
|
} else {
|
||||||
// create merge commit
|
// create merge commit
|
||||||
const commit = {
|
const commit = {
|
||||||
@ -100,113 +102,125 @@ export const merge = function (otherBranch) {
|
|||||||
message: 'merged branch ' + otherBranch + ' into ' + curBranch,
|
message: 'merged branch ' + otherBranch + ' into ' + curBranch,
|
||||||
seq: seq++,
|
seq: seq++,
|
||||||
parent: [head == null ? null : head.id, branches[otherBranch]]
|
parent: [head == null ? null : head.id, branches[otherBranch]]
|
||||||
}
|
};
|
||||||
head = commit
|
head = commit;
|
||||||
commits[commit.id] = commit
|
commits[commit.id] = commit;
|
||||||
branches[curBranch] = commit.id
|
branches[curBranch] = commit.id;
|
||||||
}
|
}
|
||||||
logger.debug(branches)
|
logger.debug(branches);
|
||||||
logger.debug('in mergeBranch')
|
logger.debug('in mergeBranch');
|
||||||
}
|
};
|
||||||
|
|
||||||
export const checkout = function (branch) {
|
export const checkout = function(branch) {
|
||||||
logger.debug('in checkout')
|
logger.debug('in checkout');
|
||||||
curBranch = branch
|
curBranch = branch;
|
||||||
const id = branches[curBranch]
|
const id = branches[curBranch];
|
||||||
head = commits[id]
|
head = commits[id];
|
||||||
}
|
};
|
||||||
|
|
||||||
export const reset = function (commitRef) {
|
export const reset = function(commitRef) {
|
||||||
logger.debug('in reset', commitRef)
|
logger.debug('in reset', commitRef);
|
||||||
const ref = commitRef.split(':')[0]
|
const ref = commitRef.split(':')[0];
|
||||||
let parentCount = parseInt(commitRef.split(':')[1])
|
let parentCount = parseInt(commitRef.split(':')[1]);
|
||||||
let commit = ref === 'HEAD' ? head : commits[branches[ref]]
|
let commit = ref === 'HEAD' ? head : commits[branches[ref]];
|
||||||
logger.debug(commit, parentCount)
|
logger.debug(commit, parentCount);
|
||||||
while (parentCount > 0) {
|
while (parentCount > 0) {
|
||||||
commit = commits[commit.parent]
|
commit = commits[commit.parent];
|
||||||
parentCount--
|
parentCount--;
|
||||||
if (!commit) {
|
if (!commit) {
|
||||||
const err = 'Critical error - unique parent commit not found during reset'
|
const err = 'Critical error - unique parent commit not found during reset';
|
||||||
logger.error(err)
|
logger.error(err);
|
||||||
throw err
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
head = commit
|
head = commit;
|
||||||
branches[curBranch] = commit.id
|
branches[curBranch] = commit.id;
|
||||||
}
|
};
|
||||||
|
|
||||||
function upsert (arr, key, newval) {
|
function upsert(arr, key, newval) {
|
||||||
const index = arr.indexOf(key)
|
const index = arr.indexOf(key);
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
arr.push(newval)
|
arr.push(newval);
|
||||||
} else {
|
} else {
|
||||||
arr.splice(index, 1, newval)
|
arr.splice(index, 1, newval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function prettyPrintCommitHistory (commitArr) {
|
function prettyPrintCommitHistory(commitArr) {
|
||||||
const commit = _.maxBy(commitArr, 'seq')
|
const commit = _.maxBy(commitArr, 'seq');
|
||||||
let line = ''
|
let line = '';
|
||||||
commitArr.forEach(function (c) {
|
commitArr.forEach(function(c) {
|
||||||
if (c === commit) {
|
if (c === commit) {
|
||||||
line += '\t*'
|
line += '\t*';
|
||||||
} else {
|
} else {
|
||||||
line += '\t|'
|
line += '\t|';
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
const label = [line, commit.id, commit.seq]
|
const label = [line, commit.id, commit.seq];
|
||||||
for (let branch in branches) {
|
for (let branch in branches) {
|
||||||
if (branches[branch] === commit.id) label.push(branch)
|
if (branches[branch] === commit.id) label.push(branch);
|
||||||
}
|
}
|
||||||
logger.debug(label.join(' '))
|
logger.debug(label.join(' '));
|
||||||
if (Array.isArray(commit.parent)) {
|
if (Array.isArray(commit.parent)) {
|
||||||
const newCommit = commits[commit.parent[0]]
|
const newCommit = commits[commit.parent[0]];
|
||||||
upsert(commitArr, commit, newCommit)
|
upsert(commitArr, commit, newCommit);
|
||||||
commitArr.push(commits[commit.parent[1]])
|
commitArr.push(commits[commit.parent[1]]);
|
||||||
} else if (commit.parent == null) {
|
} else if (commit.parent == null) {
|
||||||
return
|
return;
|
||||||
} else {
|
} else {
|
||||||
const nextCommit = commits[commit.parent]
|
const nextCommit = commits[commit.parent];
|
||||||
upsert(commitArr, commit, nextCommit)
|
upsert(commitArr, commit, nextCommit);
|
||||||
}
|
}
|
||||||
commitArr = _.uniqBy(commitArr, 'id')
|
commitArr = _.uniqBy(commitArr, 'id');
|
||||||
prettyPrintCommitHistory(commitArr)
|
prettyPrintCommitHistory(commitArr);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const prettyPrint = function () {
|
export const prettyPrint = function() {
|
||||||
logger.debug(commits)
|
logger.debug(commits);
|
||||||
const node = getCommitsArray()[0]
|
const node = getCommitsArray()[0];
|
||||||
prettyPrintCommitHistory([node])
|
prettyPrintCommitHistory([node]);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const clear = function () {
|
export const clear = function() {
|
||||||
commits = {}
|
commits = {};
|
||||||
head = null
|
head = null;
|
||||||
branches = { 'master': head }
|
branches = { master: head };
|
||||||
curBranch = 'master'
|
curBranch = 'master';
|
||||||
seq = 0
|
seq = 0;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getBranchesAsObjArray = function () {
|
export const getBranchesAsObjArray = function() {
|
||||||
const branchArr = []
|
const branchArr = [];
|
||||||
for (let branch in branches) {
|
for (let branch in branches) {
|
||||||
branchArr.push({ name: branch, commit: commits[branches[branch]] })
|
branchArr.push({ name: branch, commit: commits[branches[branch]] });
|
||||||
}
|
}
|
||||||
return branchArr
|
return branchArr;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getBranches = function () { return branches }
|
export const getBranches = function() {
|
||||||
export const getCommits = function () { return commits }
|
return branches;
|
||||||
export const getCommitsArray = function () {
|
};
|
||||||
const commitArr = Object.keys(commits).map(function (key) {
|
export const getCommits = function() {
|
||||||
return commits[key]
|
return commits;
|
||||||
})
|
};
|
||||||
commitArr.forEach(function (o) { logger.debug(o.id) })
|
export const getCommitsArray = function() {
|
||||||
return _.orderBy(commitArr, ['seq'], ['desc'])
|
const commitArr = Object.keys(commits).map(function(key) {
|
||||||
}
|
return commits[key];
|
||||||
export const getCurrentBranch = function () { return curBranch }
|
});
|
||||||
export const getDirection = function () { return direction }
|
commitArr.forEach(function(o) {
|
||||||
export const getHead = function () { return head }
|
logger.debug(o.id);
|
||||||
|
});
|
||||||
|
return _.orderBy(commitArr, ['seq'], ['desc']);
|
||||||
|
};
|
||||||
|
export const getCurrentBranch = function() {
|
||||||
|
return curBranch;
|
||||||
|
};
|
||||||
|
export const getDirection = function() {
|
||||||
|
return direction;
|
||||||
|
};
|
||||||
|
export const getHead = function() {
|
||||||
|
return head;
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setDirection,
|
setDirection,
|
||||||
@ -226,4 +240,4 @@ export default {
|
|||||||
getCurrentBranch,
|
getCurrentBranch,
|
||||||
getDirection,
|
getDirection,
|
||||||
getHead
|
getHead
|
||||||
}
|
};
|
||||||
|
@ -1,201 +1,186 @@
|
|||||||
/* eslint-env jasmine */
|
/* eslint-env jasmine */
|
||||||
import gitGraphAst from './gitGraphAst'
|
import gitGraphAst from './gitGraphAst';
|
||||||
import { parser } from './parser/gitGraph'
|
import { parser } from './parser/gitGraph';
|
||||||
|
|
||||||
describe('when parsing a gitGraph', function () {
|
describe('when parsing a gitGraph', function() {
|
||||||
beforeEach(function () {
|
beforeEach(function() {
|
||||||
parser.yy = gitGraphAst
|
parser.yy = gitGraphAst;
|
||||||
parser.yy.clear()
|
parser.yy.clear();
|
||||||
})
|
});
|
||||||
it('should handle a gitGraph defintion', function () {
|
it('should handle a gitGraph defintion', function() {
|
||||||
const str = 'gitGraph:\n' +
|
const str = 'gitGraph:\n' + 'commit\n';
|
||||||
'commit\n'
|
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
const commits = parser.yy.getCommits()
|
const commits = parser.yy.getCommits();
|
||||||
|
|
||||||
expect(Object.keys(commits).length).toBe(1)
|
expect(Object.keys(commits).length).toBe(1);
|
||||||
expect(parser.yy.getCurrentBranch()).toBe('master')
|
expect(parser.yy.getCurrentBranch()).toBe('master');
|
||||||
expect(parser.yy.getDirection()).toBe('LR')
|
expect(parser.yy.getDirection()).toBe('LR');
|
||||||
expect(Object.keys(parser.yy.getBranches()).length).toBe(1)
|
expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle a gitGraph defintion with empty options', function () {
|
it('should handle a gitGraph defintion with empty options', function() {
|
||||||
const str = 'gitGraph:\n' +
|
const str = 'gitGraph:\n' + 'options\n' + 'end\n' + 'commit\n';
|
||||||
'options\n' +
|
|
||||||
'end\n' +
|
|
||||||
'commit\n'
|
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
const commits = parser.yy.getCommits()
|
const commits = parser.yy.getCommits();
|
||||||
|
|
||||||
expect(parser.yy.getOptions()).toEqual({})
|
expect(parser.yy.getOptions()).toEqual({});
|
||||||
expect(Object.keys(commits).length).toBe(1)
|
expect(Object.keys(commits).length).toBe(1);
|
||||||
expect(parser.yy.getCurrentBranch()).toBe('master')
|
expect(parser.yy.getCurrentBranch()).toBe('master');
|
||||||
expect(parser.yy.getDirection()).toBe('LR')
|
expect(parser.yy.getDirection()).toBe('LR');
|
||||||
expect(Object.keys(parser.yy.getBranches()).length).toBe(1)
|
expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle a gitGraph defintion with valid options', function () {
|
it('should handle a gitGraph defintion with valid options', function() {
|
||||||
const str = 'gitGraph:\n' +
|
const str = 'gitGraph:\n' + 'options\n' + '{"key": "value"}\n' + 'end\n' + 'commit\n';
|
||||||
'options\n' +
|
|
||||||
'{"key": "value"}\n' +
|
|
||||||
'end\n' +
|
|
||||||
'commit\n'
|
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
const commits = parser.yy.getCommits()
|
const commits = parser.yy.getCommits();
|
||||||
expect(parser.yy.getOptions()['key']).toBe('value')
|
expect(parser.yy.getOptions()['key']).toBe('value');
|
||||||
expect(Object.keys(commits).length).toBe(1)
|
expect(Object.keys(commits).length).toBe(1);
|
||||||
expect(parser.yy.getCurrentBranch()).toBe('master')
|
expect(parser.yy.getCurrentBranch()).toBe('master');
|
||||||
expect(parser.yy.getDirection()).toBe('LR')
|
expect(parser.yy.getDirection()).toBe('LR');
|
||||||
expect(Object.keys(parser.yy.getBranches()).length).toBe(1)
|
expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should not fail on a gitGraph with malformed json', function () {
|
it('should not fail on a gitGraph with malformed json', function() {
|
||||||
const str = 'gitGraph:\n' +
|
const str = 'gitGraph:\n' + 'options\n' + '{"key": "value"\n' + 'end\n' + 'commit\n';
|
||||||
'options\n' +
|
|
||||||
'{"key": "value"\n' +
|
|
||||||
'end\n' +
|
|
||||||
'commit\n'
|
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
const commits = parser.yy.getCommits()
|
const commits = parser.yy.getCommits();
|
||||||
expect(Object.keys(commits).length).toBe(1)
|
expect(Object.keys(commits).length).toBe(1);
|
||||||
expect(parser.yy.getCurrentBranch()).toBe('master')
|
expect(parser.yy.getCurrentBranch()).toBe('master');
|
||||||
expect(parser.yy.getDirection()).toBe('LR')
|
expect(parser.yy.getDirection()).toBe('LR');
|
||||||
expect(Object.keys(parser.yy.getBranches()).length).toBe(1)
|
expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle set direction', function () {
|
it('should handle set direction', function() {
|
||||||
const str = 'gitGraph BT:\n' +
|
const str = 'gitGraph BT:\n' + 'commit\n';
|
||||||
'commit\n'
|
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
const commits = parser.yy.getCommits()
|
const commits = parser.yy.getCommits();
|
||||||
|
|
||||||
expect(Object.keys(commits).length).toBe(1)
|
expect(Object.keys(commits).length).toBe(1);
|
||||||
expect(parser.yy.getCurrentBranch()).toBe('master')
|
expect(parser.yy.getCurrentBranch()).toBe('master');
|
||||||
expect(parser.yy.getDirection()).toBe('BT')
|
expect(parser.yy.getDirection()).toBe('BT');
|
||||||
expect(Object.keys(parser.yy.getBranches()).length).toBe(1)
|
expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should checkout a branch', function () {
|
it('should checkout a branch', function() {
|
||||||
const str = 'gitGraph:\n' +
|
const str = 'gitGraph:\n' + 'branch new\n' + 'checkout new\n';
|
||||||
'branch new\n' +
|
|
||||||
'checkout new\n'
|
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
const commits = parser.yy.getCommits()
|
const commits = parser.yy.getCommits();
|
||||||
|
|
||||||
expect(Object.keys(commits).length).toBe(0)
|
expect(Object.keys(commits).length).toBe(0);
|
||||||
expect(parser.yy.getCurrentBranch()).toBe('new')
|
expect(parser.yy.getCurrentBranch()).toBe('new');
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should add commits to checked out branch', function () {
|
it('should add commits to checked out branch', function() {
|
||||||
const str = 'gitGraph:\n' +
|
const str = 'gitGraph:\n' + 'branch new\n' + 'checkout new\n' + 'commit\n' + 'commit\n';
|
||||||
'branch new\n' +
|
|
||||||
'checkout new\n' +
|
|
||||||
'commit\n' +
|
|
||||||
'commit\n'
|
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
const commits = parser.yy.getCommits()
|
const commits = parser.yy.getCommits();
|
||||||
|
|
||||||
expect(Object.keys(commits).length).toBe(2)
|
expect(Object.keys(commits).length).toBe(2);
|
||||||
expect(parser.yy.getCurrentBranch()).toBe('new')
|
expect(parser.yy.getCurrentBranch()).toBe('new');
|
||||||
const branchCommit = parser.yy.getBranches()['new']
|
const branchCommit = parser.yy.getBranches()['new'];
|
||||||
expect(branchCommit).not.toBeNull()
|
expect(branchCommit).not.toBeNull();
|
||||||
expect(commits[branchCommit].parent).not.toBeNull()
|
expect(commits[branchCommit].parent).not.toBeNull();
|
||||||
})
|
});
|
||||||
it('should handle commit with args', function () {
|
it('should handle commit with args', function() {
|
||||||
const str = 'gitGraph:\n' +
|
const str = 'gitGraph:\n' + 'commit "a commit"\n';
|
||||||
'commit "a commit"\n'
|
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
const commits = parser.yy.getCommits()
|
const commits = parser.yy.getCommits();
|
||||||
|
|
||||||
expect(Object.keys(commits).length).toBe(1)
|
expect(Object.keys(commits).length).toBe(1);
|
||||||
const key = Object.keys(commits)[0]
|
const key = Object.keys(commits)[0];
|
||||||
expect(commits[key].message).toBe('a commit')
|
expect(commits[key].message).toBe('a commit');
|
||||||
expect(parser.yy.getCurrentBranch()).toBe('master')
|
expect(parser.yy.getCurrentBranch()).toBe('master');
|
||||||
})
|
});
|
||||||
|
|
||||||
it('it should reset a branch', function () {
|
it('it should reset a branch', function() {
|
||||||
const str = 'gitGraph:\n' +
|
const str =
|
||||||
|
'gitGraph:\n' +
|
||||||
'commit\n' +
|
'commit\n' +
|
||||||
'commit\n' +
|
'commit\n' +
|
||||||
'branch newbranch\n' +
|
'branch newbranch\n' +
|
||||||
'checkout newbranch\n' +
|
'checkout newbranch\n' +
|
||||||
'commit\n' +
|
'commit\n' +
|
||||||
'reset master\n'
|
'reset master\n';
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
|
|
||||||
const commits = parser.yy.getCommits()
|
const commits = parser.yy.getCommits();
|
||||||
expect(Object.keys(commits).length).toBe(3)
|
expect(Object.keys(commits).length).toBe(3);
|
||||||
expect(parser.yy.getCurrentBranch()).toBe('newbranch')
|
expect(parser.yy.getCurrentBranch()).toBe('newbranch');
|
||||||
expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master'])
|
expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master']);
|
||||||
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch'])
|
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch']);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('reset can take an argument', function () {
|
it('reset can take an argument', function() {
|
||||||
const str = 'gitGraph:\n' +
|
const str =
|
||||||
|
'gitGraph:\n' +
|
||||||
'commit\n' +
|
'commit\n' +
|
||||||
'commit\n' +
|
'commit\n' +
|
||||||
'branch newbranch\n' +
|
'branch newbranch\n' +
|
||||||
'checkout newbranch\n' +
|
'checkout newbranch\n' +
|
||||||
'commit\n' +
|
'commit\n' +
|
||||||
'reset master^\n'
|
'reset master^\n';
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
|
|
||||||
const commits = parser.yy.getCommits()
|
const commits = parser.yy.getCommits();
|
||||||
expect(Object.keys(commits).length).toBe(3)
|
expect(Object.keys(commits).length).toBe(3);
|
||||||
expect(parser.yy.getCurrentBranch()).toBe('newbranch')
|
expect(parser.yy.getCurrentBranch()).toBe('newbranch');
|
||||||
const master = commits[parser.yy.getBranches()['master']]
|
const master = commits[parser.yy.getBranches()['master']];
|
||||||
expect(parser.yy.getHead().id).toEqual(master.parent)
|
expect(parser.yy.getHead().id).toEqual(master.parent);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('it should handle fast forwardable merges', function () {
|
it('it should handle fast forwardable merges', function() {
|
||||||
const str = 'gitGraph:\n' +
|
const str =
|
||||||
|
'gitGraph:\n' +
|
||||||
'commit\n' +
|
'commit\n' +
|
||||||
'branch newbranch\n' +
|
'branch newbranch\n' +
|
||||||
'checkout newbranch\n' +
|
'checkout newbranch\n' +
|
||||||
'commit\n' +
|
'commit\n' +
|
||||||
'commit\n' +
|
'commit\n' +
|
||||||
'checkout master\n' +
|
'checkout master\n' +
|
||||||
'merge newbranch\n'
|
'merge newbranch\n';
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
|
|
||||||
const commits = parser.yy.getCommits()
|
const commits = parser.yy.getCommits();
|
||||||
expect(Object.keys(commits).length).toBe(3)
|
expect(Object.keys(commits).length).toBe(3);
|
||||||
expect(parser.yy.getCurrentBranch()).toBe('master')
|
expect(parser.yy.getCurrentBranch()).toBe('master');
|
||||||
expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master'])
|
expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master']);
|
||||||
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch'])
|
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch']);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('it should handle cases when merge is a noop', function () {
|
it('it should handle cases when merge is a noop', function() {
|
||||||
const str = 'gitGraph:\n' +
|
const str =
|
||||||
|
'gitGraph:\n' +
|
||||||
'commit\n' +
|
'commit\n' +
|
||||||
'branch newbranch\n' +
|
'branch newbranch\n' +
|
||||||
'checkout newbranch\n' +
|
'checkout newbranch\n' +
|
||||||
'commit\n' +
|
'commit\n' +
|
||||||
'commit\n' +
|
'commit\n' +
|
||||||
'merge master\n'
|
'merge master\n';
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
|
|
||||||
const commits = parser.yy.getCommits()
|
const commits = parser.yy.getCommits();
|
||||||
expect(Object.keys(commits).length).toBe(3)
|
expect(Object.keys(commits).length).toBe(3);
|
||||||
expect(parser.yy.getCurrentBranch()).toBe('newbranch')
|
expect(parser.yy.getCurrentBranch()).toBe('newbranch');
|
||||||
expect(parser.yy.getBranches()['newbranch']).not.toEqual(parser.yy.getBranches()['master'])
|
expect(parser.yy.getBranches()['newbranch']).not.toEqual(parser.yy.getBranches()['master']);
|
||||||
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch'])
|
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch']);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('it should handle merge with 2 parents', function () {
|
it('it should handle merge with 2 parents', function() {
|
||||||
const str = 'gitGraph:\n' +
|
const str =
|
||||||
|
'gitGraph:\n' +
|
||||||
'commit\n' +
|
'commit\n' +
|
||||||
'branch newbranch\n' +
|
'branch newbranch\n' +
|
||||||
'checkout newbranch\n' +
|
'checkout newbranch\n' +
|
||||||
@ -203,19 +188,20 @@ describe('when parsing a gitGraph', function () {
|
|||||||
'commit\n' +
|
'commit\n' +
|
||||||
'checkout master\n' +
|
'checkout master\n' +
|
||||||
'commit\n' +
|
'commit\n' +
|
||||||
'merge newbranch\n'
|
'merge newbranch\n';
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
|
|
||||||
const commits = parser.yy.getCommits()
|
const commits = parser.yy.getCommits();
|
||||||
expect(Object.keys(commits).length).toBe(5)
|
expect(Object.keys(commits).length).toBe(5);
|
||||||
expect(parser.yy.getCurrentBranch()).toBe('master')
|
expect(parser.yy.getCurrentBranch()).toBe('master');
|
||||||
expect(parser.yy.getBranches()['newbranch']).not.toEqual(parser.yy.getBranches()['master'])
|
expect(parser.yy.getBranches()['newbranch']).not.toEqual(parser.yy.getBranches()['master']);
|
||||||
expect(parser.yy.getHead().id).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 () {
|
it('it should handle ff merge when history walk has two parents (merge commit)', function() {
|
||||||
const str = 'gitGraph:\n' +
|
const str =
|
||||||
|
'gitGraph:\n' +
|
||||||
'commit\n' +
|
'commit\n' +
|
||||||
'branch newbranch\n' +
|
'branch newbranch\n' +
|
||||||
'checkout newbranch\n' +
|
'checkout newbranch\n' +
|
||||||
@ -226,16 +212,16 @@ describe('when parsing a gitGraph', function () {
|
|||||||
'merge newbranch\n' +
|
'merge newbranch\n' +
|
||||||
'commit\n' +
|
'commit\n' +
|
||||||
'checkout newbranch\n' +
|
'checkout newbranch\n' +
|
||||||
'merge master\n'
|
'merge master\n';
|
||||||
|
|
||||||
parser.parse(str)
|
parser.parse(str);
|
||||||
|
|
||||||
const commits = parser.yy.getCommits()
|
const commits = parser.yy.getCommits();
|
||||||
expect(Object.keys(commits).length).toBe(6)
|
expect(Object.keys(commits).length).toBe(6);
|
||||||
expect(parser.yy.getCurrentBranch()).toBe('newbranch')
|
expect(parser.yy.getCurrentBranch()).toBe('newbranch');
|
||||||
expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master'])
|
expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master']);
|
||||||
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['master'])
|
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['master']);
|
||||||
|
|
||||||
parser.yy.prettyPrint()
|
parser.yy.prettyPrint();
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import * as d3 from 'd3'
|
import * as d3 from 'd3';
|
||||||
import _ from 'lodash'
|
import _ from 'lodash';
|
||||||
|
|
||||||
import db from './gitGraphAst'
|
import db from './gitGraphAst';
|
||||||
import gitGraphParser from './parser/gitGraph'
|
import gitGraphParser from './parser/gitGraph';
|
||||||
import { logger } from '../../logger'
|
import { logger } from '../../logger';
|
||||||
import { interpolateToCurve } from '../../utils'
|
import { interpolateToCurve } from '../../utils';
|
||||||
|
|
||||||
let allCommitsDict = {}
|
let allCommitsDict = {};
|
||||||
let branchNum
|
let branchNum;
|
||||||
let config = {
|
let config = {
|
||||||
nodeSpacing: 150,
|
nodeSpacing: 150,
|
||||||
nodeFillColor: 'yellow',
|
nodeFillColor: 'yellow',
|
||||||
@ -25,13 +25,13 @@ let config = {
|
|||||||
x: -25,
|
x: -25,
|
||||||
y: 0
|
y: 0
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
let apiConfig = {}
|
let apiConfig = {};
|
||||||
export const setConf = function (c) {
|
export const setConf = function(c) {
|
||||||
apiConfig = c
|
apiConfig = c;
|
||||||
}
|
};
|
||||||
|
|
||||||
function svgCreateDefs (svg) {
|
function svgCreateDefs(svg) {
|
||||||
svg
|
svg
|
||||||
.append('defs')
|
.append('defs')
|
||||||
.append('g')
|
.append('g')
|
||||||
@ -39,8 +39,9 @@ function svgCreateDefs (svg) {
|
|||||||
.append('circle')
|
.append('circle')
|
||||||
.attr('r', config.nodeRadius)
|
.attr('r', config.nodeRadius)
|
||||||
.attr('cx', 0)
|
.attr('cx', 0)
|
||||||
.attr('cy', 0)
|
.attr('cy', 0);
|
||||||
svg.select('#def-commit')
|
svg
|
||||||
|
.select('#def-commit')
|
||||||
.append('foreignObject')
|
.append('foreignObject')
|
||||||
.attr('width', config.nodeLabel.width)
|
.attr('width', config.nodeLabel.width)
|
||||||
.attr('height', config.nodeLabel.height)
|
.attr('height', config.nodeLabel.height)
|
||||||
@ -49,239 +50,293 @@ function svgCreateDefs (svg) {
|
|||||||
.attr('class', 'node-label')
|
.attr('class', 'node-label')
|
||||||
.attr('requiredFeatures', 'http://www.w3.org/TR/SVG11/feature#Extensibility')
|
.attr('requiredFeatures', 'http://www.w3.org/TR/SVG11/feature#Extensibility')
|
||||||
.append('p')
|
.append('p')
|
||||||
.html('')
|
.html('');
|
||||||
}
|
}
|
||||||
|
|
||||||
function svgDrawLine (svg, points, colorIdx, interpolate) {
|
function svgDrawLine(svg, points, colorIdx, interpolate) {
|
||||||
const curve = interpolateToCurve(interpolate, d3.curveBasis)
|
const curve = interpolateToCurve(interpolate, d3.curveBasis);
|
||||||
const color = config.branchColors[colorIdx % config.branchColors.length]
|
const color = config.branchColors[colorIdx % config.branchColors.length];
|
||||||
const lineGen = d3.line()
|
const lineGen = d3
|
||||||
.x(function (d) {
|
.line()
|
||||||
return Math.round(d.x)
|
.x(function(d) {
|
||||||
|
return Math.round(d.x);
|
||||||
})
|
})
|
||||||
.y(function (d) {
|
.y(function(d) {
|
||||||
return Math.round(d.y)
|
return Math.round(d.y);
|
||||||
})
|
})
|
||||||
.curve(curve)
|
.curve(curve);
|
||||||
|
|
||||||
svg
|
svg
|
||||||
.append('svg:path')
|
.append('svg:path')
|
||||||
.attr('d', lineGen(points))
|
.attr('d', lineGen(points))
|
||||||
.style('stroke', color)
|
.style('stroke', color)
|
||||||
.style('stroke-width', config.lineStrokeWidth)
|
.style('stroke-width', config.lineStrokeWidth)
|
||||||
.style('fill', 'none')
|
.style('fill', 'none');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass in the element and its pre-transform coords
|
// Pass in the element and its pre-transform coords
|
||||||
function getElementCoords (element, coords) {
|
function getElementCoords(element, coords) {
|
||||||
coords = coords || element.node().getBBox()
|
coords = coords || element.node().getBBox();
|
||||||
const ctm = element.node().getCTM()
|
const ctm = element.node().getCTM();
|
||||||
const xn = ctm.e + coords.x * ctm.a
|
const xn = ctm.e + coords.x * ctm.a;
|
||||||
const yn = ctm.f + coords.y * ctm.d
|
const yn = ctm.f + coords.y * ctm.d;
|
||||||
return {
|
return {
|
||||||
left: xn,
|
left: xn,
|
||||||
top: yn,
|
top: yn,
|
||||||
width: coords.width,
|
width: coords.width,
|
||||||
height: coords.height
|
height: coords.height
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function svgDrawLineForCommits (svg, fromId, toId, direction, color) {
|
function svgDrawLineForCommits(svg, fromId, toId, direction, color) {
|
||||||
logger.debug('svgDrawLineForCommits: ', fromId, toId)
|
logger.debug('svgDrawLineForCommits: ', fromId, toId);
|
||||||
const fromBbox = getElementCoords(svg.select('#node-' + fromId + ' circle'))
|
const fromBbox = getElementCoords(svg.select('#node-' + fromId + ' circle'));
|
||||||
const toBbox = getElementCoords(svg.select('#node-' + toId + ' circle'))
|
const toBbox = getElementCoords(svg.select('#node-' + toId + ' circle'));
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case 'LR':
|
case 'LR':
|
||||||
// (toBbox)
|
// (toBbox)
|
||||||
// +--------
|
// +--------
|
||||||
// + (fromBbox)
|
// + (fromBbox)
|
||||||
if (fromBbox.left - toBbox.left > config.nodeSpacing) {
|
if (fromBbox.left - toBbox.left > config.nodeSpacing) {
|
||||||
const lineStart = { x: fromBbox.left - config.nodeSpacing, y: toBbox.top + toBbox.height / 2 }
|
const lineStart = {
|
||||||
const lineEnd = { x: toBbox.left + toBbox.width, y: toBbox.top + toBbox.height / 2 }
|
x: fromBbox.left - config.nodeSpacing,
|
||||||
svgDrawLine(svg, [lineStart, lineEnd], color, 'linear')
|
y: toBbox.top + toBbox.height / 2
|
||||||
svgDrawLine(svg, [
|
};
|
||||||
{ x: fromBbox.left, y: fromBbox.top + fromBbox.height / 2 },
|
const lineEnd = { x: toBbox.left + toBbox.width, y: toBbox.top + toBbox.height / 2 };
|
||||||
{ x: fromBbox.left - config.nodeSpacing / 2, y: fromBbox.top + fromBbox.height / 2 },
|
svgDrawLine(svg, [lineStart, lineEnd], color, 'linear');
|
||||||
{ x: fromBbox.left - config.nodeSpacing / 2, y: lineStart.y },
|
svgDrawLine(
|
||||||
lineStart], color)
|
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 {
|
} else {
|
||||||
svgDrawLine(svg, [{
|
svgDrawLine(
|
||||||
'x': fromBbox.left,
|
svg,
|
||||||
'y': fromBbox.top + fromBbox.height / 2
|
[
|
||||||
}, {
|
{
|
||||||
'x': fromBbox.left - config.nodeSpacing / 2,
|
x: fromBbox.left,
|
||||||
'y': fromBbox.top + fromBbox.height / 2
|
y: fromBbox.top + fromBbox.height / 2
|
||||||
}, {
|
},
|
||||||
'x': fromBbox.left - config.nodeSpacing / 2,
|
{
|
||||||
'y': toBbox.top + toBbox.height / 2
|
x: fromBbox.left - config.nodeSpacing / 2,
|
||||||
}, {
|
y: fromBbox.top + fromBbox.height / 2
|
||||||
'x': toBbox.left + toBbox.width,
|
},
|
||||||
'y': toBbox.top + toBbox.height / 2
|
{
|
||||||
}], color)
|
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
|
break;
|
||||||
case 'BT':
|
case 'BT':
|
||||||
// + (fromBbox)
|
// + (fromBbox)
|
||||||
// |
|
// |
|
||||||
// |
|
// |
|
||||||
// + (toBbox)
|
// + (toBbox)
|
||||||
if (toBbox.top - fromBbox.top > config.nodeSpacing) {
|
if (toBbox.top - fromBbox.top > config.nodeSpacing) {
|
||||||
const lineStart = { x: toBbox.left + toBbox.width / 2, y: fromBbox.top + fromBbox.height + config.nodeSpacing }
|
const lineStart = {
|
||||||
const lineEnd = { x: toBbox.left + toBbox.width / 2, y: toBbox.top }
|
x: toBbox.left + toBbox.width / 2,
|
||||||
svgDrawLine(svg, [lineStart, lineEnd], color, 'linear')
|
y: fromBbox.top + fromBbox.height + config.nodeSpacing
|
||||||
svgDrawLine(svg, [
|
};
|
||||||
{ x: fromBbox.left + fromBbox.width / 2, y: fromBbox.top + fromBbox.height },
|
const lineEnd = { x: toBbox.left + toBbox.width / 2, y: toBbox.top };
|
||||||
{ x: fromBbox.left + fromBbox.width / 2, y: fromBbox.top + fromBbox.height + config.nodeSpacing / 2 },
|
svgDrawLine(svg, [lineStart, lineEnd], color, 'linear');
|
||||||
{ x: toBbox.left + toBbox.width / 2, y: lineStart.y - config.nodeSpacing / 2 },
|
svgDrawLine(
|
||||||
lineStart], color)
|
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 {
|
} else {
|
||||||
svgDrawLine(svg, [{
|
svgDrawLine(
|
||||||
'x': fromBbox.left + fromBbox.width / 2,
|
svg,
|
||||||
'y': fromBbox.top + fromBbox.height
|
[
|
||||||
}, {
|
{
|
||||||
'x': fromBbox.left + fromBbox.width / 2,
|
x: fromBbox.left + fromBbox.width / 2,
|
||||||
'y': fromBbox.top + config.nodeSpacing / 2
|
y: fromBbox.top + fromBbox.height
|
||||||
}, {
|
},
|
||||||
'x': toBbox.left + toBbox.width / 2,
|
{
|
||||||
'y': toBbox.top - config.nodeSpacing / 2
|
x: fromBbox.left + fromBbox.width / 2,
|
||||||
}, {
|
y: fromBbox.top + config.nodeSpacing / 2
|
||||||
'x': toBbox.left + toBbox.width / 2,
|
},
|
||||||
'y': toBbox.top
|
{
|
||||||
}], color)
|
x: toBbox.left + toBbox.width / 2,
|
||||||
|
y: toBbox.top - config.nodeSpacing / 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: toBbox.left + toBbox.width / 2,
|
||||||
|
y: toBbox.top
|
||||||
|
}
|
||||||
|
],
|
||||||
|
color
|
||||||
|
);
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function cloneNode (svg, selector) {
|
function cloneNode(svg, selector) {
|
||||||
return svg.select(selector).node().cloneNode(true)
|
return svg
|
||||||
|
.select(selector)
|
||||||
|
.node()
|
||||||
|
.cloneNode(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderCommitHistory (svg, commitid, branches, direction) {
|
function renderCommitHistory(svg, commitid, branches, direction) {
|
||||||
let commit
|
let commit;
|
||||||
const numCommits = Object.keys(allCommitsDict).length
|
const numCommits = Object.keys(allCommitsDict).length;
|
||||||
if (typeof commitid === 'string') {
|
if (typeof commitid === 'string') {
|
||||||
do {
|
do {
|
||||||
commit = allCommitsDict[commitid]
|
commit = allCommitsDict[commitid];
|
||||||
logger.debug('in renderCommitHistory', commit.id, commit.seq)
|
logger.debug('in renderCommitHistory', commit.id, commit.seq);
|
||||||
if (svg.select('#node-' + commitid).size() > 0) {
|
if (svg.select('#node-' + commitid).size() > 0) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
svg
|
svg
|
||||||
.append(function () {
|
.append(function() {
|
||||||
return cloneNode(svg, '#def-commit')
|
return cloneNode(svg, '#def-commit');
|
||||||
})
|
})
|
||||||
.attr('class', 'commit')
|
.attr('class', 'commit')
|
||||||
.attr('id', function () {
|
.attr('id', function() {
|
||||||
return 'node-' + commit.id
|
return 'node-' + commit.id;
|
||||||
})
|
})
|
||||||
.attr('transform', function () {
|
.attr('transform', function() {
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case 'LR':
|
case 'LR':
|
||||||
return 'translate(' + (commit.seq * config.nodeSpacing + config.leftMargin) + ', ' +
|
return (
|
||||||
(branchNum * config.branchOffset) + ')'
|
'translate(' +
|
||||||
|
(commit.seq * config.nodeSpacing + config.leftMargin) +
|
||||||
|
', ' +
|
||||||
|
branchNum * config.branchOffset +
|
||||||
|
')'
|
||||||
|
);
|
||||||
case 'BT':
|
case 'BT':
|
||||||
return 'translate(' + (branchNum * config.branchOffset + config.leftMargin) + ', ' +
|
return (
|
||||||
((numCommits - commit.seq) * config.nodeSpacing) + ')'
|
'translate(' +
|
||||||
|
(branchNum * config.branchOffset + config.leftMargin) +
|
||||||
|
', ' +
|
||||||
|
(numCommits - commit.seq) * config.nodeSpacing +
|
||||||
|
')'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.attr('fill', config.nodeFillColor)
|
.attr('fill', config.nodeFillColor)
|
||||||
.attr('stroke', config.nodeStrokeColor)
|
.attr('stroke', config.nodeStrokeColor)
|
||||||
.attr('stroke-width', config.nodeStrokeWidth)
|
.attr('stroke-width', config.nodeStrokeWidth);
|
||||||
|
|
||||||
let branch
|
let branch;
|
||||||
for (let branchName in branches) {
|
for (let branchName in branches) {
|
||||||
if (branches[branchName].commit === commit) {
|
if (branches[branchName].commit === commit) {
|
||||||
branch = branches[branchName]
|
branch = branches[branchName];
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (branch) {
|
if (branch) {
|
||||||
logger.debug('found branch ', branch.name)
|
logger.debug('found branch ', branch.name);
|
||||||
svg.select('#node-' + commit.id + ' p')
|
svg
|
||||||
|
.select('#node-' + commit.id + ' p')
|
||||||
.append('xhtml:span')
|
.append('xhtml:span')
|
||||||
.attr('class', 'branch-label')
|
.attr('class', 'branch-label')
|
||||||
.text(branch.name + ', ')
|
.text(branch.name + ', ');
|
||||||
}
|
}
|
||||||
svg.select('#node-' + commit.id + ' p')
|
svg
|
||||||
|
.select('#node-' + commit.id + ' p')
|
||||||
.append('xhtml:span')
|
.append('xhtml:span')
|
||||||
.attr('class', 'commit-id')
|
.attr('class', 'commit-id')
|
||||||
.text(commit.id)
|
.text(commit.id);
|
||||||
if (commit.message !== '' && direction === 'BT') {
|
if (commit.message !== '' && direction === 'BT') {
|
||||||
svg.select('#node-' + commit.id + ' p')
|
svg
|
||||||
|
.select('#node-' + commit.id + ' p')
|
||||||
.append('xhtml:span')
|
.append('xhtml:span')
|
||||||
.attr('class', 'commit-msg')
|
.attr('class', 'commit-msg')
|
||||||
.text(', ' + commit.message)
|
.text(', ' + commit.message);
|
||||||
}
|
}
|
||||||
commitid = commit.parent
|
commitid = commit.parent;
|
||||||
} while (commitid && allCommitsDict[commitid])
|
} while (commitid && allCommitsDict[commitid]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(commitid)) {
|
if (Array.isArray(commitid)) {
|
||||||
logger.debug('found merge commmit', commitid)
|
logger.debug('found merge commmit', commitid);
|
||||||
renderCommitHistory(svg, commitid[0], branches, direction)
|
renderCommitHistory(svg, commitid[0], branches, direction);
|
||||||
branchNum++
|
branchNum++;
|
||||||
renderCommitHistory(svg, commitid[1], branches, direction)
|
renderCommitHistory(svg, commitid[1], branches, direction);
|
||||||
branchNum--
|
branchNum--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderLines (svg, commit, direction, branchColor) {
|
function renderLines(svg, commit, direction, branchColor) {
|
||||||
branchColor = branchColor || 0
|
branchColor = branchColor || 0;
|
||||||
while (commit.seq > 0 && !commit.lineDrawn) {
|
while (commit.seq > 0 && !commit.lineDrawn) {
|
||||||
if (typeof commit.parent === 'string') {
|
if (typeof commit.parent === 'string') {
|
||||||
svgDrawLineForCommits(svg, commit.id, commit.parent, direction, branchColor)
|
svgDrawLineForCommits(svg, commit.id, commit.parent, direction, branchColor);
|
||||||
commit.lineDrawn = true
|
commit.lineDrawn = true;
|
||||||
commit = allCommitsDict[commit.parent]
|
commit = allCommitsDict[commit.parent];
|
||||||
} else if (Array.isArray(commit.parent)) {
|
} else if (Array.isArray(commit.parent)) {
|
||||||
svgDrawLineForCommits(svg, commit.id, commit.parent[0], direction, branchColor)
|
svgDrawLineForCommits(svg, commit.id, commit.parent[0], direction, branchColor);
|
||||||
svgDrawLineForCommits(svg, commit.id, commit.parent[1], direction, branchColor + 1)
|
svgDrawLineForCommits(svg, commit.id, commit.parent[1], direction, branchColor + 1);
|
||||||
renderLines(svg, allCommitsDict[commit.parent[1]], direction, branchColor + 1)
|
renderLines(svg, allCommitsDict[commit.parent[1]], direction, branchColor + 1);
|
||||||
commit.lineDrawn = true
|
commit.lineDrawn = true;
|
||||||
commit = allCommitsDict[commit.parent[0]]
|
commit = allCommitsDict[commit.parent[0]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const draw = function (txt, id, ver) {
|
export const draw = function(txt, id, ver) {
|
||||||
try {
|
try {
|
||||||
const parser = gitGraphParser.parser
|
const parser = gitGraphParser.parser;
|
||||||
parser.yy = db
|
parser.yy = db;
|
||||||
|
|
||||||
logger.debug('in gitgraph renderer', txt, id, ver)
|
logger.debug('in gitgraph renderer', txt, id, ver);
|
||||||
// Parse the graph definition
|
// Parse the graph definition
|
||||||
parser.parse(txt + '\n')
|
parser.parse(txt + '\n');
|
||||||
|
|
||||||
config = _.assign(config, apiConfig, db.getOptions())
|
config = _.assign(config, apiConfig, db.getOptions());
|
||||||
logger.debug('effective options', config)
|
logger.debug('effective options', config);
|
||||||
const direction = db.getDirection()
|
const direction = db.getDirection();
|
||||||
allCommitsDict = db.getCommits()
|
allCommitsDict = db.getCommits();
|
||||||
const branches = db.getBranchesAsObjArray()
|
const branches = db.getBranchesAsObjArray();
|
||||||
if (direction === 'BT') {
|
if (direction === 'BT') {
|
||||||
config.nodeLabel.x = branches.length * config.branchOffset
|
config.nodeLabel.x = branches.length * config.branchOffset;
|
||||||
config.nodeLabel.width = '100%'
|
config.nodeLabel.width = '100%';
|
||||||
config.nodeLabel.y = -1 * 2 * config.nodeRadius
|
config.nodeLabel.y = -1 * 2 * config.nodeRadius;
|
||||||
}
|
}
|
||||||
const svg = d3.select(`[id="${id}"]`)
|
const svg = d3.select(`[id="${id}"]`);
|
||||||
svgCreateDefs(svg)
|
svgCreateDefs(svg);
|
||||||
branchNum = 1
|
branchNum = 1;
|
||||||
for (let branch in branches) {
|
for (let branch in branches) {
|
||||||
const v = branches[branch]
|
const v = branches[branch];
|
||||||
renderCommitHistory(svg, v.commit.id, branches, direction)
|
renderCommitHistory(svg, v.commit.id, branches, direction);
|
||||||
renderLines(svg, v.commit, direction)
|
renderLines(svg, v.commit, direction);
|
||||||
branchNum++
|
branchNum++;
|
||||||
}
|
}
|
||||||
svg.attr('height', function () {
|
svg.attr('height', function() {
|
||||||
if (direction === 'BT') return Object.keys(allCommitsDict).length * config.nodeSpacing
|
if (direction === 'BT') return Object.keys(allCommitsDict).length * config.nodeSpacing;
|
||||||
return (branches.length + 1) * config.branchOffset
|
return (branches.length + 1) * config.branchOffset;
|
||||||
})
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Error while rendering gitgraph')
|
logger.error('Error while rendering gitgraph');
|
||||||
logger.error(e.message)
|
logger.error(e.message);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setConf,
|
setConf,
|
||||||
draw
|
draw
|
||||||
}
|
};
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
/* eslint-env jasmine */
|
/* eslint-env jasmine */
|
||||||
describe('when parsing an info graph it', function () {
|
describe('when parsing an info graph it', function() {
|
||||||
var ex
|
var ex;
|
||||||
beforeEach(function () {
|
beforeEach(function() {
|
||||||
ex = require('./parser/info').parser
|
ex = require('./parser/info').parser;
|
||||||
ex.yy = require('./infoDb')
|
ex.yy = require('./infoDb');
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle an info definition', function () {
|
it('should handle an info definition', function() {
|
||||||
var str = `info
|
var str = `info
|
||||||
showInfo`
|
showInfo`;
|
||||||
|
|
||||||
ex.parse(str)
|
ex.parse(str);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
/**
|
/**
|
||||||
* Created by knut on 15-01-14.
|
* Created by knut on 15-01-14.
|
||||||
*/
|
*/
|
||||||
import { logger } from '../../logger'
|
import { logger } from '../../logger';
|
||||||
|
|
||||||
var message = ''
|
var message = '';
|
||||||
var info = false
|
var info = false;
|
||||||
|
|
||||||
export const setMessage = txt => {
|
export const setMessage = txt => {
|
||||||
logger.debug('Setting message to: ' + txt)
|
logger.debug('Setting message to: ' + txt);
|
||||||
message = txt
|
message = txt;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getMessage = () => {
|
export const getMessage = () => {
|
||||||
return message
|
return message;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const setInfo = inf => {
|
export const setInfo = inf => {
|
||||||
info = inf
|
info = inf;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getInfo = () => {
|
export const getInfo = () => {
|
||||||
return info
|
return info;
|
||||||
}
|
};
|
||||||
|
|
||||||
// export const parseError = (err, hash) => {
|
// export const parseError = (err, hash) => {
|
||||||
// global.mermaidAPI.parseError(err, hash)
|
// global.mermaidAPI.parseError(err, hash)
|
||||||
@ -33,4 +33,4 @@ export default {
|
|||||||
setInfo,
|
setInfo,
|
||||||
getInfo
|
getInfo
|
||||||
// parseError
|
// parseError
|
||||||
}
|
};
|
||||||
|
@ -1,20 +1,19 @@
|
|||||||
/**
|
/**
|
||||||
* Created by knut on 14-12-11.
|
* Created by knut on 14-12-11.
|
||||||
*/
|
*/
|
||||||
import * as d3 from 'd3'
|
import * as d3 from 'd3';
|
||||||
import db from './infoDb'
|
import db from './infoDb';
|
||||||
import infoParser from './parser/info'
|
import infoParser from './parser/info';
|
||||||
import { logger } from '../../logger'
|
import { logger } from '../../logger';
|
||||||
|
|
||||||
const conf = {
|
const conf = {};
|
||||||
}
|
export const setConf = function(cnf) {
|
||||||
export const setConf = function (cnf) {
|
const keys = Object.keys(cnf);
|
||||||
const keys = Object.keys(cnf)
|
|
||||||
|
|
||||||
keys.forEach(function (key) {
|
keys.forEach(function(key) {
|
||||||
conf[key] = cnf[key]
|
conf[key] = cnf[key];
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a an info picture in the tag with id: id based on the graph definition in text.
|
* Draws a an info picture in the tag with id: id based on the graph definition in text.
|
||||||
@ -23,16 +22,16 @@ export const setConf = function (cnf) {
|
|||||||
*/
|
*/
|
||||||
export const draw = (txt, id, ver) => {
|
export const draw = (txt, id, ver) => {
|
||||||
try {
|
try {
|
||||||
const parser = infoParser.parser
|
const parser = infoParser.parser;
|
||||||
parser.yy = db
|
parser.yy = db;
|
||||||
logger.debug('Renering info diagram\n' + txt)
|
logger.debug('Renering info diagram\n' + txt);
|
||||||
// Parse the graph definition
|
// Parse the graph definition
|
||||||
parser.parse(txt)
|
parser.parse(txt);
|
||||||
logger.debug('Parsed info diagram')
|
logger.debug('Parsed info diagram');
|
||||||
// Fetch the default direction, use TD if none was found
|
// Fetch the default direction, use TD if none was found
|
||||||
const svg = d3.select('#' + id)
|
const svg = d3.select('#' + id);
|
||||||
|
|
||||||
const g = svg.append('g')
|
const g = svg.append('g');
|
||||||
|
|
||||||
g.append('text') // text label for the x axis
|
g.append('text') // text label for the x axis
|
||||||
.attr('x', 100)
|
.attr('x', 100)
|
||||||
@ -40,18 +39,18 @@ export const draw = (txt, id, ver) => {
|
|||||||
.attr('class', 'version')
|
.attr('class', 'version')
|
||||||
.attr('font-size', '32px')
|
.attr('font-size', '32px')
|
||||||
.style('text-anchor', 'middle')
|
.style('text-anchor', 'middle')
|
||||||
.text('v ' + ver)
|
.text('v ' + ver);
|
||||||
|
|
||||||
svg.attr('height', 100)
|
svg.attr('height', 100);
|
||||||
svg.attr('width', 400)
|
svg.attr('width', 400);
|
||||||
// svg.attr('viewBox', '0 0 300 150');
|
// svg.attr('viewBox', '0 0 300 150');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Error while rendering info diagram')
|
logger.error('Error while rendering info diagram');
|
||||||
logger.error(e.message)
|
logger.error(e.message);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setConf,
|
setConf,
|
||||||
draw
|
draw
|
||||||
}
|
};
|
||||||
|
@ -1,37 +1,36 @@
|
|||||||
|
|
||||||
/* eslint-env jasmine */
|
/* eslint-env jasmine */
|
||||||
import pieDb from '../pieDb'
|
import pieDb from '../pieDb';
|
||||||
import pie from './pie'
|
import pie from './pie';
|
||||||
import { setConfig } from '../../../config'
|
import { setConfig } from '../../../config';
|
||||||
|
|
||||||
setConfig({
|
setConfig({
|
||||||
securityLevel: 'strict'
|
securityLevel: 'strict'
|
||||||
})
|
});
|
||||||
|
|
||||||
describe('when parsing pie', function () {
|
describe('when parsing pie', function() {
|
||||||
beforeEach(function () {
|
beforeEach(function() {
|
||||||
pie.parser.yy = pieDb
|
pie.parser.yy = pieDb;
|
||||||
pie.parser.yy.clear()
|
pie.parser.yy.clear();
|
||||||
})
|
});
|
||||||
it('should handle simple pie', function () {
|
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 \n"ash" : 60\n"bat" : 40\n');
|
||||||
const sections = pieDb.getSections()
|
const sections = pieDb.getSections();
|
||||||
console.log('sections: ', sections)
|
console.log('sections: ', sections);
|
||||||
const section1 = sections['ash']
|
const section1 = sections['ash'];
|
||||||
expect(section1).toBe(60)
|
expect(section1).toBe(60);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle simple pie with positive decimal', function () {
|
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 \n"ash" : 60.67\n"bat" : 40\n');
|
||||||
const sections = pieDb.getSections()
|
const sections = pieDb.getSections();
|
||||||
console.log('sections: ', sections)
|
console.log('sections: ', sections);
|
||||||
const section1 = sections['ash']
|
const section1 = sections['ash'];
|
||||||
expect(section1).toBe(60.67)
|
expect(section1).toBe(60.67);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle simple pie with negative decimal', function () {
|
it('should handle simple pie with negative decimal', function() {
|
||||||
expect(()=>{
|
expect(() => {
|
||||||
pie.parser.parse('pie \n"ash" : 60.67\n"bat" : 40..12\n');
|
pie.parser.parse('pie \n"ash" : 60.67\n"bat" : 40..12\n');
|
||||||
}).toThrowError();
|
}).toThrowError();
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
@ -1,40 +1,40 @@
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import { logger } from '../../logger'
|
import { logger } from '../../logger';
|
||||||
|
|
||||||
let sections = {}
|
let sections = {};
|
||||||
let title = ''
|
let title = '';
|
||||||
|
|
||||||
const addSection = function (id, value) {
|
const addSection = function(id, value) {
|
||||||
if (typeof sections[id] === 'undefined') {
|
if (typeof sections[id] === 'undefined') {
|
||||||
sections[id] = value
|
sections[id] = value;
|
||||||
logger.debug('Added new section :', id)
|
logger.debug('Added new section :', id);
|
||||||
// console.log('Added new section:', id, value)
|
// console.log('Added new section:', id, value)
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
const getSections = () => sections
|
const getSections = () => sections;
|
||||||
|
|
||||||
const setTitle = function (txt) {
|
const setTitle = function(txt) {
|
||||||
title = txt
|
title = txt;
|
||||||
}
|
};
|
||||||
|
|
||||||
const getTitle = function () {
|
const getTitle = function() {
|
||||||
return title
|
return title;
|
||||||
}
|
};
|
||||||
const cleanupValue = function (value) {
|
const cleanupValue = function(value) {
|
||||||
if (value.substring(0, 1) === ':') {
|
if (value.substring(0, 1) === ':') {
|
||||||
value = value.substring(1).trim()
|
value = value.substring(1).trim();
|
||||||
return Number(value.trim())
|
return Number(value.trim());
|
||||||
} else {
|
} else {
|
||||||
return Number(value.trim())
|
return Number(value.trim());
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const clear = function () {
|
const clear = function() {
|
||||||
sections = {}
|
sections = {};
|
||||||
title = ''
|
title = '';
|
||||||
}
|
};
|
||||||
// export const parseError = (err, hash) => {
|
// export const parseError = (err, hash) => {
|
||||||
// global.mermaidAPI.parseError(err, hash)
|
// global.mermaidAPI.parseError(err, hash)
|
||||||
// }
|
// }
|
||||||
@ -47,4 +47,4 @@ export default {
|
|||||||
setTitle,
|
setTitle,
|
||||||
getTitle
|
getTitle
|
||||||
// parseError
|
// parseError
|
||||||
}
|
};
|
||||||
|
@ -1,83 +1,87 @@
|
|||||||
/**
|
/**
|
||||||
* Created by AshishJ on 11-09-2019.
|
* Created by AshishJ on 11-09-2019.
|
||||||
*/
|
*/
|
||||||
import * as d3 from 'd3'
|
import * as d3 from 'd3';
|
||||||
import pieData from './pieDb'
|
import pieData from './pieDb';
|
||||||
import pieParser from './parser/pie'
|
import pieParser from './parser/pie';
|
||||||
import { logger } from '../../logger'
|
import { logger } from '../../logger';
|
||||||
|
|
||||||
const conf = {
|
const conf = {};
|
||||||
}
|
export const setConf = function(cnf) {
|
||||||
export const setConf = function (cnf) {
|
const keys = Object.keys(cnf);
|
||||||
const keys = Object.keys(cnf)
|
|
||||||
|
|
||||||
keys.forEach(function (key) {
|
keys.forEach(function(key) {
|
||||||
conf[key] = cnf[key]
|
conf[key] = cnf[key];
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a Pie Chart with the data given in text.
|
* Draws a Pie Chart with the data given in text.
|
||||||
* @param text
|
* @param text
|
||||||
* @param id
|
* @param id
|
||||||
*/
|
*/
|
||||||
let w
|
let w;
|
||||||
export const draw = (txt, id, ver) => {
|
export const draw = (txt, id, ver) => {
|
||||||
try {
|
try {
|
||||||
const parser = pieParser.parser
|
const parser = pieParser.parser;
|
||||||
parser.yy = pieData
|
parser.yy = pieData;
|
||||||
logger.debug('Rendering info diagram\n' + txt)
|
logger.debug('Rendering info diagram\n' + txt);
|
||||||
// Parse the Pie Chart definition
|
// Parse the Pie Chart definition
|
||||||
parser.yy.clear()
|
parser.yy.clear();
|
||||||
parser.parse(txt)
|
parser.parse(txt);
|
||||||
logger.debug('Parsed info diagram')
|
logger.debug('Parsed info diagram');
|
||||||
const elem = document.getElementById(id)
|
const elem = document.getElementById(id);
|
||||||
w = elem.parentElement.offsetWidth
|
w = elem.parentElement.offsetWidth;
|
||||||
|
|
||||||
if (typeof w === 'undefined') {
|
if (typeof w === 'undefined') {
|
||||||
w = 1200
|
w = 1200;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof conf.useWidth !== 'undefined') {
|
if (typeof conf.useWidth !== 'undefined') {
|
||||||
w = conf.useWidth
|
w = conf.useWidth;
|
||||||
}
|
}
|
||||||
const h = 450
|
const h = 450;
|
||||||
elem.setAttribute('height', '100%')
|
elem.setAttribute('height', '100%');
|
||||||
// Set viewBox
|
// Set viewBox
|
||||||
elem.setAttribute('viewBox', '0 0 ' + w + ' ' + h)
|
elem.setAttribute('viewBox', '0 0 ' + w + ' ' + h);
|
||||||
|
|
||||||
// Fetch the default direction, use TD if none was found
|
// Fetch the default direction, use TD if none was found
|
||||||
|
|
||||||
var width = w// 450
|
var width = w; // 450
|
||||||
var height = 450
|
var height = 450;
|
||||||
var margin = 40
|
var margin = 40;
|
||||||
|
|
||||||
var radius = Math.min(width, height) / 2 - margin
|
var radius = Math.min(width, height) / 2 - margin;
|
||||||
|
|
||||||
var svg = d3.select('#' + id).append('svg')
|
var svg = d3
|
||||||
|
.select('#' + id)
|
||||||
|
.append('svg')
|
||||||
.attr('width', width)
|
.attr('width', width)
|
||||||
.attr('height', height)
|
.attr('height', height)
|
||||||
.append('g')
|
.append('g')
|
||||||
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')')
|
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
|
||||||
|
|
||||||
var data = pieData.getSections()
|
var data = pieData.getSections();
|
||||||
logger.info(data)
|
logger.info(data);
|
||||||
|
|
||||||
// set the color scale
|
// set the color scale
|
||||||
var color = d3.scaleOrdinal()
|
var color = d3
|
||||||
|
.scaleOrdinal()
|
||||||
.domain(data)
|
.domain(data)
|
||||||
.range(d3.schemeSet2)
|
.range(d3.schemeSet2);
|
||||||
|
|
||||||
// Compute the position of each group on the pie:
|
// Compute the position of each group on the pie:
|
||||||
var pie = d3.pie()
|
var pie = d3.pie().value(function(d) {
|
||||||
.value(function (d) { return d.value })
|
return d.value;
|
||||||
var dataReady = pie(d3.entries(data))
|
});
|
||||||
|
var dataReady = pie(d3.entries(data));
|
||||||
// Now I know that group A goes from 0 degrees to x degrees and so on.
|
// Now I know that group A goes from 0 degrees to x degrees and so on.
|
||||||
|
|
||||||
// shape helper to build arcs:
|
// shape helper to build arcs:
|
||||||
var arcGenerator = d3.arc()
|
var arcGenerator = d3
|
||||||
|
.arc()
|
||||||
.innerRadius(0)
|
.innerRadius(0)
|
||||||
.outerRadius(radius)
|
.outerRadius(radius);
|
||||||
|
|
||||||
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
|
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
|
||||||
svg
|
svg
|
||||||
@ -86,10 +90,12 @@ export const draw = (txt, id, ver) => {
|
|||||||
.enter()
|
.enter()
|
||||||
.append('path')
|
.append('path')
|
||||||
.attr('d', arcGenerator)
|
.attr('d', arcGenerator)
|
||||||
.attr('fill', function (d) { return (color(d.data.key)) })
|
.attr('fill', function(d) {
|
||||||
|
return color(d.data.key);
|
||||||
|
})
|
||||||
.attr('stroke', 'black')
|
.attr('stroke', 'black')
|
||||||
.style('stroke-width', '2px')
|
.style('stroke-width', '2px')
|
||||||
.style('opacity', 0.7)
|
.style('opacity', 0.7);
|
||||||
|
|
||||||
// Now add the annotation. Use the centroid method to get the best coordinates
|
// Now add the annotation. Use the centroid method to get the best coordinates
|
||||||
svg
|
svg
|
||||||
@ -97,23 +103,28 @@ export const draw = (txt, id, ver) => {
|
|||||||
.data(dataReady)
|
.data(dataReady)
|
||||||
.enter()
|
.enter()
|
||||||
.append('text')
|
.append('text')
|
||||||
.text(function (d) { return d.data.key })
|
.text(function(d) {
|
||||||
.attr('transform', function (d) { return 'translate(' + arcGenerator.centroid(d) + ')' })
|
return d.data.key;
|
||||||
|
})
|
||||||
|
.attr('transform', function(d) {
|
||||||
|
return 'translate(' + arcGenerator.centroid(d) + ')';
|
||||||
|
})
|
||||||
.style('text-anchor', 'middle')
|
.style('text-anchor', 'middle')
|
||||||
.style('font-size', 17)
|
.style('font-size', 17);
|
||||||
|
|
||||||
svg.append('text')
|
svg
|
||||||
|
.append('text')
|
||||||
.text(parser.yy.getTitle())
|
.text(parser.yy.getTitle())
|
||||||
.attr('x', 0)
|
.attr('x', 0)
|
||||||
.attr('y', -(h - 50) / 2)
|
.attr('y', -(h - 50) / 2)
|
||||||
.attr('class', 'pieTitleText')
|
.attr('class', 'pieTitleText');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Error while rendering info diagram')
|
logger.error('Error while rendering info diagram');
|
||||||
logger.error(e.message)
|
logger.error(e.message);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setConf,
|
setConf,
|
||||||
draw
|
draw
|
||||||
}
|
};
|
||||||
|
@ -1,51 +1,53 @@
|
|||||||
import { logger } from '../../logger'
|
import { logger } from '../../logger';
|
||||||
|
|
||||||
let actors = {}
|
let actors = {};
|
||||||
let messages = []
|
let messages = [];
|
||||||
const notes = []
|
const notes = [];
|
||||||
let title = ''
|
let title = '';
|
||||||
|
|
||||||
export const addActor = function (id, name, description) {
|
export const addActor = function(id, name, description) {
|
||||||
// Don't allow description nulling
|
// Don't allow description nulling
|
||||||
const old = actors[id]
|
const old = actors[id];
|
||||||
if (old && name === old.name && description == null) return
|
if (old && name === old.name && description == null) return;
|
||||||
|
|
||||||
// Don't allow null descriptions, either
|
// Don't allow null descriptions, either
|
||||||
if (description == null) description = name
|
if (description == null) description = name;
|
||||||
|
|
||||||
actors[id] = { name: name, description: description }
|
actors[id] = { name: name, description: description };
|
||||||
}
|
};
|
||||||
|
|
||||||
export const addMessage = function (idFrom, idTo, message, answer) {
|
export const addMessage = function(idFrom, idTo, message, answer) {
|
||||||
messages.push({ from: idFrom, to: idTo, message: message, answer: answer })
|
messages.push({ from: idFrom, to: idTo, message: message, answer: answer });
|
||||||
}
|
};
|
||||||
|
|
||||||
export const addSignal = function (idFrom, idTo, message, messageType) {
|
export const addSignal = function(idFrom, idTo, message, messageType) {
|
||||||
logger.debug('Adding message from=' + idFrom + ' to=' + idTo + ' message=' + message + ' type=' + messageType)
|
logger.debug(
|
||||||
messages.push({ from: idFrom, to: idTo, message: message, type: messageType })
|
'Adding message from=' + idFrom + ' to=' + idTo + ' message=' + message + ' type=' + messageType
|
||||||
}
|
);
|
||||||
|
messages.push({ from: idFrom, to: idTo, message: message, type: messageType });
|
||||||
|
};
|
||||||
|
|
||||||
export const getMessages = function () {
|
export const getMessages = function() {
|
||||||
return messages
|
return messages;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getActors = function () {
|
export const getActors = function() {
|
||||||
return actors
|
return actors;
|
||||||
}
|
};
|
||||||
export const getActor = function (id) {
|
export const getActor = function(id) {
|
||||||
return actors[id]
|
return actors[id];
|
||||||
}
|
};
|
||||||
export const getActorKeys = function () {
|
export const getActorKeys = function() {
|
||||||
return Object.keys(actors)
|
return Object.keys(actors);
|
||||||
}
|
};
|
||||||
export const getTitle = function () {
|
export const getTitle = function() {
|
||||||
return title
|
return title;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const clear = function () {
|
export const clear = function() {
|
||||||
actors = {}
|
actors = {};
|
||||||
messages = []
|
messages = [];
|
||||||
}
|
};
|
||||||
|
|
||||||
export const LINETYPE = {
|
export const LINETYPE = {
|
||||||
SOLID: 0,
|
SOLID: 0,
|
||||||
@ -69,97 +71,103 @@ export const LINETYPE = {
|
|||||||
PAR_END: 21,
|
PAR_END: 21,
|
||||||
RECT_START: 22,
|
RECT_START: 22,
|
||||||
RECT_END: 23
|
RECT_END: 23
|
||||||
}
|
};
|
||||||
|
|
||||||
export const ARROWTYPE = {
|
export const ARROWTYPE = {
|
||||||
FILLED: 0,
|
FILLED: 0,
|
||||||
OPEN: 1
|
OPEN: 1
|
||||||
}
|
};
|
||||||
|
|
||||||
export const PLACEMENT = {
|
export const PLACEMENT = {
|
||||||
LEFTOF: 0,
|
LEFTOF: 0,
|
||||||
RIGHTOF: 1,
|
RIGHTOF: 1,
|
||||||
OVER: 2
|
OVER: 2
|
||||||
}
|
};
|
||||||
|
|
||||||
export const addNote = function (actor, placement, message) {
|
export const addNote = function(actor, placement, message) {
|
||||||
const note = { actor: actor, placement: placement, message: message }
|
const note = { actor: actor, placement: placement, message: message };
|
||||||
|
|
||||||
// Coerce actor into a [to, from, ...] array
|
// Coerce actor into a [to, from, ...] array
|
||||||
const actors = [].concat(actor, actor)
|
const actors = [].concat(actor, actor);
|
||||||
|
|
||||||
notes.push(note)
|
notes.push(note);
|
||||||
messages.push({ from: actors[0], to: actors[1], message: message, type: LINETYPE.NOTE, placement: placement })
|
messages.push({
|
||||||
}
|
from: actors[0],
|
||||||
|
to: actors[1],
|
||||||
|
message: message,
|
||||||
|
type: LINETYPE.NOTE,
|
||||||
|
placement: placement
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const setTitle = function (titleText) {
|
export const setTitle = function(titleText) {
|
||||||
title = titleText
|
title = titleText;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const apply = function (param) {
|
export const apply = function(param) {
|
||||||
if (param instanceof Array) {
|
if (param instanceof Array) {
|
||||||
param.forEach(function (item) {
|
param.forEach(function(item) {
|
||||||
apply(item)
|
apply(item);
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
switch (param.type) {
|
switch (param.type) {
|
||||||
case 'addActor':
|
case 'addActor':
|
||||||
addActor(param.actor, param.actor, param.description)
|
addActor(param.actor, param.actor, param.description);
|
||||||
break
|
break;
|
||||||
case 'activeStart':
|
case 'activeStart':
|
||||||
addSignal(param.actor, undefined, undefined, param.signalType)
|
addSignal(param.actor, undefined, undefined, param.signalType);
|
||||||
break
|
break;
|
||||||
case 'activeEnd':
|
case 'activeEnd':
|
||||||
addSignal(param.actor, undefined, undefined, param.signalType)
|
addSignal(param.actor, undefined, undefined, param.signalType);
|
||||||
break
|
break;
|
||||||
case 'addNote':
|
case 'addNote':
|
||||||
addNote(param.actor, param.placement, param.text)
|
addNote(param.actor, param.placement, param.text);
|
||||||
break
|
break;
|
||||||
case 'addMessage':
|
case 'addMessage':
|
||||||
addSignal(param.from, param.to, param.msg, param.signalType)
|
addSignal(param.from, param.to, param.msg, param.signalType);
|
||||||
break
|
break;
|
||||||
case 'loopStart':
|
case 'loopStart':
|
||||||
addSignal(undefined, undefined, param.loopText, param.signalType)
|
addSignal(undefined, undefined, param.loopText, param.signalType);
|
||||||
break
|
break;
|
||||||
case 'loopEnd':
|
case 'loopEnd':
|
||||||
addSignal(undefined, undefined, undefined, param.signalType)
|
addSignal(undefined, undefined, undefined, param.signalType);
|
||||||
break
|
break;
|
||||||
case 'rectStart':
|
case 'rectStart':
|
||||||
addSignal(undefined, undefined, param.color, param.signalType)
|
addSignal(undefined, undefined, param.color, param.signalType);
|
||||||
break
|
break;
|
||||||
case 'rectEnd':
|
case 'rectEnd':
|
||||||
addSignal(undefined, undefined, undefined, param.signalType)
|
addSignal(undefined, undefined, undefined, param.signalType);
|
||||||
break
|
break;
|
||||||
case 'optStart':
|
case 'optStart':
|
||||||
addSignal(undefined, undefined, param.optText, param.signalType)
|
addSignal(undefined, undefined, param.optText, param.signalType);
|
||||||
break
|
break;
|
||||||
case 'optEnd':
|
case 'optEnd':
|
||||||
addSignal(undefined, undefined, undefined, param.signalType)
|
addSignal(undefined, undefined, undefined, param.signalType);
|
||||||
break
|
break;
|
||||||
case 'altStart':
|
case 'altStart':
|
||||||
addSignal(undefined, undefined, param.altText, param.signalType)
|
addSignal(undefined, undefined, param.altText, param.signalType);
|
||||||
break
|
break;
|
||||||
case 'else':
|
case 'else':
|
||||||
addSignal(undefined, undefined, param.altText, param.signalType)
|
addSignal(undefined, undefined, param.altText, param.signalType);
|
||||||
break
|
break;
|
||||||
case 'altEnd':
|
case 'altEnd':
|
||||||
addSignal(undefined, undefined, undefined, param.signalType)
|
addSignal(undefined, undefined, undefined, param.signalType);
|
||||||
break
|
break;
|
||||||
case 'setTitle':
|
case 'setTitle':
|
||||||
setTitle(param.text)
|
setTitle(param.text);
|
||||||
break
|
break;
|
||||||
case 'parStart':
|
case 'parStart':
|
||||||
addSignal(undefined, undefined, param.parText, param.signalType)
|
addSignal(undefined, undefined, param.parText, param.signalType);
|
||||||
break
|
break;
|
||||||
case 'and':
|
case 'and':
|
||||||
addSignal(undefined, undefined, param.parText, param.signalType)
|
addSignal(undefined, undefined, param.parText, param.signalType);
|
||||||
break
|
break;
|
||||||
case 'parEnd':
|
case 'parEnd':
|
||||||
addSignal(undefined, undefined, undefined, param.signalType)
|
addSignal(undefined, undefined, undefined, param.signalType);
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
addActor,
|
addActor,
|
||||||
@ -177,4 +185,4 @@ export default {
|
|||||||
addNote,
|
addNote,
|
||||||
setTitle,
|
setTitle,
|
||||||
apply
|
apply
|
||||||
}
|
};
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,14 +1,13 @@
|
|||||||
import * as d3 from 'd3'
|
import * as d3 from 'd3';
|
||||||
|
|
||||||
import svgDraw from './svgDraw'
|
import svgDraw from './svgDraw';
|
||||||
import { logger } from '../../logger'
|
import { logger } from '../../logger';
|
||||||
import { parser } from './parser/sequenceDiagram'
|
import { parser } from './parser/sequenceDiagram';
|
||||||
import sequenceDb from './sequenceDb'
|
import sequenceDb from './sequenceDb';
|
||||||
|
|
||||||
parser.yy = sequenceDb
|
parser.yy = sequenceDb;
|
||||||
|
|
||||||
const conf = {
|
const conf = {
|
||||||
|
|
||||||
diagramMarginX: 50,
|
diagramMarginX: 50,
|
||||||
diagramMarginY: 30,
|
diagramMarginY: 30,
|
||||||
// Margin between actors
|
// Margin between actors
|
||||||
@ -38,7 +37,7 @@ const conf = {
|
|||||||
textPlacement: 'tspan',
|
textPlacement: 'tspan',
|
||||||
|
|
||||||
showSequenceNumbers: false
|
showSequenceNumbers: false
|
||||||
}
|
};
|
||||||
|
|
||||||
export const bounds = {
|
export const bounds = {
|
||||||
data: {
|
data: {
|
||||||
@ -51,69 +50,69 @@ export const bounds = {
|
|||||||
|
|
||||||
sequenceItems: [],
|
sequenceItems: [],
|
||||||
activations: [],
|
activations: [],
|
||||||
init: function () {
|
init: function() {
|
||||||
this.sequenceItems = []
|
this.sequenceItems = [];
|
||||||
this.activations = []
|
this.activations = [];
|
||||||
this.data = {
|
this.data = {
|
||||||
startx: undefined,
|
startx: undefined,
|
||||||
stopx: undefined,
|
stopx: undefined,
|
||||||
starty: undefined,
|
starty: undefined,
|
||||||
stopy: undefined
|
stopy: undefined
|
||||||
}
|
};
|
||||||
this.verticalPos = 0
|
this.verticalPos = 0;
|
||||||
},
|
},
|
||||||
updateVal: function (obj, key, val, fun) {
|
updateVal: function(obj, key, val, fun) {
|
||||||
if (typeof obj[key] === 'undefined') {
|
if (typeof obj[key] === 'undefined') {
|
||||||
obj[key] = val
|
obj[key] = val;
|
||||||
} else {
|
} else {
|
||||||
obj[key] = fun(val, obj[key])
|
obj[key] = fun(val, obj[key]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateBounds: function (startx, starty, stopx, stopy) {
|
updateBounds: function(startx, starty, stopx, stopy) {
|
||||||
const _self = this
|
const _self = this;
|
||||||
let cnt = 0
|
let cnt = 0;
|
||||||
function updateFn (type) {
|
function updateFn(type) {
|
||||||
return function updateItemBounds (item) {
|
return function updateItemBounds(item) {
|
||||||
cnt++
|
cnt++;
|
||||||
// The loop sequenceItems is a stack so the biggest margins in the beginning of the sequenceItems
|
// The loop sequenceItems is a stack so the biggest margins in the beginning of the sequenceItems
|
||||||
const n = _self.sequenceItems.length - cnt + 1
|
const n = _self.sequenceItems.length - cnt + 1;
|
||||||
|
|
||||||
_self.updateVal(item, 'starty', starty - n * conf.boxMargin, Math.min)
|
_self.updateVal(item, 'starty', starty - n * conf.boxMargin, Math.min);
|
||||||
_self.updateVal(item, 'stopy', stopy + n * conf.boxMargin, Math.max)
|
_self.updateVal(item, 'stopy', stopy + n * conf.boxMargin, Math.max);
|
||||||
|
|
||||||
_self.updateVal(bounds.data, 'startx', startx - n * conf.boxMargin, Math.min)
|
_self.updateVal(bounds.data, 'startx', startx - n * conf.boxMargin, Math.min);
|
||||||
_self.updateVal(bounds.data, 'stopx', stopx + n * conf.boxMargin, Math.max)
|
_self.updateVal(bounds.data, 'stopx', stopx + n * conf.boxMargin, Math.max);
|
||||||
|
|
||||||
if (!(type === 'activation')) {
|
if (!(type === 'activation')) {
|
||||||
_self.updateVal(item, 'startx', startx - n * conf.boxMargin, Math.min)
|
_self.updateVal(item, 'startx', startx - n * conf.boxMargin, Math.min);
|
||||||
_self.updateVal(item, 'stopx', stopx + n * conf.boxMargin, Math.max)
|
_self.updateVal(item, 'stopx', stopx + n * conf.boxMargin, Math.max);
|
||||||
|
|
||||||
_self.updateVal(bounds.data, 'starty', starty - n * conf.boxMargin, Math.min)
|
_self.updateVal(bounds.data, 'starty', starty - n * conf.boxMargin, Math.min);
|
||||||
_self.updateVal(bounds.data, 'stopy', stopy + n * conf.boxMargin, Math.max)
|
_self.updateVal(bounds.data, 'stopy', stopy + n * conf.boxMargin, Math.max);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sequenceItems.forEach(updateFn())
|
this.sequenceItems.forEach(updateFn());
|
||||||
this.activations.forEach(updateFn('activation'))
|
this.activations.forEach(updateFn('activation'));
|
||||||
},
|
},
|
||||||
insert: function (startx, starty, stopx, stopy) {
|
insert: function(startx, starty, stopx, stopy) {
|
||||||
const _startx = Math.min(startx, stopx)
|
const _startx = Math.min(startx, stopx);
|
||||||
const _stopx = Math.max(startx, stopx)
|
const _stopx = Math.max(startx, stopx);
|
||||||
const _starty = Math.min(starty, stopy)
|
const _starty = Math.min(starty, stopy);
|
||||||
const _stopy = Math.max(starty, stopy)
|
const _stopy = Math.max(starty, stopy);
|
||||||
|
|
||||||
this.updateVal(bounds.data, 'startx', _startx, Math.min)
|
this.updateVal(bounds.data, 'startx', _startx, Math.min);
|
||||||
this.updateVal(bounds.data, 'starty', _starty, Math.min)
|
this.updateVal(bounds.data, 'starty', _starty, Math.min);
|
||||||
this.updateVal(bounds.data, 'stopx', _stopx, Math.max)
|
this.updateVal(bounds.data, 'stopx', _stopx, Math.max);
|
||||||
this.updateVal(bounds.data, 'stopy', _stopy, Math.max)
|
this.updateVal(bounds.data, 'stopy', _stopy, Math.max);
|
||||||
|
|
||||||
this.updateBounds(_startx, _starty, _stopx, _stopy)
|
this.updateBounds(_startx, _starty, _stopx, _stopy);
|
||||||
},
|
},
|
||||||
newActivation: function (message, diagram) {
|
newActivation: function(message, diagram) {
|
||||||
const actorRect = parser.yy.getActors()[message.from.actor]
|
const actorRect = parser.yy.getActors()[message.from.actor];
|
||||||
const stackedSize = actorActivations(message.from.actor).length
|
const stackedSize = actorActivations(message.from.actor).length;
|
||||||
const x = actorRect.x + conf.width / 2 + (stackedSize - 1) * conf.activationWidth / 2
|
const x = actorRect.x + conf.width / 2 + ((stackedSize - 1) * conf.activationWidth) / 2;
|
||||||
this.activations.push({
|
this.activations.push({
|
||||||
startx: x,
|
startx: x,
|
||||||
starty: this.verticalPos + 2,
|
starty: this.verticalPos + 2,
|
||||||
@ -121,59 +120,68 @@ export const bounds = {
|
|||||||
stopy: undefined,
|
stopy: undefined,
|
||||||
actor: message.from.actor,
|
actor: message.from.actor,
|
||||||
anchored: svgDraw.anchorElement(diagram)
|
anchored: svgDraw.anchorElement(diagram)
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
endActivation: function (message) {
|
endActivation: function(message) {
|
||||||
// find most recent activation for given actor
|
// find most recent activation for given actor
|
||||||
const lastActorActivationIdx = this.activations
|
const lastActorActivationIdx = this.activations
|
||||||
.map(function (activation) { return activation.actor })
|
.map(function(activation) {
|
||||||
.lastIndexOf(message.from.actor)
|
return activation.actor;
|
||||||
const activation = this.activations.splice(lastActorActivationIdx, 1)[0]
|
})
|
||||||
return activation
|
.lastIndexOf(message.from.actor);
|
||||||
|
const activation = this.activations.splice(lastActorActivationIdx, 1)[0];
|
||||||
|
return activation;
|
||||||
},
|
},
|
||||||
newLoop: function (title, fill) {
|
newLoop: function(title, fill) {
|
||||||
this.sequenceItems.push({ startx: undefined, starty: this.verticalPos, stopx: undefined, stopy: undefined, title: title, fill: fill })
|
this.sequenceItems.push({
|
||||||
|
startx: undefined,
|
||||||
|
starty: this.verticalPos,
|
||||||
|
stopx: undefined,
|
||||||
|
stopy: undefined,
|
||||||
|
title: title,
|
||||||
|
fill: fill
|
||||||
|
});
|
||||||
},
|
},
|
||||||
endLoop: function () {
|
endLoop: function() {
|
||||||
const loop = this.sequenceItems.pop()
|
const loop = this.sequenceItems.pop();
|
||||||
return loop
|
return loop;
|
||||||
},
|
},
|
||||||
addSectionToLoop: function (message) {
|
addSectionToLoop: function(message) {
|
||||||
const loop = this.sequenceItems.pop()
|
const loop = this.sequenceItems.pop();
|
||||||
loop.sections = loop.sections || []
|
loop.sections = loop.sections || [];
|
||||||
loop.sectionTitles = loop.sectionTitles || []
|
loop.sectionTitles = loop.sectionTitles || [];
|
||||||
loop.sections.push(bounds.getVerticalPos())
|
loop.sections.push(bounds.getVerticalPos());
|
||||||
loop.sectionTitles.push(message)
|
loop.sectionTitles.push(message);
|
||||||
this.sequenceItems.push(loop)
|
this.sequenceItems.push(loop);
|
||||||
},
|
},
|
||||||
bumpVerticalPos: function (bump) {
|
bumpVerticalPos: function(bump) {
|
||||||
this.verticalPos = this.verticalPos + bump
|
this.verticalPos = this.verticalPos + bump;
|
||||||
this.data.stopy = this.verticalPos
|
this.data.stopy = this.verticalPos;
|
||||||
},
|
},
|
||||||
getVerticalPos: function () {
|
getVerticalPos: function() {
|
||||||
return this.verticalPos
|
return this.verticalPos;
|
||||||
},
|
},
|
||||||
getBounds: function () {
|
getBounds: function() {
|
||||||
return this.data
|
return this.data;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const _drawLongText = (text, x, y, g, width) => {
|
const _drawLongText = (text, x, y, g, width) => {
|
||||||
let textHeight = 0
|
let textHeight = 0;
|
||||||
const lines = text.split(/<br\/?>/ig)
|
const lines = text.split(/<br\/?>/gi);
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const textObj = svgDraw.getTextObj()
|
const textObj = svgDraw.getTextObj();
|
||||||
textObj.x = x
|
textObj.x = x;
|
||||||
textObj.y = y + textHeight
|
textObj.y = y + textHeight;
|
||||||
textObj.textMargin = conf.noteMargin
|
textObj.textMargin = conf.noteMargin;
|
||||||
textObj.dy = '1em'
|
textObj.dy = '1em';
|
||||||
textObj.text = line
|
textObj.text = line;
|
||||||
textObj.class = 'noteText'
|
textObj.class = 'noteText';
|
||||||
const textElem = svgDraw.drawText(g, textObj, width)
|
const textElem = svgDraw.drawText(g, textObj, width);
|
||||||
textHeight += (textElem._groups || textElem)[0][0].getBBox().height
|
textHeight += (textElem._groups || textElem)[0][0].getBBox().height;
|
||||||
}
|
}
|
||||||
return textHeight
|
return textHeight;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws an actor in the diagram with the attaced line
|
* Draws an actor in the diagram with the attaced line
|
||||||
@ -181,22 +189,33 @@ const _drawLongText = (text, x, y, g, width) => {
|
|||||||
* @param pos The position if the actor in the liost of actors
|
* @param pos The position if the actor in the liost of actors
|
||||||
* @param description The text in the box
|
* @param description The text in the box
|
||||||
*/
|
*/
|
||||||
const drawNote = function (elem, startx, verticalPos, msg, forceWidth) {
|
const drawNote = function(elem, startx, verticalPos, msg, forceWidth) {
|
||||||
const rect = svgDraw.getNoteRect()
|
const rect = svgDraw.getNoteRect();
|
||||||
rect.x = startx
|
rect.x = startx;
|
||||||
rect.y = verticalPos
|
rect.y = verticalPos;
|
||||||
rect.width = forceWidth || conf.width
|
rect.width = forceWidth || conf.width;
|
||||||
rect.class = 'note'
|
rect.class = 'note';
|
||||||
|
|
||||||
let g = elem.append('g')
|
let g = elem.append('g');
|
||||||
const rectElem = svgDraw.drawRect(g, rect)
|
const rectElem = svgDraw.drawRect(g, rect);
|
||||||
|
|
||||||
const textHeight = _drawLongText(msg.message, startx - 4, verticalPos + 24, g, rect.width - conf.noteMargin)
|
const textHeight = _drawLongText(
|
||||||
|
msg.message,
|
||||||
|
startx - 4,
|
||||||
|
verticalPos + 24,
|
||||||
|
g,
|
||||||
|
rect.width - conf.noteMargin
|
||||||
|
);
|
||||||
|
|
||||||
bounds.insert(startx, verticalPos, startx + rect.width, verticalPos + 2 * conf.noteMargin + textHeight)
|
bounds.insert(
|
||||||
rectElem.attr('height', textHeight + 2 * conf.noteMargin)
|
startx,
|
||||||
bounds.bumpVerticalPos(textHeight + 2 * conf.noteMargin)
|
verticalPos,
|
||||||
}
|
startx + rect.width,
|
||||||
|
verticalPos + 2 * conf.noteMargin + textHeight
|
||||||
|
);
|
||||||
|
rectElem.attr('height', textHeight + 2 * conf.noteMargin);
|
||||||
|
bounds.bumpVerticalPos(textHeight + 2 * conf.noteMargin);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a message
|
* Draws a message
|
||||||
@ -207,70 +226,104 @@ const drawNote = function (elem, startx, verticalPos, msg, forceWidth) {
|
|||||||
* @param txtCenter
|
* @param txtCenter
|
||||||
* @param msg
|
* @param msg
|
||||||
*/
|
*/
|
||||||
const drawMessage = function (elem, startx, stopx, verticalPos, msg, sequenceIndex) {
|
const drawMessage = function(elem, startx, stopx, verticalPos, msg, sequenceIndex) {
|
||||||
const g = elem.append('g')
|
const g = elem.append('g');
|
||||||
const txtCenter = startx + (stopx - startx) / 2
|
const txtCenter = startx + (stopx - startx) / 2;
|
||||||
|
|
||||||
const textElem = g.append('text') // text label for the x axis
|
const textElem = g
|
||||||
|
.append('text') // text label for the x axis
|
||||||
.attr('x', txtCenter)
|
.attr('x', txtCenter)
|
||||||
.attr('y', verticalPos - 7)
|
.attr('y', verticalPos - 7)
|
||||||
.style('text-anchor', 'middle')
|
.style('text-anchor', 'middle')
|
||||||
.attr('class', 'messageText')
|
.attr('class', 'messageText')
|
||||||
.text(msg.message)
|
.text(msg.message);
|
||||||
|
|
||||||
let textWidth = (textElem._groups || textElem)[0][0].getBBox().width
|
let textWidth = (textElem._groups || textElem)[0][0].getBBox().width;
|
||||||
|
|
||||||
let line
|
let line;
|
||||||
if (startx === stopx) {
|
if (startx === stopx) {
|
||||||
if (conf.rightAngles) {
|
if (conf.rightAngles) {
|
||||||
line = g.append('path').attr('d', `M ${startx},${verticalPos} H ${startx + (conf.width / 2)} V ${verticalPos + 25} H ${startx}`)
|
line = g
|
||||||
|
.append('path')
|
||||||
|
.attr(
|
||||||
|
'd',
|
||||||
|
`M ${startx},${verticalPos} H ${startx + conf.width / 2} V ${verticalPos +
|
||||||
|
25} H ${startx}`
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
line = g.append('path')
|
line = g
|
||||||
.attr('d', 'M ' + startx + ',' + verticalPos + ' C ' + (startx + 60) + ',' + (verticalPos - 10) + ' ' + (startx + 60) + ',' +
|
.append('path')
|
||||||
(verticalPos + 30) + ' ' + startx + ',' + (verticalPos + 20))
|
.attr(
|
||||||
|
'd',
|
||||||
|
'M ' +
|
||||||
|
startx +
|
||||||
|
',' +
|
||||||
|
verticalPos +
|
||||||
|
' C ' +
|
||||||
|
(startx + 60) +
|
||||||
|
',' +
|
||||||
|
(verticalPos - 10) +
|
||||||
|
' ' +
|
||||||
|
(startx + 60) +
|
||||||
|
',' +
|
||||||
|
(verticalPos + 30) +
|
||||||
|
' ' +
|
||||||
|
startx +
|
||||||
|
',' +
|
||||||
|
(verticalPos + 20)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bounds.bumpVerticalPos(30)
|
bounds.bumpVerticalPos(30);
|
||||||
const dx = Math.max(textWidth / 2, 100)
|
const dx = Math.max(textWidth / 2, 100);
|
||||||
bounds.insert(startx - dx, bounds.getVerticalPos() - 10, stopx + dx, bounds.getVerticalPos())
|
bounds.insert(startx - dx, bounds.getVerticalPos() - 10, stopx + dx, bounds.getVerticalPos());
|
||||||
} else {
|
} else {
|
||||||
line = g.append('line')
|
line = g.append('line');
|
||||||
line.attr('x1', startx)
|
line.attr('x1', startx);
|
||||||
line.attr('y1', verticalPos)
|
line.attr('y1', verticalPos);
|
||||||
line.attr('x2', stopx)
|
line.attr('x2', stopx);
|
||||||
line.attr('y2', verticalPos)
|
line.attr('y2', verticalPos);
|
||||||
bounds.insert(startx, bounds.getVerticalPos() - 10, stopx, bounds.getVerticalPos())
|
bounds.insert(startx, bounds.getVerticalPos() - 10, stopx, bounds.getVerticalPos());
|
||||||
}
|
}
|
||||||
// Make an SVG Container
|
// Make an SVG Container
|
||||||
// Draw the line
|
// Draw the line
|
||||||
if (msg.type === parser.yy.LINETYPE.DOTTED || msg.type === parser.yy.LINETYPE.DOTTED_CROSS || msg.type === parser.yy.LINETYPE.DOTTED_OPEN) {
|
if (
|
||||||
line.style('stroke-dasharray', ('3, 3'))
|
msg.type === parser.yy.LINETYPE.DOTTED ||
|
||||||
line.attr('class', 'messageLine1')
|
msg.type === parser.yy.LINETYPE.DOTTED_CROSS ||
|
||||||
|
msg.type === parser.yy.LINETYPE.DOTTED_OPEN
|
||||||
|
) {
|
||||||
|
line.style('stroke-dasharray', '3, 3');
|
||||||
|
line.attr('class', 'messageLine1');
|
||||||
} else {
|
} else {
|
||||||
line.attr('class', 'messageLine0')
|
line.attr('class', 'messageLine0');
|
||||||
}
|
}
|
||||||
|
|
||||||
let url = ''
|
let url = '';
|
||||||
if (conf.arrowMarkerAbsolute) {
|
if (conf.arrowMarkerAbsolute) {
|
||||||
url = window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search
|
url =
|
||||||
url = url.replace(/\(/g, '\\(')
|
window.location.protocol +
|
||||||
url = url.replace(/\)/g, '\\)')
|
'//' +
|
||||||
|
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-width', 2);
|
||||||
line.attr('stroke', 'black')
|
line.attr('stroke', 'black');
|
||||||
line.style('fill', 'none') // remove any fill colour
|
line.style('fill', 'none'); // remove any fill colour
|
||||||
if (msg.type === parser.yy.LINETYPE.SOLID || msg.type === parser.yy.LINETYPE.DOTTED) {
|
if (msg.type === parser.yy.LINETYPE.SOLID || msg.type === parser.yy.LINETYPE.DOTTED) {
|
||||||
line.attr('marker-end', 'url(' + url + '#arrowhead)')
|
line.attr('marker-end', 'url(' + url + '#arrowhead)');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.type === parser.yy.LINETYPE.SOLID_CROSS || msg.type === parser.yy.LINETYPE.DOTTED_CROSS) {
|
if (msg.type === parser.yy.LINETYPE.SOLID_CROSS || msg.type === parser.yy.LINETYPE.DOTTED_CROSS) {
|
||||||
line.attr('marker-end', 'url(' + url + '#crosshead)')
|
line.attr('marker-end', 'url(' + url + '#crosshead)');
|
||||||
}
|
}
|
||||||
|
|
||||||
// add node number
|
// add node number
|
||||||
if (conf.showSequenceNumbers) {
|
if (conf.showSequenceNumbers) {
|
||||||
line.attr('marker-start', 'url(' + url + '#sequencenumber)')
|
line.attr('marker-start', 'url(' + url + '#sequencenumber)');
|
||||||
g.append('text')
|
g.append('text')
|
||||||
.attr('x', startx)
|
.attr('x', startx)
|
||||||
.attr('y', verticalPos + 4)
|
.attr('y', verticalPos + 4)
|
||||||
@ -279,263 +332,306 @@ const drawMessage = function (elem, startx, stopx, verticalPos, msg, sequenceInd
|
|||||||
.attr('text-anchor', 'middle')
|
.attr('text-anchor', 'middle')
|
||||||
.attr('textLength', '16px')
|
.attr('textLength', '16px')
|
||||||
.attr('class', 'sequenceNumber')
|
.attr('class', 'sequenceNumber')
|
||||||
.text(sequenceIndex)
|
.text(sequenceIndex);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const drawActors = function (diagram, actors, actorKeys, verticalPos) {
|
export const drawActors = function(diagram, actors, actorKeys, verticalPos) {
|
||||||
// Draw the actors
|
// Draw the actors
|
||||||
for (let i = 0; i < actorKeys.length; i++) {
|
for (let i = 0; i < actorKeys.length; i++) {
|
||||||
const key = actorKeys[i]
|
const key = actorKeys[i];
|
||||||
|
|
||||||
// Add some rendering data to the object
|
// Add some rendering data to the object
|
||||||
actors[key].x = i * conf.actorMargin + i * conf.width
|
actors[key].x = i * conf.actorMargin + i * conf.width;
|
||||||
actors[key].y = verticalPos
|
actors[key].y = verticalPos;
|
||||||
actors[key].width = conf.diagramMarginX
|
actors[key].width = conf.diagramMarginX;
|
||||||
actors[key].height = conf.diagramMarginY
|
actors[key].height = conf.diagramMarginY;
|
||||||
|
|
||||||
// Draw the box with the attached line
|
// Draw the box with the attached line
|
||||||
svgDraw.drawActor(diagram, actors[key].x, verticalPos, actors[key].description, conf)
|
svgDraw.drawActor(diagram, actors[key].x, verticalPos, actors[key].description, conf);
|
||||||
bounds.insert(actors[key].x, verticalPos, actors[key].x + conf.width, conf.height)
|
bounds.insert(actors[key].x, verticalPos, actors[key].x + conf.width, conf.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a margin between the actor boxes and the first arrow
|
// Add a margin between the actor boxes and the first arrow
|
||||||
bounds.bumpVerticalPos(conf.height)
|
bounds.bumpVerticalPos(conf.height);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const setConf = function (cnf) {
|
export const setConf = function(cnf) {
|
||||||
const keys = Object.keys(cnf)
|
const keys = Object.keys(cnf);
|
||||||
|
|
||||||
keys.forEach(function (key) {
|
keys.forEach(function(key) {
|
||||||
conf[key] = cnf[key]
|
conf[key] = cnf[key];
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
const actorActivations = function (actor) {
|
const actorActivations = function(actor) {
|
||||||
return bounds.activations.filter(function (activation) {
|
return bounds.activations.filter(function(activation) {
|
||||||
return activation.actor === actor
|
return activation.actor === actor;
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
const actorFlowVerticaBounds = function (actor) {
|
const actorFlowVerticaBounds = function(actor) {
|
||||||
// handle multiple stacked activations for same actor
|
// handle multiple stacked activations for same actor
|
||||||
const actors = parser.yy.getActors()
|
const actors = parser.yy.getActors();
|
||||||
const activations = actorActivations(actor)
|
const activations = actorActivations(actor);
|
||||||
|
|
||||||
const left = activations.reduce(function (acc, activation) { return Math.min(acc, activation.startx) }, actors[actor].x + conf.width / 2)
|
const left = activations.reduce(function(acc, activation) {
|
||||||
const right = activations.reduce(function (acc, activation) { return Math.max(acc, activation.stopx) }, actors[actor].x + conf.width / 2)
|
return Math.min(acc, activation.startx);
|
||||||
return [left, right]
|
}, actors[actor].x + conf.width / 2);
|
||||||
}
|
const 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.
|
* Draws a flowchart in the tag with id: id based on the graph definition in text.
|
||||||
* @param text
|
* @param text
|
||||||
* @param id
|
* @param id
|
||||||
*/
|
*/
|
||||||
export const draw = function (text, id) {
|
export const draw = function(text, id) {
|
||||||
parser.yy.clear()
|
parser.yy.clear();
|
||||||
parser.parse(text + '\n')
|
parser.parse(text + '\n');
|
||||||
|
|
||||||
bounds.init()
|
bounds.init();
|
||||||
const diagram = d3.select(`[id="${id}"]`)
|
const diagram = d3.select(`[id="${id}"]`);
|
||||||
|
|
||||||
let startx
|
let startx;
|
||||||
let stopx
|
let stopx;
|
||||||
let forceWidth
|
let forceWidth;
|
||||||
|
|
||||||
// Fetch data from the parsing
|
// Fetch data from the parsing
|
||||||
const actors = parser.yy.getActors()
|
const actors = parser.yy.getActors();
|
||||||
const actorKeys = parser.yy.getActorKeys()
|
const actorKeys = parser.yy.getActorKeys();
|
||||||
const messages = parser.yy.getMessages()
|
const messages = parser.yy.getMessages();
|
||||||
const title = parser.yy.getTitle()
|
const title = parser.yy.getTitle();
|
||||||
drawActors(diagram, actors, actorKeys, 0)
|
drawActors(diagram, actors, actorKeys, 0);
|
||||||
|
|
||||||
// The arrow head definition is attached to the svg once
|
// The arrow head definition is attached to the svg once
|
||||||
svgDraw.insertArrowHead(diagram)
|
svgDraw.insertArrowHead(diagram);
|
||||||
svgDraw.insertArrowCrossHead(diagram)
|
svgDraw.insertArrowCrossHead(diagram);
|
||||||
svgDraw.insertSequenceNumber(diagram)
|
svgDraw.insertSequenceNumber(diagram);
|
||||||
|
|
||||||
function activeEnd (msg, verticalPos) {
|
function activeEnd(msg, verticalPos) {
|
||||||
const activationData = bounds.endActivation(msg)
|
const activationData = bounds.endActivation(msg);
|
||||||
if (activationData.starty + 18 > verticalPos) {
|
if (activationData.starty + 18 > verticalPos) {
|
||||||
activationData.starty = verticalPos - 6
|
activationData.starty = verticalPos - 6;
|
||||||
verticalPos += 12
|
verticalPos += 12;
|
||||||
}
|
}
|
||||||
svgDraw.drawActivation(diagram, activationData, verticalPos, conf, actorActivations(msg.from.actor).length)
|
svgDraw.drawActivation(
|
||||||
|
diagram,
|
||||||
|
activationData,
|
||||||
|
verticalPos,
|
||||||
|
conf,
|
||||||
|
actorActivations(msg.from.actor).length
|
||||||
|
);
|
||||||
|
|
||||||
bounds.insert(activationData.startx, verticalPos - 10, activationData.stopx, verticalPos)
|
bounds.insert(activationData.startx, verticalPos - 10, activationData.stopx, verticalPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// const lastMsg
|
// const lastMsg
|
||||||
|
|
||||||
// Draw the messages/signals
|
// Draw the messages/signals
|
||||||
let sequenceIndex = 1
|
let sequenceIndex = 1;
|
||||||
messages.forEach(function (msg) {
|
messages.forEach(function(msg) {
|
||||||
let loopData
|
let loopData;
|
||||||
switch (msg.type) {
|
switch (msg.type) {
|
||||||
case parser.yy.LINETYPE.NOTE:
|
case parser.yy.LINETYPE.NOTE:
|
||||||
bounds.bumpVerticalPos(conf.boxMargin)
|
bounds.bumpVerticalPos(conf.boxMargin);
|
||||||
|
|
||||||
startx = actors[msg.from].x
|
startx = actors[msg.from].x;
|
||||||
stopx = actors[msg.to].x
|
stopx = actors[msg.to].x;
|
||||||
|
|
||||||
if (msg.placement === parser.yy.PLACEMENT.RIGHTOF) {
|
if (msg.placement === parser.yy.PLACEMENT.RIGHTOF) {
|
||||||
drawNote(diagram, startx + (conf.width + conf.actorMargin) / 2, bounds.getVerticalPos(), msg)
|
drawNote(
|
||||||
|
diagram,
|
||||||
|
startx + (conf.width + conf.actorMargin) / 2,
|
||||||
|
bounds.getVerticalPos(),
|
||||||
|
msg
|
||||||
|
);
|
||||||
} else if (msg.placement === parser.yy.PLACEMENT.LEFTOF) {
|
} else if (msg.placement === parser.yy.PLACEMENT.LEFTOF) {
|
||||||
drawNote(diagram, startx - (conf.width + conf.actorMargin) / 2, bounds.getVerticalPos(), msg)
|
drawNote(
|
||||||
|
diagram,
|
||||||
|
startx - (conf.width + conf.actorMargin) / 2,
|
||||||
|
bounds.getVerticalPos(),
|
||||||
|
msg
|
||||||
|
);
|
||||||
} else if (msg.to === msg.from) {
|
} else if (msg.to === msg.from) {
|
||||||
// Single-actor over
|
// Single-actor over
|
||||||
drawNote(diagram, startx, bounds.getVerticalPos(), msg)
|
drawNote(diagram, startx, bounds.getVerticalPos(), msg);
|
||||||
} else {
|
} else {
|
||||||
// Multi-actor over
|
// Multi-actor over
|
||||||
forceWidth = Math.abs(startx - stopx) + conf.actorMargin
|
forceWidth = Math.abs(startx - stopx) + conf.actorMargin;
|
||||||
drawNote(diagram, (startx + stopx + conf.width - forceWidth) / 2, bounds.getVerticalPos(), msg,
|
drawNote(
|
||||||
forceWidth)
|
diagram,
|
||||||
|
(startx + stopx + conf.width - forceWidth) / 2,
|
||||||
|
bounds.getVerticalPos(),
|
||||||
|
msg,
|
||||||
|
forceWidth
|
||||||
|
);
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
case parser.yy.LINETYPE.ACTIVE_START:
|
case parser.yy.LINETYPE.ACTIVE_START:
|
||||||
bounds.newActivation(msg, diagram)
|
bounds.newActivation(msg, diagram);
|
||||||
break
|
break;
|
||||||
case parser.yy.LINETYPE.ACTIVE_END:
|
case parser.yy.LINETYPE.ACTIVE_END:
|
||||||
activeEnd(msg, bounds.getVerticalPos())
|
activeEnd(msg, bounds.getVerticalPos());
|
||||||
break
|
break;
|
||||||
case parser.yy.LINETYPE.LOOP_START:
|
case parser.yy.LINETYPE.LOOP_START:
|
||||||
bounds.bumpVerticalPos(conf.boxMargin)
|
bounds.bumpVerticalPos(conf.boxMargin);
|
||||||
bounds.newLoop(msg.message)
|
bounds.newLoop(msg.message);
|
||||||
bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin)
|
bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin);
|
||||||
break
|
break;
|
||||||
case parser.yy.LINETYPE.LOOP_END:
|
case parser.yy.LINETYPE.LOOP_END:
|
||||||
loopData = bounds.endLoop()
|
loopData = bounds.endLoop();
|
||||||
|
|
||||||
svgDraw.drawLoop(diagram, loopData, 'loop', conf)
|
svgDraw.drawLoop(diagram, loopData, 'loop', conf);
|
||||||
bounds.bumpVerticalPos(conf.boxMargin)
|
bounds.bumpVerticalPos(conf.boxMargin);
|
||||||
break
|
break;
|
||||||
case parser.yy.LINETYPE.RECT_START:
|
case parser.yy.LINETYPE.RECT_START:
|
||||||
bounds.bumpVerticalPos(conf.boxMargin)
|
bounds.bumpVerticalPos(conf.boxMargin);
|
||||||
bounds.newLoop(undefined, msg.message)
|
bounds.newLoop(undefined, msg.message);
|
||||||
bounds.bumpVerticalPos(conf.boxMargin)
|
bounds.bumpVerticalPos(conf.boxMargin);
|
||||||
break
|
break;
|
||||||
case parser.yy.LINETYPE.RECT_END:
|
case parser.yy.LINETYPE.RECT_END:
|
||||||
const rectData = bounds.endLoop()
|
const rectData = bounds.endLoop();
|
||||||
svgDraw.drawBackgroundRect(diagram, rectData)
|
svgDraw.drawBackgroundRect(diagram, rectData);
|
||||||
bounds.bumpVerticalPos(conf.boxMargin)
|
bounds.bumpVerticalPos(conf.boxMargin);
|
||||||
break
|
break;
|
||||||
case parser.yy.LINETYPE.OPT_START:
|
case parser.yy.LINETYPE.OPT_START:
|
||||||
bounds.bumpVerticalPos(conf.boxMargin)
|
bounds.bumpVerticalPos(conf.boxMargin);
|
||||||
bounds.newLoop(msg.message)
|
bounds.newLoop(msg.message);
|
||||||
bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin)
|
bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin);
|
||||||
break
|
break;
|
||||||
case parser.yy.LINETYPE.OPT_END:
|
case parser.yy.LINETYPE.OPT_END:
|
||||||
loopData = bounds.endLoop()
|
loopData = bounds.endLoop();
|
||||||
|
|
||||||
svgDraw.drawLoop(diagram, loopData, 'opt', conf)
|
svgDraw.drawLoop(diagram, loopData, 'opt', conf);
|
||||||
bounds.bumpVerticalPos(conf.boxMargin)
|
bounds.bumpVerticalPos(conf.boxMargin);
|
||||||
break
|
break;
|
||||||
case parser.yy.LINETYPE.ALT_START:
|
case parser.yy.LINETYPE.ALT_START:
|
||||||
bounds.bumpVerticalPos(conf.boxMargin)
|
bounds.bumpVerticalPos(conf.boxMargin);
|
||||||
bounds.newLoop(msg.message)
|
bounds.newLoop(msg.message);
|
||||||
bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin)
|
bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin);
|
||||||
break
|
break;
|
||||||
case parser.yy.LINETYPE.ALT_ELSE:
|
case parser.yy.LINETYPE.ALT_ELSE:
|
||||||
bounds.bumpVerticalPos(conf.boxMargin)
|
bounds.bumpVerticalPos(conf.boxMargin);
|
||||||
loopData = bounds.addSectionToLoop(msg.message)
|
loopData = bounds.addSectionToLoop(msg.message);
|
||||||
bounds.bumpVerticalPos(conf.boxMargin)
|
bounds.bumpVerticalPos(conf.boxMargin);
|
||||||
break
|
break;
|
||||||
case parser.yy.LINETYPE.ALT_END:
|
case parser.yy.LINETYPE.ALT_END:
|
||||||
loopData = bounds.endLoop()
|
loopData = bounds.endLoop();
|
||||||
|
|
||||||
svgDraw.drawLoop(diagram, loopData, 'alt', conf)
|
svgDraw.drawLoop(diagram, loopData, 'alt', conf);
|
||||||
bounds.bumpVerticalPos(conf.boxMargin)
|
bounds.bumpVerticalPos(conf.boxMargin);
|
||||||
break
|
break;
|
||||||
case parser.yy.LINETYPE.PAR_START:
|
case parser.yy.LINETYPE.PAR_START:
|
||||||
bounds.bumpVerticalPos(conf.boxMargin)
|
bounds.bumpVerticalPos(conf.boxMargin);
|
||||||
bounds.newLoop(msg.message)
|
bounds.newLoop(msg.message);
|
||||||
bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin)
|
bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin);
|
||||||
break
|
break;
|
||||||
case parser.yy.LINETYPE.PAR_AND:
|
case parser.yy.LINETYPE.PAR_AND:
|
||||||
bounds.bumpVerticalPos(conf.boxMargin)
|
bounds.bumpVerticalPos(conf.boxMargin);
|
||||||
loopData = bounds.addSectionToLoop(msg.message)
|
loopData = bounds.addSectionToLoop(msg.message);
|
||||||
bounds.bumpVerticalPos(conf.boxMargin)
|
bounds.bumpVerticalPos(conf.boxMargin);
|
||||||
break
|
break;
|
||||||
case parser.yy.LINETYPE.PAR_END:
|
case parser.yy.LINETYPE.PAR_END:
|
||||||
loopData = bounds.endLoop()
|
loopData = bounds.endLoop();
|
||||||
svgDraw.drawLoop(diagram, loopData, 'par', conf)
|
svgDraw.drawLoop(diagram, loopData, 'par', conf);
|
||||||
bounds.bumpVerticalPos(conf.boxMargin)
|
bounds.bumpVerticalPos(conf.boxMargin);
|
||||||
break
|
break;
|
||||||
default:
|
default:
|
||||||
try {
|
try {
|
||||||
// lastMsg = msg
|
// lastMsg = msg
|
||||||
bounds.bumpVerticalPos(conf.messageMargin)
|
bounds.bumpVerticalPos(conf.messageMargin);
|
||||||
const fromBounds = actorFlowVerticaBounds(msg.from)
|
const fromBounds = actorFlowVerticaBounds(msg.from);
|
||||||
const toBounds = actorFlowVerticaBounds(msg.to)
|
const toBounds = actorFlowVerticaBounds(msg.to);
|
||||||
const fromIdx = fromBounds[0] <= toBounds[0] ? 1 : 0
|
const fromIdx = fromBounds[0] <= toBounds[0] ? 1 : 0;
|
||||||
const toIdx = fromBounds[0] < toBounds[0] ? 0 : 1
|
const toIdx = fromBounds[0] < toBounds[0] ? 0 : 1;
|
||||||
startx = fromBounds[fromIdx]
|
startx = fromBounds[fromIdx];
|
||||||
stopx = toBounds[toIdx]
|
stopx = toBounds[toIdx];
|
||||||
|
|
||||||
const verticalPos = bounds.getVerticalPos()
|
const verticalPos = bounds.getVerticalPos();
|
||||||
drawMessage(diagram, startx, stopx, verticalPos, msg, sequenceIndex)
|
drawMessage(diagram, startx, stopx, verticalPos, msg, sequenceIndex);
|
||||||
const allBounds = fromBounds.concat(toBounds)
|
const allBounds = fromBounds.concat(toBounds);
|
||||||
bounds.insert(Math.min.apply(null, allBounds), verticalPos, Math.max.apply(null, allBounds), verticalPos)
|
bounds.insert(
|
||||||
|
Math.min.apply(null, allBounds),
|
||||||
|
verticalPos,
|
||||||
|
Math.max.apply(null, allBounds),
|
||||||
|
verticalPos
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('error while drawing message', e)
|
logger.error('error while drawing message', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Increment sequence counter if msg.type is a line (and not another event like activation or note, etc)
|
// Increment sequence counter if msg.type is a line (and not another event like activation or note, etc)
|
||||||
if ([
|
if (
|
||||||
parser.yy.LINETYPE.SOLID_OPEN,
|
[
|
||||||
parser.yy.LINETYPE.DOTTED_OPEN,
|
parser.yy.LINETYPE.SOLID_OPEN,
|
||||||
parser.yy.LINETYPE.SOLID,
|
parser.yy.LINETYPE.DOTTED_OPEN,
|
||||||
parser.yy.LINETYPE.DOTTED,
|
parser.yy.LINETYPE.SOLID,
|
||||||
parser.yy.LINETYPE.SOLID_CROSS,
|
parser.yy.LINETYPE.DOTTED,
|
||||||
parser.yy.LINETYPE.DOTTED_CROSS
|
parser.yy.LINETYPE.SOLID_CROSS,
|
||||||
].includes(msg.type)) {
|
parser.yy.LINETYPE.DOTTED_CROSS
|
||||||
sequenceIndex++
|
].includes(msg.type)
|
||||||
|
) {
|
||||||
|
sequenceIndex++;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
if (conf.mirrorActors) {
|
if (conf.mirrorActors) {
|
||||||
// Draw actors below diagram
|
// Draw actors below diagram
|
||||||
bounds.bumpVerticalPos(conf.boxMargin * 2)
|
bounds.bumpVerticalPos(conf.boxMargin * 2);
|
||||||
drawActors(diagram, actors, actorKeys, bounds.getVerticalPos())
|
drawActors(diagram, actors, actorKeys, bounds.getVerticalPos());
|
||||||
}
|
}
|
||||||
|
|
||||||
const box = bounds.getBounds()
|
const box = bounds.getBounds();
|
||||||
|
|
||||||
// Adjust line height of actor lines now that the height of the diagram is known
|
// Adjust line height of actor lines now that the height of the diagram is known
|
||||||
logger.debug('For line height fix Querying: #' + id + ' .actor-line')
|
logger.debug('For line height fix Querying: #' + id + ' .actor-line');
|
||||||
const actorLines = d3.selectAll('#' + id + ' .actor-line')
|
const actorLines = d3.selectAll('#' + id + ' .actor-line');
|
||||||
actorLines.attr('y2', box.stopy)
|
actorLines.attr('y2', box.stopy);
|
||||||
|
|
||||||
let height = box.stopy - box.starty + 2 * conf.diagramMarginY
|
let height = box.stopy - box.starty + 2 * conf.diagramMarginY;
|
||||||
if (conf.mirrorActors) {
|
if (conf.mirrorActors) {
|
||||||
height = height - conf.boxMargin + conf.bottomMarginAdj
|
height = height - conf.boxMargin + conf.bottomMarginAdj;
|
||||||
}
|
}
|
||||||
|
|
||||||
const width = (box.stopx - box.startx) + (2 * conf.diagramMarginX)
|
const width = box.stopx - box.startx + 2 * conf.diagramMarginX;
|
||||||
|
|
||||||
if (title) {
|
if (title) {
|
||||||
diagram.append('text')
|
diagram
|
||||||
|
.append('text')
|
||||||
.text(title)
|
.text(title)
|
||||||
.attr('x', ((box.stopx - box.startx) / 2) - (2 * conf.diagramMarginX))
|
.attr('x', (box.stopx - box.startx) / 2 - 2 * conf.diagramMarginX)
|
||||||
.attr('y', -25)
|
.attr('y', -25);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conf.useMaxWidth) {
|
if (conf.useMaxWidth) {
|
||||||
diagram.attr('height', '100%')
|
diagram.attr('height', '100%');
|
||||||
diagram.attr('width', '100%')
|
diagram.attr('width', '100%');
|
||||||
diagram.attr('style', 'max-width:' + (width) + 'px;')
|
diagram.attr('style', 'max-width:' + width + 'px;');
|
||||||
} else {
|
} else {
|
||||||
diagram.attr('height', height)
|
diagram.attr('height', height);
|
||||||
diagram.attr('width', width)
|
diagram.attr('width', width);
|
||||||
}
|
}
|
||||||
const extraVertForTitle = title ? 40 : 0
|
const extraVertForTitle = title ? 40 : 0;
|
||||||
diagram.attr('viewBox', (box.startx - conf.diagramMarginX) + ' -' + (conf.diagramMarginY + extraVertForTitle) + ' ' + width + ' ' + (height + extraVertForTitle))
|
diagram.attr(
|
||||||
}
|
'viewBox',
|
||||||
|
box.startx -
|
||||||
|
conf.diagramMarginX +
|
||||||
|
' -' +
|
||||||
|
(conf.diagramMarginY + extraVertForTitle) +
|
||||||
|
' ' +
|
||||||
|
width +
|
||||||
|
' ' +
|
||||||
|
(height + extraVertForTitle)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
bounds,
|
bounds,
|
||||||
drawActors,
|
drawActors,
|
||||||
setConf,
|
setConf,
|
||||||
draw
|
draw
|
||||||
}
|
};
|
||||||
|
@ -1,71 +1,87 @@
|
|||||||
export const drawRect = function (elem, rectData) {
|
export const drawRect = function(elem, rectData) {
|
||||||
const rectElem = elem.append('rect')
|
const rectElem = elem.append('rect');
|
||||||
rectElem.attr('x', rectData.x)
|
rectElem.attr('x', rectData.x);
|
||||||
rectElem.attr('y', rectData.y)
|
rectElem.attr('y', rectData.y);
|
||||||
rectElem.attr('fill', rectData.fill)
|
rectElem.attr('fill', rectData.fill);
|
||||||
rectElem.attr('stroke', rectData.stroke)
|
rectElem.attr('stroke', rectData.stroke);
|
||||||
rectElem.attr('width', rectData.width)
|
rectElem.attr('width', rectData.width);
|
||||||
rectElem.attr('height', rectData.height)
|
rectElem.attr('height', rectData.height);
|
||||||
rectElem.attr('rx', rectData.rx)
|
rectElem.attr('rx', rectData.rx);
|
||||||
rectElem.attr('ry', rectData.ry)
|
rectElem.attr('ry', rectData.ry);
|
||||||
|
|
||||||
if (typeof rectData.class !== 'undefined') {
|
if (typeof rectData.class !== 'undefined') {
|
||||||
rectElem.attr('class', rectData.class)
|
rectElem.attr('class', rectData.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rectElem
|
return rectElem;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const drawText = function (elem, textData, width) {
|
export const drawText = function(elem, textData, width) {
|
||||||
// Remove and ignore br:s
|
// Remove and ignore br:s
|
||||||
const nText = textData.text.replace(/<br\/?>/ig, ' ')
|
const nText = textData.text.replace(/<br\/?>/gi, ' ');
|
||||||
|
|
||||||
const textElem = elem.append('text')
|
const textElem = elem.append('text');
|
||||||
textElem.attr('x', textData.x)
|
textElem.attr('x', textData.x);
|
||||||
textElem.attr('y', textData.y)
|
textElem.attr('y', textData.y);
|
||||||
textElem.style('text-anchor', textData.anchor)
|
textElem.style('text-anchor', textData.anchor);
|
||||||
textElem.attr('fill', textData.fill)
|
textElem.attr('fill', textData.fill);
|
||||||
if (typeof textData.class !== 'undefined') {
|
if (typeof textData.class !== 'undefined') {
|
||||||
textElem.attr('class', textData.class)
|
textElem.attr('class', textData.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
const span = textElem.append('tspan')
|
const span = textElem.append('tspan');
|
||||||
span.attr('x', textData.x + textData.textMargin * 2)
|
span.attr('x', textData.x + textData.textMargin * 2);
|
||||||
span.attr('fill', textData.fill)
|
span.attr('fill', textData.fill);
|
||||||
span.text(nText)
|
span.text(nText);
|
||||||
|
|
||||||
return textElem
|
return textElem;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const drawLabel = function (elem, txtObject) {
|
export const drawLabel = function(elem, txtObject) {
|
||||||
function genPoints (x, y, width, height, cut) {
|
function genPoints(x, y, width, height, cut) {
|
||||||
return x + ',' + y + ' ' +
|
return (
|
||||||
(x + width) + ',' + y + ' ' +
|
x +
|
||||||
(x + width) + ',' + (y + height - cut) + ' ' +
|
',' +
|
||||||
(x + width - cut * 1.2) + ',' + (y + height) + ' ' +
|
y +
|
||||||
(x) + ',' + (y + height)
|
' ' +
|
||||||
|
(x + width) +
|
||||||
|
',' +
|
||||||
|
y +
|
||||||
|
' ' +
|
||||||
|
(x + width) +
|
||||||
|
',' +
|
||||||
|
(y + height - cut) +
|
||||||
|
' ' +
|
||||||
|
(x + width - cut * 1.2) +
|
||||||
|
',' +
|
||||||
|
(y + height) +
|
||||||
|
' ' +
|
||||||
|
x +
|
||||||
|
',' +
|
||||||
|
(y + height)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const polygon = elem.append('polygon')
|
const polygon = elem.append('polygon');
|
||||||
polygon.attr('points', genPoints(txtObject.x, txtObject.y, 50, 20, 7))
|
polygon.attr('points', genPoints(txtObject.x, txtObject.y, 50, 20, 7));
|
||||||
polygon.attr('class', 'labelBox')
|
polygon.attr('class', 'labelBox');
|
||||||
|
|
||||||
txtObject.y = txtObject.y + txtObject.labelMargin
|
txtObject.y = txtObject.y + txtObject.labelMargin;
|
||||||
txtObject.x = txtObject.x + 0.5 * txtObject.labelMargin
|
txtObject.x = txtObject.x + 0.5 * txtObject.labelMargin;
|
||||||
drawText(elem, txtObject)
|
drawText(elem, txtObject);
|
||||||
}
|
};
|
||||||
|
|
||||||
let actorCnt = -1
|
let actorCnt = -1;
|
||||||
/**
|
/**
|
||||||
* Draws an actor in the diagram with the attaced line
|
* Draws an actor in the diagram with the attaced line
|
||||||
* @param center - The center of the the actor
|
* @param center - The center of the the actor
|
||||||
* @param pos The position if the actor in the liost of actors
|
* @param pos The position if the actor in the liost of actors
|
||||||
* @param description The text in the box
|
* @param description The text in the box
|
||||||
*/
|
*/
|
||||||
export const drawActor = function (elem, left, verticalPos, description, conf) {
|
export const drawActor = function(elem, left, verticalPos, description, conf) {
|
||||||
const center = left + (conf.width / 2)
|
const center = left + conf.width / 2;
|
||||||
const g = elem.append('g')
|
const g = elem.append('g');
|
||||||
if (verticalPos === 0) {
|
if (verticalPos === 0) {
|
||||||
actorCnt++
|
actorCnt++;
|
||||||
g.append('line')
|
g.append('line')
|
||||||
.attr('id', 'actor' + actorCnt)
|
.attr('id', 'actor' + actorCnt)
|
||||||
.attr('x1', center)
|
.attr('x1', center)
|
||||||
@ -74,43 +90,51 @@ export const drawActor = function (elem, left, verticalPos, description, conf) {
|
|||||||
.attr('y2', 2000)
|
.attr('y2', 2000)
|
||||||
.attr('class', 'actor-line')
|
.attr('class', 'actor-line')
|
||||||
.attr('stroke-width', '0.5px')
|
.attr('stroke-width', '0.5px')
|
||||||
.attr('stroke', '#999')
|
.attr('stroke', '#999');
|
||||||
}
|
}
|
||||||
|
|
||||||
const rect = getNoteRect()
|
const rect = getNoteRect();
|
||||||
rect.x = left
|
rect.x = left;
|
||||||
rect.y = verticalPos
|
rect.y = verticalPos;
|
||||||
rect.fill = '#eaeaea'
|
rect.fill = '#eaeaea';
|
||||||
rect.width = conf.width
|
rect.width = conf.width;
|
||||||
rect.height = conf.height
|
rect.height = conf.height;
|
||||||
rect.class = 'actor'
|
rect.class = 'actor';
|
||||||
rect.rx = 3
|
rect.rx = 3;
|
||||||
rect.ry = 3
|
rect.ry = 3;
|
||||||
drawRect(g, rect)
|
drawRect(g, rect);
|
||||||
|
|
||||||
_drawTextCandidateFunc(conf)(description, g,
|
_drawTextCandidateFunc(conf)(
|
||||||
rect.x, rect.y, rect.width, rect.height, { 'class': 'actor' }, conf)
|
description,
|
||||||
}
|
g,
|
||||||
|
rect.x,
|
||||||
|
rect.y,
|
||||||
|
rect.width,
|
||||||
|
rect.height,
|
||||||
|
{ class: 'actor' },
|
||||||
|
conf
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const anchorElement = function (elem) {
|
export const anchorElement = function(elem) {
|
||||||
return elem.append('g')
|
return elem.append('g');
|
||||||
}
|
};
|
||||||
/**
|
/**
|
||||||
* Draws an actor in the diagram with the attaced line
|
* Draws an actor in the diagram with the attaced line
|
||||||
* @param elem - element to append activation rect
|
* @param elem - element to append activation rect
|
||||||
* @param bounds - activation box bounds
|
* @param bounds - activation box bounds
|
||||||
* @param verticalPos - precise y cooridnate of bottom activation box edge
|
* @param verticalPos - precise y cooridnate of bottom activation box edge
|
||||||
*/
|
*/
|
||||||
export const drawActivation = function (elem, bounds, verticalPos, conf, actorActivations) {
|
export const drawActivation = function(elem, bounds, verticalPos, conf, actorActivations) {
|
||||||
const rect = getNoteRect()
|
const rect = getNoteRect();
|
||||||
const g = bounds.anchored
|
const g = bounds.anchored;
|
||||||
rect.x = bounds.startx
|
rect.x = bounds.startx;
|
||||||
rect.y = bounds.starty
|
rect.y = bounds.starty;
|
||||||
rect.class = 'activation' + (actorActivations % 3) // Will evaluate to 0, 1 or 2
|
rect.class = 'activation' + (actorActivations % 3); // Will evaluate to 0, 1 or 2
|
||||||
rect.width = bounds.stopx - bounds.startx
|
rect.width = bounds.stopx - bounds.startx;
|
||||||
rect.height = verticalPos - bounds.starty
|
rect.height = verticalPos - bounds.starty;
|
||||||
drawRect(g, rect)
|
drawRect(g, rect);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws an actor in the diagram with the attaced line
|
* Draws an actor in the diagram with the attaced line
|
||||||
@ -118,60 +142,61 @@ export const drawActivation = function (elem, bounds, verticalPos, conf, actorAc
|
|||||||
* @param pos The position if the actor in the list of actors
|
* @param pos The position if the actor in the list of actors
|
||||||
* @param description The text in the box
|
* @param description The text in the box
|
||||||
*/
|
*/
|
||||||
export const drawLoop = function (elem, bounds, labelText, conf) {
|
export const drawLoop = function(elem, bounds, labelText, conf) {
|
||||||
const g = elem.append('g')
|
const g = elem.append('g');
|
||||||
const drawLoopLine = function (startx, starty, stopx, stopy) {
|
const drawLoopLine = function(startx, starty, stopx, stopy) {
|
||||||
return g.append('line')
|
return g
|
||||||
|
.append('line')
|
||||||
.attr('x1', startx)
|
.attr('x1', startx)
|
||||||
.attr('y1', starty)
|
.attr('y1', starty)
|
||||||
.attr('x2', stopx)
|
.attr('x2', stopx)
|
||||||
.attr('y2', stopy)
|
.attr('y2', stopy)
|
||||||
.attr('class', 'loopLine')
|
.attr('class', 'loopLine');
|
||||||
}
|
};
|
||||||
drawLoopLine(bounds.startx, bounds.starty, bounds.stopx, bounds.starty)
|
drawLoopLine(bounds.startx, bounds.starty, bounds.stopx, bounds.starty);
|
||||||
drawLoopLine(bounds.stopx, bounds.starty, bounds.stopx, bounds.stopy)
|
drawLoopLine(bounds.stopx, bounds.starty, bounds.stopx, bounds.stopy);
|
||||||
drawLoopLine(bounds.startx, bounds.stopy, bounds.stopx, bounds.stopy)
|
drawLoopLine(bounds.startx, bounds.stopy, bounds.stopx, bounds.stopy);
|
||||||
drawLoopLine(bounds.startx, bounds.starty, bounds.startx, bounds.stopy)
|
drawLoopLine(bounds.startx, bounds.starty, bounds.startx, bounds.stopy);
|
||||||
if (typeof bounds.sections !== 'undefined') {
|
if (typeof bounds.sections !== 'undefined') {
|
||||||
bounds.sections.forEach(function (item) {
|
bounds.sections.forEach(function(item) {
|
||||||
drawLoopLine(bounds.startx, item, bounds.stopx, item).style('stroke-dasharray', '3, 3')
|
drawLoopLine(bounds.startx, item, bounds.stopx, item).style('stroke-dasharray', '3, 3');
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let txt = getTextObj()
|
let txt = getTextObj();
|
||||||
txt.text = labelText
|
txt.text = labelText;
|
||||||
txt.x = bounds.startx
|
txt.x = bounds.startx;
|
||||||
txt.y = bounds.starty
|
txt.y = bounds.starty;
|
||||||
txt.labelMargin = 1.5 * 10 // This is the small box that says "loop"
|
txt.labelMargin = 1.5 * 10; // This is the small box that says "loop"
|
||||||
txt.class = 'labelText' // Its size & position are fixed.
|
txt.class = 'labelText'; // Its size & position are fixed.
|
||||||
|
|
||||||
drawLabel(g, txt)
|
drawLabel(g, txt);
|
||||||
|
|
||||||
txt = getTextObj()
|
txt = getTextObj();
|
||||||
txt.text = '[ ' + bounds.title + ' ]'
|
txt.text = '[ ' + bounds.title + ' ]';
|
||||||
txt.x = bounds.startx + (bounds.stopx - bounds.startx) / 2
|
txt.x = bounds.startx + (bounds.stopx - bounds.startx) / 2;
|
||||||
txt.y = bounds.starty + 1.5 * conf.boxMargin
|
txt.y = bounds.starty + 1.5 * conf.boxMargin;
|
||||||
txt.anchor = 'middle'
|
txt.anchor = 'middle';
|
||||||
txt.class = 'loopText'
|
txt.class = 'loopText';
|
||||||
|
|
||||||
drawText(g, txt)
|
drawText(g, txt);
|
||||||
|
|
||||||
if (typeof bounds.sectionTitles !== 'undefined') {
|
if (typeof bounds.sectionTitles !== 'undefined') {
|
||||||
bounds.sectionTitles.forEach(function (item, idx) {
|
bounds.sectionTitles.forEach(function(item, idx) {
|
||||||
if (item !== '') {
|
if (item !== '') {
|
||||||
txt.text = '[ ' + item + ' ]'
|
txt.text = '[ ' + item + ' ]';
|
||||||
txt.y = bounds.sections[idx] + 1.5 * conf.boxMargin
|
txt.y = bounds.sections[idx] + 1.5 * conf.boxMargin;
|
||||||
drawText(g, txt)
|
drawText(g, txt);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a background rectangle
|
* Draws a background rectangle
|
||||||
* @param color - The fill color for the background
|
* @param color - The fill color for the background
|
||||||
*/
|
*/
|
||||||
export const drawBackgroundRect = function (elem, bounds) {
|
export const drawBackgroundRect = function(elem, bounds) {
|
||||||
const rectElem = drawRect(elem, {
|
const rectElem = drawRect(elem, {
|
||||||
x: bounds.startx,
|
x: bounds.startx,
|
||||||
y: bounds.starty,
|
y: bounds.starty,
|
||||||
@ -179,14 +204,16 @@ export const drawBackgroundRect = function (elem, bounds) {
|
|||||||
height: bounds.stopy - bounds.starty,
|
height: bounds.stopy - bounds.starty,
|
||||||
fill: bounds.fill,
|
fill: bounds.fill,
|
||||||
class: 'rect'
|
class: 'rect'
|
||||||
})
|
});
|
||||||
rectElem.lower()
|
rectElem.lower();
|
||||||
}
|
};
|
||||||
/**
|
/**
|
||||||
* Setup arrow head and define the marker. The result is appended to the svg.
|
* Setup arrow head and define the marker. The result is appended to the svg.
|
||||||
*/
|
*/
|
||||||
export const insertArrowHead = function (elem) {
|
export const insertArrowHead = function(elem) {
|
||||||
elem.append('defs').append('marker')
|
elem
|
||||||
|
.append('defs')
|
||||||
|
.append('marker')
|
||||||
.attr('id', 'arrowhead')
|
.attr('id', 'arrowhead')
|
||||||
.attr('refX', 5)
|
.attr('refX', 5)
|
||||||
.attr('refY', 2)
|
.attr('refY', 2)
|
||||||
@ -194,13 +221,15 @@ export const insertArrowHead = function (elem) {
|
|||||||
.attr('markerHeight', 4)
|
.attr('markerHeight', 4)
|
||||||
.attr('orient', 'auto')
|
.attr('orient', 'auto')
|
||||||
.append('path')
|
.append('path')
|
||||||
.attr('d', 'M 0,0 V 4 L6,2 Z') // this is actual shape for arrowhead
|
.attr('d', 'M 0,0 V 4 L6,2 Z'); // this is actual shape for arrowhead
|
||||||
}
|
};
|
||||||
/**
|
/**
|
||||||
* Setup node number. The result is appended to the svg.
|
* Setup node number. The result is appended to the svg.
|
||||||
*/
|
*/
|
||||||
export const insertSequenceNumber = function (elem) {
|
export const insertSequenceNumber = function(elem) {
|
||||||
elem.append('defs').append('marker')
|
elem
|
||||||
|
.append('defs')
|
||||||
|
.append('marker')
|
||||||
.attr('id', 'sequencenumber')
|
.attr('id', 'sequencenumber')
|
||||||
.attr('refX', 15)
|
.attr('refX', 15)
|
||||||
.attr('refY', 15)
|
.attr('refY', 15)
|
||||||
@ -210,45 +239,48 @@ export const insertSequenceNumber = function (elem) {
|
|||||||
.append('circle')
|
.append('circle')
|
||||||
.attr('cx', 15)
|
.attr('cx', 15)
|
||||||
.attr('cy', 15)
|
.attr('cy', 15)
|
||||||
.attr('r', 6)
|
.attr('r', 6);
|
||||||
// .style("fill", '#f00');
|
// .style("fill", '#f00');
|
||||||
}
|
};
|
||||||
/**
|
/**
|
||||||
* Setup arrow head and define the marker. The result is appended to the svg.
|
* Setup arrow head and define the marker. The result is appended to the svg.
|
||||||
*/
|
*/
|
||||||
export const insertArrowCrossHead = function (elem) {
|
export const insertArrowCrossHead = function(elem) {
|
||||||
const defs = elem.append('defs')
|
const defs = elem.append('defs');
|
||||||
const marker = defs.append('marker')
|
const marker = defs
|
||||||
|
.append('marker')
|
||||||
.attr('id', 'crosshead')
|
.attr('id', 'crosshead')
|
||||||
.attr('markerWidth', 15)
|
.attr('markerWidth', 15)
|
||||||
.attr('markerHeight', 8)
|
.attr('markerHeight', 8)
|
||||||
.attr('orient', 'auto')
|
.attr('orient', 'auto')
|
||||||
.attr('refX', 16)
|
.attr('refX', 16)
|
||||||
.attr('refY', 4)
|
.attr('refY', 4);
|
||||||
|
|
||||||
// The arrow
|
// The arrow
|
||||||
marker.append('path')
|
marker
|
||||||
|
.append('path')
|
||||||
.attr('fill', 'black')
|
.attr('fill', 'black')
|
||||||
.attr('stroke', '#000000')
|
.attr('stroke', '#000000')
|
||||||
.style('stroke-dasharray', ('0, 0'))
|
.style('stroke-dasharray', '0, 0')
|
||||||
.attr('stroke-width', '1px')
|
.attr('stroke-width', '1px')
|
||||||
.attr('d', 'M 9,2 V 6 L16,4 Z')
|
.attr('d', 'M 9,2 V 6 L16,4 Z');
|
||||||
|
|
||||||
// The cross
|
// The cross
|
||||||
marker.append('path')
|
marker
|
||||||
|
.append('path')
|
||||||
.attr('fill', 'none')
|
.attr('fill', 'none')
|
||||||
.attr('stroke', '#000000')
|
.attr('stroke', '#000000')
|
||||||
.style('stroke-dasharray', ('0, 0'))
|
.style('stroke-dasharray', '0, 0')
|
||||||
.attr('stroke-width', '1px')
|
.attr('stroke-width', '1px')
|
||||||
.attr('d', 'M 0,1 L 6,7 M 6,1 L 0,7')
|
.attr('d', 'M 0,1 L 6,7 M 6,1 L 0,7');
|
||||||
// this is actual shape for arrowhead
|
// this is actual shape for arrowhead
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getTextObj = function () {
|
export const getTextObj = function() {
|
||||||
const txt = {
|
const txt = {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
'fill': undefined,
|
fill: undefined,
|
||||||
'text-anchor': 'start',
|
'text-anchor': 'start',
|
||||||
style: '#666',
|
style: '#666',
|
||||||
width: 100,
|
width: 100,
|
||||||
@ -256,11 +288,11 @@ export const getTextObj = function () {
|
|||||||
textMargin: 0,
|
textMargin: 0,
|
||||||
rx: 0,
|
rx: 0,
|
||||||
ry: 0
|
ry: 0
|
||||||
}
|
};
|
||||||
return txt
|
return txt;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getNoteRect = function () {
|
export const getNoteRect = function() {
|
||||||
const rect = {
|
const rect = {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
@ -271,72 +303,87 @@ export const getNoteRect = function () {
|
|||||||
height: 100,
|
height: 100,
|
||||||
rx: 0,
|
rx: 0,
|
||||||
ry: 0
|
ry: 0
|
||||||
}
|
};
|
||||||
return rect
|
return rect;
|
||||||
}
|
};
|
||||||
|
|
||||||
const _drawTextCandidateFunc = (function () {
|
const _drawTextCandidateFunc = (function() {
|
||||||
function byText (content, g, x, y, width, height, textAttrs) {
|
function byText(content, g, x, y, width, height, textAttrs) {
|
||||||
const text = g.append('text')
|
const text = g
|
||||||
.attr('x', x + width / 2).attr('y', y + height / 2 + 5)
|
.append('text')
|
||||||
|
.attr('x', x + width / 2)
|
||||||
|
.attr('y', y + height / 2 + 5)
|
||||||
.style('text-anchor', 'middle')
|
.style('text-anchor', 'middle')
|
||||||
.text(content)
|
.text(content);
|
||||||
_setTextAttrs(text, textAttrs)
|
_setTextAttrs(text, textAttrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
function byTspan (content, g, x, y, width, height, textAttrs, conf) {
|
function byTspan(content, g, x, y, width, height, textAttrs, conf) {
|
||||||
const { actorFontSize, actorFontFamily } = conf
|
const { actorFontSize, actorFontFamily } = conf;
|
||||||
|
|
||||||
const lines = content.split(/<br\/?>/ig)
|
const lines = content.split(/<br\/?>/gi);
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const dy = (i * actorFontSize) - (actorFontSize * (lines.length - 1) / 2)
|
const dy = i * actorFontSize - (actorFontSize * (lines.length - 1)) / 2;
|
||||||
const text = g.append('text')
|
const text = g
|
||||||
.attr('x', x + width / 2).attr('y', y)
|
.append('text')
|
||||||
|
.attr('x', x + width / 2)
|
||||||
|
.attr('y', y)
|
||||||
.style('text-anchor', 'middle')
|
.style('text-anchor', 'middle')
|
||||||
.style('font-size', actorFontSize)
|
.style('font-size', actorFontSize)
|
||||||
.style('font-family', actorFontFamily)
|
.style('font-family', actorFontFamily);
|
||||||
text.append('tspan')
|
text
|
||||||
.attr('x', x + width / 2).attr('dy', dy)
|
.append('tspan')
|
||||||
.text(lines[i])
|
.attr('x', x + width / 2)
|
||||||
|
.attr('dy', dy)
|
||||||
|
.text(lines[i]);
|
||||||
|
|
||||||
text.attr('y', y + height / 2.0)
|
text
|
||||||
|
.attr('y', y + height / 2.0)
|
||||||
.attr('dominant-baseline', 'central')
|
.attr('dominant-baseline', 'central')
|
||||||
.attr('alignment-baseline', 'central')
|
.attr('alignment-baseline', 'central');
|
||||||
|
|
||||||
_setTextAttrs(text, textAttrs)
|
_setTextAttrs(text, textAttrs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function byFo (content, g, x, y, width, height, textAttrs, conf) {
|
function byFo(content, g, x, y, width, height, textAttrs, conf) {
|
||||||
const s = g.append('switch')
|
const s = g.append('switch');
|
||||||
const f = s.append('foreignObject')
|
const f = s
|
||||||
.attr('x', x).attr('y', y)
|
.append('foreignObject')
|
||||||
.attr('width', width).attr('height', height)
|
.attr('x', x)
|
||||||
|
.attr('y', y)
|
||||||
|
.attr('width', width)
|
||||||
|
.attr('height', height);
|
||||||
|
|
||||||
const text = f.append('div').style('display', 'table')
|
const text = f
|
||||||
.style('height', '100%').style('width', '100%')
|
.append('div')
|
||||||
|
.style('display', 'table')
|
||||||
|
.style('height', '100%')
|
||||||
|
.style('width', '100%');
|
||||||
|
|
||||||
text.append('div').style('display', 'table-cell')
|
text
|
||||||
.style('text-align', 'center').style('vertical-align', 'middle')
|
.append('div')
|
||||||
.text(content)
|
.style('display', 'table-cell')
|
||||||
|
.style('text-align', 'center')
|
||||||
|
.style('vertical-align', 'middle')
|
||||||
|
.text(content);
|
||||||
|
|
||||||
byTspan(content, s, x, y, width, height, textAttrs, conf)
|
byTspan(content, s, x, y, width, height, textAttrs, conf);
|
||||||
_setTextAttrs(text, textAttrs)
|
_setTextAttrs(text, textAttrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _setTextAttrs (toText, fromTextAttrsDict) {
|
function _setTextAttrs(toText, fromTextAttrsDict) {
|
||||||
for (const key in fromTextAttrsDict) {
|
for (const key in fromTextAttrsDict) {
|
||||||
if (fromTextAttrsDict.hasOwnProperty(key)) {
|
if (fromTextAttrsDict.hasOwnProperty(key)) {
|
||||||
toText.attr(key, fromTextAttrsDict[key])
|
toText.attr(key, fromTextAttrsDict[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return function (conf) {
|
return function(conf) {
|
||||||
return conf.textPlacement === 'fo' ? byFo : (
|
return conf.textPlacement === 'fo' ? byFo : conf.textPlacement === 'old' ? byText : byTspan;
|
||||||
conf.textPlacement === 'old' ? byText : byTspan)
|
};
|
||||||
}
|
})();
|
||||||
})()
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
drawRect,
|
drawRect,
|
||||||
@ -352,4 +399,4 @@ export default {
|
|||||||
insertArrowCrossHead,
|
insertArrowCrossHead,
|
||||||
getTextObj,
|
getTextObj,
|
||||||
getNoteRect
|
getNoteRect
|
||||||
}
|
};
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
/* eslint-env jasmine */
|
/* eslint-env jasmine */
|
||||||
const svgDraw = require('./svgDraw')
|
const svgDraw = require('./svgDraw');
|
||||||
const { MockD3 } = require('d3')
|
const { MockD3 } = require('d3');
|
||||||
|
|
||||||
describe('svgDraw', function () {
|
describe('svgDraw', function() {
|
||||||
describe('drawRect', function () {
|
describe('drawRect', function() {
|
||||||
it('it should append a rectangle', function () {
|
it('it should append a rectangle', function() {
|
||||||
const svg = MockD3('svg')
|
const svg = MockD3('svg');
|
||||||
svgDraw.drawRect(svg, {
|
svgDraw.drawRect(svg, {
|
||||||
x: 10,
|
x: 10,
|
||||||
y: 10,
|
y: 10,
|
||||||
@ -16,22 +16,22 @@ describe('svgDraw', function () {
|
|||||||
rx: '10',
|
rx: '10',
|
||||||
ry: '10',
|
ry: '10',
|
||||||
class: 'unitTestRectangleClass'
|
class: 'unitTestRectangleClass'
|
||||||
})
|
});
|
||||||
expect(svg.__children.length).toBe(1)
|
expect(svg.__children.length).toBe(1);
|
||||||
const rect = svg.__children[0]
|
const rect = svg.__children[0];
|
||||||
expect(rect.__name).toBe('rect')
|
expect(rect.__name).toBe('rect');
|
||||||
expect(rect.attr).toHaveBeenCalledWith('x', 10)
|
expect(rect.attr).toHaveBeenCalledWith('x', 10);
|
||||||
expect(rect.attr).toHaveBeenCalledWith('y', 10)
|
expect(rect.attr).toHaveBeenCalledWith('y', 10);
|
||||||
expect(rect.attr).toHaveBeenCalledWith('fill', '#ccc')
|
expect(rect.attr).toHaveBeenCalledWith('fill', '#ccc');
|
||||||
expect(rect.attr).toHaveBeenCalledWith('stroke', 'red')
|
expect(rect.attr).toHaveBeenCalledWith('stroke', 'red');
|
||||||
expect(rect.attr).toHaveBeenCalledWith('width', '20')
|
expect(rect.attr).toHaveBeenCalledWith('width', '20');
|
||||||
expect(rect.attr).toHaveBeenCalledWith('height', '20')
|
expect(rect.attr).toHaveBeenCalledWith('height', '20');
|
||||||
expect(rect.attr).toHaveBeenCalledWith('rx', '10')
|
expect(rect.attr).toHaveBeenCalledWith('rx', '10');
|
||||||
expect(rect.attr).toHaveBeenCalledWith('ry', '10')
|
expect(rect.attr).toHaveBeenCalledWith('ry', '10');
|
||||||
expect(rect.attr).toHaveBeenCalledWith('class', 'unitTestRectangleClass')
|
expect(rect.attr).toHaveBeenCalledWith('class', 'unitTestRectangleClass');
|
||||||
})
|
});
|
||||||
it('it should not add the class attribute if a class isn`t provided', () => {
|
it('it should not add the class attribute if a class isn`t provided', () => {
|
||||||
const svg = MockD3('svg')
|
const svg = MockD3('svg');
|
||||||
svgDraw.drawRect(svg, {
|
svgDraw.drawRect(svg, {
|
||||||
x: 10,
|
x: 10,
|
||||||
y: 10,
|
y: 10,
|
||||||
@ -41,17 +41,17 @@ describe('svgDraw', function () {
|
|||||||
height: '20',
|
height: '20',
|
||||||
rx: '10',
|
rx: '10',
|
||||||
ry: '10'
|
ry: '10'
|
||||||
})
|
});
|
||||||
expect(svg.__children.length).toBe(1)
|
expect(svg.__children.length).toBe(1);
|
||||||
const rect = svg.__children[0]
|
const rect = svg.__children[0];
|
||||||
expect(rect.__name).toBe('rect')
|
expect(rect.__name).toBe('rect');
|
||||||
expect(rect.attr).toHaveBeenCalledWith('fill', '#ccc')
|
expect(rect.attr).toHaveBeenCalledWith('fill', '#ccc');
|
||||||
expect(rect.attr).not.toHaveBeenCalledWith('class', expect.anything())
|
expect(rect.attr).not.toHaveBeenCalledWith('class', expect.anything());
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
describe('drawBackgroundRect', function () {
|
describe('drawBackgroundRect', function() {
|
||||||
it('it should append a rect before the previous element within a given bound', function () {
|
it('it should append a rect before the previous element within a given bound', function() {
|
||||||
const svg = MockD3('svg')
|
const svg = MockD3('svg');
|
||||||
const boundingRect = {
|
const boundingRect = {
|
||||||
startx: 50,
|
startx: 50,
|
||||||
starty: 200,
|
starty: 200,
|
||||||
@ -59,18 +59,18 @@ describe('svgDraw', function () {
|
|||||||
stopy: 260,
|
stopy: 260,
|
||||||
title: undefined,
|
title: undefined,
|
||||||
fill: '#ccc'
|
fill: '#ccc'
|
||||||
}
|
};
|
||||||
svgDraw.drawBackgroundRect(svg, boundingRect)
|
svgDraw.drawBackgroundRect(svg, boundingRect);
|
||||||
expect(svg.__children.length).toBe(1)
|
expect(svg.__children.length).toBe(1);
|
||||||
const rect = svg.__children[0]
|
const rect = svg.__children[0];
|
||||||
expect(rect.__name).toBe('rect')
|
expect(rect.__name).toBe('rect');
|
||||||
expect(rect.attr).toHaveBeenCalledWith('x', 50)
|
expect(rect.attr).toHaveBeenCalledWith('x', 50);
|
||||||
expect(rect.attr).toHaveBeenCalledWith('y', 200)
|
expect(rect.attr).toHaveBeenCalledWith('y', 200);
|
||||||
expect(rect.attr).toHaveBeenCalledWith('width', 100)
|
expect(rect.attr).toHaveBeenCalledWith('width', 100);
|
||||||
expect(rect.attr).toHaveBeenCalledWith('height', 60)
|
expect(rect.attr).toHaveBeenCalledWith('height', 60);
|
||||||
expect(rect.attr).toHaveBeenCalledWith('fill', '#ccc')
|
expect(rect.attr).toHaveBeenCalledWith('fill', '#ccc');
|
||||||
expect(rect.attr).toHaveBeenCalledWith('class', 'rect')
|
expect(rect.attr).toHaveBeenCalledWith('class', 'rect');
|
||||||
expect(rect.lower).toHaveBeenCalled()
|
expect(rect.lower).toHaveBeenCalled();
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import moment from 'moment-mini'
|
import moment from 'moment-mini';
|
||||||
|
|
||||||
export const LEVELS = {
|
export const LEVELS = {
|
||||||
debug: 1,
|
debug: 1,
|
||||||
@ -6,7 +6,7 @@ export const LEVELS = {
|
|||||||
warn: 3,
|
warn: 3,
|
||||||
error: 4,
|
error: 4,
|
||||||
fatal: 5
|
fatal: 5
|
||||||
}
|
};
|
||||||
|
|
||||||
export const logger = {
|
export const logger = {
|
||||||
debug: () => {},
|
debug: () => {},
|
||||||
@ -14,32 +14,32 @@ export const logger = {
|
|||||||
warn: () => {},
|
warn: () => {},
|
||||||
error: () => {},
|
error: () => {},
|
||||||
fatal: () => {}
|
fatal: () => {}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const setLogLevel = function (level) {
|
export const setLogLevel = function(level) {
|
||||||
logger.debug = () => {}
|
logger.debug = () => {};
|
||||||
logger.info = () => {}
|
logger.info = () => {};
|
||||||
logger.warn = () => {}
|
logger.warn = () => {};
|
||||||
logger.error = () => {}
|
logger.error = () => {};
|
||||||
logger.fatal = () => {}
|
logger.fatal = () => {};
|
||||||
if (level <= LEVELS.fatal) {
|
if (level <= LEVELS.fatal) {
|
||||||
logger.fatal = console.log.bind(console, '\x1b[35m', format('FATAL'))
|
logger.fatal = console.log.bind(console, '\x1b[35m', format('FATAL'));
|
||||||
}
|
}
|
||||||
if (level <= LEVELS.error) {
|
if (level <= LEVELS.error) {
|
||||||
logger.error = console.log.bind(console, '\x1b[31m', format('ERROR'))
|
logger.error = console.log.bind(console, '\x1b[31m', format('ERROR'));
|
||||||
}
|
}
|
||||||
if (level <= LEVELS.warn) {
|
if (level <= LEVELS.warn) {
|
||||||
logger.warn = console.log.bind(console, `\x1b[33m`, format('WARN'))
|
logger.warn = console.log.bind(console, `\x1b[33m`, format('WARN'));
|
||||||
}
|
}
|
||||||
if (level <= LEVELS.info) {
|
if (level <= LEVELS.info) {
|
||||||
logger.info = console.log.bind(console, '\x1b[34m', format('INFO'))
|
logger.info = console.log.bind(console, '\x1b[34m', format('INFO'));
|
||||||
}
|
}
|
||||||
if (level <= LEVELS.debug) {
|
if (level <= LEVELS.debug) {
|
||||||
logger.debug = console.log.bind(console, '\x1b[32m', format('DEBUG'))
|
logger.debug = console.log.bind(console, '\x1b[32m', format('DEBUG'));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const format = (level) => {
|
const format = level => {
|
||||||
const time = moment().format('HH:mm:ss.SSS')
|
const time = moment().format('HH:mm:ss.SSS');
|
||||||
return `${time} : ${level} : `
|
return `${time} : ${level} : `;
|
||||||
}
|
};
|
||||||
|
132
src/mermaid.js
132
src/mermaid.js
@ -2,10 +2,10 @@
|
|||||||
* Web page integration module for the mermaid framework. It uses the mermaidAPI for mermaid functionality and to render
|
* Web page integration module for the mermaid framework. It uses the mermaidAPI for mermaid functionality and to render
|
||||||
* the diagrams to svg code.
|
* the diagrams to svg code.
|
||||||
*/
|
*/
|
||||||
import he from 'he'
|
import he from 'he';
|
||||||
|
|
||||||
import mermaidAPI from './mermaidAPI'
|
import mermaidAPI from './mermaidAPI';
|
||||||
import { logger } from './logger'
|
import { logger } from './logger';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ## init
|
* ## init
|
||||||
@ -28,126 +28,142 @@ import { logger } from './logger'
|
|||||||
* Renders the mermaid diagrams
|
* Renders the mermaid diagrams
|
||||||
* @param nodes a css selector or an array of nodes
|
* @param nodes a css selector or an array of nodes
|
||||||
*/
|
*/
|
||||||
const init = function () {
|
const init = function() {
|
||||||
const conf = mermaidAPI.getConfig()
|
const conf = mermaidAPI.getConfig();
|
||||||
logger.debug('Starting rendering diagrams')
|
logger.debug('Starting rendering diagrams');
|
||||||
let nodes
|
let nodes;
|
||||||
if (arguments.length >= 2) {
|
if (arguments.length >= 2) {
|
||||||
/*! sequence config was passed as #1 */
|
/*! sequence config was passed as #1 */
|
||||||
if (typeof arguments[0] !== 'undefined') {
|
if (typeof arguments[0] !== 'undefined') {
|
||||||
mermaid.sequenceConfig = arguments[0]
|
mermaid.sequenceConfig = arguments[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes = arguments[1]
|
nodes = arguments[1];
|
||||||
} else {
|
} else {
|
||||||
nodes = arguments[0]
|
nodes = arguments[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// if last argument is a function this is the callback function
|
// if last argument is a function this is the callback function
|
||||||
let callback
|
let callback;
|
||||||
if (typeof arguments[arguments.length - 1] === 'function') {
|
if (typeof arguments[arguments.length - 1] === 'function') {
|
||||||
callback = arguments[arguments.length - 1]
|
callback = arguments[arguments.length - 1];
|
||||||
logger.debug('Callback function found')
|
logger.debug('Callback function found');
|
||||||
} else {
|
} else {
|
||||||
if (typeof conf.mermaid !== 'undefined') {
|
if (typeof conf.mermaid !== 'undefined') {
|
||||||
if (typeof conf.mermaid.callback === 'function') {
|
if (typeof conf.mermaid.callback === 'function') {
|
||||||
callback = conf.mermaid.callback
|
callback = conf.mermaid.callback;
|
||||||
logger.debug('Callback function found')
|
logger.debug('Callback function found');
|
||||||
} else {
|
} else {
|
||||||
logger.debug('No Callback function found')
|
logger.debug('No Callback function found');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nodes = nodes === undefined ? document.querySelectorAll('.mermaid')
|
nodes =
|
||||||
: typeof nodes === 'string' ? document.querySelectorAll(nodes)
|
nodes === undefined
|
||||||
: nodes instanceof window.Node ? [nodes]
|
? document.querySelectorAll('.mermaid')
|
||||||
: nodes // Last case - sequence config was passed pick next
|
: typeof nodes === 'string'
|
||||||
|
? document.querySelectorAll(nodes)
|
||||||
|
: nodes instanceof window.Node
|
||||||
|
? [nodes]
|
||||||
|
: nodes; // Last case - sequence config was passed pick next
|
||||||
|
|
||||||
logger.debug('Start On Load before: ' + mermaid.startOnLoad)
|
logger.debug('Start On Load before: ' + mermaid.startOnLoad);
|
||||||
if (typeof mermaid.startOnLoad !== 'undefined') {
|
if (typeof mermaid.startOnLoad !== 'undefined') {
|
||||||
logger.debug('Start On Load inner: ' + mermaid.startOnLoad)
|
logger.debug('Start On Load inner: ' + mermaid.startOnLoad);
|
||||||
mermaidAPI.initialize({ startOnLoad: mermaid.startOnLoad })
|
mermaidAPI.initialize({ startOnLoad: mermaid.startOnLoad });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof mermaid.ganttConfig !== 'undefined') {
|
if (typeof mermaid.ganttConfig !== 'undefined') {
|
||||||
mermaidAPI.initialize({ gantt: mermaid.ganttConfig })
|
mermaidAPI.initialize({ gantt: mermaid.ganttConfig });
|
||||||
}
|
}
|
||||||
|
|
||||||
let txt
|
let txt;
|
||||||
|
|
||||||
for (let i = 0; i < nodes.length; i++) {
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
const element = nodes[i]
|
const element = nodes[i];
|
||||||
|
|
||||||
/*! Check if previously processed */
|
/*! Check if previously processed */
|
||||||
if (!element.getAttribute('data-processed')) {
|
if (!element.getAttribute('data-processed')) {
|
||||||
element.setAttribute('data-processed', true)
|
element.setAttribute('data-processed', true);
|
||||||
} else {
|
} else {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = `mermaid-${Date.now()}`
|
const id = `mermaid-${Date.now()}`;
|
||||||
|
|
||||||
// Fetch the graph definition including tags
|
// Fetch the graph definition including tags
|
||||||
txt = element.innerHTML
|
txt = element.innerHTML;
|
||||||
|
|
||||||
// transforms the html to pure text
|
// transforms the html to pure text
|
||||||
txt = he.decode(txt).trim().replace(/<br>/ig, '<br/>')
|
txt = he
|
||||||
|
.decode(txt)
|
||||||
|
.trim()
|
||||||
|
.replace(/<br>/gi, '<br/>');
|
||||||
|
|
||||||
mermaidAPI.render(id, txt, (svgCode, bindFunctions) => {
|
mermaidAPI.render(
|
||||||
element.innerHTML = svgCode
|
id,
|
||||||
if (typeof callback !== 'undefined') {
|
txt,
|
||||||
callback(id)
|
(svgCode, bindFunctions) => {
|
||||||
}
|
element.innerHTML = svgCode;
|
||||||
if (bindFunctions) bindFunctions(element)
|
if (typeof callback !== 'undefined') {
|
||||||
}, element)
|
callback(id);
|
||||||
|
}
|
||||||
|
if (bindFunctions) bindFunctions(element);
|
||||||
|
},
|
||||||
|
element
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const initialize = function (config) {
|
const initialize = function(config) {
|
||||||
logger.debug('Initializing mermaid ')
|
logger.debug('Initializing mermaid ');
|
||||||
if (typeof config.mermaid !== 'undefined') {
|
if (typeof config.mermaid !== 'undefined') {
|
||||||
if (typeof config.mermaid.startOnLoad !== 'undefined') {
|
if (typeof config.mermaid.startOnLoad !== 'undefined') {
|
||||||
mermaid.startOnLoad = config.mermaid.startOnLoad
|
mermaid.startOnLoad = config.mermaid.startOnLoad;
|
||||||
}
|
}
|
||||||
if (typeof config.mermaid.htmlLabels !== 'undefined') {
|
if (typeof config.mermaid.htmlLabels !== 'undefined') {
|
||||||
mermaid.htmlLabels = config.mermaid.htmlLabels
|
mermaid.htmlLabels = config.mermaid.htmlLabels;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mermaidAPI.initialize(config)
|
mermaidAPI.initialize(config);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ##contentLoaded
|
* ##contentLoaded
|
||||||
* Callback function that is called when page is loaded. This functions fetches configuration for mermaid rendering and
|
* Callback function that is called when page is loaded. This functions fetches configuration for mermaid rendering and
|
||||||
* calls init for rendering the mermaid diagrams on the page.
|
* calls init for rendering the mermaid diagrams on the page.
|
||||||
*/
|
*/
|
||||||
const contentLoaded = function () {
|
const contentLoaded = function() {
|
||||||
let config
|
let config;
|
||||||
|
|
||||||
if (mermaid.startOnLoad) {
|
if (mermaid.startOnLoad) {
|
||||||
// No config found, do check API config
|
// No config found, do check API config
|
||||||
config = mermaidAPI.getConfig()
|
config = mermaidAPI.getConfig();
|
||||||
if (config.startOnLoad) {
|
if (config.startOnLoad) {
|
||||||
mermaid.init()
|
mermaid.init();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (typeof mermaid.startOnLoad === 'undefined') {
|
if (typeof mermaid.startOnLoad === 'undefined') {
|
||||||
logger.debug('In start, no config')
|
logger.debug('In start, no config');
|
||||||
config = mermaidAPI.getConfig()
|
config = mermaidAPI.getConfig();
|
||||||
if (config.startOnLoad) {
|
if (config.startOnLoad) {
|
||||||
mermaid.init()
|
mermaid.init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
if (typeof document !== 'undefined') {
|
if (typeof document !== 'undefined') {
|
||||||
/*!
|
/*!
|
||||||
* Wait for document loaded before starting the execution
|
* Wait for document loaded before starting the execution
|
||||||
*/
|
*/
|
||||||
window.addEventListener('load', function () {
|
window.addEventListener(
|
||||||
contentLoaded()
|
'load',
|
||||||
}, false)
|
function() {
|
||||||
|
contentLoaded();
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const mermaid = {
|
const mermaid = {
|
||||||
@ -162,6 +178,6 @@ const mermaid = {
|
|||||||
initialize,
|
initialize,
|
||||||
|
|
||||||
contentLoaded
|
contentLoaded
|
||||||
}
|
};
|
||||||
|
|
||||||
export default mermaid
|
export default mermaid;
|
||||||
|
@ -1,195 +1,200 @@
|
|||||||
/* eslint-env jasmine */
|
/* eslint-env jasmine */
|
||||||
import mermaid from './mermaid'
|
import mermaid from './mermaid';
|
||||||
import flowDb from './diagrams/flowchart/flowDb'
|
import flowDb from './diagrams/flowchart/flowDb';
|
||||||
import flowParser from './diagrams/flowchart/parser/flow'
|
import flowParser from './diagrams/flowchart/parser/flow';
|
||||||
import flowRenderer from './diagrams/flowchart/flowRenderer'
|
import flowRenderer from './diagrams/flowchart/flowRenderer';
|
||||||
|
|
||||||
describe('when using mermaid and ', function () {
|
describe('when using mermaid and ', function() {
|
||||||
describe('when detecting chart type ', function () {
|
describe('when detecting chart type ', function() {
|
||||||
it('should not start rendering with mermaid.startOnLoad set to false', function () {
|
it('should not start rendering with mermaid.startOnLoad set to false', function() {
|
||||||
mermaid.startOnLoad = false
|
mermaid.startOnLoad = false;
|
||||||
document.body.innerHTML = '<div class="mermaid">graph TD;\na;</div>'
|
document.body.innerHTML = '<div class="mermaid">graph TD;\na;</div>';
|
||||||
spyOn(mermaid, 'init')
|
spyOn(mermaid, 'init');
|
||||||
mermaid.contentLoaded()
|
mermaid.contentLoaded();
|
||||||
expect(mermaid.init).not.toHaveBeenCalled()
|
expect(mermaid.init).not.toHaveBeenCalled();
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should start rendering with both startOnLoad set', function () {
|
it('should start rendering with both startOnLoad set', function() {
|
||||||
mermaid.startOnLoad = true
|
mermaid.startOnLoad = true;
|
||||||
document.body.innerHTML = '<div class="mermaid">graph TD;\na;</div>'
|
document.body.innerHTML = '<div class="mermaid">graph TD;\na;</div>';
|
||||||
spyOn(mermaid, 'init')
|
spyOn(mermaid, 'init');
|
||||||
mermaid.contentLoaded()
|
mermaid.contentLoaded();
|
||||||
expect(mermaid.init).toHaveBeenCalled()
|
expect(mermaid.init).toHaveBeenCalled();
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should start rendering with mermaid.startOnLoad', function () {
|
it('should start rendering with mermaid.startOnLoad', function() {
|
||||||
mermaid.startOnLoad = true
|
mermaid.startOnLoad = true;
|
||||||
document.body.innerHTML = '<div class="mermaid">graph TD;\na;</div>'
|
document.body.innerHTML = '<div class="mermaid">graph TD;\na;</div>';
|
||||||
spyOn(mermaid, 'init')
|
spyOn(mermaid, 'init');
|
||||||
mermaid.contentLoaded()
|
mermaid.contentLoaded();
|
||||||
expect(mermaid.init).toHaveBeenCalled()
|
expect(mermaid.init).toHaveBeenCalled();
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should start rendering as a default with no changes performed', function () {
|
it('should start rendering as a default with no changes performed', function() {
|
||||||
document.body.innerHTML = '<div class="mermaid">graph TD;\na;</div>'
|
document.body.innerHTML = '<div class="mermaid">graph TD;\na;</div>';
|
||||||
spyOn(mermaid, 'init')
|
spyOn(mermaid, 'init');
|
||||||
mermaid.contentLoaded()
|
mermaid.contentLoaded();
|
||||||
expect(mermaid.init).toHaveBeenCalled()
|
expect(mermaid.init).toHaveBeenCalled();
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
describe('when calling addEdges ', function () {
|
describe('when calling addEdges ', function() {
|
||||||
beforeEach(function () {
|
beforeEach(function() {
|
||||||
flowParser.parser.yy = flowDb
|
flowParser.parser.yy = flowDb;
|
||||||
flowDb.clear()
|
flowDb.clear();
|
||||||
})
|
});
|
||||||
it('it should handle edges with text', function () {
|
it('it should handle edges with text', function() {
|
||||||
flowParser.parser.parse('graph TD;A-->|text ex|B;')
|
flowParser.parser.parse('graph TD;A-->|text ex|B;');
|
||||||
flowParser.parser.yy.getVertices()
|
flowParser.parser.yy.getVertices();
|
||||||
const edges = flowParser.parser.yy.getEdges()
|
const edges = flowParser.parser.yy.getEdges();
|
||||||
|
|
||||||
const mockG = {
|
const mockG = {
|
||||||
setEdge: function (start, end, options) {
|
setEdge: function(start, end, options) {
|
||||||
expect(start).toBe('A')
|
expect(start).toBe('A');
|
||||||
expect(end).toBe('B')
|
expect(end).toBe('B');
|
||||||
expect(options.arrowhead).toBe('normal')
|
expect(options.arrowhead).toBe('normal');
|
||||||
expect(options.label.match('text ex')).toBeTruthy()
|
expect(options.label.match('text ex')).toBeTruthy();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
flowRenderer.addEdges(edges, mockG)
|
flowRenderer.addEdges(edges, mockG);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle edges without text', function () {
|
it('should handle edges without text', function() {
|
||||||
flowParser.parser.parse('graph TD;A-->B;')
|
flowParser.parser.parse('graph TD;A-->B;');
|
||||||
flowParser.parser.yy.getVertices()
|
flowParser.parser.yy.getVertices();
|
||||||
const edges = flowParser.parser.yy.getEdges()
|
const edges = flowParser.parser.yy.getEdges();
|
||||||
|
|
||||||
const mockG = {
|
const mockG = {
|
||||||
setEdge: function (start, end, options) {
|
setEdge: function(start, end, options) {
|
||||||
expect(start).toBe('A')
|
expect(start).toBe('A');
|
||||||
expect(end).toBe('B')
|
expect(end).toBe('B');
|
||||||
expect(options.arrowhead).toBe('normal')
|
expect(options.arrowhead).toBe('normal');
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
flowRenderer.addEdges(edges, mockG)
|
flowRenderer.addEdges(edges, mockG);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle open-ended edges', function () {
|
it('should handle open-ended edges', function() {
|
||||||
flowParser.parser.parse('graph TD;A---B;')
|
flowParser.parser.parse('graph TD;A---B;');
|
||||||
flowParser.parser.yy.getVertices()
|
flowParser.parser.yy.getVertices();
|
||||||
const edges = flowParser.parser.yy.getEdges()
|
const edges = flowParser.parser.yy.getEdges();
|
||||||
|
|
||||||
const mockG = {
|
const mockG = {
|
||||||
setEdge: function (start, end, options) {
|
setEdge: function(start, end, options) {
|
||||||
expect(start).toBe('A')
|
expect(start).toBe('A');
|
||||||
expect(end).toBe('B')
|
expect(end).toBe('B');
|
||||||
expect(options.arrowhead).toBe('none')
|
expect(options.arrowhead).toBe('none');
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
flowRenderer.addEdges(edges, mockG)
|
flowRenderer.addEdges(edges, mockG);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle edges with styles defined', function () {
|
it('should handle edges with styles defined', function() {
|
||||||
flowParser.parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2;')
|
flowParser.parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2;');
|
||||||
flowParser.parser.yy.getVertices()
|
flowParser.parser.yy.getVertices();
|
||||||
const edges = flowParser.parser.yy.getEdges()
|
const edges = flowParser.parser.yy.getEdges();
|
||||||
|
|
||||||
const mockG = {
|
const mockG = {
|
||||||
setEdge: function (start, end, options) {
|
setEdge: function(start, end, options) {
|
||||||
expect(start).toBe('A')
|
expect(start).toBe('A');
|
||||||
expect(end).toBe('B')
|
expect(end).toBe('B');
|
||||||
expect(options.arrowhead).toBe('none')
|
expect(options.arrowhead).toBe('none');
|
||||||
expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:none;')
|
expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:none;');
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
flowRenderer.addEdges(edges, mockG)
|
flowRenderer.addEdges(edges, mockG);
|
||||||
})
|
});
|
||||||
it('should handle edges with interpolation defined', function () {
|
it('should handle edges with interpolation defined', function() {
|
||||||
flowParser.parser.parse('graph TD;A---B; linkStyle 0 interpolate basis')
|
flowParser.parser.parse('graph TD;A---B; linkStyle 0 interpolate basis');
|
||||||
flowParser.parser.yy.getVertices()
|
flowParser.parser.yy.getVertices();
|
||||||
const edges = flowParser.parser.yy.getEdges()
|
const edges = flowParser.parser.yy.getEdges();
|
||||||
|
|
||||||
const mockG = {
|
const mockG = {
|
||||||
setEdge: function (start, end, options) {
|
setEdge: function(start, end, options) {
|
||||||
expect(start).toBe('A')
|
expect(start).toBe('A');
|
||||||
expect(end).toBe('B')
|
expect(end).toBe('B');
|
||||||
expect(options.arrowhead).toBe('none')
|
expect(options.arrowhead).toBe('none');
|
||||||
expect(options.curve).toBe('basis') // mocked as string
|
expect(options.curve).toBe('basis'); // mocked as string
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
flowRenderer.addEdges(edges, mockG)
|
flowRenderer.addEdges(edges, mockG);
|
||||||
})
|
});
|
||||||
it('should handle edges with text and styles defined', function () {
|
it('should handle edges with text and styles defined', function() {
|
||||||
flowParser.parser.parse('graph TD;A---|the text|B; linkStyle 0 stroke:val1,stroke-width:val2;')
|
flowParser.parser.parse(
|
||||||
flowParser.parser.yy.getVertices()
|
'graph TD;A---|the text|B; linkStyle 0 stroke:val1,stroke-width:val2;'
|
||||||
const edges = flowParser.parser.yy.getEdges()
|
);
|
||||||
|
flowParser.parser.yy.getVertices();
|
||||||
|
const edges = flowParser.parser.yy.getEdges();
|
||||||
|
|
||||||
const mockG = {
|
const mockG = {
|
||||||
setEdge: function (start, end, options) {
|
setEdge: function(start, end, options) {
|
||||||
expect(start).toBe('A')
|
expect(start).toBe('A');
|
||||||
expect(end).toBe('B')
|
expect(end).toBe('B');
|
||||||
expect(options.arrowhead).toBe('none')
|
expect(options.arrowhead).toBe('none');
|
||||||
expect(options.label.match('the text')).toBeTruthy()
|
expect(options.label.match('the text')).toBeTruthy();
|
||||||
expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:none;')
|
expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:none;');
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
flowRenderer.addEdges(edges, mockG)
|
flowRenderer.addEdges(edges, mockG);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should set fill to "none" by default when handling edges', function () {
|
it('should set fill to "none" by default when handling edges', function() {
|
||||||
flowParser.parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2;')
|
flowParser.parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2;');
|
||||||
flowParser.parser.yy.getVertices()
|
flowParser.parser.yy.getVertices();
|
||||||
const edges = flowParser.parser.yy.getEdges()
|
const edges = flowParser.parser.yy.getEdges();
|
||||||
|
|
||||||
const mockG = {
|
const mockG = {
|
||||||
setEdge: function (start, end, options) {
|
setEdge: function(start, end, options) {
|
||||||
expect(start).toBe('A')
|
expect(start).toBe('A');
|
||||||
expect(end).toBe('B')
|
expect(end).toBe('B');
|
||||||
expect(options.arrowhead).toBe('none')
|
expect(options.arrowhead).toBe('none');
|
||||||
expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:none;')
|
expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:none;');
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
flowRenderer.addEdges(edges, mockG)
|
flowRenderer.addEdges(edges, mockG);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should not set fill to none if fill is set in linkStyle', function () {
|
it('should not set fill to none if fill is set in linkStyle', function() {
|
||||||
flowParser.parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2,fill:blue;')
|
flowParser.parser.parse(
|
||||||
flowParser.parser.yy.getVertices()
|
'graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2,fill:blue;'
|
||||||
const edges = flowParser.parser.yy.getEdges()
|
);
|
||||||
|
flowParser.parser.yy.getVertices();
|
||||||
|
const edges = flowParser.parser.yy.getEdges();
|
||||||
const mockG = {
|
const mockG = {
|
||||||
setEdge: function (start, end, options) {
|
setEdge: function(start, end, options) {
|
||||||
expect(start).toBe('A')
|
expect(start).toBe('A');
|
||||||
expect(end).toBe('B')
|
expect(end).toBe('B');
|
||||||
expect(options.arrowhead).toBe('none')
|
expect(options.arrowhead).toBe('none');
|
||||||
expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:blue;')
|
expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:blue;');
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
flowRenderer.addEdges(edges, mockG)
|
flowRenderer.addEdges(edges, mockG);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
describe('checking validity of input ', function () {
|
describe('checking validity of input ', function() {
|
||||||
it('it should throw for an invalid definiton', function () {
|
it('it should throw for an invalid definiton', function() {
|
||||||
expect(() => mermaid.parse('this is not a mermaid diagram definition')).toThrow()
|
expect(() => mermaid.parse('this is not a mermaid diagram definition')).toThrow();
|
||||||
})
|
});
|
||||||
|
|
||||||
it('it should not throw for a valid flow definition', function () {
|
it('it should not throw for a valid flow definition', function() {
|
||||||
expect(() => mermaid.parse('graph TD;A--x|text including URL space|B;')).not.toThrow()
|
expect(() => mermaid.parse('graph TD;A--x|text including URL space|B;')).not.toThrow();
|
||||||
})
|
});
|
||||||
it('it should throw for an invalid flow definition', function () {
|
it('it should throw for an invalid flow definition', function() {
|
||||||
expect(() => mermaid.parse('graph TQ;A--x|text including URL space|B;')).toThrow()
|
expect(() => mermaid.parse('graph TQ;A--x|text including URL space|B;')).toThrow();
|
||||||
})
|
});
|
||||||
|
|
||||||
it('it should not throw for a valid sequenceDiagram definition', function () {
|
it('it should not throw for a valid sequenceDiagram definition', function() {
|
||||||
const text = 'sequenceDiagram\n' +
|
const text =
|
||||||
|
'sequenceDiagram\n' +
|
||||||
'Alice->Bob: Hello Bob, how are you?\n\n' +
|
'Alice->Bob: Hello Bob, how are you?\n\n' +
|
||||||
'%% Comment\n' +
|
'%% Comment\n' +
|
||||||
'Note right of Bob: Bob thinks\n' +
|
'Note right of Bob: Bob thinks\n' +
|
||||||
@ -197,12 +202,13 @@ describe('when using mermaid and ', function () {
|
|||||||
'Bob-->Alice: I am good thanks!\n' +
|
'Bob-->Alice: I am good thanks!\n' +
|
||||||
'else isSick\n' +
|
'else isSick\n' +
|
||||||
'Bob-->Alice: Feel sick...\n' +
|
'Bob-->Alice: Feel sick...\n' +
|
||||||
'end'
|
'end';
|
||||||
expect(() => mermaid.parse(text)).not.toThrow()
|
expect(() => mermaid.parse(text)).not.toThrow();
|
||||||
})
|
});
|
||||||
|
|
||||||
it('it should throw for an invalid sequenceDiagram definition', function () {
|
it('it should throw for an invalid sequenceDiagram definition', function() {
|
||||||
const text = 'sequenceDiagram\n' +
|
const text =
|
||||||
|
'sequenceDiagram\n' +
|
||||||
'Alice:->Bob: Hello Bob, how are you?\n\n' +
|
'Alice:->Bob: Hello Bob, how are you?\n\n' +
|
||||||
'%% Comment\n' +
|
'%% Comment\n' +
|
||||||
'Note right of Bob: Bob thinks\n' +
|
'Note right of Bob: Bob thinks\n' +
|
||||||
@ -210,8 +216,8 @@ describe('when using mermaid and ', function () {
|
|||||||
'Bob-->Alice: I am good thanks!\n' +
|
'Bob-->Alice: I am good thanks!\n' +
|
||||||
'else isSick\n' +
|
'else isSick\n' +
|
||||||
'Bob-->Alice: Feel sick...\n' +
|
'Bob-->Alice: Feel sick...\n' +
|
||||||
'end'
|
'end';
|
||||||
expect(() => mermaid.parse(text)).toThrow()
|
expect(() => mermaid.parse(text)).toThrow();
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
@ -9,38 +9,38 @@
|
|||||||
* In addition to the render function, a number of behavioral configuration options are available.
|
* In addition to the render function, a number of behavioral configuration options are available.
|
||||||
*
|
*
|
||||||
* @name mermaidAPI
|
* @name mermaidAPI
|
||||||
*/
|
*/
|
||||||
import * as d3 from 'd3'
|
import * as d3 from 'd3';
|
||||||
import scope from 'scope-css'
|
import scope from 'scope-css';
|
||||||
import pkg from '../package.json'
|
import pkg from '../package.json';
|
||||||
import { setConfig, getConfig } from './config'
|
import { setConfig, getConfig } from './config';
|
||||||
import { logger, setLogLevel } from './logger'
|
import { logger, setLogLevel } from './logger';
|
||||||
import utils from './utils'
|
import utils from './utils';
|
||||||
import flowRenderer from './diagrams/flowchart/flowRenderer'
|
import flowRenderer from './diagrams/flowchart/flowRenderer';
|
||||||
import flowParser from './diagrams/flowchart/parser/flow'
|
import flowParser from './diagrams/flowchart/parser/flow';
|
||||||
import flowDb from './diagrams/flowchart/flowDb'
|
import flowDb from './diagrams/flowchart/flowDb';
|
||||||
import sequenceRenderer from './diagrams/sequence/sequenceRenderer'
|
import sequenceRenderer from './diagrams/sequence/sequenceRenderer';
|
||||||
import sequenceParser from './diagrams/sequence/parser/sequenceDiagram'
|
import sequenceParser from './diagrams/sequence/parser/sequenceDiagram';
|
||||||
import sequenceDb from './diagrams/sequence/sequenceDb'
|
import sequenceDb from './diagrams/sequence/sequenceDb';
|
||||||
import ganttRenderer from './diagrams/gantt/ganttRenderer'
|
import ganttRenderer from './diagrams/gantt/ganttRenderer';
|
||||||
import ganttParser from './diagrams/gantt/parser/gantt'
|
import ganttParser from './diagrams/gantt/parser/gantt';
|
||||||
import ganttDb from './diagrams/gantt/ganttDb'
|
import ganttDb from './diagrams/gantt/ganttDb';
|
||||||
import classRenderer from './diagrams/class/classRenderer'
|
import classRenderer from './diagrams/class/classRenderer';
|
||||||
import classParser from './diagrams/class/parser/classDiagram'
|
import classParser from './diagrams/class/parser/classDiagram';
|
||||||
import classDb from './diagrams/class/classDb'
|
import classDb from './diagrams/class/classDb';
|
||||||
import gitGraphRenderer from './diagrams/git/gitGraphRenderer'
|
import gitGraphRenderer from './diagrams/git/gitGraphRenderer';
|
||||||
import gitGraphParser from './diagrams/git/parser/gitGraph'
|
import gitGraphParser from './diagrams/git/parser/gitGraph';
|
||||||
import gitGraphAst from './diagrams/git/gitGraphAst'
|
import gitGraphAst from './diagrams/git/gitGraphAst';
|
||||||
import infoRenderer from './diagrams/info/infoRenderer'
|
import infoRenderer from './diagrams/info/infoRenderer';
|
||||||
import infoParser from './diagrams/info/parser/info'
|
import infoParser from './diagrams/info/parser/info';
|
||||||
import infoDb from './diagrams/info/infoDb'
|
import infoDb from './diagrams/info/infoDb';
|
||||||
import pieRenderer from './diagrams/pie/pieRenderer'
|
import pieRenderer from './diagrams/pie/pieRenderer';
|
||||||
import pieParser from './diagrams/pie/parser/pie'
|
import pieParser from './diagrams/pie/parser/pie';
|
||||||
import pieDb from './diagrams/pie/pieDb'
|
import pieDb from './diagrams/pie/pieDb';
|
||||||
|
|
||||||
const themes = {}
|
const themes = {};
|
||||||
for (const themeName of ['default', 'forest', 'dark', 'neutral']) {
|
for (const themeName of ['default', 'forest', 'dark', 'neutral']) {
|
||||||
themes[themeName] = require(`./themes/${themeName}/index.scss`)
|
themes[themeName] = require(`./themes/${themeName}/index.scss`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,22 +75,21 @@ for (const themeName of ['default', 'forest', 'dark', 'neutral']) {
|
|||||||
* @name Configuration
|
* @name Configuration
|
||||||
*/
|
*/
|
||||||
const config = {
|
const config = {
|
||||||
|
|
||||||
/** theme , the CSS style sheet
|
/** theme , the CSS style sheet
|
||||||
*
|
*
|
||||||
* **theme** - Choose one of the built-in themes:
|
* **theme** - Choose one of the built-in themes:
|
||||||
* * default
|
* * default
|
||||||
* * forest
|
* * forest
|
||||||
* * dark
|
* * dark
|
||||||
* * neutral.
|
* * neutral.
|
||||||
* To disable any pre-defined mermaid theme, use "null".
|
* To disable any pre-defined mermaid theme, use "null".
|
||||||
*
|
*
|
||||||
* **themeCSS** - Use your own CSS. This overrides **theme**.
|
* **themeCSS** - Use your own CSS. This overrides **theme**.
|
||||||
* <pre>
|
* <pre>
|
||||||
* "theme": "forest",
|
* "theme": "forest",
|
||||||
* "themeCSS": ".node rect { fill: red; }"
|
* "themeCSS": ".node rect { fill: red; }"
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
theme: 'default',
|
theme: 'default',
|
||||||
themeCSS: undefined,
|
themeCSS: undefined,
|
||||||
@ -149,7 +148,6 @@ const config = {
|
|||||||
* The object containing configurations specific for sequence diagrams
|
* The object containing configurations specific for sequence diagrams
|
||||||
*/
|
*/
|
||||||
sequence: {
|
sequence: {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* margin to the right and left of the sequence diagram.
|
* margin to the right and left of the sequence diagram.
|
||||||
* **Default value 50**.
|
* **Default value 50**.
|
||||||
@ -234,7 +232,6 @@ const config = {
|
|||||||
* **Default value false**.
|
* **Default value false**.
|
||||||
*/
|
*/
|
||||||
showSequenceNumbers: false
|
showSequenceNumbers: false
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -303,100 +300,100 @@ const config = {
|
|||||||
},
|
},
|
||||||
class: {},
|
class: {},
|
||||||
git: {}
|
git: {}
|
||||||
}
|
};
|
||||||
|
|
||||||
setLogLevel(config.logLevel)
|
setLogLevel(config.logLevel);
|
||||||
setConfig(config)
|
setConfig(config);
|
||||||
|
|
||||||
function parse (text) {
|
function parse(text) {
|
||||||
const graphType = utils.detectType(text)
|
const graphType = utils.detectType(text);
|
||||||
let parser
|
let parser;
|
||||||
|
|
||||||
logger.debug('Type ' + graphType)
|
logger.debug('Type ' + graphType);
|
||||||
switch (graphType) {
|
switch (graphType) {
|
||||||
case 'git':
|
case 'git':
|
||||||
parser = gitGraphParser
|
parser = gitGraphParser;
|
||||||
parser.parser.yy = gitGraphAst
|
parser.parser.yy = gitGraphAst;
|
||||||
break
|
break;
|
||||||
case 'flowchart':
|
case 'flowchart':
|
||||||
parser = flowParser
|
parser = flowParser;
|
||||||
parser.parser.yy = flowDb
|
parser.parser.yy = flowDb;
|
||||||
break
|
break;
|
||||||
case 'sequence':
|
case 'sequence':
|
||||||
parser = sequenceParser
|
parser = sequenceParser;
|
||||||
parser.parser.yy = sequenceDb
|
parser.parser.yy = sequenceDb;
|
||||||
break
|
break;
|
||||||
case 'gantt':
|
case 'gantt':
|
||||||
parser = ganttParser
|
parser = ganttParser;
|
||||||
parser.parser.yy = ganttDb
|
parser.parser.yy = ganttDb;
|
||||||
break
|
break;
|
||||||
case 'class':
|
case 'class':
|
||||||
parser = classParser
|
parser = classParser;
|
||||||
parser.parser.yy = classDb
|
parser.parser.yy = classDb;
|
||||||
break
|
break;
|
||||||
case 'info':
|
case 'info':
|
||||||
logger.debug('info info info')
|
logger.debug('info info info');
|
||||||
console.warn('In API', pkg.version)
|
console.warn('In API', pkg.version);
|
||||||
|
|
||||||
parser = infoParser
|
parser = infoParser;
|
||||||
parser.parser.yy = infoDb
|
parser.parser.yy = infoDb;
|
||||||
break
|
break;
|
||||||
case 'pie':
|
case 'pie':
|
||||||
logger.debug('pie')
|
logger.debug('pie');
|
||||||
parser = pieParser
|
parser = pieParser;
|
||||||
parser.parser.yy = pieDb
|
parser.parser.yy = pieDb;
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
parser.parser.yy.parseError = (str, hash) => {
|
parser.parser.yy.parseError = (str, hash) => {
|
||||||
const error = { str, hash }
|
const error = { str, hash };
|
||||||
throw error
|
throw error;
|
||||||
}
|
};
|
||||||
|
|
||||||
parser.parse(text)
|
parser.parse(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const encodeEntities = function (text) {
|
export const encodeEntities = function(text) {
|
||||||
let txt = text
|
let txt = text;
|
||||||
|
|
||||||
txt = txt.replace(/style.*:\S*#.*;/g, function (s) {
|
txt = txt.replace(/style.*:\S*#.*;/g, function(s) {
|
||||||
const innerTxt = s.substring(0, s.length - 1)
|
const innerTxt = s.substring(0, s.length - 1);
|
||||||
return innerTxt
|
return innerTxt;
|
||||||
})
|
});
|
||||||
txt = txt.replace(/classDef.*:\S*#.*;/g, function (s) {
|
txt = txt.replace(/classDef.*:\S*#.*;/g, function(s) {
|
||||||
const innerTxt = s.substring(0, s.length - 1)
|
const innerTxt = s.substring(0, s.length - 1);
|
||||||
return innerTxt
|
return innerTxt;
|
||||||
})
|
});
|
||||||
|
|
||||||
txt = txt.replace(/#\w+;/g, function (s) {
|
txt = txt.replace(/#\w+;/g, function(s) {
|
||||||
const innerTxt = s.substring(1, s.length - 1)
|
const innerTxt = s.substring(1, s.length - 1);
|
||||||
|
|
||||||
const isInt = /^\+?\d+$/.test(innerTxt)
|
const isInt = /^\+?\d+$/.test(innerTxt);
|
||||||
if (isInt) {
|
if (isInt) {
|
||||||
return 'fl°°' + innerTxt + '¶ß'
|
return 'fl°°' + innerTxt + '¶ß';
|
||||||
} else {
|
} else {
|
||||||
return 'fl°' + innerTxt + '¶ß'
|
return 'fl°' + innerTxt + '¶ß';
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
return txt
|
return txt;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const decodeEntities = function (text) {
|
export const decodeEntities = function(text) {
|
||||||
let txt = text
|
let txt = text;
|
||||||
|
|
||||||
txt = txt.replace(/fl°°/g, function () {
|
txt = txt.replace(/fl°°/g, function() {
|
||||||
return '&#'
|
return '&#';
|
||||||
})
|
});
|
||||||
txt = txt.replace(/fl°/g, function () {
|
txt = txt.replace(/fl°/g, function() {
|
||||||
return '&'
|
return '&';
|
||||||
})
|
});
|
||||||
txt = txt.replace(/¶ß/g, function () {
|
txt = txt.replace(/¶ß/g, function() {
|
||||||
return ';'
|
return ';';
|
||||||
})
|
});
|
||||||
|
|
||||||
return txt
|
return txt;
|
||||||
}
|
};
|
||||||
/**
|
/**
|
||||||
* Function that renders an svg with a graph from a chart definition. Usage example below.
|
* Function that renders an svg with a graph from a chart definition. Usage example below.
|
||||||
*
|
*
|
||||||
@ -419,183 +416,209 @@ export const decodeEntities = function (text) {
|
|||||||
* provided a hidden div will be inserted in the body of the page instead. The element will be removed when rendering is
|
* provided a hidden div will be inserted in the body of the page instead. The element will be removed when rendering is
|
||||||
* completed.
|
* completed.
|
||||||
*/
|
*/
|
||||||
const render = function (id, txt, cb, container) {
|
const render = function(id, txt, cb, container) {
|
||||||
if (typeof container !== 'undefined') {
|
if (typeof container !== 'undefined') {
|
||||||
container.innerHTML = ''
|
container.innerHTML = '';
|
||||||
|
|
||||||
d3.select(container).append('div')
|
d3.select(container)
|
||||||
|
.append('div')
|
||||||
.attr('id', 'd' + id)
|
.attr('id', 'd' + id)
|
||||||
.append('svg')
|
.append('svg')
|
||||||
.attr('id', id)
|
.attr('id', id)
|
||||||
.attr('width', '100%')
|
.attr('width', '100%')
|
||||||
.attr('xmlns', 'http://www.w3.org/2000/svg')
|
.attr('xmlns', 'http://www.w3.org/2000/svg')
|
||||||
.append('g')
|
.append('g');
|
||||||
} else {
|
} else {
|
||||||
const element = document.querySelector('#' + 'd' + id)
|
const element = document.querySelector('#' + 'd' + id);
|
||||||
if (element) {
|
if (element) {
|
||||||
element.innerHTML = ''
|
element.innerHTML = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
d3.select('body').append('div')
|
d3.select('body')
|
||||||
|
.append('div')
|
||||||
.attr('id', 'd' + id)
|
.attr('id', 'd' + id)
|
||||||
.append('svg')
|
.append('svg')
|
||||||
.attr('id', id)
|
.attr('id', id)
|
||||||
.attr('width', '100%')
|
.attr('width', '100%')
|
||||||
.attr('xmlns', 'http://www.w3.org/2000/svg')
|
.attr('xmlns', 'http://www.w3.org/2000/svg')
|
||||||
.append('g')
|
.append('g');
|
||||||
}
|
}
|
||||||
|
|
||||||
window.txt = txt
|
window.txt = txt;
|
||||||
txt = encodeEntities(txt)
|
txt = encodeEntities(txt);
|
||||||
|
|
||||||
const element = d3.select('#d' + id).node()
|
const element = d3.select('#d' + id).node();
|
||||||
const graphType = utils.detectType(txt)
|
const graphType = utils.detectType(txt);
|
||||||
|
|
||||||
// insert inline style into svg
|
// insert inline style into svg
|
||||||
const svg = element.firstChild
|
const svg = element.firstChild;
|
||||||
const firstChild = svg.firstChild
|
const firstChild = svg.firstChild;
|
||||||
|
|
||||||
// pre-defined theme
|
// pre-defined theme
|
||||||
let style = themes[config.theme]
|
let style = themes[config.theme];
|
||||||
if (style === undefined) {
|
if (style === undefined) {
|
||||||
style = ''
|
style = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// user provided theme CSS
|
// user provided theme CSS
|
||||||
if (config.themeCSS !== undefined) {
|
if (config.themeCSS !== undefined) {
|
||||||
style += `\n${config.themeCSS}`
|
style += `\n${config.themeCSS}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// classDef
|
// classDef
|
||||||
if (graphType === 'flowchart') {
|
if (graphType === 'flowchart') {
|
||||||
const classes = flowRenderer.getClasses(txt)
|
const classes = flowRenderer.getClasses(txt);
|
||||||
for (const className in classes) {
|
for (const className in classes) {
|
||||||
style += `\n.${className} > * { ${classes[className].styles.join(' !important; ')} !important; }`
|
style += `\n.${className} > * { ${classes[className].styles.join(
|
||||||
|
' !important; '
|
||||||
|
)} !important; }`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const style1 = document.createElement('style')
|
const style1 = document.createElement('style');
|
||||||
style1.innerHTML = scope(style, `#${id}`)
|
style1.innerHTML = scope(style, `#${id}`);
|
||||||
svg.insertBefore(style1, firstChild)
|
svg.insertBefore(style1, firstChild);
|
||||||
|
|
||||||
const style2 = document.createElement('style')
|
const style2 = document.createElement('style');
|
||||||
const cs = window.getComputedStyle(svg)
|
const cs = window.getComputedStyle(svg);
|
||||||
style2.innerHTML = `#${id} {
|
style2.innerHTML = `#${id} {
|
||||||
color: ${cs.color};
|
color: ${cs.color};
|
||||||
font: ${cs.font};
|
font: ${cs.font};
|
||||||
}`
|
}`;
|
||||||
svg.insertBefore(style2, firstChild)
|
svg.insertBefore(style2, firstChild);
|
||||||
|
|
||||||
switch (graphType) {
|
switch (graphType) {
|
||||||
case 'git':
|
case 'git':
|
||||||
config.flowchart.arrowMarkerAbsolute = config.arrowMarkerAbsolute
|
config.flowchart.arrowMarkerAbsolute = config.arrowMarkerAbsolute;
|
||||||
gitGraphRenderer.setConf(config.git)
|
gitGraphRenderer.setConf(config.git);
|
||||||
gitGraphRenderer.draw(txt, id, false)
|
gitGraphRenderer.draw(txt, id, false);
|
||||||
break
|
break;
|
||||||
case 'flowchart':
|
case 'flowchart':
|
||||||
config.flowchart.arrowMarkerAbsolute = config.arrowMarkerAbsolute
|
config.flowchart.arrowMarkerAbsolute = config.arrowMarkerAbsolute;
|
||||||
flowRenderer.setConf(config.flowchart)
|
flowRenderer.setConf(config.flowchart);
|
||||||
flowRenderer.draw(txt, id, false)
|
flowRenderer.draw(txt, id, false);
|
||||||
break
|
break;
|
||||||
case 'sequence':
|
case 'sequence':
|
||||||
config.sequence.arrowMarkerAbsolute = config.arrowMarkerAbsolute
|
config.sequence.arrowMarkerAbsolute = config.arrowMarkerAbsolute;
|
||||||
if (config.sequenceDiagram) { // backwards compatibility
|
if (config.sequenceDiagram) {
|
||||||
sequenceRenderer.setConf(Object.assign(config.sequence, config.sequenceDiagram))
|
// backwards compatibility
|
||||||
console.error('`mermaid config.sequenceDiagram` has been renamed to `config.sequence`. Please update your mermaid config.')
|
sequenceRenderer.setConf(Object.assign(config.sequence, config.sequenceDiagram));
|
||||||
|
console.error(
|
||||||
|
'`mermaid config.sequenceDiagram` has been renamed to `config.sequence`. Please update your mermaid config.'
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
sequenceRenderer.setConf(config.sequence)
|
sequenceRenderer.setConf(config.sequence);
|
||||||
}
|
}
|
||||||
sequenceRenderer.draw(txt, id)
|
sequenceRenderer.draw(txt, id);
|
||||||
break
|
break;
|
||||||
case 'gantt':
|
case 'gantt':
|
||||||
config.gantt.arrowMarkerAbsolute = config.arrowMarkerAbsolute
|
config.gantt.arrowMarkerAbsolute = config.arrowMarkerAbsolute;
|
||||||
ganttRenderer.setConf(config.gantt)
|
ganttRenderer.setConf(config.gantt);
|
||||||
ganttRenderer.draw(txt, id)
|
ganttRenderer.draw(txt, id);
|
||||||
break
|
break;
|
||||||
case 'class':
|
case 'class':
|
||||||
config.class.arrowMarkerAbsolute = config.arrowMarkerAbsolute
|
config.class.arrowMarkerAbsolute = config.arrowMarkerAbsolute;
|
||||||
classRenderer.setConf(config.class)
|
classRenderer.setConf(config.class);
|
||||||
classRenderer.draw(txt, id)
|
classRenderer.draw(txt, id);
|
||||||
break
|
break;
|
||||||
case 'info':
|
case 'info':
|
||||||
config.class.arrowMarkerAbsolute = config.arrowMarkerAbsolute
|
config.class.arrowMarkerAbsolute = config.arrowMarkerAbsolute;
|
||||||
infoRenderer.setConf(config.class)
|
infoRenderer.setConf(config.class);
|
||||||
infoRenderer.draw(txt, id, pkg.version)
|
infoRenderer.draw(txt, id, pkg.version);
|
||||||
break
|
break;
|
||||||
case 'pie':
|
case 'pie':
|
||||||
config.class.arrowMarkerAbsolute = config.arrowMarkerAbsolute
|
config.class.arrowMarkerAbsolute = config.arrowMarkerAbsolute;
|
||||||
pieRenderer.setConf(config.class)
|
pieRenderer.setConf(config.class);
|
||||||
pieRenderer.draw(txt, id, pkg.version)
|
pieRenderer.draw(txt, id, pkg.version);
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
d3.select(`[id="${id}"]`).selectAll('foreignobject > *').attr('xmlns', 'http://www.w3.org/1999/xhtml')
|
d3.select(`[id="${id}"]`)
|
||||||
|
.selectAll('foreignobject > *')
|
||||||
|
.attr('xmlns', 'http://www.w3.org/1999/xhtml');
|
||||||
|
|
||||||
let url = ''
|
let url = '';
|
||||||
if (config.arrowMarkerAbsolute) {
|
if (config.arrowMarkerAbsolute) {
|
||||||
url = window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search
|
url =
|
||||||
url = url.replace(/\(/g, '\\(')
|
window.location.protocol +
|
||||||
url = url.replace(/\)/g, '\\)')
|
'//' +
|
||||||
|
window.location.host +
|
||||||
|
window.location.pathname +
|
||||||
|
window.location.search;
|
||||||
|
url = url.replace(/\(/g, '\\(');
|
||||||
|
url = url.replace(/\)/g, '\\)');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix for when the base tag is used
|
// Fix for when the base tag is used
|
||||||
let svgCode = d3.select('#d' + id).node().innerHTML.replace(/url\(#arrowhead/g, 'url(' + url + '#arrowhead', 'g')
|
let svgCode = d3
|
||||||
|
.select('#d' + id)
|
||||||
|
.node()
|
||||||
|
.innerHTML.replace(/url\(#arrowhead/g, 'url(' + url + '#arrowhead', 'g');
|
||||||
|
|
||||||
svgCode = decodeEntities(svgCode)
|
svgCode = decodeEntities(svgCode);
|
||||||
|
|
||||||
if (typeof cb !== 'undefined') {
|
if (typeof cb !== 'undefined') {
|
||||||
switch (graphType) {
|
switch (graphType) {
|
||||||
case 'flowchart':
|
case 'flowchart':
|
||||||
cb(svgCode, flowDb.bindFunctions)
|
cb(svgCode, flowDb.bindFunctions);
|
||||||
break
|
break;
|
||||||
case 'gantt':
|
case 'gantt':
|
||||||
cb(svgCode, ganttDb.bindFunctions)
|
cb(svgCode, ganttDb.bindFunctions);
|
||||||
break
|
break;
|
||||||
default:
|
default:
|
||||||
cb(svgCode)
|
cb(svgCode);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.debug('CB = undefined!')
|
logger.debug('CB = undefined!');
|
||||||
}
|
}
|
||||||
|
|
||||||
const node = d3.select('#d' + id).node()
|
const node = d3.select('#d' + id).node();
|
||||||
if (node !== null && typeof node.remove === 'function') {
|
if (node !== null && typeof node.remove === 'function') {
|
||||||
d3.select('#d' + id).node().remove()
|
d3.select('#d' + id)
|
||||||
|
.node()
|
||||||
|
.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
return svgCode
|
return svgCode;
|
||||||
}
|
};
|
||||||
|
|
||||||
const setConf = function (cnf) {
|
const setConf = function(cnf) {
|
||||||
// Top level initially mermaid, gflow, sequenceDiagram and gantt
|
// Top level initially mermaid, gflow, sequenceDiagram and gantt
|
||||||
const lvl1Keys = Object.keys(cnf)
|
const lvl1Keys = Object.keys(cnf);
|
||||||
for (let i = 0; i < lvl1Keys.length; i++) {
|
for (let i = 0; i < lvl1Keys.length; i++) {
|
||||||
if (typeof cnf[lvl1Keys[i]] === 'object' && cnf[lvl1Keys[i]] != null) {
|
if (typeof cnf[lvl1Keys[i]] === 'object' && cnf[lvl1Keys[i]] != null) {
|
||||||
const lvl2Keys = Object.keys(cnf[lvl1Keys[i]])
|
const lvl2Keys = Object.keys(cnf[lvl1Keys[i]]);
|
||||||
|
|
||||||
for (let j = 0; j < lvl2Keys.length; j++) {
|
for (let j = 0; j < lvl2Keys.length; j++) {
|
||||||
logger.debug('Setting conf ', lvl1Keys[i], '-', lvl2Keys[j])
|
logger.debug('Setting conf ', lvl1Keys[i], '-', lvl2Keys[j]);
|
||||||
if (typeof config[lvl1Keys[i]] === 'undefined') {
|
if (typeof config[lvl1Keys[i]] === 'undefined') {
|
||||||
config[lvl1Keys[i]] = {}
|
config[lvl1Keys[i]] = {};
|
||||||
}
|
}
|
||||||
logger.debug('Setting config: ' + lvl1Keys[i] + ' ' + lvl2Keys[j] + ' to ' + cnf[lvl1Keys[i]][lvl2Keys[j]])
|
logger.debug(
|
||||||
config[lvl1Keys[i]][lvl2Keys[j]] = cnf[lvl1Keys[i]][lvl2Keys[j]]
|
'Setting config: ' +
|
||||||
|
lvl1Keys[i] +
|
||||||
|
' ' +
|
||||||
|
lvl2Keys[j] +
|
||||||
|
' to ' +
|
||||||
|
cnf[lvl1Keys[i]][lvl2Keys[j]]
|
||||||
|
);
|
||||||
|
config[lvl1Keys[i]][lvl2Keys[j]] = cnf[lvl1Keys[i]][lvl2Keys[j]];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
config[lvl1Keys[i]] = cnf[lvl1Keys[i]]
|
config[lvl1Keys[i]] = cnf[lvl1Keys[i]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
function initialize (options) {
|
function initialize(options) {
|
||||||
logger.debug('Initializing mermaidAPI ', pkg.version)
|
logger.debug('Initializing mermaidAPI ', pkg.version);
|
||||||
|
|
||||||
// Update default config with options supplied at initialization
|
// Update default config with options supplied at initialization
|
||||||
if (typeof options === 'object') {
|
if (typeof options === 'object') {
|
||||||
setConf(options)
|
setConf(options);
|
||||||
}
|
}
|
||||||
setConfig(config)
|
setConfig(config);
|
||||||
setLogLevel(config.logLevel)
|
setLogLevel(config.logLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// function getConfig () {
|
// function getConfig () {
|
||||||
@ -608,9 +631,9 @@ const mermaidAPI = {
|
|||||||
parse,
|
parse,
|
||||||
initialize,
|
initialize,
|
||||||
getConfig
|
getConfig
|
||||||
}
|
};
|
||||||
|
|
||||||
export default mermaidAPI
|
export default mermaidAPI;
|
||||||
/**
|
/**
|
||||||
* ## mermaidAPI configuration defaults
|
* ## mermaidAPI configuration defaults
|
||||||
* <pre>
|
* <pre>
|
||||||
|
@ -1,45 +1,45 @@
|
|||||||
/* eslint-env jasmine */
|
/* eslint-env jasmine */
|
||||||
import mermaidAPI from './mermaidAPI'
|
import mermaidAPI from './mermaidAPI';
|
||||||
|
|
||||||
describe('when using mermaidAPI and ', function () {
|
describe('when using mermaidAPI and ', function() {
|
||||||
describe('doing initialize ', function () {
|
describe('doing initialize ', function() {
|
||||||
beforeEach(function () {
|
beforeEach(function() {
|
||||||
document.body.innerHTML = ''
|
document.body.innerHTML = '';
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should copy a literal into the configuration', function () {
|
it('should copy a literal into the configuration', function() {
|
||||||
const orgConfig = mermaidAPI.getConfig()
|
const orgConfig = mermaidAPI.getConfig();
|
||||||
expect(orgConfig.testLiteral).toBe(undefined)
|
expect(orgConfig.testLiteral).toBe(undefined);
|
||||||
|
|
||||||
mermaidAPI.initialize({ 'testLiteral': true })
|
mermaidAPI.initialize({ testLiteral: true });
|
||||||
const config = mermaidAPI.getConfig()
|
const config = mermaidAPI.getConfig();
|
||||||
|
|
||||||
expect(config.testLiteral).toBe(true)
|
expect(config.testLiteral).toBe(true);
|
||||||
})
|
});
|
||||||
it('should copy a an object into the configuration', function () {
|
it('should copy a an object into the configuration', function() {
|
||||||
const orgConfig = mermaidAPI.getConfig()
|
const orgConfig = mermaidAPI.getConfig();
|
||||||
expect(orgConfig.testObject).toBe(undefined)
|
expect(orgConfig.testObject).toBe(undefined);
|
||||||
|
|
||||||
const object = {
|
const object = {
|
||||||
test1: 1,
|
test1: 1,
|
||||||
test2: false
|
test2: false
|
||||||
}
|
};
|
||||||
|
|
||||||
mermaidAPI.initialize({ 'testObject': object })
|
mermaidAPI.initialize({ testObject: object });
|
||||||
mermaidAPI.initialize({ 'testObject': { 'test3': true } })
|
mermaidAPI.initialize({ testObject: { test3: true } });
|
||||||
const config = mermaidAPI.getConfig()
|
const config = mermaidAPI.getConfig();
|
||||||
|
|
||||||
expect(config.testObject.test1).toBe(1)
|
expect(config.testObject.test1).toBe(1);
|
||||||
expect(config.testObject.test2).toBe(false)
|
expect(config.testObject.test2).toBe(false);
|
||||||
expect(config.testObject.test3).toBe(true)
|
expect(config.testObject.test3).toBe(true);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
describe('checking validity of input ', function () {
|
describe('checking validity of input ', function() {
|
||||||
it('it should throw for an invalid definiton', function () {
|
it('it should throw for an invalid definiton', function() {
|
||||||
expect(() => mermaidAPI.parse('this is not a mermaid diagram definition')).toThrow()
|
expect(() => mermaidAPI.parse('this is not a mermaid diagram definition')).toThrow();
|
||||||
})
|
});
|
||||||
it('it should not throw for a valid definiton', function () {
|
it('it should not throw for a valid definiton', function() {
|
||||||
expect(() => mermaidAPI.parse('graph TD;A--x|text including URL space|B;')).not.toThrow()
|
expect(() => mermaidAPI.parse('graph TD;A--x|text including URL space|B;')).not.toThrow();
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
44
src/utils.js
44
src/utils.js
@ -1,5 +1,5 @@
|
|||||||
import * as d3 from 'd3'
|
import * as d3 from 'd3';
|
||||||
import { logger } from './logger'
|
import { logger } from './logger';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function detectType
|
* @function detectType
|
||||||
@ -18,34 +18,34 @@ import { logger } from './logger'
|
|||||||
* @param {string} text The text defining the graph
|
* @param {string} text The text defining the graph
|
||||||
* @returns {string} A graph definition key
|
* @returns {string} A graph definition key
|
||||||
*/
|
*/
|
||||||
export const detectType = function (text) {
|
export const detectType = function(text) {
|
||||||
text = text.replace(/^\s*%%.*\n/g, '\n')
|
text = text.replace(/^\s*%%.*\n/g, '\n');
|
||||||
logger.debug('Detecting diagram type based on the text ' + text)
|
logger.debug('Detecting diagram type based on the text ' + text);
|
||||||
if (text.match(/^\s*sequenceDiagram/)) {
|
if (text.match(/^\s*sequenceDiagram/)) {
|
||||||
return 'sequence'
|
return 'sequence';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (text.match(/^\s*gantt/)) {
|
if (text.match(/^\s*gantt/)) {
|
||||||
return 'gantt'
|
return 'gantt';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (text.match(/^\s*classDiagram/)) {
|
if (text.match(/^\s*classDiagram/)) {
|
||||||
return 'class'
|
return 'class';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (text.match(/^\s*gitGraph/)) {
|
if (text.match(/^\s*gitGraph/)) {
|
||||||
return 'git'
|
return 'git';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (text.match(/^\s*info/)) {
|
if (text.match(/^\s*info/)) {
|
||||||
return 'info'
|
return 'info';
|
||||||
}
|
}
|
||||||
if (text.match(/^\s*pie/)) {
|
if (text.match(/^\s*pie/)) {
|
||||||
return 'pie'
|
return 'pie';
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'flowchart'
|
return 'flowchart';
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function isSubstringInArray
|
* @function isSubstringInArray
|
||||||
@ -54,23 +54,23 @@ export const detectType = function (text) {
|
|||||||
* @param {array} arr The array to search
|
* @param {array} arr The array to search
|
||||||
* @returns {number} the array index containing the substring or -1 if not present
|
* @returns {number} the array index containing the substring or -1 if not present
|
||||||
**/
|
**/
|
||||||
export const isSubstringInArray = function (str, arr) {
|
export const isSubstringInArray = function(str, arr) {
|
||||||
for (let i = 0; i < arr.length; i++) {
|
for (let i = 0; i < arr.length; i++) {
|
||||||
if (arr[i].match(str)) return i
|
if (arr[i].match(str)) return i;
|
||||||
}
|
}
|
||||||
return -1
|
return -1;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const interpolateToCurve = (interpolate, defaultCurve) => {
|
export const interpolateToCurve = (interpolate, defaultCurve) => {
|
||||||
if (!interpolate) {
|
if (!interpolate) {
|
||||||
return defaultCurve
|
return defaultCurve;
|
||||||
}
|
}
|
||||||
const curveName = `curve${interpolate.charAt(0).toUpperCase() + interpolate.slice(1)}`
|
const curveName = `curve${interpolate.charAt(0).toUpperCase() + interpolate.slice(1)}`;
|
||||||
return d3[curveName] || defaultCurve
|
return d3[curveName] || defaultCurve;
|
||||||
}
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
detectType,
|
detectType,
|
||||||
isSubstringInArray,
|
isSubstringInArray,
|
||||||
interpolateToCurve
|
interpolateToCurve
|
||||||
}
|
};
|
||||||
|
@ -1,39 +1,39 @@
|
|||||||
/* eslint-env jasmine */
|
/* eslint-env jasmine */
|
||||||
import utils from './utils'
|
import utils from './utils';
|
||||||
|
|
||||||
describe('when detecting chart type ', function () {
|
describe('when detecting chart type ', function() {
|
||||||
it('should handle a graph defintion', function () {
|
it('should handle a graph defintion', function() {
|
||||||
const str = 'graph TB\nbfs1:queue'
|
const str = 'graph TB\nbfs1:queue';
|
||||||
const type = utils.detectType(str)
|
const type = utils.detectType(str);
|
||||||
expect(type).toBe('flowchart')
|
expect(type).toBe('flowchart');
|
||||||
})
|
});
|
||||||
it('should handle a graph defintion with leading spaces', function () {
|
it('should handle a graph defintion with leading spaces', function() {
|
||||||
const str = ' graph TB\nbfs1:queue'
|
const str = ' graph TB\nbfs1:queue';
|
||||||
const type = utils.detectType(str)
|
const type = utils.detectType(str);
|
||||||
expect(type).toBe('flowchart')
|
expect(type).toBe('flowchart');
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle a graph defintion with leading spaces and newline', function () {
|
it('should handle a graph defintion with leading spaces and newline', function() {
|
||||||
const str = ' \n graph TB\nbfs1:queue'
|
const str = ' \n graph TB\nbfs1:queue';
|
||||||
const type = utils.detectType(str)
|
const type = utils.detectType(str);
|
||||||
expect(type).toBe('flowchart')
|
expect(type).toBe('flowchart');
|
||||||
})
|
});
|
||||||
it('should handle a graph defintion for gitGraph', function () {
|
it('should handle a graph defintion for gitGraph', function() {
|
||||||
const str = ' \n gitGraph TB:\nbfs1:queue'
|
const str = ' \n gitGraph TB:\nbfs1:queue';
|
||||||
const type = utils.detectType(str)
|
const type = utils.detectType(str);
|
||||||
expect(type).toBe('git')
|
expect(type).toBe('git');
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
describe('when finding substring in array ', function () {
|
describe('when finding substring in array ', function() {
|
||||||
it('should return the array index that contains the substring', function () {
|
it('should return the array index that contains the substring', function() {
|
||||||
const arr = ['stroke:val1', 'fill:val2']
|
const arr = ['stroke:val1', 'fill:val2'];
|
||||||
const result = utils.isSubstringInArray('fill', arr)
|
const result = utils.isSubstringInArray('fill', arr);
|
||||||
expect(result).toEqual(1)
|
expect(result).toEqual(1);
|
||||||
})
|
});
|
||||||
it('should return -1 if the substring is not found in the array', function () {
|
it('should return -1 if the substring is not found in the array', function() {
|
||||||
const arr = ['stroke:val1', 'stroke-width:val2']
|
const arr = ['stroke:val1', 'stroke-width:val2'];
|
||||||
const result = utils.isSubstringInArray('fill', arr)
|
const result = utils.isSubstringInArray('fill', arr);
|
||||||
expect(result).toEqual(-1)
|
expect(result).toEqual(-1);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
273
yarn.lock
273
yarn.lock
@ -1528,6 +1528,11 @@ acorn-jsx@^5.0.0:
|
|||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e"
|
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e"
|
||||||
|
|
||||||
|
acorn-jsx@^5.0.2:
|
||||||
|
version "5.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.2.tgz#84b68ea44b373c4f8686023a551f61a21b7c4a4f"
|
||||||
|
integrity sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==
|
||||||
|
|
||||||
acorn-walk@^6.0.1:
|
acorn-walk@^6.0.1:
|
||||||
version "6.1.1"
|
version "6.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.1.1.tgz#d363b66f5fac5f018ff9c3a1e7b6f8e310cc3913"
|
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.1.1.tgz#d363b66f5fac5f018ff9c3a1e7b6f8e310cc3913"
|
||||||
@ -1540,6 +1545,11 @@ acorn@^6.0.1, acorn@^6.0.2:
|
|||||||
version "6.0.4"
|
version "6.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.4.tgz#77377e7353b72ec5104550aa2d2097a2fd40b754"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.4.tgz#77377e7353b72ec5104550aa2d2097a2fd40b754"
|
||||||
|
|
||||||
|
acorn@^7.0.0:
|
||||||
|
version "7.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.0.0.tgz#26b8d1cd9a9b700350b71c0905546f64d1284e7a"
|
||||||
|
integrity sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ==
|
||||||
|
|
||||||
agent-base@^4.1.0:
|
agent-base@^4.1.0:
|
||||||
version "4.2.1"
|
version "4.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
|
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
|
||||||
@ -1575,6 +1585,16 @@ ajv@^6.1.0:
|
|||||||
fast-json-stable-stringify "^2.0.0"
|
fast-json-stable-stringify "^2.0.0"
|
||||||
json-schema-traverse "^0.3.0"
|
json-schema-traverse "^0.3.0"
|
||||||
|
|
||||||
|
ajv@^6.10.0, ajv@^6.10.2:
|
||||||
|
version "6.10.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52"
|
||||||
|
integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==
|
||||||
|
dependencies:
|
||||||
|
fast-deep-equal "^2.0.1"
|
||||||
|
fast-json-stable-stringify "^2.0.0"
|
||||||
|
json-schema-traverse "^0.4.1"
|
||||||
|
uri-js "^4.2.2"
|
||||||
|
|
||||||
amdefine@>=0.0.4:
|
amdefine@>=0.0.4:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
|
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
|
||||||
@ -1591,9 +1611,10 @@ ansi-escapes@^3.0.0:
|
|||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92"
|
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92"
|
||||||
|
|
||||||
ansi-escapes@^3.1.0:
|
ansi-escapes@^3.1.0, ansi-escapes@^3.2.0:
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
|
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
|
||||||
|
integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==
|
||||||
|
|
||||||
ansi-html@0.0.7, ansi-html@^0.0.7:
|
ansi-html@0.0.7, ansi-html@^0.0.7:
|
||||||
version "0.0.7"
|
version "0.0.7"
|
||||||
@ -2383,6 +2404,11 @@ callsites@^2.0.0:
|
|||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
|
resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
|
||||||
|
|
||||||
|
callsites@^3.0.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||||
|
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||||
|
|
||||||
camel-case@3.0.x:
|
camel-case@3.0.x:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
|
resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
|
||||||
@ -2504,6 +2530,11 @@ chardet@^0.4.0:
|
|||||||
version "0.4.2"
|
version "0.4.2"
|
||||||
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
|
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
|
||||||
|
|
||||||
|
chardet@^0.7.0:
|
||||||
|
version "0.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
||||||
|
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
|
||||||
|
|
||||||
check-more-types@2.24.0:
|
check-more-types@2.24.0:
|
||||||
version "2.24.0"
|
version "2.24.0"
|
||||||
resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600"
|
resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600"
|
||||||
@ -3394,9 +3425,10 @@ debug@3.2.6, debug@^3.2.5, debug@^3.2.6:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.1.1"
|
ms "^2.1.1"
|
||||||
|
|
||||||
debug@4.1.1, debug@^4.1.1:
|
debug@4.1.1, debug@^4.0.1, debug@^4.1.1:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
|
||||||
|
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.1.1"
|
ms "^2.1.1"
|
||||||
|
|
||||||
@ -3627,6 +3659,13 @@ doctrine@^2.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
esutils "^2.0.2"
|
esutils "^2.0.2"
|
||||||
|
|
||||||
|
doctrine@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
|
||||||
|
integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
|
||||||
|
dependencies:
|
||||||
|
esutils "^2.0.2"
|
||||||
|
|
||||||
documentation@^12.0.1:
|
documentation@^12.0.1:
|
||||||
version "12.0.1"
|
version "12.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/documentation/-/documentation-12.0.1.tgz#4abe263d5415f3ed7ee737829921ef6159e6a335"
|
resolved "https://registry.yarnpkg.com/documentation/-/documentation-12.0.1.tgz#4abe263d5415f3ed7ee737829921ef6159e6a335"
|
||||||
@ -3899,6 +3938,13 @@ escodegen@^1.9.1:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
source-map "~0.6.1"
|
source-map "~0.6.1"
|
||||||
|
|
||||||
|
eslint-config-prettier@^6.3.0:
|
||||||
|
version "6.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.3.0.tgz#e73b48e59dc49d950843f3eb96d519e2248286a3"
|
||||||
|
integrity sha512-EWaGjlDAZRzVFveh2Jsglcere2KK5CJBhkNSa1xs3KfMUGdRiT7lG089eqPdvlzWHpAqaekubOsOMu8W8Yk71A==
|
||||||
|
dependencies:
|
||||||
|
get-stdin "^6.0.0"
|
||||||
|
|
||||||
eslint-config-standard-jsx@6.0.2:
|
eslint-config-standard-jsx@6.0.2:
|
||||||
version "6.0.2"
|
version "6.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-config-standard-jsx/-/eslint-config-standard-jsx-6.0.2.tgz#90c9aa16ac2c4f8970c13fc7efc608bacd02da70"
|
resolved "https://registry.yarnpkg.com/eslint-config-standard-jsx/-/eslint-config-standard-jsx-6.0.2.tgz#90c9aa16ac2c4f8970c13fc7efc608bacd02da70"
|
||||||
@ -3954,6 +4000,13 @@ eslint-plugin-node@~7.0.1:
|
|||||||
resolve "^1.8.1"
|
resolve "^1.8.1"
|
||||||
semver "^5.5.0"
|
semver "^5.5.0"
|
||||||
|
|
||||||
|
eslint-plugin-prettier@^3.1.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.0.tgz#8695188f95daa93b0dc54b249347ca3b79c4686d"
|
||||||
|
integrity sha512-XWX2yVuwVNLOUhQijAkXz+rMPPoCr7WFiAl8ig6I7Xn+pPVhDhzg4DxHpmbeb0iqjO9UronEA3Tb09ChnFVHHA==
|
||||||
|
dependencies:
|
||||||
|
prettier-linter-helpers "^1.0.0"
|
||||||
|
|
||||||
eslint-plugin-promise@~4.0.0:
|
eslint-plugin-promise@~4.0.0:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.0.1.tgz#2d074b653f35a23d1ba89d8e976a985117d1c6a2"
|
resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.0.1.tgz#2d074b653f35a23d1ba89d8e976a985117d1c6a2"
|
||||||
@ -3979,14 +4032,77 @@ eslint-scope@^4.0.0:
|
|||||||
esrecurse "^4.1.0"
|
esrecurse "^4.1.0"
|
||||||
estraverse "^4.1.1"
|
estraverse "^4.1.1"
|
||||||
|
|
||||||
|
eslint-scope@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9"
|
||||||
|
integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==
|
||||||
|
dependencies:
|
||||||
|
esrecurse "^4.1.0"
|
||||||
|
estraverse "^4.1.1"
|
||||||
|
|
||||||
eslint-utils@^1.3.0, eslint-utils@^1.3.1:
|
eslint-utils@^1.3.0, eslint-utils@^1.3.1:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.3.1.tgz#9a851ba89ee7c460346f97cf8939c7298827e512"
|
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.3.1.tgz#9a851ba89ee7c460346f97cf8939c7298827e512"
|
||||||
|
|
||||||
|
eslint-utils@^1.4.2:
|
||||||
|
version "1.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.2.tgz#166a5180ef6ab7eb462f162fd0e6f2463d7309ab"
|
||||||
|
integrity sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==
|
||||||
|
dependencies:
|
||||||
|
eslint-visitor-keys "^1.0.0"
|
||||||
|
|
||||||
eslint-visitor-keys@^1.0.0:
|
eslint-visitor-keys@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d"
|
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d"
|
||||||
|
|
||||||
|
eslint-visitor-keys@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2"
|
||||||
|
integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==
|
||||||
|
|
||||||
|
eslint@^6.3.0:
|
||||||
|
version "6.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.3.0.tgz#1f1a902f67bfd4c354e7288b81e40654d927eb6a"
|
||||||
|
integrity sha512-ZvZTKaqDue+N8Y9g0kp6UPZtS4FSY3qARxBs7p4f0H0iof381XHduqVerFWtK8DPtKmemqbqCFENWSQgPR/Gow==
|
||||||
|
dependencies:
|
||||||
|
"@babel/code-frame" "^7.0.0"
|
||||||
|
ajv "^6.10.0"
|
||||||
|
chalk "^2.1.0"
|
||||||
|
cross-spawn "^6.0.5"
|
||||||
|
debug "^4.0.1"
|
||||||
|
doctrine "^3.0.0"
|
||||||
|
eslint-scope "^5.0.0"
|
||||||
|
eslint-utils "^1.4.2"
|
||||||
|
eslint-visitor-keys "^1.1.0"
|
||||||
|
espree "^6.1.1"
|
||||||
|
esquery "^1.0.1"
|
||||||
|
esutils "^2.0.2"
|
||||||
|
file-entry-cache "^5.0.1"
|
||||||
|
functional-red-black-tree "^1.0.1"
|
||||||
|
glob-parent "^5.0.0"
|
||||||
|
globals "^11.7.0"
|
||||||
|
ignore "^4.0.6"
|
||||||
|
import-fresh "^3.0.0"
|
||||||
|
imurmurhash "^0.1.4"
|
||||||
|
inquirer "^6.4.1"
|
||||||
|
is-glob "^4.0.0"
|
||||||
|
js-yaml "^3.13.1"
|
||||||
|
json-stable-stringify-without-jsonify "^1.0.1"
|
||||||
|
levn "^0.3.0"
|
||||||
|
lodash "^4.17.14"
|
||||||
|
minimatch "^3.0.4"
|
||||||
|
mkdirp "^0.5.1"
|
||||||
|
natural-compare "^1.4.0"
|
||||||
|
optionator "^0.8.2"
|
||||||
|
progress "^2.0.0"
|
||||||
|
regexpp "^2.0.1"
|
||||||
|
semver "^6.1.2"
|
||||||
|
strip-ansi "^5.2.0"
|
||||||
|
strip-json-comments "^3.0.1"
|
||||||
|
table "^5.2.3"
|
||||||
|
text-table "^0.2.0"
|
||||||
|
v8-compile-cache "^2.0.3"
|
||||||
|
|
||||||
eslint@~5.4.0:
|
eslint@~5.4.0:
|
||||||
version "5.4.0"
|
version "5.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.4.0.tgz#d068ec03006bb9e06b429dc85f7e46c1b69fac62"
|
resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.4.0.tgz#d068ec03006bb9e06b429dc85f7e46c1b69fac62"
|
||||||
@ -4038,6 +4154,15 @@ espree@^4.0.0:
|
|||||||
acorn-jsx "^5.0.0"
|
acorn-jsx "^5.0.0"
|
||||||
eslint-visitor-keys "^1.0.0"
|
eslint-visitor-keys "^1.0.0"
|
||||||
|
|
||||||
|
espree@^6.1.1:
|
||||||
|
version "6.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.1.tgz#7f80e5f7257fc47db450022d723e356daeb1e5de"
|
||||||
|
integrity sha512-EYbr8XZUhWbYCqQRW0duU5LxzL5bETN6AjKBGy1302qqzPaCH10QbRg3Wvco79Z8x9WbiE8HYB4e75xl6qUYvQ==
|
||||||
|
dependencies:
|
||||||
|
acorn "^7.0.0"
|
||||||
|
acorn-jsx "^5.0.2"
|
||||||
|
eslint-visitor-keys "^1.1.0"
|
||||||
|
|
||||||
esprima@1.1.x, esprima@~1.1.1:
|
esprima@1.1.x, esprima@~1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.1.1.tgz#5b6f1547f4d102e670e140c509be6771d6aeb549"
|
resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.1.1.tgz#5b6f1547f4d102e670e140c509be6771d6aeb549"
|
||||||
@ -4289,6 +4414,15 @@ external-editor@^2.1.0:
|
|||||||
iconv-lite "^0.4.17"
|
iconv-lite "^0.4.17"
|
||||||
tmp "^0.0.33"
|
tmp "^0.0.33"
|
||||||
|
|
||||||
|
external-editor@^3.0.3:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495"
|
||||||
|
integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==
|
||||||
|
dependencies:
|
||||||
|
chardet "^0.7.0"
|
||||||
|
iconv-lite "^0.4.24"
|
||||||
|
tmp "^0.0.33"
|
||||||
|
|
||||||
extglob@^0.3.1:
|
extglob@^0.3.1:
|
||||||
version "0.3.2"
|
version "0.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
|
resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
|
||||||
@ -4337,6 +4471,11 @@ fast-deep-equal@^2.0.1:
|
|||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
|
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
|
||||||
|
|
||||||
|
fast-diff@^1.1.2:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
|
||||||
|
integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
|
||||||
|
|
||||||
fast-glob@^3.0.3:
|
fast-glob@^3.0.3:
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.0.4.tgz#d484a41005cb6faeb399b951fd1bd70ddaebb602"
|
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.0.4.tgz#d484a41005cb6faeb399b951fd1bd70ddaebb602"
|
||||||
@ -4420,6 +4559,13 @@ file-entry-cache@^2.0.0:
|
|||||||
flat-cache "^1.2.1"
|
flat-cache "^1.2.1"
|
||||||
object-assign "^4.0.1"
|
object-assign "^4.0.1"
|
||||||
|
|
||||||
|
file-entry-cache@^5.0.1:
|
||||||
|
version "5.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c"
|
||||||
|
integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==
|
||||||
|
dependencies:
|
||||||
|
flat-cache "^2.0.1"
|
||||||
|
|
||||||
filename-regex@^2.0.0:
|
filename-regex@^2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
|
resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
|
||||||
@ -4537,6 +4683,20 @@ flat-cache@^1.2.1:
|
|||||||
rimraf "~2.6.2"
|
rimraf "~2.6.2"
|
||||||
write "^0.2.1"
|
write "^0.2.1"
|
||||||
|
|
||||||
|
flat-cache@^2.0.1:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0"
|
||||||
|
integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==
|
||||||
|
dependencies:
|
||||||
|
flatted "^2.0.0"
|
||||||
|
rimraf "2.6.3"
|
||||||
|
write "1.0.3"
|
||||||
|
|
||||||
|
flatted@^2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08"
|
||||||
|
integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==
|
||||||
|
|
||||||
flush-write-stream@^1.0.0, flush-write-stream@^1.0.2:
|
flush-write-stream@^1.0.0, flush-write-stream@^1.0.2:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd"
|
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd"
|
||||||
@ -5260,7 +5420,7 @@ hyperlinker@^1.0.0:
|
|||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e"
|
resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e"
|
||||||
|
|
||||||
iconv-lite@0.4, iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.4:
|
iconv-lite@0.4, iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
|
||||||
version "0.4.24"
|
version "0.4.24"
|
||||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -5300,9 +5460,10 @@ ignore@^3.0.9:
|
|||||||
version "3.3.10"
|
version "3.3.10"
|
||||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043"
|
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043"
|
||||||
|
|
||||||
ignore@^4.0.2:
|
ignore@^4.0.2, ignore@^4.0.6:
|
||||||
version "4.0.6"
|
version "4.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
|
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
|
||||||
|
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
|
||||||
|
|
||||||
ignore@^5.1.1:
|
ignore@^5.1.1:
|
||||||
version "5.1.4"
|
version "5.1.4"
|
||||||
@ -5315,6 +5476,14 @@ import-fresh@^2.0.0:
|
|||||||
caller-path "^2.0.0"
|
caller-path "^2.0.0"
|
||||||
resolve-from "^3.0.0"
|
resolve-from "^3.0.0"
|
||||||
|
|
||||||
|
import-fresh@^3.0.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.1.0.tgz#6d33fa1dcef6df930fae003446f33415af905118"
|
||||||
|
integrity sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==
|
||||||
|
dependencies:
|
||||||
|
parent-module "^1.0.0"
|
||||||
|
resolve-from "^4.0.0"
|
||||||
|
|
||||||
import-local@^1.0.0:
|
import-local@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc"
|
resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc"
|
||||||
@ -5396,6 +5565,25 @@ inquirer@^5.2.0:
|
|||||||
strip-ansi "^4.0.0"
|
strip-ansi "^4.0.0"
|
||||||
through "^2.3.6"
|
through "^2.3.6"
|
||||||
|
|
||||||
|
inquirer@^6.4.1:
|
||||||
|
version "6.5.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca"
|
||||||
|
integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==
|
||||||
|
dependencies:
|
||||||
|
ansi-escapes "^3.2.0"
|
||||||
|
chalk "^2.4.2"
|
||||||
|
cli-cursor "^2.1.0"
|
||||||
|
cli-width "^2.0.0"
|
||||||
|
external-editor "^3.0.3"
|
||||||
|
figures "^2.0.0"
|
||||||
|
lodash "^4.17.12"
|
||||||
|
mute-stream "0.0.7"
|
||||||
|
run-async "^2.2.0"
|
||||||
|
rxjs "^6.4.0"
|
||||||
|
string-width "^2.1.0"
|
||||||
|
strip-ansi "^5.1.0"
|
||||||
|
through "^2.3.6"
|
||||||
|
|
||||||
internal-ip@^4.3.0:
|
internal-ip@^4.3.0:
|
||||||
version "4.3.0"
|
version "4.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907"
|
resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907"
|
||||||
@ -6661,9 +6849,10 @@ lodash@^4.0.0, lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4,
|
|||||||
version "4.17.13"
|
version "4.17.13"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.13.tgz#0bdc3a6adc873d2f4e0c4bac285df91b64fc7b93"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.13.tgz#0bdc3a6adc873d2f4e0c4bac285df91b64fc7b93"
|
||||||
|
|
||||||
lodash@^4.17.13:
|
lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14:
|
||||||
version "4.17.15"
|
version "4.17.15"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
||||||
|
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
|
||||||
|
|
||||||
log-driver@^1.2.7:
|
log-driver@^1.2.7:
|
||||||
version "1.2.7"
|
version "1.2.7"
|
||||||
@ -7699,6 +7888,13 @@ param-case@2.1.x:
|
|||||||
dependencies:
|
dependencies:
|
||||||
no-case "^2.2.0"
|
no-case "^2.2.0"
|
||||||
|
|
||||||
|
parent-module@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
|
||||||
|
integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
|
||||||
|
dependencies:
|
||||||
|
callsites "^3.0.0"
|
||||||
|
|
||||||
parse-asn1@^5.0.0:
|
parse-asn1@^5.0.0:
|
||||||
version "5.1.1"
|
version "5.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.1.tgz#f6bf293818332bd0dab54efb16087724745e6ca8"
|
resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.1.tgz#f6bf293818332bd0dab54efb16087724745e6ca8"
|
||||||
@ -8062,6 +8258,18 @@ preserve@^0.2.0:
|
|||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
|
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
|
||||||
|
|
||||||
|
prettier-linter-helpers@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
|
||||||
|
integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
|
||||||
|
dependencies:
|
||||||
|
fast-diff "^1.1.2"
|
||||||
|
|
||||||
|
prettier@^1.18.2:
|
||||||
|
version "1.18.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea"
|
||||||
|
integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==
|
||||||
|
|
||||||
pretty-format@^23.6.0:
|
pretty-format@^23.6.0:
|
||||||
version "23.6.0"
|
version "23.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.6.0.tgz#5eaac8eeb6b33b987b7fe6097ea6a8a146ab5760"
|
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.6.0.tgz#5eaac8eeb6b33b987b7fe6097ea6a8a146ab5760"
|
||||||
@ -8804,6 +9012,11 @@ resolve-from@^3.0.0:
|
|||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
|
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
|
||||||
|
|
||||||
|
resolve-from@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
|
||||||
|
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
|
||||||
|
|
||||||
resolve-options@^1.1.0:
|
resolve-options@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/resolve-options/-/resolve-options-1.1.0.tgz#32bb9e39c06d67338dc9378c0d6d6074566ad131"
|
resolved "https://registry.yarnpkg.com/resolve-options/-/resolve-options-1.1.0.tgz#32bb9e39c06d67338dc9378c0d6d6074566ad131"
|
||||||
@ -8856,7 +9069,7 @@ reusify@^1.0.0:
|
|||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
||||||
|
|
||||||
rimraf@2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@~2.6.2:
|
rimraf@2, rimraf@2.6.3, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@~2.6.2:
|
||||||
version "2.6.3"
|
version "2.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
|
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -8907,6 +9120,13 @@ rxjs@^5.0.0-beta.11, rxjs@^5.5.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
symbol-observable "1.0.1"
|
symbol-observable "1.0.1"
|
||||||
|
|
||||||
|
rxjs@^6.4.0:
|
||||||
|
version "6.5.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a"
|
||||||
|
integrity sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==
|
||||||
|
dependencies:
|
||||||
|
tslib "^1.9.0"
|
||||||
|
|
||||||
safe-buffer@5.1.2, safe-buffer@^5.1.1, safe-buffer@^5.1.2:
|
safe-buffer@5.1.2, safe-buffer@^5.1.1, safe-buffer@^5.1.2:
|
||||||
version "5.1.2"
|
version "5.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||||
@ -9036,6 +9256,11 @@ semver@^6.1.1:
|
|||||||
version "6.2.0"
|
version "6.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.2.0.tgz#4d813d9590aaf8a9192693d6c85b9344de5901db"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-6.2.0.tgz#4d813d9590aaf8a9192693d6c85b9344de5901db"
|
||||||
|
|
||||||
|
semver@^6.1.2:
|
||||||
|
version "6.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||||
|
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||||
|
|
||||||
semver@~5.3.0:
|
semver@~5.3.0:
|
||||||
version "5.3.0"
|
version "5.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
|
||||||
@ -9199,6 +9424,15 @@ slice-ansi@1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-fullwidth-code-point "^2.0.0"
|
is-fullwidth-code-point "^2.0.0"
|
||||||
|
|
||||||
|
slice-ansi@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636"
|
||||||
|
integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==
|
||||||
|
dependencies:
|
||||||
|
ansi-styles "^3.2.0"
|
||||||
|
astral-regex "^1.0.0"
|
||||||
|
is-fullwidth-code-point "^2.0.0"
|
||||||
|
|
||||||
slugify@^1.3.1:
|
slugify@^1.3.1:
|
||||||
version "1.3.4"
|
version "1.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.3.4.tgz#78d2792d7222b55cd9fc81fa018df99af779efeb"
|
resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.3.4.tgz#78d2792d7222b55cd9fc81fa018df99af779efeb"
|
||||||
@ -9648,6 +9882,11 @@ strip-json-comments@^2.0.1, strip-json-comments@~2.0.1:
|
|||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
||||||
|
|
||||||
|
strip-json-comments@^3.0.1:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7"
|
||||||
|
integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==
|
||||||
|
|
||||||
subarg@^1.0.0:
|
subarg@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2"
|
resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2"
|
||||||
@ -9708,6 +9947,16 @@ table@^4.0.3:
|
|||||||
slice-ansi "1.0.0"
|
slice-ansi "1.0.0"
|
||||||
string-width "^2.1.1"
|
string-width "^2.1.1"
|
||||||
|
|
||||||
|
table@^5.2.3:
|
||||||
|
version "5.4.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e"
|
||||||
|
integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==
|
||||||
|
dependencies:
|
||||||
|
ajv "^6.10.2"
|
||||||
|
lodash "^4.17.14"
|
||||||
|
slice-ansi "^2.1.0"
|
||||||
|
string-width "^3.0.0"
|
||||||
|
|
||||||
tapable@^1.0.0:
|
tapable@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.0.0.tgz#cbb639d9002eed9c6b5975eb20598d7936f1f9f2"
|
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.0.0.tgz#cbb639d9002eed9c6b5975eb20598d7936f1f9f2"
|
||||||
@ -10253,6 +10502,11 @@ v8-compile-cache@^2.0.2:
|
|||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz#a428b28bb26790734c4fc8bc9fa106fccebf6a6c"
|
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz#a428b28bb26790734c4fc8bc9fa106fccebf6a6c"
|
||||||
|
|
||||||
|
v8-compile-cache@^2.0.3:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e"
|
||||||
|
integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==
|
||||||
|
|
||||||
validate-npm-package-license@^3.0.1:
|
validate-npm-package-license@^3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc"
|
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc"
|
||||||
@ -10687,6 +10941,13 @@ write-file-atomic@^2.1.0:
|
|||||||
imurmurhash "^0.1.4"
|
imurmurhash "^0.1.4"
|
||||||
signal-exit "^3.0.2"
|
signal-exit "^3.0.2"
|
||||||
|
|
||||||
|
write@1.0.3:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3"
|
||||||
|
integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==
|
||||||
|
dependencies:
|
||||||
|
mkdirp "^0.5.1"
|
||||||
|
|
||||||
write@^0.2.1:
|
write@^0.2.1:
|
||||||
version "0.2.1"
|
version "0.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757"
|
resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user