mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-19 23:39:50 +02:00
fixed class diagram hopefuly no conflict
This commit is contained in:
@@ -44,17 +44,24 @@ export const addRelation = function (relation) {
|
||||
relations.push(relation)
|
||||
}
|
||||
|
||||
export const addMembers = function (className, MembersArr) {
|
||||
export const addMember = function (className, member) {
|
||||
const theClass = classes[className]
|
||||
if (typeof MembersArr === 'string') {
|
||||
if (MembersArr.substr(-1) === ')') {
|
||||
theClass.methods.push(MembersArr)
|
||||
if (typeof member === 'string') {
|
||||
if (member.substr(-1) === ')') {
|
||||
theClass.methods.push(member)
|
||||
} else {
|
||||
theClass.members.push(MembersArr)
|
||||
theClass.members.push(member)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const addMembers = function (className, MembersArr) {
|
||||
console.log(className, MembersArr)
|
||||
if (Array.isArray(MembersArr)) {
|
||||
MembersArr.forEach(member => addMember(className, member))
|
||||
}
|
||||
}
|
||||
|
||||
export const cleanupLabel = function (label) {
|
||||
if (label.substring(0, 1) === ':') {
|
||||
return label.substr(2).trim()
|
||||
@@ -82,6 +89,7 @@ export default {
|
||||
getClasses,
|
||||
getRelations,
|
||||
addRelation,
|
||||
addMember,
|
||||
addMembers,
|
||||
cleanupLabel,
|
||||
lineType,
|
||||
|
@@ -127,6 +127,7 @@ graphConfig
|
||||
|
||||
statements
|
||||
: statement
|
||||
| statement NEWLINE
|
||||
| statement NEWLINE statements
|
||||
;
|
||||
|
||||
@@ -144,8 +145,8 @@ statement
|
||||
;
|
||||
|
||||
classStatement
|
||||
: CLASS className
|
||||
| CLASS className STRUCT_START members STRUCT_STOP {/*console.log($2,JSON.stringify($4));*/yy.addMembers($2,$4);}
|
||||
: CLASS className {yy.addClass($2);}
|
||||
| CLASS className STRUCT_START members STRUCT_STOP {/*console.log($2,JSON.stringify($4));*/yy.addClass($2);yy.addMembers($2,$4);}
|
||||
;
|
||||
|
||||
members
|
||||
@@ -155,7 +156,7 @@ members
|
||||
|
||||
methodStatement
|
||||
: className {/*console.log('Rel found',$1);*/}
|
||||
| className LABEL {yy.addMembers($1,yy.cleanupLabel($2));}
|
||||
| className LABEL {yy.addMember($1,yy.cleanupLabel($2));}
|
||||
| MEMBER {console.warn('Member',$1);}
|
||||
| SEPARATOR {/*console.log('sep found',$1);*/}
|
||||
;
|
||||
|
@@ -77,91 +77,94 @@ var parser = {trace: function trace () { },
|
||||
yy: {},
|
||||
symbols_: {"error":2,"mermaidDoc":3,"graphConfig":4,"CLASS_DIAGRAM":5,"NEWLINE":6,"statements":7,"EOF":8,"statement":9,"className":10,"alphaNumToken":11,"relationStatement":12,"LABEL":13,"classStatement":14,"methodStatement":15,"CLASS":16,"STRUCT_START":17,"members":18,"STRUCT_STOP":19,"MEMBER":20,"SEPARATOR":21,"relation":22,"STR":23,"relationType":24,"lineType":25,"AGGREGATION":26,"EXTENSION":27,"COMPOSITION":28,"DEPENDENCY":29,"LINE":30,"DOTTED_LINE":31,"commentToken":32,"textToken":33,"graphCodeTokens":34,"textNoTagsToken":35,"TAGSTART":36,"TAGEND":37,"==":38,"--":39,"PCT":40,"DEFAULT":41,"SPACE":42,"MINUS":43,"keywords":44,"UNICODE_TEXT":45,"NUM":46,"ALPHA":47,"$accept":0,"$end":1},
|
||||
terminals_: {2:"error",5:"CLASS_DIAGRAM",6:"NEWLINE",8:"EOF",13:"LABEL",16:"CLASS",17:"STRUCT_START",19:"STRUCT_STOP",20:"MEMBER",21:"SEPARATOR",23:"STR",26:"AGGREGATION",27:"EXTENSION",28:"COMPOSITION",29:"DEPENDENCY",30:"LINE",31:"DOTTED_LINE",34:"graphCodeTokens",36:"TAGSTART",37:"TAGEND",38:"==",39:"--",40:"PCT",41:"DEFAULT",42:"SPACE",43:"MINUS",44:"keywords",45:"UNICODE_TEXT",46:"NUM",47:"ALPHA"},
|
||||
productions_: [0,[3,1],[4,4],[7,1],[7,3],[10,2],[10,1],[9,1],[9,2],[9,1],[9,1],[14,2],[14,5],[18,1],[18,2],[15,1],[15,2],[15,1],[15,1],[12,3],[12,4],[12,4],[12,5],[22,3],[22,2],[22,2],[22,1],[24,1],[24,1],[24,1],[24,1],[25,1],[25,1],[32,1],[32,1],[33,1],[33,1],[33,1],[33,1],[33,1],[33,1],[33,1],[35,1],[35,1],[35,1],[35,1],[11,1],[11,1],[11,1]],
|
||||
productions_: [0,[3,1],[4,4],[7,1],[7,2],[7,3],[10,2],[10,1],[9,1],[9,2],[9,1],[9,1],[14,2],[14,5],[18,1],[18,2],[15,1],[15,2],[15,1],[15,1],[12,3],[12,4],[12,4],[12,5],[22,3],[22,2],[22,2],[22,1],[24,1],[24,1],[24,1],[24,1],[25,1],[25,1],[32,1],[32,1],[33,1],[33,1],[33,1],[33,1],[33,1],[33,1],[33,1],[35,1],[35,1],[35,1],[35,1],[11,1],[11,1],[11,1]],
|
||||
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
|
||||
/* this == yyval */
|
||||
|
||||
var $0 = $$.length - 1;
|
||||
switch (yystate) {
|
||||
case 5:
|
||||
case 6:
|
||||
this.$=$$[$0-1]+$$[$0];
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
this.$=$$[$0];
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
yy.addRelation($$[$0]);
|
||||
break;
|
||||
case 8:
|
||||
case 9:
|
||||
$$[$0-1].title = yy.cleanupLabel($$[$0]); yy.addRelation($$[$0-1]);
|
||||
break;
|
||||
case 12:
|
||||
/*console.log($$[$0-3],JSON.stringify($$[$0-1]));*/yy.addMembers($$[$0-3],$$[$0-1]);
|
||||
yy.addClass($$[$0]);
|
||||
break;
|
||||
case 13:
|
||||
this.$ = [$$[$0]];
|
||||
/*console.log($$[$0-3],JSON.stringify($$[$0-1]));*/yy.addClass($$[$0-3]);yy.addMembers($$[$0-3],$$[$0-1]);
|
||||
break;
|
||||
case 14:
|
||||
$$[$0].push($$[$0-1]);this.$=$$[$0];
|
||||
this.$ = [$$[$0]];
|
||||
break;
|
||||
case 15:
|
||||
/*console.log('Rel found',$$[$0]);*/
|
||||
$$[$0].push($$[$0-1]);this.$=$$[$0];
|
||||
break;
|
||||
case 16:
|
||||
yy.addMembers($$[$0-1],yy.cleanupLabel($$[$0]));
|
||||
/*console.log('Rel found',$$[$0]);*/
|
||||
break;
|
||||
case 17:
|
||||
console.warn('Member',$$[$0]);
|
||||
yy.addMember($$[$0-1],yy.cleanupLabel($$[$0]));
|
||||
break;
|
||||
case 18:
|
||||
/*console.log('sep found',$$[$0]);*/
|
||||
console.warn('Member',$$[$0]);
|
||||
break;
|
||||
case 19:
|
||||
this.$ = {'id1':$$[$0-2],'id2':$$[$0], relation:$$[$0-1], relationTitle1:'none', relationTitle2:'none'};
|
||||
/*console.log('sep found',$$[$0]);*/
|
||||
break;
|
||||
case 20:
|
||||
this.$ = {id1:$$[$0-3], id2:$$[$0], relation:$$[$0-1], relationTitle1:$$[$0-2], relationTitle2:'none'}
|
||||
this.$ = {'id1':$$[$0-2],'id2':$$[$0], relation:$$[$0-1], relationTitle1:'none', relationTitle2:'none'};
|
||||
break;
|
||||
case 21:
|
||||
this.$ = {id1:$$[$0-3], id2:$$[$0], relation:$$[$0-2], relationTitle1:'none', relationTitle2:$$[$0-1]};
|
||||
this.$ = {id1:$$[$0-3], id2:$$[$0], relation:$$[$0-1], relationTitle1:$$[$0-2], relationTitle2:'none'}
|
||||
break;
|
||||
case 22:
|
||||
this.$ = {id1:$$[$0-4], id2:$$[$0], relation:$$[$0-2], relationTitle1:$$[$0-3], relationTitle2:$$[$0-1]}
|
||||
this.$ = {id1:$$[$0-3], id2:$$[$0], relation:$$[$0-2], relationTitle1:'none', relationTitle2:$$[$0-1]};
|
||||
break;
|
||||
case 23:
|
||||
this.$={type1:$$[$0-2],type2:$$[$0],lineType:$$[$0-1]};
|
||||
this.$ = {id1:$$[$0-4], id2:$$[$0], relation:$$[$0-2], relationTitle1:$$[$0-3], relationTitle2:$$[$0-1]}
|
||||
break;
|
||||
case 24:
|
||||
this.$={type1:'none',type2:$$[$0],lineType:$$[$0-1]};
|
||||
this.$={type1:$$[$0-2],type2:$$[$0],lineType:$$[$0-1]};
|
||||
break;
|
||||
case 25:
|
||||
this.$={type1:$$[$0-1],type2:'none',lineType:$$[$0]};
|
||||
this.$={type1:'none',type2:$$[$0],lineType:$$[$0-1]};
|
||||
break;
|
||||
case 26:
|
||||
this.$={type1:'none',type2:'none',lineType:$$[$0]};
|
||||
this.$={type1:$$[$0-1],type2:'none',lineType:$$[$0]};
|
||||
break;
|
||||
case 27:
|
||||
this.$=yy.relationType.AGGREGATION;
|
||||
this.$={type1:'none',type2:'none',lineType:$$[$0]};
|
||||
break;
|
||||
case 28:
|
||||
this.$=yy.relationType.EXTENSION;
|
||||
this.$=yy.relationType.AGGREGATION;
|
||||
break;
|
||||
case 29:
|
||||
this.$=yy.relationType.COMPOSITION;
|
||||
this.$=yy.relationType.EXTENSION;
|
||||
break;
|
||||
case 30:
|
||||
this.$=yy.relationType.DEPENDENCY;
|
||||
this.$=yy.relationType.COMPOSITION;
|
||||
break;
|
||||
case 31:
|
||||
this.$=yy.lineType.LINE;
|
||||
this.$=yy.relationType.DEPENDENCY;
|
||||
break;
|
||||
case 32:
|
||||
this.$=yy.lineType.LINE;
|
||||
break;
|
||||
case 33:
|
||||
this.$=yy.lineType.DOTTED_LINE;
|
||||
break;
|
||||
}
|
||||
},
|
||||
table: [{3:1,4:2,5:[1,3]},{1:[3]},{1:[2,1]},{6:[1,4]},{7:5,9:6,10:10,11:14,12:7,14:8,15:9,16:$V0,20:$V1,21:$V2,45:$V3,46:$V4,47:$V5},{8:[1,18]},{6:[1,19],8:[2,3]},o($V6,[2,7],{13:[1,20]}),o($V6,[2,9]),o($V6,[2,10]),o($V6,[2,15],{22:21,24:24,25:25,13:[1,23],23:[1,22],26:$V7,27:$V8,28:$V9,29:$Va,30:$Vb,31:$Vc}),{10:32,11:14,45:$V3,46:$V4,47:$V5},o($V6,[2,17]),o($V6,[2,18]),o($Vd,[2,6],{11:14,10:33,45:$V3,46:$V4,47:$V5}),o($Ve,[2,46]),o($Ve,[2,47]),o($Ve,[2,48]),{1:[2,2]},{7:34,9:6,10:10,11:14,12:7,14:8,15:9,16:$V0,20:$V1,21:$V2,45:$V3,46:$V4,47:$V5},o($V6,[2,8]),{10:35,11:14,23:[1,36],45:$V3,46:$V4,47:$V5},{22:37,24:24,25:25,26:$V7,27:$V8,28:$V9,29:$Va,30:$Vb,31:$Vc},o($V6,[2,16]),{25:38,30:$Vb,31:$Vc},o($Vf,[2,26],{24:39,26:$V7,27:$V8,28:$V9,29:$Va}),o($Vg,[2,27]),o($Vg,[2,28]),o($Vg,[2,29]),o($Vg,[2,30]),o($Vh,[2,31]),o($Vh,[2,32]),o($V6,[2,11],{17:[1,40]}),o($Vd,[2,5]),{8:[2,4]},o($Vi,[2,19]),{10:41,11:14,45:$V3,46:$V4,47:$V5},{10:42,11:14,23:[1,43],45:$V3,46:$V4,47:$V5},o($Vf,[2,25],{24:44,26:$V7,27:$V8,28:$V9,29:$Va}),o($Vf,[2,24]),{18:45,20:$Vj},o($Vi,[2,21]),o($Vi,[2,20]),{10:47,11:14,45:$V3,46:$V4,47:$V5},o($Vf,[2,23]),{19:[1,48]},{18:49,19:[2,13],20:$Vj},o($Vi,[2,22]),o($V6,[2,12]),{19:[2,14]}],
|
||||
defaultActions: {2:[2,1],18:[2,2],34:[2,4],49:[2,14]},
|
||||
table: [{3:1,4:2,5:[1,3]},{1:[3]},{1:[2,1]},{6:[1,4]},{7:5,9:6,10:10,11:14,12:7,14:8,15:9,16:$V0,20:$V1,21:$V2,45:$V3,46:$V4,47:$V5},{8:[1,18]},{6:[1,19],8:[2,3]},o($V6,[2,8],{13:[1,20]}),o($V6,[2,10]),o($V6,[2,11]),o($V6,[2,16],{22:21,24:24,25:25,13:[1,23],23:[1,22],26:$V7,27:$V8,28:$V9,29:$Va,30:$Vb,31:$Vc}),{10:32,11:14,45:$V3,46:$V4,47:$V5},o($V6,[2,18]),o($V6,[2,19]),o($Vd,[2,7],{11:14,10:33,45:$V3,46:$V4,47:$V5}),o($Ve,[2,47]),o($Ve,[2,48]),o($Ve,[2,49]),{1:[2,2]},{7:34,8:[2,4],9:6,10:10,11:14,12:7,14:8,15:9,16:$V0,20:$V1,21:$V2,45:$V3,46:$V4,47:$V5},o($V6,[2,9]),{10:35,11:14,23:[1,36],45:$V3,46:$V4,47:$V5},{22:37,24:24,25:25,26:$V7,27:$V8,28:$V9,29:$Va,30:$Vb,31:$Vc},o($V6,[2,17]),{25:38,30:$Vb,31:$Vc},o($Vf,[2,27],{24:39,26:$V7,27:$V8,28:$V9,29:$Va}),o($Vg,[2,28]),o($Vg,[2,29]),o($Vg,[2,30]),o($Vg,[2,31]),o($Vh,[2,32]),o($Vh,[2,33]),o($V6,[2,12],{17:[1,40]}),o($Vd,[2,6]),{8:[2,5]},o($Vi,[2,20]),{10:41,11:14,45:$V3,46:$V4,47:$V5},{10:42,11:14,23:[1,43],45:$V3,46:$V4,47:$V5},o($Vf,[2,26],{24:44,26:$V7,27:$V8,28:$V9,29:$Va}),o($Vf,[2,25]),{18:45,20:$Vj},o($Vi,[2,22]),o($Vi,[2,21]),{10:47,11:14,45:$V3,46:$V4,47:$V5},o($Vf,[2,24]),{19:[1,48]},{18:49,19:[2,14],20:$Vj},o($Vi,[2,23]),o($V6,[2,13]),{19:[2,15]}],
|
||||
defaultActions: {2:[2,1],18:[2,2],34:[2,5],49:[2,15]},
|
||||
parseError: function parseError (str, hash) {
|
||||
if (hash.recoverable) {
|
||||
this.trace(str);
|
||||
|
@@ -7,6 +7,7 @@ let vertices = {}
|
||||
let edges = []
|
||||
let classes = []
|
||||
let subGraphs = []
|
||||
let subGraphLookup = {}
|
||||
let tooltips = {}
|
||||
let subCount = 0
|
||||
let direction
|
||||
@@ -18,8 +19,9 @@ let funs = []
|
||||
* @param text
|
||||
* @param type
|
||||
* @param style
|
||||
* @param classes
|
||||
*/
|
||||
export const addVertex = function (id, text, type, style) {
|
||||
export const addVertex = function (id, text, type, style, classes) {
|
||||
let txt
|
||||
|
||||
if (typeof id === 'undefined') {
|
||||
@@ -52,6 +54,13 @@ export const addVertex = function (id, text, type, style) {
|
||||
})
|
||||
}
|
||||
}
|
||||
if (typeof classes !== 'undefined') {
|
||||
if (classes !== null) {
|
||||
classes.forEach(function (s) {
|
||||
vertices[id].classes.push(s)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,12 +96,14 @@ export const addLink = function (start, end, type, linktext) {
|
||||
* @param pos
|
||||
* @param interpolate
|
||||
*/
|
||||
export const updateLinkInterpolate = function (pos, interp) {
|
||||
if (pos === 'default') {
|
||||
edges.defaultInterpolate = interp
|
||||
} else {
|
||||
edges[pos].interpolate = interp
|
||||
}
|
||||
export const updateLinkInterpolate = function (positions, interp) {
|
||||
positions.forEach(function (pos) {
|
||||
if (pos === 'default') {
|
||||
edges.defaultInterpolate = interp
|
||||
} else {
|
||||
edges[pos].interpolate = interp
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,15 +111,17 @@ export const updateLinkInterpolate = function (pos, interp) {
|
||||
* @param pos
|
||||
* @param style
|
||||
*/
|
||||
export const updateLink = function (pos, style) {
|
||||
if (pos === 'default') {
|
||||
edges.defaultStyle = style
|
||||
} else {
|
||||
if (utils.isSubstringInArray('fill', style) === -1) {
|
||||
style.push('fill:none')
|
||||
export const updateLink = function (positions, style) {
|
||||
positions.forEach(function (pos) {
|
||||
if (pos === 'default') {
|
||||
edges.defaultStyle = style
|
||||
} else {
|
||||
if (utils.isSubstringInArray('fill', style) === -1) {
|
||||
style.push('fill:none')
|
||||
}
|
||||
edges[pos].style = style
|
||||
}
|
||||
edges[pos].style = style
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const addClass = function (id, style) {
|
||||
@@ -143,6 +156,10 @@ export const setClass = function (ids, className) {
|
||||
if (typeof vertices[id] !== 'undefined') {
|
||||
vertices[id].classes.push(className)
|
||||
}
|
||||
|
||||
if (typeof subGraphLookup[id] !== 'undefined') {
|
||||
subGraphLookup[id].classes.push(className)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -283,6 +300,7 @@ export const clear = function () {
|
||||
funs = []
|
||||
funs.push(setupToolTips)
|
||||
subGraphs = []
|
||||
subGraphLookup = {}
|
||||
subCount = 0
|
||||
tooltips = []
|
||||
}
|
||||
@@ -297,7 +315,7 @@ export const defaultStyle = function () {
|
||||
/**
|
||||
* Clears the internal graph db so that a new graph can be parsed.
|
||||
*/
|
||||
export const addSubGraph = function (list, title) {
|
||||
export const addSubGraph = function (id, list, title) {
|
||||
function uniq (a) {
|
||||
const prims = { 'boolean': {}, 'number': {}, 'string': {} }
|
||||
const objs = []
|
||||
@@ -315,10 +333,13 @@ export const addSubGraph = function (list, title) {
|
||||
|
||||
nodeList = uniq(nodeList.concat.apply(nodeList, list))
|
||||
|
||||
const subGraph = { id: 'subGraph' + subCount, nodes: nodeList, title: title.trim() }
|
||||
subGraphs.push(subGraph)
|
||||
id = id || ('subGraph' + subCount)
|
||||
title = title || ''
|
||||
subCount = subCount + 1
|
||||
return subGraph.id
|
||||
const subGraph = { id: id, nodes: nodeList, title: title.trim(), classes: [] }
|
||||
subGraphs.push(subGraph)
|
||||
subGraphLookup[id] = subGraph
|
||||
return id
|
||||
}
|
||||
|
||||
const getPosForId = function (id) {
|
||||
|
@@ -4,6 +4,7 @@ import * as d3 from 'd3'
|
||||
import flowDb from './flowDb'
|
||||
import flow from './parser/flow'
|
||||
import dagreD3 from 'dagre-d3-renderer'
|
||||
import addHtmlLabel from 'dagre-d3-renderer/lib/label/add-html-label.js'
|
||||
import { logger } from '../../logger'
|
||||
import { interpolateToCurve } from '../../utils'
|
||||
|
||||
@@ -21,7 +22,8 @@ export const setConf = function (cnf) {
|
||||
* @param vert Object containing the vertices.
|
||||
* @param g The graph that is to be drawn.
|
||||
*/
|
||||
export const addVertices = function (vert, g) {
|
||||
export const addVertices = function (vert, g, svgId) {
|
||||
const svg = d3.select(`[id="${svgId}"]`)
|
||||
const keys = Object.keys(vert)
|
||||
|
||||
const styleFromStyleArr = function (styleStr, arr) {
|
||||
@@ -35,46 +37,41 @@ export const addVertices = function (vert, g) {
|
||||
return styleStr
|
||||
}
|
||||
|
||||
// Iterate through each item in the vertice object (containing all the vertices found) in the graph definition
|
||||
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
|
||||
keys.forEach(function (id) {
|
||||
const vertice = vert[id]
|
||||
let verticeText
|
||||
const vertex = vert[id]
|
||||
|
||||
/**
|
||||
* Variable for storing the classes for the vertice
|
||||
* Variable for storing the classes for the vertex
|
||||
* @type {string}
|
||||
*/
|
||||
let classStr = ''
|
||||
if (vertice.classes.length > 0) {
|
||||
classStr = vertice.classes.join(' ')
|
||||
if (vertex.classes.length > 0) {
|
||||
classStr = vertex.classes.join(' ')
|
||||
}
|
||||
|
||||
/**
|
||||
* Variable for storing the extracted style for the vertice
|
||||
* Variable for storing the extracted style for the vertex
|
||||
* @type {string}
|
||||
*/
|
||||
let style = ''
|
||||
// Create a compound style definition from the style definitions found for the node in the graph definition
|
||||
style = styleFromStyleArr(style, vertice.styles)
|
||||
style = styleFromStyleArr(style, vertex.styles)
|
||||
|
||||
// Use vertice id as text in the box if no text is provided by the graph definition
|
||||
if (typeof vertice.text === 'undefined') {
|
||||
verticeText = vertice.id
|
||||
} else {
|
||||
verticeText = vertice.text
|
||||
}
|
||||
// Use vertex id as text in the box if no text is provided by the graph definition
|
||||
let vertexText = vertex.text !== undefined ? vertex.text : vertex.id
|
||||
|
||||
let labelTypeStr = ''
|
||||
// We create a SVG label, either by delegating to addHtmlLabel or manually
|
||||
let vertexNode
|
||||
if (conf.htmlLabels) {
|
||||
labelTypeStr = 'html'
|
||||
verticeText = verticeText.replace(/fa[lrsb]?:fa-[\w-]+/g, s => `<i class='${s.replace(':', ' ')}'></i>`)
|
||||
if (vertice.link) {
|
||||
verticeText = '<a href="' + vertice.link + '" rel="noopener">' + verticeText + '</a>'
|
||||
}
|
||||
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
||||
const node = { label: vertexText.replace(/fa[lrsb]?:fa-[\w-]+/g, s => `<i class='${s.replace(':', ' ')}'></i>`) }
|
||||
vertexNode = addHtmlLabel(svg, node).node()
|
||||
vertexNode.parentNode.removeChild(vertexNode)
|
||||
} else {
|
||||
const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text')
|
||||
|
||||
const rows = verticeText.split(/<br>/)
|
||||
const rows = vertexText.split(/<br[/]{0,1}>/)
|
||||
|
||||
for (let j = 0; j < rows.length; j++) {
|
||||
const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan')
|
||||
@@ -84,22 +81,22 @@ export const addVertices = function (vert, g) {
|
||||
tspan.textContent = rows[j]
|
||||
svgLabel.appendChild(tspan)
|
||||
}
|
||||
vertexNode = svgLabel
|
||||
}
|
||||
|
||||
labelTypeStr = 'svg'
|
||||
if (vertice.link) {
|
||||
const link = document.createElementNS('http://www.w3.org/2000/svg', 'a')
|
||||
link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertice.link)
|
||||
link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener')
|
||||
verticeText = link
|
||||
} else {
|
||||
verticeText = svgLabel
|
||||
}
|
||||
// If the node has a link, we wrap it in a SVG link
|
||||
if (vertex.link) {
|
||||
const link = document.createElementNS('http://www.w3.org/2000/svg', 'a')
|
||||
link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link)
|
||||
link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener')
|
||||
link.appendChild(vertexNode)
|
||||
vertexNode = link
|
||||
}
|
||||
|
||||
let radious = 0
|
||||
let _shape = ''
|
||||
// Set the shape based parameters
|
||||
switch (vertice.type) {
|
||||
switch (vertex.type) {
|
||||
case 'round':
|
||||
radious = 5
|
||||
_shape = 'rect'
|
||||
@@ -124,14 +121,12 @@ export const addVertices = function (vert, g) {
|
||||
break
|
||||
case 'group':
|
||||
_shape = 'rect'
|
||||
// Need to create a text node if using svg labels, see #367
|
||||
verticeText = conf.htmlLabels ? '' : document.createElementNS('http://www.w3.org/2000/svg', 'text')
|
||||
break
|
||||
default:
|
||||
_shape = 'rect'
|
||||
}
|
||||
// Add the node
|
||||
g.setNode(vertice.id, { labelType: labelTypeStr, shape: _shape, label: verticeText, rx: radious, ry: radious, 'class': classStr, style: style, id: vertice.id })
|
||||
g.setNode(vertex.id, { labelType: 'svg', shape: _shape, label: vertexNode, rx: radious, ry: radious, 'class': classStr, style: style, id: vertex.id })
|
||||
})
|
||||
}
|
||||
|
||||
@@ -203,7 +198,7 @@ export const addEdges = function (edges, g) {
|
||||
edgeData.label = '<span class="edgeLabel">' + edge.text + '</span>'
|
||||
} else {
|
||||
edgeData.labelType = 'text'
|
||||
edgeData.style = 'stroke: #333; stroke-width: 1.5px;fill:none'
|
||||
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none'
|
||||
edgeData.label = edge.text.replace(/<br>/g, '\n')
|
||||
}
|
||||
} else {
|
||||
@@ -272,7 +267,7 @@ export const draw = function (text, id) {
|
||||
const subGraphs = flowDb.getSubGraphs()
|
||||
for (let i = subGraphs.length - 1; i >= 0; i--) {
|
||||
subG = subGraphs[i]
|
||||
flowDb.addVertex(subG.id, subG.title, 'group', undefined)
|
||||
flowDb.addVertex(subG.id, subG.title, 'group', undefined, subG.classes)
|
||||
}
|
||||
|
||||
// Fetch the verices/nodes and edges/links from the parsed graph definition
|
||||
@@ -290,7 +285,7 @@ export const draw = function (text, id) {
|
||||
g.setParent(subG.nodes[j], subG.id)
|
||||
}
|
||||
}
|
||||
addVertices(vert, g)
|
||||
addVertices(vert, g, id)
|
||||
addEdges(edges, g)
|
||||
|
||||
// Create the renderer
|
||||
@@ -424,6 +419,7 @@ export const draw = function (text, id) {
|
||||
// Index nodes
|
||||
flowDb.indexNodes('subGraph' + i)
|
||||
|
||||
// reposition labels
|
||||
for (i = 0; i < subGraphs.length; i++) {
|
||||
subG = subGraphs[i]
|
||||
|
||||
@@ -435,19 +431,9 @@ export const draw = function (text, id) {
|
||||
const yPos = clusterRects[0].y.baseVal.value
|
||||
const width = clusterRects[0].width.baseVal.value
|
||||
const cluster = d3.select(clusterEl[0])
|
||||
const te = cluster.append('text')
|
||||
te.attr('x', xPos + width / 2)
|
||||
te.attr('y', yPos + 14)
|
||||
te.attr('fill', 'black')
|
||||
te.attr('stroke', 'none')
|
||||
const te = cluster.select('.label')
|
||||
te.attr('transform', `translate(${xPos + width / 2}, ${yPos + 14})`)
|
||||
te.attr('id', id + 'Text')
|
||||
te.style('text-anchor', 'middle')
|
||||
|
||||
if (typeof subG.title === 'undefined') {
|
||||
te.text('Undef')
|
||||
} else {
|
||||
te.text(subG.title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -225,10 +225,14 @@ statement
|
||||
{$$=[];}
|
||||
| clickStatement separator
|
||||
{$$=[];}
|
||||
| subgraph text separator document end
|
||||
{$$=yy.addSubGraph($4,$2);}
|
||||
| subgraph SPACE alphaNum SQS text SQE separator document end
|
||||
{$$=yy.addSubGraph($3,$8,$5);}
|
||||
| subgraph SPACE STR separator document end
|
||||
{$$=yy.addSubGraph(undefined,$5,$3);}
|
||||
| subgraph SPACE alphaNum separator document end
|
||||
{$$=yy.addSubGraph($3,$5,$3);}
|
||||
| subgraph separator document end
|
||||
{$$=yy.addSubGraph($3,undefined);}
|
||||
{$$=yy.addSubGraph(undefined,$3,undefined);}
|
||||
;
|
||||
|
||||
separator: NEWLINE | SEMI | EOF ;
|
||||
@@ -410,21 +414,27 @@ styleStatement:STYLE SPACE alphaNum SPACE stylesOpt
|
||||
|
||||
linkStyleStatement
|
||||
: LINKSTYLE SPACE DEFAULT SPACE stylesOpt
|
||||
{$$ = $1;yy.updateLink($3,$5);}
|
||||
| LINKSTYLE SPACE NUM SPACE stylesOpt
|
||||
{$$ = $1;yy.updateLink([$3],$5);}
|
||||
| LINKSTYLE SPACE numList SPACE stylesOpt
|
||||
{$$ = $1;yy.updateLink($3,$5);}
|
||||
| LINKSTYLE SPACE DEFAULT SPACE INTERPOLATE SPACE alphaNum SPACE stylesOpt
|
||||
{$$ = $1;yy.updateLinkInterpolate($3,$7);yy.updateLink($3,$9);}
|
||||
| LINKSTYLE SPACE NUM SPACE INTERPOLATE SPACE alphaNum SPACE stylesOpt
|
||||
{$$ = $1;yy.updateLinkInterpolate([$3],$7);yy.updateLink([$3],$9);}
|
||||
| LINKSTYLE SPACE numList SPACE INTERPOLATE SPACE alphaNum SPACE stylesOpt
|
||||
{$$ = $1;yy.updateLinkInterpolate($3,$7);yy.updateLink($3,$9);}
|
||||
| LINKSTYLE SPACE DEFAULT SPACE INTERPOLATE SPACE alphaNum
|
||||
{$$ = $1;yy.updateLinkInterpolate($3,$7);}
|
||||
| LINKSTYLE SPACE NUM SPACE INTERPOLATE SPACE alphaNum
|
||||
{$$ = $1;yy.updateLinkInterpolate([$3],$7);}
|
||||
| LINKSTYLE SPACE numList SPACE INTERPOLATE SPACE alphaNum
|
||||
{$$ = $1;yy.updateLinkInterpolate($3,$7);}
|
||||
;
|
||||
|
||||
commentStatement: PCT PCT commentText;
|
||||
|
||||
numList: NUM
|
||||
{$$ = [$1]}
|
||||
| numList COMMA NUM
|
||||
{$1.push($3);$$ = $1;}
|
||||
;
|
||||
|
||||
stylesOpt: style
|
||||
{$$ = [$1]}
|
||||
| stylesOpt COMMA style
|
||||
|
File diff suppressed because one or more lines are too long
@@ -31,8 +31,33 @@ describe('when parsing ', function () {
|
||||
expect(subgraph.nodes[0]).toBe('a1')
|
||||
expect(subgraph.nodes[1]).toBe('a2')
|
||||
expect(subgraph.title).toBe('One')
|
||||
expect(subgraph.id).toBe('One')
|
||||
})
|
||||
|
||||
it('should handle subgraph with multiple words in title', function () {
|
||||
const res = flow.parser.parse('graph TB\nsubgraph "Some Title"\n\ta1-->a2\nend')
|
||||
const subgraphs = flow.parser.yy.getSubGraphs()
|
||||
expect(subgraphs.length).toBe(1)
|
||||
const subgraph = subgraphs[0]
|
||||
expect(subgraph.nodes.length).toBe(2)
|
||||
expect(subgraph.nodes[0]).toBe('a1')
|
||||
expect(subgraph.nodes[1]).toBe('a2')
|
||||
expect(subgraph.title).toBe('Some Title')
|
||||
expect(subgraph.id).toBe('subGraph0')
|
||||
});
|
||||
|
||||
it('should handle subgraph with id and title notation', function () {
|
||||
const res = flow.parser.parse('graph TB\nsubgraph some-id[Some Title]\n\ta1-->a2\nend')
|
||||
const subgraphs = flow.parser.yy.getSubGraphs()
|
||||
expect(subgraphs.length).toBe(1)
|
||||
const subgraph = subgraphs[0]
|
||||
expect(subgraph.nodes.length).toBe(2)
|
||||
expect(subgraph.nodes[0]).toBe('a1')
|
||||
expect(subgraph.nodes[1]).toBe('a2')
|
||||
expect(subgraph.title).toBe('Some Title')
|
||||
expect(subgraph.id).toBe('some-id')
|
||||
});
|
||||
|
||||
it("should handle angle bracket ' > ' as direction LR", function () {
|
||||
const res = flow.parser.parse('graph >;A-->B;')
|
||||
|
||||
@@ -428,6 +453,28 @@ describe('when parsing ', function () {
|
||||
expect(edges[0].type).toBe('arrow')
|
||||
})
|
||||
|
||||
it('should handle multi-numbered style definitons with more then 1 digit in a row', function () {
|
||||
const res = flow.parser.parse('graph TD\n' +
|
||||
'A-->B1\n' +
|
||||
'A-->B2\n' +
|
||||
'A-->B3\n' +
|
||||
'A-->B4\n' +
|
||||
'A-->B5\n' +
|
||||
'A-->B6\n' +
|
||||
'A-->B7\n' +
|
||||
'A-->B8\n' +
|
||||
'A-->B9\n' +
|
||||
'A-->B10\n' +
|
||||
'A-->B11\n' +
|
||||
'A-->B12\n' +
|
||||
'linkStyle 10,11 stroke-width:1px;')
|
||||
|
||||
const vert = flow.parser.yy.getVertices()
|
||||
const edges = flow.parser.yy.getEdges()
|
||||
|
||||
expect(edges[0].type).toBe('arrow')
|
||||
})
|
||||
|
||||
it('should handle line interpolation default definitions', function () {
|
||||
const res = flow.parser.parse('graph TD\n' +
|
||||
'A-->B\n' +
|
||||
@@ -453,6 +500,19 @@ describe('when parsing ', function () {
|
||||
expect(edges[1].interpolate).toBe('cardinal')
|
||||
})
|
||||
|
||||
it('should handle line interpolation multi-numbered definitions', function () {
|
||||
const res = flow.parser.parse('graph TD\n' +
|
||||
'A-->B\n' +
|
||||
'A-->C\n' +
|
||||
'linkStyle 0,1 interpolate basis')
|
||||
|
||||
const vert = flow.parser.yy.getVertices()
|
||||
const edges = flow.parser.yy.getEdges()
|
||||
|
||||
expect(edges[0].interpolate).toBe('basis')
|
||||
expect(edges[1].interpolate).toBe('basis')
|
||||
})
|
||||
|
||||
it('should handle line interpolation default with style', function () {
|
||||
const res = flow.parser.parse('graph TD\n' +
|
||||
'A-->B\n' +
|
||||
@@ -478,6 +538,19 @@ describe('when parsing ', function () {
|
||||
expect(edges[1].interpolate).toBe('cardinal')
|
||||
})
|
||||
|
||||
it('should handle line interpolation multi-numbered with style', function () {
|
||||
const res = flow.parser.parse('graph TD\n' +
|
||||
'A-->B\n' +
|
||||
'A-->C\n' +
|
||||
'linkStyle 0,1 interpolate basis stroke-width:1px;')
|
||||
|
||||
const vert = flow.parser.yy.getVertices()
|
||||
const edges = flow.parser.yy.getEdges()
|
||||
|
||||
expect(edges[0].interpolate).toBe('basis')
|
||||
expect(edges[1].interpolate).toBe('basis')
|
||||
})
|
||||
|
||||
describe('it should handle interaction, ', function () {
|
||||
it('it should be possible to use click to a callback', function () {
|
||||
spyOn(flowDb, 'setClickEvent')
|
||||
|
@@ -1,48 +0,0 @@
|
||||
/* eslint-env jasmine */
|
||||
import { parser } from './parser/gantt'
|
||||
import ganttDb from './ganttDb'
|
||||
|
||||
describe('when parsing a gantt diagram it', function () {
|
||||
beforeEach(function () {
|
||||
parser.yy = ganttDb
|
||||
})
|
||||
|
||||
it('should handle a dateFormat definition', function () {
|
||||
const str = 'gantt\ndateFormat yyyy-mm-dd'
|
||||
|
||||
parser.parse(str)
|
||||
})
|
||||
it('should handle a title definition', function () {
|
||||
const str = 'gantt\ndateFormat yyyy-mm-dd\ntitle Adding gantt diagram functionality to mermaid'
|
||||
|
||||
parser.parse(str)
|
||||
})
|
||||
it('should handle a section definition', function () {
|
||||
const str = 'gantt\n' +
|
||||
'dateFormat yyyy-mm-dd\n' +
|
||||
'title Adding gantt diagram functionality to mermaid\n' +
|
||||
'section Documentation'
|
||||
|
||||
parser.parse(str)
|
||||
})
|
||||
/**
|
||||
* Beslutsflöde inligt nedan. Obs bla bla bla
|
||||
* ```
|
||||
* graph TD
|
||||
* A[Hard pledge] -- text on link -->B(Round edge)
|
||||
* B --> C{to do or not to do}
|
||||
* C -->|Too| D[Result one]
|
||||
* C -->|Doo| E[Result two]
|
||||
```
|
||||
* params bapa - a unique bapap
|
||||
*/
|
||||
it('should handle a task definition', function () {
|
||||
const str = 'gantt\n' +
|
||||
'dateFormat yyyy-mm-dd\n' +
|
||||
'title Adding gantt diagram functionality to mermaid\n' +
|
||||
'section Documentation\n' +
|
||||
'Design jison grammar:des1, 2014-01-01, 2014-01-04'
|
||||
|
||||
parser.parse(str)
|
||||
})
|
||||
})
|
@@ -1,17 +1,22 @@
|
||||
import moment from 'moment'
|
||||
import moment from 'moment-mini'
|
||||
import { logger } from '../../logger'
|
||||
import * as d3 from 'd3'
|
||||
|
||||
let dateFormat = ''
|
||||
let axisFormat = ''
|
||||
let excludes = []
|
||||
let title = ''
|
||||
let sections = []
|
||||
let tasks = []
|
||||
let currentSection = ''
|
||||
const tags = ['active', 'done', 'crit', 'milestone']
|
||||
let funs = []
|
||||
|
||||
export const clear = function () {
|
||||
sections = []
|
||||
tasks = []
|
||||
currentSection = ''
|
||||
funs = []
|
||||
title = ''
|
||||
taskCnt = 0
|
||||
lastTask = undefined
|
||||
@@ -31,6 +36,10 @@ export const setDateFormat = function (txt) {
|
||||
dateFormat = txt
|
||||
}
|
||||
|
||||
export const setExcludes = function (txt) {
|
||||
excludes = txt.toLowerCase().split(/[\s,]+/)
|
||||
}
|
||||
|
||||
export const setTitle = function (txt) {
|
||||
title = txt
|
||||
}
|
||||
@@ -58,6 +67,42 @@ export const getTasks = function () {
|
||||
return tasks
|
||||
}
|
||||
|
||||
const isInvalidDate = function (date, dateFormat, excludes) {
|
||||
if (date.isoWeekday() >= 6 && excludes.indexOf('weekends') >= 0) {
|
||||
return true
|
||||
}
|
||||
if (excludes.indexOf(date.format('dddd').toLowerCase()) >= 0) {
|
||||
return true
|
||||
}
|
||||
return excludes.indexOf(date.format(dateFormat.trim())) >= 0
|
||||
}
|
||||
|
||||
const checkTaskDates = function (task, dateFormat, excludes) {
|
||||
if (!excludes.length || task.manualEndTime) return
|
||||
let startTime = moment(task.startTime, dateFormat, true)
|
||||
startTime.add(1, 'd')
|
||||
let endTime = moment(task.endTime, dateFormat, true)
|
||||
let renderEndTime = fixTaskDates(startTime, endTime, dateFormat, excludes)
|
||||
task.endTime = endTime.toDate()
|
||||
task.renderEndTime = renderEndTime
|
||||
}
|
||||
|
||||
const fixTaskDates = function (startTime, endTime, dateFormat, excludes) {
|
||||
let invalid = false
|
||||
let renderEndTime = null
|
||||
while (startTime.date() <= endTime.date()) {
|
||||
if (!invalid) {
|
||||
renderEndTime = endTime.toDate()
|
||||
}
|
||||
invalid = isInvalidDate(startTime, dateFormat, excludes)
|
||||
if (invalid) {
|
||||
endTime.add(1, 'd')
|
||||
}
|
||||
startTime.add(1, 'd')
|
||||
}
|
||||
return renderEndTime
|
||||
}
|
||||
|
||||
const getStartDate = function (prevTime, dateFormat, str) {
|
||||
str = str.trim()
|
||||
|
||||
@@ -77,8 +122,9 @@ const getStartDate = function (prevTime, dateFormat, str) {
|
||||
}
|
||||
|
||||
// Check for actual date set
|
||||
if (moment(str, dateFormat.trim(), true).isValid()) {
|
||||
return moment(str, dateFormat.trim(), true).toDate()
|
||||
let mDate = moment(str, dateFormat.trim(), true)
|
||||
if (mDate.isValid()) {
|
||||
return mDate.toDate()
|
||||
} else {
|
||||
logger.debug('Invalid date:' + str)
|
||||
logger.debug('With date format:' + dateFormat.trim())
|
||||
@@ -92,8 +138,9 @@ const getEndDate = function (prevTime, dateFormat, str) {
|
||||
str = str.trim()
|
||||
|
||||
// Check for actual date
|
||||
if (moment(str, dateFormat.trim(), true).isValid()) {
|
||||
return moment(str, dateFormat.trim()).toDate()
|
||||
let mDate = moment(str, dateFormat.trim(), true)
|
||||
if (mDate.isValid()) {
|
||||
return mDate.toDate()
|
||||
}
|
||||
|
||||
const d = moment(prevTime)
|
||||
@@ -119,7 +166,6 @@ const getEndDate = function (prevTime, dateFormat, str) {
|
||||
d.add(durationStatement[1], 'weeks')
|
||||
break
|
||||
}
|
||||
return d.toDate()
|
||||
}
|
||||
// Default date - now
|
||||
return d.toDate()
|
||||
@@ -157,49 +203,39 @@ const compileData = function (prevTask, dataStr) {
|
||||
|
||||
const task = {}
|
||||
|
||||
// Get tags like active, done cand crit
|
||||
let matchFound = true
|
||||
while (matchFound) {
|
||||
matchFound = false
|
||||
if (data[0].match(/^\s*active\s*$/)) {
|
||||
task.active = true
|
||||
data.shift(1)
|
||||
matchFound = true
|
||||
}
|
||||
if (data[0].match(/^\s*done\s*$/)) {
|
||||
task.done = true
|
||||
data.shift(1)
|
||||
matchFound = true
|
||||
}
|
||||
if (data[0].match(/^\s*crit\s*$/)) {
|
||||
task.crit = true
|
||||
data.shift(1)
|
||||
matchFound = true
|
||||
}
|
||||
}
|
||||
// Get tags like active, done, crit and milestone
|
||||
getTaskTags(data, task, tags)
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
data[i] = data[i].trim()
|
||||
}
|
||||
|
||||
let endTimeData = ''
|
||||
switch (data.length) {
|
||||
case 1:
|
||||
task.id = parseId()
|
||||
task.startTime = prevTask.endTime
|
||||
task.endTime = getEndDate(task.startTime, dateFormat, data[0])
|
||||
endTimeData = data[0]
|
||||
break
|
||||
case 2:
|
||||
task.id = parseId()
|
||||
task.startTime = getStartDate(undefined, dateFormat, data[0])
|
||||
task.endTime = getEndDate(task.startTime, dateFormat, data[1])
|
||||
endTimeData = data[1]
|
||||
break
|
||||
case 3:
|
||||
task.id = parseId(data[0])
|
||||
task.startTime = getStartDate(undefined, dateFormat, data[1])
|
||||
task.endTime = getEndDate(task.startTime, dateFormat, data[2])
|
||||
endTimeData = data[2]
|
||||
break
|
||||
default:
|
||||
}
|
||||
|
||||
if (endTimeData) {
|
||||
task.endTime = getEndDate(task.startTime, dateFormat, endTimeData)
|
||||
task.manualEndTime = endTimeData === moment(task.endTime).format(dateFormat.trim())
|
||||
checkTaskDates(task, dateFormat, excludes)
|
||||
}
|
||||
|
||||
return task
|
||||
}
|
||||
|
||||
@@ -215,26 +251,9 @@ const parseData = function (prevTaskId, dataStr) {
|
||||
|
||||
const task = {}
|
||||
|
||||
// Get tags like active, done cand crit
|
||||
let matchFound = true
|
||||
while (matchFound) {
|
||||
matchFound = false
|
||||
if (data[0].match(/^\s*active\s*$/)) {
|
||||
task.active = true
|
||||
data.shift(1)
|
||||
matchFound = true
|
||||
}
|
||||
if (data[0].match(/^\s*done\s*$/)) {
|
||||
task.done = true
|
||||
data.shift(1)
|
||||
matchFound = true
|
||||
}
|
||||
if (data[0].match(/^\s*crit\s*$/)) {
|
||||
task.crit = true
|
||||
data.shift(1)
|
||||
matchFound = true
|
||||
}
|
||||
}
|
||||
// Get tags like active, done, crit and milestone
|
||||
getTaskTags(data, task, tags)
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
data[i] = data[i].trim()
|
||||
}
|
||||
@@ -242,18 +261,33 @@ const parseData = function (prevTaskId, dataStr) {
|
||||
switch (data.length) {
|
||||
case 1:
|
||||
task.id = parseId()
|
||||
task.startTime = { type: 'prevTaskEnd', id: prevTaskId }
|
||||
task.endTime = { data: data[0] }
|
||||
task.startTime = {
|
||||
type: 'prevTaskEnd',
|
||||
id: prevTaskId
|
||||
}
|
||||
task.endTime = {
|
||||
data: data[0]
|
||||
}
|
||||
break
|
||||
case 2:
|
||||
task.id = parseId()
|
||||
task.startTime = { type: 'getStartDate', startData: data[0] }
|
||||
task.endTime = { data: data[1] }
|
||||
task.startTime = {
|
||||
type: 'getStartDate',
|
||||
startData: data[0]
|
||||
}
|
||||
task.endTime = {
|
||||
data: data[1]
|
||||
}
|
||||
break
|
||||
case 3:
|
||||
task.id = parseId(data[0])
|
||||
task.startTime = { type: 'getStartDate', startData: data[1] }
|
||||
task.endTime = { data: data[2] }
|
||||
task.startTime = {
|
||||
type: 'getStartDate',
|
||||
startData: data[1]
|
||||
}
|
||||
task.endTime = {
|
||||
data: data[2]
|
||||
}
|
||||
break
|
||||
default:
|
||||
}
|
||||
@@ -270,8 +304,11 @@ export const addTask = function (descr, data) {
|
||||
section: currentSection,
|
||||
type: currentSection,
|
||||
processed: false,
|
||||
manualEndTime: false,
|
||||
renderEndTime: null,
|
||||
raw: { data: data },
|
||||
task: descr
|
||||
task: descr,
|
||||
classes: []
|
||||
}
|
||||
const taskInfo = parseData(lastTaskID, data)
|
||||
rawTask.raw.startTime = taskInfo.startTime
|
||||
@@ -281,6 +318,7 @@ export const addTask = function (descr, data) {
|
||||
rawTask.active = taskInfo.active
|
||||
rawTask.done = taskInfo.done
|
||||
rawTask.crit = taskInfo.crit
|
||||
rawTask.milestone = taskInfo.milestone
|
||||
|
||||
const pos = rawTasks.push(rawTask)
|
||||
|
||||
@@ -299,7 +337,8 @@ export const addTaskOrg = function (descr, data) {
|
||||
section: currentSection,
|
||||
type: currentSection,
|
||||
description: descr,
|
||||
task: descr
|
||||
task: descr,
|
||||
classes: []
|
||||
}
|
||||
const taskInfo = compileData(lastTask, data)
|
||||
newTask.startTime = taskInfo.startTime
|
||||
@@ -308,6 +347,7 @@ export const addTaskOrg = function (descr, data) {
|
||||
newTask.active = taskInfo.active
|
||||
newTask.done = taskInfo.done
|
||||
newTask.crit = taskInfo.crit
|
||||
newTask.milestone = taskInfo.milestone
|
||||
lastTask = newTask
|
||||
tasks.push(newTask)
|
||||
}
|
||||
@@ -333,6 +373,8 @@ const compileTasks = function () {
|
||||
rawTasks[pos].endTime = getEndDate(rawTasks[pos].startTime, dateFormat, rawTasks[pos].raw.endTime.data)
|
||||
if (rawTasks[pos].endTime) {
|
||||
rawTasks[pos].processed = true
|
||||
rawTasks[pos].manualEndTime = rawTasks[pos].raw.endTime.data === moment(rawTasks[pos].endTime).format(dateFormat.trim())
|
||||
checkTaskDates(rawTasks[pos], dateFormat, excludes)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,6 +390,108 @@ const compileTasks = function () {
|
||||
return allProcessed
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by parser when a link is found. Adds the URL to the vertex data.
|
||||
* @param ids Comma separated list of ids
|
||||
* @param linkStr URL to create a link for
|
||||
*/
|
||||
export const setLink = function (ids, linkStr) {
|
||||
ids.split(',').forEach(function (id) {
|
||||
let rawTask = findTaskById(id)
|
||||
if (typeof rawTask !== 'undefined') {
|
||||
pushFun(id, () => { window.open(linkStr, '_self') })
|
||||
}
|
||||
})
|
||||
setClass(ids, 'clickable')
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by parser when a special node is found, e.g. a clickable element.
|
||||
* @param ids Comma separated list of ids
|
||||
* @param className Class to add
|
||||
*/
|
||||
export const setClass = function (ids, className) {
|
||||
ids.split(',').forEach(function (id) {
|
||||
let rawTask = findTaskById(id)
|
||||
if (typeof rawTask !== 'undefined') {
|
||||
rawTask.classes.push(className)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const setClickFun = function (id, functionName, functionArgs) {
|
||||
if (typeof functionName === 'undefined') {
|
||||
return
|
||||
}
|
||||
|
||||
let argList = []
|
||||
if (typeof functionArgs === 'string') {
|
||||
/* Splits functionArgs by ',', ignoring all ',' in double quoted strings */
|
||||
argList = functionArgs.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/)
|
||||
for (let i = 0; i < argList.length; i++) {
|
||||
let item = argList[i].trim()
|
||||
/* Removes all double quotes at the start and end of an argument */
|
||||
/* This preserves all starting and ending whitespace inside */
|
||||
if (item.charAt(0) === '"' && item.charAt(item.length - 1) === '"') {
|
||||
item = item.substr(1, item.length - 2)
|
||||
}
|
||||
argList[i] = item
|
||||
}
|
||||
}
|
||||
|
||||
let rawTask = findTaskById(id)
|
||||
if (typeof rawTask !== 'undefined') {
|
||||
pushFun(id, () => { window[functionName](...argList) })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The callbackFunction is executed in a click event bound to the task with the specified id or the task's assigned text
|
||||
* @param id The task's id
|
||||
* @param callbackFunction A function to be executed when clicked on the task or the task's text
|
||||
*/
|
||||
const pushFun = function (id, callbackFunction) {
|
||||
funs.push(function (element) {
|
||||
const elem = d3.select(element).select(`[id="${id}"]`)
|
||||
if (elem !== null) {
|
||||
elem.on('click', function () {
|
||||
callbackFunction()
|
||||
})
|
||||
}
|
||||
})
|
||||
funs.push(function (element) {
|
||||
const elem = d3.select(element).select(`[id="${id}-text"]`)
|
||||
if (elem !== null) {
|
||||
elem.on('click', function () {
|
||||
callbackFunction()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by parser when a click definition is found. Registers an event handler.
|
||||
* @param ids Comma separated list of ids
|
||||
* @param functionName Function to be called on click
|
||||
* @param functionArgs Function args the function should be called with
|
||||
*/
|
||||
export const setClickEvent = function (ids, functionName, functionArgs) {
|
||||
ids.split(',').forEach(function (id) {
|
||||
setClickFun(id, functionName, functionArgs)
|
||||
})
|
||||
setClass(ids, 'clickable')
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds all functions previously added to fun (specified through click) to the element
|
||||
* @param element
|
||||
*/
|
||||
export const bindFunctions = function (element) {
|
||||
funs.forEach(function (fun) {
|
||||
fun(element)
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
clear,
|
||||
setDateFormat,
|
||||
@@ -359,5 +503,25 @@ export default {
|
||||
getTasks,
|
||||
addTask,
|
||||
findTaskById,
|
||||
addTaskOrg
|
||||
addTaskOrg,
|
||||
setExcludes,
|
||||
setClickEvent,
|
||||
setLink,
|
||||
bindFunctions
|
||||
}
|
||||
|
||||
function getTaskTags (data, task, tags) {
|
||||
let matchFound = true
|
||||
while (matchFound) {
|
||||
matchFound = false
|
||||
tags.forEach(function (t) {
|
||||
const pattern = '^\\s*' + t + '\\s*$'
|
||||
const regex = new RegExp(pattern)
|
||||
if (data[0].match(regex)) {
|
||||
task[t] = true
|
||||
data.shift(1)
|
||||
matchFound = true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* eslint-env jasmine */
|
||||
import moment from 'moment'
|
||||
import moment from 'moment-mini'
|
||||
import ganttDb from './ganttDb'
|
||||
|
||||
describe('when using the ganttDb', function () {
|
||||
@@ -7,151 +7,48 @@ describe('when using the ganttDb', function () {
|
||||
ganttDb.clear()
|
||||
})
|
||||
|
||||
it('should handle an fixed dates', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2013-01-12')
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].endTime).toEqual(moment('2013-01-12', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].id).toEqual('id1')
|
||||
expect(tasks[0].task).toEqual('test1')
|
||||
})
|
||||
it('should handle duration (days) instead of fixed date to determine end date', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2d')
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].endTime).toEqual(moment('2013-01-03', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].id).toEqual('id1')
|
||||
expect(tasks[0].task).toEqual('test1')
|
||||
})
|
||||
it('should handle duration (hours) instead of fixed date to determine end date', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2h')
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].endTime).toEqual(moment('2013-01-01 2:00', 'YYYY-MM-DD hh:mm').toDate())
|
||||
expect(tasks[0].id).toEqual('id1')
|
||||
expect(tasks[0].task).toEqual('test1')
|
||||
})
|
||||
it('should handle duration (minutes) instead of fixed date to determine end date', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2m')
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].endTime).toEqual(moment('2013-01-01 00:02', 'YYYY-MM-DD hh:mm').toDate())
|
||||
expect(tasks[0].id).toEqual('id1')
|
||||
expect(tasks[0].task).toEqual('test1')
|
||||
})
|
||||
it('should handle duration (seconds) instead of fixed date to determine end date', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2s')
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].endTime).toEqual(moment('2013-01-01 00:00:02', 'YYYY-MM-DD hh:mm:ss').toDate())
|
||||
expect(tasks[0].id).toEqual('id1')
|
||||
expect(tasks[0].task).toEqual('test1')
|
||||
})
|
||||
it('should handle duration (weeks) instead of fixed date to determine end date', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2w')
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].endTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].id).toEqual('id1')
|
||||
expect(tasks[0].task).toEqual('test1')
|
||||
})
|
||||
it.each`
|
||||
testName | section | taskName | taskData | expStartDate | expEndDate | expId | expTask
|
||||
${'should handle fixed dates'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2013-01-12'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 12)} | ${'id1'} | ${'test1'}
|
||||
${'should handle duration (days) instead of fixed date to determine end date'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2d'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 3)} | ${'id1'} | ${'test1'}
|
||||
${'should handle duration (hours) instead of fixed date to determine end date'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2h'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 1, 2)} | ${'id1'} | ${'test1'}
|
||||
${'should handle duration (minutes) instead of fixed date to determine end date'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2m'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 1, 0, 2)} | ${'id1'} | ${'test1'}
|
||||
${'should handle duration (seconds) instead of fixed date to determine end date'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2s'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 1, 0, 0, 2)} | ${'id1'} | ${'test1'}
|
||||
${'should handle duration (weeks) instead of fixed date to determine end date'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2w'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 15)} | ${'id1'} | ${'test1'}
|
||||
${'should handle fixed dates without id'} | ${'testa1'} | ${'test1'} | ${'2013-01-01,2013-01-12'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 12)} | ${'task1'} | ${'test1'}
|
||||
${'should handle duration instead of a fixed date to determine end date without id'} | ${'testa1'} | ${'test1'} | ${'2013-01-01,4d'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 5)} | ${'task1'} | ${'test1'}
|
||||
`('$testName', ({ section, taskName, taskData, expStartDate, expEndDate, expId, expTask }) => {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection(section)
|
||||
ganttDb.addTask(taskName, taskData)
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[0].startTime).toEqual(expStartDate)
|
||||
expect(tasks[0].endTime).toEqual(expEndDate)
|
||||
expect(tasks[0].id).toEqual(expId)
|
||||
expect(tasks[0].task).toEqual(expTask)
|
||||
})
|
||||
|
||||
it('should handle relative start date based on id', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2w')
|
||||
ganttDb.addTask('test2', 'id2,after id1,1d')
|
||||
it.each`
|
||||
section | taskName1 | taskName2 | taskData1 | taskData2 | expStartDate2 | expEndDate2 | expId2 | expTask2
|
||||
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'id2,after id1,1d'} | ${new Date(2013, 0, 15)} | ${undefined} | ${'id2'} | ${'test2'}
|
||||
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'id2,after id3,1d'} | ${new Date((new Date()).setHours(0, 0, 0, 0))} | ${undefined} | ${'id2'} | ${'test2'}
|
||||
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'after id1,1d'} | ${new Date(2013, 0, 15)} | ${undefined} | ${'task1'} | ${'test2'}
|
||||
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'2013-01-26'} | ${new Date(2013, 0, 15)} | ${new Date(2013, 0, 26)} | ${'task1'} | ${'test2'}
|
||||
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'2d'} | ${new Date(2013, 0, 15)} | ${new Date(2013, 0, 17)} | ${'task1'} | ${'test2'}
|
||||
`('$testName', ({ section, taskName1, taskName2, taskData1, taskData2, expStartDate2, expEndDate2, expId2, expTask2 }) => {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection(section)
|
||||
ganttDb.addTask(taskName1, taskData1)
|
||||
ganttDb.addTask(taskName2, taskData2)
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[1].startTime).toEqual(expStartDate2)
|
||||
if (!expEndDate2 === undefined) {
|
||||
expect(tasks[1].endTime).toEqual(expEndDate2)
|
||||
}
|
||||
expect(tasks[1].id).toEqual(expId2)
|
||||
expect(tasks[1].task).toEqual(expTask2)
|
||||
})
|
||||
|
||||
const tasks = ganttDb.getTasks()
|
||||
|
||||
expect(tasks[1].startTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].id).toEqual('id2')
|
||||
expect(tasks[1].task).toEqual('test2')
|
||||
})
|
||||
|
||||
it('should handle relative start date based on id when id is invalid', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2w')
|
||||
ganttDb.addTask('test2', 'id2,after id3,1d')
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[1].startTime).toEqual(new Date((new Date()).setHours(0, 0, 0, 0)))
|
||||
expect(tasks[1].id).toEqual('id2')
|
||||
expect(tasks[1].task).toEqual('test2')
|
||||
})
|
||||
|
||||
it('should handle fixed dates without id', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', '2013-01-01,2013-01-12')
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].endTime).toEqual(moment('2013-01-12', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].id).toEqual('task1')
|
||||
expect(tasks[0].task).toEqual('test1')
|
||||
})
|
||||
|
||||
it('should handle duration instead of a fixed date to determine end date without id', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', '2013-01-01,4d')
|
||||
const tasks = ganttDb.getTasks()
|
||||
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].endTime).toEqual(moment('2013-01-05', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].id).toEqual('task1')
|
||||
expect(tasks[0].task).toEqual('test1')
|
||||
})
|
||||
|
||||
it('should handle relative start date of a fixed date to determine end date without id', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2w')
|
||||
ganttDb.addTask('test2', 'after id1,1d')
|
||||
|
||||
const tasks = ganttDb.getTasks()
|
||||
|
||||
expect(tasks[1].startTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].id).toEqual('task1')
|
||||
expect(tasks[1].task).toEqual('test2')
|
||||
})
|
||||
it('should handle a new task with only an end date as definition', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2w')
|
||||
ganttDb.addTask('test2', '2013-01-26')
|
||||
|
||||
const tasks = ganttDb.getTasks()
|
||||
|
||||
expect(tasks[1].startTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].endTime).toEqual(moment('2013-01-26', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].id).toEqual('task1')
|
||||
expect(tasks[1].task).toEqual('test2')
|
||||
})
|
||||
it('should handle a new task with only an end date as definition', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
ganttDb.addTask('test1', 'id1,2013-01-01,2w')
|
||||
ganttDb.addTask('test2', '2d')
|
||||
|
||||
const tasks = ganttDb.getTasks()
|
||||
|
||||
expect(tasks[1].startTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].endTime).toEqual(moment('2013-01-17', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].id).toEqual('task1')
|
||||
expect(tasks[1].task).toEqual('test2')
|
||||
})
|
||||
it('should handle relative start date based on id regardless of sections', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.addSection('testa1')
|
||||
@@ -162,14 +59,70 @@ describe('when using the ganttDb', function () {
|
||||
|
||||
const tasks = ganttDb.getTasks()
|
||||
|
||||
expect(tasks[1].startTime).toEqual(moment('2013-01-17', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].endTime).toEqual(moment('2013-01-18', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].startTime).toEqual(new Date(2013, 0, 17))
|
||||
expect(tasks[1].endTime).toEqual(new Date(2013, 0, 18))
|
||||
expect(tasks[1].id).toEqual('id2')
|
||||
expect(tasks[1].task).toEqual('test2')
|
||||
|
||||
expect(tasks[2].id).toEqual('id3')
|
||||
expect(tasks[2].task).toEqual('test3')
|
||||
expect(tasks[2].startTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[2].endTime).toEqual(moment('2013-01-17', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[2].startTime).toEqual(new Date(2013, 0, 15))
|
||||
expect(tasks[2].endTime).toEqual(new Date(2013, 0, 17))
|
||||
})
|
||||
it('should ignore weekends', function () {
|
||||
ganttDb.setDateFormat('YYYY-MM-DD')
|
||||
ganttDb.setExcludes('weekends 2019-02-06,friday')
|
||||
ganttDb.addSection('weekends skip test')
|
||||
ganttDb.addTask('test1', 'id1,2019-02-01,1d')
|
||||
ganttDb.addTask('test2', 'id2,after id1,2d')
|
||||
ganttDb.addTask('test3', 'id3,after id2,7d')
|
||||
ganttDb.addTask('test4', 'id4,2019-02-01,2019-02-20') // Fixed endTime
|
||||
ganttDb.addTask('test5', 'id5,after id4,1d')
|
||||
ganttDb.addSection('full ending taks on last day')
|
||||
ganttDb.addTask('test6', 'id6,2019-02-13,2d')
|
||||
ganttDb.addTask('test7', 'id7,after id6,1d')
|
||||
|
||||
const tasks = ganttDb.getTasks()
|
||||
|
||||
expect(tasks[0].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].endTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].renderEndTime).toEqual(moment('2019-02-02', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[0].id).toEqual('id1')
|
||||
expect(tasks[0].task).toEqual('test1')
|
||||
|
||||
expect(tasks[1].startTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].endTime).toEqual(moment('2019-02-07', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].renderEndTime).toEqual(moment('2019-02-06', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[1].id).toEqual('id2')
|
||||
expect(tasks[1].task).toEqual('test2')
|
||||
|
||||
expect(tasks[2].startTime).toEqual(moment('2019-02-07', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[2].endTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[2].renderEndTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[2].id).toEqual('id3')
|
||||
expect(tasks[2].task).toEqual('test3')
|
||||
|
||||
expect(tasks[3].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[3].endTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[3].renderEndTime).toBeNull() // Fixed end
|
||||
expect(tasks[3].id).toEqual('id4')
|
||||
expect(tasks[3].task).toEqual('test4')
|
||||
|
||||
expect(tasks[4].startTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[4].endTime).toEqual(moment('2019-02-21', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[4].renderEndTime).toEqual(moment('2019-02-21', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[4].id).toEqual('id5')
|
||||
expect(tasks[4].task).toEqual('test5')
|
||||
|
||||
expect(tasks[5].startTime).toEqual(moment('2019-02-13', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[5].endTime).toEqual(moment('2019-02-18', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[5].renderEndTime).toEqual(moment('2019-02-15', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[5].id).toEqual('id6')
|
||||
expect(tasks[5].task).toEqual('test6')
|
||||
|
||||
expect(tasks[6].startTime).toEqual(moment('2019-02-18', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[6].endTime).toEqual(moment('2019-02-19', 'YYYY-MM-DD').toDate())
|
||||
expect(tasks[6].id).toEqual('id7')
|
||||
expect(tasks[6].task).toEqual('test7')
|
||||
})
|
||||
})
|
||||
|
@@ -98,6 +98,7 @@ export const draw = function (text, id) {
|
||||
}
|
||||
|
||||
function drawRects (theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w, h) {
|
||||
// Draw background rects covering the entire width of the graph, these form the section rows.
|
||||
svg.append('g')
|
||||
.selectAll('rect')
|
||||
.data(theArray)
|
||||
@@ -120,26 +121,42 @@ export const draw = function (text, id) {
|
||||
return 'section section0'
|
||||
})
|
||||
|
||||
// Draw the rects representing the tasks
|
||||
const rectangles = svg.append('g')
|
||||
.selectAll('rect')
|
||||
.data(theArray)
|
||||
.enter()
|
||||
|
||||
rectangles.append('rect')
|
||||
.attr('id', function (d) { return d.id })
|
||||
.attr('rx', 3)
|
||||
.attr('ry', 3)
|
||||
.attr('x', function (d) {
|
||||
if (d.milestone) {
|
||||
return timeScale(d.startTime) + theSidePad + (0.5 * (timeScale(d.endTime) - timeScale(d.startTime))) - (0.5 * theBarHeight)
|
||||
}
|
||||
return timeScale(d.startTime) + theSidePad
|
||||
})
|
||||
.attr('y', function (d, i) {
|
||||
return i * theGap + theTopPad
|
||||
})
|
||||
.attr('width', function (d) {
|
||||
return (timeScale(d.endTime) - timeScale(d.startTime))
|
||||
if (d.milestone) {
|
||||
return theBarHeight
|
||||
}
|
||||
return (timeScale(d.renderEndTime || d.endTime) - timeScale(d.startTime))
|
||||
})
|
||||
.attr('height', theBarHeight)
|
||||
.attr('transform-origin', function (d, i) {
|
||||
return (timeScale(d.startTime) + theSidePad + 0.5 * (timeScale(d.endTime) - timeScale(d.startTime))).toString() + 'px ' + (i * theGap + theTopPad + 0.5 * theBarHeight).toString() + 'px'
|
||||
})
|
||||
.attr('class', function (d) {
|
||||
const res = 'task '
|
||||
const res = 'task'
|
||||
|
||||
let classStr = ''
|
||||
if (d.classes.length > 0) {
|
||||
classStr = d.classes.join(' ')
|
||||
}
|
||||
|
||||
let secNum = 0
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
@@ -148,37 +165,55 @@ export const draw = function (text, id) {
|
||||
}
|
||||
}
|
||||
|
||||
let taskClass = classStr
|
||||
if (d.active) {
|
||||
if (d.crit) {
|
||||
return res + ' activeCrit' + secNum
|
||||
taskClass += ' activeCrit'
|
||||
} else {
|
||||
return res + ' active' + secNum
|
||||
taskClass = 'active'
|
||||
}
|
||||
}
|
||||
|
||||
if (d.done) {
|
||||
} else if (d.done) {
|
||||
if (d.crit) {
|
||||
return res + ' doneCrit' + secNum
|
||||
taskClass = ' doneCrit'
|
||||
} else {
|
||||
return res + ' done' + secNum
|
||||
taskClass = 'done'
|
||||
}
|
||||
}
|
||||
|
||||
if (d.crit) {
|
||||
return res + ' crit' + secNum
|
||||
if (taskClass.length > 0) {
|
||||
taskClass += 'crit'
|
||||
}
|
||||
}
|
||||
|
||||
return res + ' task' + secNum
|
||||
if (taskClass.length === 0) {
|
||||
taskClass = ' task'
|
||||
}
|
||||
|
||||
if (d.milestone) {
|
||||
taskClass = ' milestone' + taskClass
|
||||
}
|
||||
|
||||
taskClass += secNum
|
||||
|
||||
return res + taskClass
|
||||
})
|
||||
|
||||
// Append task labels
|
||||
rectangles.append('text')
|
||||
.text(function (d) {
|
||||
return d.task
|
||||
})
|
||||
.attr('font-size', conf.fontSize)
|
||||
.attr('x', function (d) {
|
||||
const startX = timeScale(d.startTime)
|
||||
const endX = timeScale(d.endTime)
|
||||
let startX = timeScale(d.startTime)
|
||||
let endX = timeScale(d.renderEndTime || d.endTime)
|
||||
if (d.milestone) {
|
||||
startX += (0.5 * (timeScale(d.endTime) - timeScale(d.startTime))) - (0.5 * theBarHeight)
|
||||
}
|
||||
if (d.milestone) {
|
||||
endX = startX + theBarHeight
|
||||
}
|
||||
const textWidth = this.getBBox().width
|
||||
|
||||
// Check id text width > width of rectangle
|
||||
@@ -198,8 +233,17 @@ export const draw = function (text, id) {
|
||||
.attr('text-height', theBarHeight)
|
||||
.attr('class', function (d) {
|
||||
const startX = timeScale(d.startTime)
|
||||
const endX = timeScale(d.endTime)
|
||||
let endX = timeScale(d.endTime)
|
||||
if (d.milestone) {
|
||||
endX = startX + theBarHeight
|
||||
}
|
||||
const textWidth = this.getBBox().width
|
||||
|
||||
let classStr = ''
|
||||
if (d.classes.length > 0) {
|
||||
classStr = d.classes.join(' ')
|
||||
}
|
||||
|
||||
let secNum = 0
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
if (d.type === categories[i]) {
|
||||
@@ -228,15 +272,19 @@ export const draw = function (text, id) {
|
||||
}
|
||||
}
|
||||
|
||||
if (d.milestone) {
|
||||
taskType += ' milestoneText'
|
||||
}
|
||||
|
||||
// Check id text width > width of rectangle
|
||||
if (textWidth > (endX - startX)) {
|
||||
if (endX + textWidth + 1.5 * conf.leftPadding > w) {
|
||||
return 'taskTextOutsideLeft taskTextOutside' + secNum + ' ' + taskType
|
||||
return classStr + ' taskTextOutsideLeft taskTextOutside' + secNum + ' ' + taskType
|
||||
} else {
|
||||
return 'taskTextOutsideRight taskTextOutside' + secNum + ' ' + taskType
|
||||
return classStr + ' taskTextOutsideRight taskTextOutside' + secNum + ' ' + taskType
|
||||
}
|
||||
} else {
|
||||
return 'taskText taskText' + secNum + ' ' + taskType
|
||||
return classStr + ' taskText taskText' + secNum + ' ' + taskType
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@@ -7,27 +7,64 @@
|
||||
|
||||
%options case-insensitive
|
||||
|
||||
%{
|
||||
// Pre-lexer code can go here
|
||||
%}
|
||||
|
||||
%x click
|
||||
%x href
|
||||
%x callbackname
|
||||
%x callbackargs
|
||||
%%
|
||||
|
||||
[\n]+ return 'NL';
|
||||
\s+ /* skip whitespace */
|
||||
\#[^\n]* /* skip comments */
|
||||
\%%[^\n]* /* skip comments */
|
||||
"gantt" return 'gantt';
|
||||
|
||||
/*
|
||||
---interactivity command---
|
||||
'href' adds a link to the specified task. 'href' can only be specified when the
|
||||
line was introduced with 'click'.
|
||||
'href "<link>"' attaches the specified link to the task that was specified by 'click'.
|
||||
*/
|
||||
"href"[\s]+["] this.begin("href");
|
||||
<href>["] this.popState();
|
||||
<href>[^"]* return 'href';
|
||||
|
||||
/*
|
||||
---interactivity command---
|
||||
'call' adds a callback to the specified task. 'call' can only be specified when
|
||||
the line was introdcued with 'click'.
|
||||
'call <callbackname>(<args>)' attaches the function 'callbackname' with the specified
|
||||
arguments to the task that was specified by 'click'.
|
||||
Function arguments are optional: 'call <callbackname>()' simply executes 'callbackname' without any arguments.
|
||||
*/
|
||||
"call"[\s]+ this.begin("callbackname");
|
||||
<callbackname>\([\s]*\) this.popState();
|
||||
<callbackname>\( this.popState(); this.begin("callbackargs");
|
||||
<callbackname>[^(]* return 'callbackname';
|
||||
<callbackargs>\) this.popState();
|
||||
<callbackargs>[^)]* return 'callbackargs';
|
||||
|
||||
/*
|
||||
'click' is the keyword to introduce a line that contains interactivity commands.
|
||||
'click' must be followed by an existing task-id. All commands are attached to
|
||||
that id.
|
||||
'click <id>' can be followed by href or call commands in any desired order
|
||||
*/
|
||||
"click"[\s]+ this.begin("click");
|
||||
<click>[\s\n] this.popState();
|
||||
<click>[^\s\n]* return 'click';
|
||||
|
||||
"gantt" return 'gantt';
|
||||
"dateFormat"\s[^#\n;]+ return 'dateFormat';
|
||||
"axisFormat"\s[^#\n;]+ return 'axisFormat';
|
||||
"excludes"\s[^#\n;]+ return 'excludes';
|
||||
\d\d\d\d"-"\d\d"-"\d\d return 'date';
|
||||
"title"\s[^#\n;]+ return 'title';
|
||||
"section"\s[^#:\n;]+ return 'section';
|
||||
[^#:\n;]+ return 'taskTxt';
|
||||
[^#:\n;]+ return 'taskTxt';
|
||||
":"[^#\n;]+ return 'taskData';
|
||||
":" return ':';
|
||||
<<EOF>> return 'EOF';
|
||||
. return 'INVALID';
|
||||
":" return ':';
|
||||
<<EOF>> return 'EOF';
|
||||
. return 'INVALID';
|
||||
|
||||
/lex
|
||||
|
||||
@@ -54,11 +91,40 @@ line
|
||||
;
|
||||
|
||||
statement
|
||||
: 'dateFormat' {yy.setDateFormat($1.substr(11));$$=$1.substr(11);}
|
||||
: 'dateFormat' {yy.setDateFormat($1.substr(11));$$=$1.substr(11);}
|
||||
| 'axisFormat' {yy.setAxisFormat($1.substr(11));$$=$1.substr(11);}
|
||||
| title {yy.setTitle($1.substr(6));$$=$1.substr(6);}
|
||||
| section {yy.addSection($1.substr(8));$$=$1.substr(8);}
|
||||
| taskTxt taskData {yy.addTask($1,$2);$$='task';}
|
||||
;
|
||||
| 'excludes' {yy.setExcludes($1.substr(9));$$=$1.substr(9);}
|
||||
| title {yy.setTitle($1.substr(6));$$=$1.substr(6);}
|
||||
| section {yy.addSection($1.substr(8));$$=$1.substr(8);}
|
||||
| clickStatement
|
||||
| taskTxt taskData {yy.addTask($1,$2);$$='task';}
|
||||
;
|
||||
|
||||
%%
|
||||
/*
|
||||
click allows any combination of href and call.
|
||||
*/
|
||||
clickStatement
|
||||
: click callbackname {$$ = $1;yy.setClickEvent($1, $2, null);}
|
||||
| click callbackname callbackargs {$$ = $1;yy.setClickEvent($1, $2, $3);}
|
||||
|
||||
| click callbackname href {$$ = $1;yy.setClickEvent($1, $2, null);yy.setLink($1,$3);}
|
||||
| click callbackname callbackargs href {$$ = $1;yy.setClickEvent($1, $2, $3);yy.setLink($1,$4);}
|
||||
|
||||
| click href callbackname {$$ = $1;yy.setClickEvent($1, $3, null);yy.setLink($1,$2);}
|
||||
| click href callbackname callbackargs {$$ = $1;yy.setClickEvent($1, $3, $4);yy.setLink($1,$2);}
|
||||
|
||||
| click href {$$ = $1;yy.setLink($1, $2);}
|
||||
;
|
||||
|
||||
clickStatementDebug
|
||||
: click callbackname {$$=$1 + ' ' + $2;}
|
||||
| click callbackname href {$$=$1 + ' ' + $2 + ' ' + $3;}
|
||||
|
||||
| click callbackname callbackargs {$$=$1 + ' ' + $2 + ' ' + $3;}
|
||||
| click callbackname callbackargs href {$$=$1 + ' ' + $2 + ' ' + $3 + ' ' + $4;}
|
||||
|
||||
| click href callbackname {$$=$1 + ' ' + $2 + ' ' + $3;}
|
||||
| click href callbackname callbackargs {$$=$1 + ' ' + $2 + ' ' + $3 + ' ' + $4;}
|
||||
|
||||
| click href {$$=$1 + ' ' + $2;}
|
||||
;%%
|
||||
|
@@ -72,12 +72,12 @@
|
||||
}
|
||||
*/
|
||||
var parser = (function(){
|
||||
var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[6,8,10,11,12,13,14,15],$V1=[1,9],$V2=[1,10],$V3=[1,11],$V4=[1,12],$V5=[1,13];
|
||||
var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[6,8,10,11,12,13,14,15,17,19],$V1=[1,9],$V2=[1,10],$V3=[1,11],$V4=[1,12],$V5=[1,13],$V6=[1,15],$V7=[1,16];
|
||||
var parser = {trace: function trace () { },
|
||||
yy: {},
|
||||
symbols_: {"error":2,"start":3,"gantt":4,"document":5,"EOF":6,"line":7,"SPACE":8,"statement":9,"NL":10,"dateFormat":11,"axisFormat":12,"title":13,"section":14,"taskTxt":15,"taskData":16,"$accept":0,"$end":1},
|
||||
terminals_: {2:"error",4:"gantt",6:"EOF",8:"SPACE",10:"NL",11:"dateFormat",12:"axisFormat",13:"title",14:"section",15:"taskTxt",16:"taskData"},
|
||||
productions_: [0,[3,3],[5,0],[5,2],[7,2],[7,1],[7,1],[7,1],[9,1],[9,1],[9,1],[9,1],[9,2]],
|
||||
symbols_: {"error":2,"start":3,"gantt":4,"document":5,"EOF":6,"line":7,"SPACE":8,"statement":9,"NL":10,"dateFormat":11,"axisFormat":12,"excludes":13,"title":14,"section":15,"clickStatement":16,"taskTxt":17,"taskData":18,"click":19,"callbackname":20,"callbackargs":21,"href":22,"clickStatementDebug":23,"$accept":0,"$end":1},
|
||||
terminals_: {2:"error",4:"gantt",6:"EOF",8:"SPACE",10:"NL",11:"dateFormat",12:"axisFormat",13:"excludes",14:"title",15:"section",17:"taskTxt",18:"taskData",19:"click",20:"callbackname",21:"callbackargs",22:"href"},
|
||||
productions_: [0,[3,3],[5,0],[5,2],[7,2],[7,1],[7,1],[7,1],[9,1],[9,1],[9,1],[9,1],[9,1],[9,1],[9,2],[16,2],[16,3],[16,3],[16,4],[16,3],[16,4],[16,2],[23,2],[23,3],[23,3],[23,4],[23,3],[23,4],[23,2]],
|
||||
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
|
||||
/* this == yyval */
|
||||
|
||||
@@ -105,17 +105,50 @@ case 9:
|
||||
yy.setAxisFormat($$[$0].substr(11));this.$=$$[$0].substr(11);
|
||||
break;
|
||||
case 10:
|
||||
yy.setTitle($$[$0].substr(6));this.$=$$[$0].substr(6);
|
||||
yy.setExcludes($$[$0].substr(9));this.$=$$[$0].substr(9);
|
||||
break;
|
||||
case 11:
|
||||
yy.addSection($$[$0].substr(8));this.$=$$[$0].substr(8);
|
||||
yy.setTitle($$[$0].substr(6));this.$=$$[$0].substr(6);
|
||||
break;
|
||||
case 12:
|
||||
yy.addSection($$[$0].substr(8));this.$=$$[$0].substr(8);
|
||||
break;
|
||||
case 14:
|
||||
yy.addTask($$[$0-1],$$[$0]);this.$='task';
|
||||
break;
|
||||
case 15:
|
||||
this.$ = $$[$0-1];yy.setClickEvent($$[$0-1], $$[$0], null);
|
||||
break;
|
||||
case 16:
|
||||
this.$ = $$[$0-2];yy.setClickEvent($$[$0-2], $$[$0-1], $$[$0]);
|
||||
break;
|
||||
case 17:
|
||||
this.$ = $$[$0-2];yy.setClickEvent($$[$0-2], $$[$0-1], null);yy.setLink($$[$0-2],$$[$0]);
|
||||
break;
|
||||
case 18:
|
||||
this.$ = $$[$0-3];yy.setClickEvent($$[$0-3], $$[$0-2], $$[$0-1]);yy.setLink($$[$0-3],$$[$0]);
|
||||
break;
|
||||
case 19:
|
||||
this.$ = $$[$0-2];yy.setClickEvent($$[$0-2], $$[$0], null);yy.setLink($$[$0-2],$$[$0-1]);
|
||||
break;
|
||||
case 20:
|
||||
this.$ = $$[$0-3];yy.setClickEvent($$[$0-3], $$[$0-1], $$[$0]);yy.setLink($$[$0-3],$$[$0-2]);
|
||||
break;
|
||||
case 21:
|
||||
this.$ = $$[$0-1];yy.setLink($$[$0-1], $$[$0]);
|
||||
break;
|
||||
case 22: case 28:
|
||||
this.$=$$[$0-1] + ' ' + $$[$0];
|
||||
break;
|
||||
case 23: case 24: case 26:
|
||||
this.$=$$[$0-2] + ' ' + $$[$0-1] + ' ' + $$[$0];
|
||||
break;
|
||||
case 25: case 27:
|
||||
this.$=$$[$0-3] + ' ' + $$[$0-2] + ' ' + $$[$0-1] + ' ' + $$[$0];
|
||||
break;
|
||||
}
|
||||
},
|
||||
table: [{3:1,4:[1,2]},{1:[3]},o($V0,[2,2],{5:3}),{6:[1,4],7:5,8:[1,6],9:7,10:[1,8],11:$V1,12:$V2,13:$V3,14:$V4,15:$V5},o($V0,[2,7],{1:[2,1]}),o($V0,[2,3]),{9:14,11:$V1,12:$V2,13:$V3,14:$V4,15:$V5},o($V0,[2,5]),o($V0,[2,6]),o($V0,[2,8]),o($V0,[2,9]),o($V0,[2,10]),o($V0,[2,11]),{16:[1,15]},o($V0,[2,4]),o($V0,[2,12])],
|
||||
table: [{3:1,4:[1,2]},{1:[3]},o($V0,[2,2],{5:3}),{6:[1,4],7:5,8:[1,6],9:7,10:[1,8],11:$V1,12:$V2,13:$V3,14:$V4,15:$V5,16:14,17:$V6,19:$V7},o($V0,[2,7],{1:[2,1]}),o($V0,[2,3]),{9:17,11:$V1,12:$V2,13:$V3,14:$V4,15:$V5,16:14,17:$V6,19:$V7},o($V0,[2,5]),o($V0,[2,6]),o($V0,[2,8]),o($V0,[2,9]),o($V0,[2,10]),o($V0,[2,11]),o($V0,[2,12]),o($V0,[2,13]),{18:[1,18]},{20:[1,19],22:[1,20]},o($V0,[2,4]),o($V0,[2,14]),o($V0,[2,15],{21:[1,21],22:[1,22]}),o($V0,[2,21],{20:[1,23]}),o($V0,[2,16],{22:[1,24]}),o($V0,[2,17]),o($V0,[2,19],{21:[1,25]}),o($V0,[2,18]),o($V0,[2,20])],
|
||||
defaultActions: {},
|
||||
parseError: function parseError (str, hash) {
|
||||
if (hash.recoverable) {
|
||||
@@ -593,8 +626,6 @@ stateStackSize:function stateStackSize() {
|
||||
},
|
||||
options: {"case-insensitive":true},
|
||||
performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
|
||||
// Pre-lexer code can go here
|
||||
|
||||
var YYSTATE=YY_START;
|
||||
switch($avoiding_name_collisions) {
|
||||
case 0:return 10;
|
||||
@@ -605,32 +636,58 @@ case 2:/* skip comments */
|
||||
break;
|
||||
case 3:/* skip comments */
|
||||
break;
|
||||
case 4:return 4;
|
||||
case 4:this.begin("href");
|
||||
break;
|
||||
case 5:return 11;
|
||||
case 5:this.popState();
|
||||
break;
|
||||
case 6:return 12;
|
||||
case 6:return 22;
|
||||
break;
|
||||
case 7:return 'date';
|
||||
case 7:this.begin("callbackname");
|
||||
break;
|
||||
case 8:return 13;
|
||||
case 8:this.popState();
|
||||
break;
|
||||
case 9:return 14;
|
||||
case 9:this.popState(); this.begin("callbackargs");
|
||||
break;
|
||||
case 10:return 15;
|
||||
case 10:return 20;
|
||||
break;
|
||||
case 11:return 16;
|
||||
case 11:this.popState();
|
||||
break;
|
||||
case 12:return ':';
|
||||
case 12:return 21;
|
||||
break;
|
||||
case 13:return 6;
|
||||
case 13:this.begin("click");
|
||||
break;
|
||||
case 14:return 'INVALID';
|
||||
case 14:this.popState();
|
||||
break;
|
||||
case 15:return 19;
|
||||
break;
|
||||
case 16:return 4;
|
||||
break;
|
||||
case 17:return 11;
|
||||
break;
|
||||
case 18:return 12;
|
||||
break;
|
||||
case 19:return 13;
|
||||
break;
|
||||
case 20:return 'date';
|
||||
break;
|
||||
case 21:return 14;
|
||||
break;
|
||||
case 22:return 15;
|
||||
break;
|
||||
case 23:return 17;
|
||||
break;
|
||||
case 24:return 18;
|
||||
break;
|
||||
case 25:return ':';
|
||||
break;
|
||||
case 26:return 6;
|
||||
break;
|
||||
case 27:return 'INVALID';
|
||||
break;
|
||||
}
|
||||
},
|
||||
rules: [/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:gantt\b)/i,/^(?:dateFormat\s[^#\n;]+)/i,/^(?:axisFormat\s[^#\n;]+)/i,/^(?:\d\d\d\d-\d\d-\d\d\b)/i,/^(?:title\s[^#\n;]+)/i,/^(?:section\s[^#:\n;]+)/i,/^(?:[^#:\n;]+)/i,/^(?::[^#\n;]+)/i,/^(?::)/i,/^(?:$)/i,/^(?:.)/i],
|
||||
conditions: {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14],"inclusive":true}}
|
||||
rules: [/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:href[\s]+["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:call[\s]+)/i,/^(?:\([\s]*\))/i,/^(?:\()/i,/^(?:[^(]*)/i,/^(?:\))/i,/^(?:[^)]*)/i,/^(?:click[\s]+)/i,/^(?:[\s\n])/i,/^(?:[^\s\n]*)/i,/^(?:gantt\b)/i,/^(?:dateFormat\s[^#\n;]+)/i,/^(?:axisFormat\s[^#\n;]+)/i,/^(?:excludes\s[^#\n;]+)/i,/^(?:\d\d\d\d-\d\d-\d\d\b)/i,/^(?:title\s[^#\n;]+)/i,/^(?:section\s[^#:\n;]+)/i,/^(?:[^#:\n;]+)/i,/^(?::[^#\n;]+)/i,/^(?::)/i,/^(?:$)/i,/^(?:.)/i],
|
||||
conditions: {"callbackargs":{"rules":[11,12],"inclusive":false},"callbackname":{"rules":[8,9,10],"inclusive":false},"href":{"rules":[5,6],"inclusive":false},"click":{"rules":[14,15],"inclusive":false},"INITIAL":{"rules":[0,1,2,3,4,7,13,16,17,18,19,20,21,22,23,24,25,26,27],"inclusive":true}}
|
||||
});
|
||||
return lexer;
|
||||
})();
|
||||
|
91
src/diagrams/gantt/parser/gantt.spec.js
Normal file
91
src/diagrams/gantt/parser/gantt.spec.js
Normal file
@@ -0,0 +1,91 @@
|
||||
/* eslint-env jasmine */
|
||||
/* eslint-disable no-eval */
|
||||
import { parser } from './gantt'
|
||||
import ganttDb from '../ganttDb'
|
||||
|
||||
describe('when parsing a gantt diagram it', function () {
|
||||
beforeEach(function () {
|
||||
parser.yy = ganttDb
|
||||
parser.yy.clear()
|
||||
})
|
||||
|
||||
it('should handle a dateFormat definition', function () {
|
||||
const str = 'gantt\ndateFormat yyyy-mm-dd'
|
||||
|
||||
parser.parse(str)
|
||||
})
|
||||
it('should handle a title definition', function () {
|
||||
const str = 'gantt\ndateFormat yyyy-mm-dd\ntitle Adding gantt diagram functionality to mermaid'
|
||||
|
||||
parser.parse(str)
|
||||
})
|
||||
it('should handle an excludes definition', function () {
|
||||
const str = 'gantt\ndateFormat yyyy-mm-dd\ntitle Adding gantt diagram functionality to mermaid\nexcludes weekdays 2019-02-01'
|
||||
|
||||
parser.parse(str)
|
||||
})
|
||||
it('should handle a section definition', function () {
|
||||
const str = 'gantt\n' +
|
||||
'dateFormat yyyy-mm-dd\n' +
|
||||
'title Adding gantt diagram functionality to mermaid\n' +
|
||||
'excludes weekdays 2019-02-01\n' +
|
||||
'section Documentation'
|
||||
|
||||
parser.parse(str)
|
||||
})
|
||||
/**
|
||||
* Beslutsflöde inligt nedan. Obs bla bla bla
|
||||
* ```
|
||||
* graph TD
|
||||
* A[Hard pledge] -- text on link -->B(Round edge)
|
||||
* B --> C{to do or not to do}
|
||||
* C -->|Too| D[Result one]
|
||||
* C -->|Doo| E[Result two]
|
||||
```
|
||||
* params bapa - a unique bapap
|
||||
*/
|
||||
it('should handle a task definition', function () {
|
||||
const str = 'gantt\n' +
|
||||
'dateFormat YYYY-MM-DD\n' +
|
||||
'title Adding gantt diagram functionality to mermaid\n' +
|
||||
'section Documentation\n' +
|
||||
'Design jison grammar:des1, 2014-01-01, 2014-01-04'
|
||||
|
||||
parser.parse(str)
|
||||
|
||||
const tasks = parser.yy.getTasks()
|
||||
|
||||
expect(tasks[0].startTime).toEqual(new Date(2014, 0, 1))
|
||||
expect(tasks[0].endTime).toEqual(new Date(2014, 0, 4))
|
||||
expect(tasks[0].id).toEqual('des1')
|
||||
expect(tasks[0].task).toEqual('Design jison grammar')
|
||||
})
|
||||
it.each`
|
||||
tags | milestone | done | crit | active
|
||||
${'milestone'} | ${true} | ${false} | ${false} | ${false}
|
||||
${'done'} | ${false} | ${true} | ${false} | ${false}
|
||||
${'crit'} | ${false} | ${false} | ${true} | ${false}
|
||||
${'active'} | ${false} | ${false} | ${false} | ${true}
|
||||
${'crit,milestone,done'} | ${true} | ${true} | ${true} | ${false}
|
||||
`('should handle a task with tags $tags', ({ tags, milestone, done, crit, active }) => {
|
||||
const str = 'gantt\n' +
|
||||
'dateFormat YYYY-MM-DD\n' +
|
||||
'title Adding gantt diagram functionality to mermaid\n' +
|
||||
'section Documentation\n' +
|
||||
'test task:' + tags + ', 2014-01-01, 2014-01-04'
|
||||
|
||||
const allowedTags = ['active', 'done', 'crit', 'milestone']
|
||||
|
||||
parser.parse(str)
|
||||
|
||||
const tasks = parser.yy.getTasks()
|
||||
|
||||
allowedTags.forEach(function (t) {
|
||||
if (eval(t)) {
|
||||
expect(tasks[0][t]).toBeTruthy()
|
||||
} else {
|
||||
expect(tasks[0][t]).toBeFalsy()
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
@@ -155,9 +155,9 @@ function prettyPrintCommitHistory (commitArr) {
|
||||
}
|
||||
})
|
||||
const label = [line, commit.id, commit.seq]
|
||||
_.each(branches, function (value, key) {
|
||||
if (value === commit.id) label.push(key)
|
||||
})
|
||||
for (let branch in branches) {
|
||||
if (branches[branch] === commit.id) label.push(branch)
|
||||
}
|
||||
logger.debug(label.join(' '))
|
||||
if (Array.isArray(commit.parent)) {
|
||||
const newCommit = commits[commit.parent[0]]
|
||||
@@ -188,9 +188,10 @@ export const clear = function () {
|
||||
}
|
||||
|
||||
export const getBranchesAsObjArray = function () {
|
||||
const branchArr = _.map(branches, function (value, key) {
|
||||
return { 'name': key, 'commit': commits[value] }
|
||||
})
|
||||
const branchArr = []
|
||||
for (let branch in branches) {
|
||||
branchArr.push({ name: branch, commit: commits[branches[branch]] })
|
||||
}
|
||||
return branchArr
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import _ from 'lodash'
|
||||
import * as d3 from 'd3'
|
||||
import _ from 'lodash'
|
||||
|
||||
import db from './gitGraphAst'
|
||||
import gitGraphParser from './parser/gitGraph'
|
||||
@@ -160,7 +160,7 @@ function cloneNode (svg, selector) {
|
||||
function renderCommitHistory (svg, commitid, branches, direction) {
|
||||
let commit
|
||||
const numCommits = Object.keys(allCommitsDict).length
|
||||
if (_.isString(commitid)) {
|
||||
if (typeof commitid === 'string') {
|
||||
do {
|
||||
commit = allCommitsDict[commitid]
|
||||
logger.debug('in renderCommitHistory', commit.id, commit.seq)
|
||||
@@ -189,7 +189,13 @@ function renderCommitHistory (svg, commitid, branches, direction) {
|
||||
.attr('stroke', config.nodeStrokeColor)
|
||||
.attr('stroke-width', config.nodeStrokeWidth)
|
||||
|
||||
const branch = _.find(branches, ['commit', commit])
|
||||
let branch
|
||||
for (let branchName in branches) {
|
||||
if (branches[branchName].commit === commit) {
|
||||
branch = branches[branchName]
|
||||
break
|
||||
}
|
||||
}
|
||||
if (branch) {
|
||||
logger.debug('found branch ', branch.name)
|
||||
svg.select('#node-' + commit.id + ' p')
|
||||
@@ -211,7 +217,7 @@ function renderCommitHistory (svg, commitid, branches, direction) {
|
||||
} while (commitid && allCommitsDict[commitid])
|
||||
}
|
||||
|
||||
if (_.isArray(commitid)) {
|
||||
if (Array.isArray(commitid)) {
|
||||
logger.debug('found merge commmit', commitid)
|
||||
renderCommitHistory(svg, commitid[0], branches, direction)
|
||||
branchNum++
|
||||
@@ -223,11 +229,11 @@ function renderCommitHistory (svg, commitid, branches, direction) {
|
||||
function renderLines (svg, commit, direction, branchColor) {
|
||||
branchColor = branchColor || 0
|
||||
while (commit.seq > 0 && !commit.lineDrawn) {
|
||||
if (_.isString(commit.parent)) {
|
||||
if (typeof commit.parent === 'string') {
|
||||
svgDrawLineForCommits(svg, commit.id, commit.parent, direction, branchColor)
|
||||
commit.lineDrawn = true
|
||||
commit = allCommitsDict[commit.parent]
|
||||
} else if (_.isArray(commit.parent)) {
|
||||
} else if (Array.isArray(commit.parent)) {
|
||||
svgDrawLineForCommits(svg, commit.id, commit.parent[0], direction, branchColor)
|
||||
svgDrawLineForCommits(svg, commit.id, commit.parent[1], direction, branchColor + 1)
|
||||
renderLines(svg, allCommitsDict[commit.parent[1]], direction, branchColor + 1)
|
||||
@@ -246,7 +252,7 @@ export const draw = function (txt, id, ver) {
|
||||
// Parse the graph definition
|
||||
parser.parse(txt + '\n')
|
||||
|
||||
config = _.extend(config, apiConfig, db.getOptions())
|
||||
config = _.assign(config, apiConfig, db.getOptions())
|
||||
logger.debug('effective options', config)
|
||||
const direction = db.getDirection()
|
||||
allCommitsDict = db.getCommits()
|
||||
@@ -259,11 +265,12 @@ export const draw = function (txt, id, ver) {
|
||||
const svg = d3.select(`[id="${id}"]`)
|
||||
svgCreateDefs(svg)
|
||||
branchNum = 1
|
||||
_.each(branches, function (v) {
|
||||
for (let branch in branches) {
|
||||
const v = branches[branch]
|
||||
renderCommitHistory(svg, v.commit.id, branches, direction)
|
||||
renderLines(svg, v.commit, direction)
|
||||
branchNum++
|
||||
})
|
||||
}
|
||||
svg.attr('height', function () {
|
||||
if (direction === 'BT') return Object.keys(allCommitsDict).length * config.nodeSpacing
|
||||
return (branches.length + 1) * config.branchOffset
|
||||
|
@@ -216,7 +216,7 @@ export const getTextObj = function () {
|
||||
const txt = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
'fill': 'black',
|
||||
'fill': undefined,
|
||||
'text-anchor': 'start',
|
||||
style: '#666',
|
||||
width: 100,
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import moment from 'moment'
|
||||
import moment from 'moment-mini'
|
||||
|
||||
export const LEVELS = {
|
||||
debug: 1,
|
||||
|
@@ -447,6 +447,7 @@ const render = function (id, txt, cb, container) {
|
||||
|
||||
if (typeof cb !== 'undefined') {
|
||||
cb(svgCode, flowDb.bindFunctions)
|
||||
cb(svgCode, ganttDb.bindFunctions)
|
||||
} else {
|
||||
logger.warn('CB = undefined!')
|
||||
}
|
||||
|
@@ -27,7 +27,8 @@ $signalColor: $mainContrastColor;
|
||||
$signalTextColor: $mainContrastColor;
|
||||
$labelBoxBkgColor: $actorBkg;
|
||||
$labelBoxBorderColor: $actorBorder;
|
||||
$labelTextColor: $mainContrastColor;
|
||||
$labelTextColor: $darkTextColor;
|
||||
$loopTextColor: $mainContrastColor;
|
||||
$noteBorderColor: $border2;
|
||||
$noteBkgColor: #fff5ad;
|
||||
$activationBorderColor: #666;
|
||||
@@ -43,6 +44,7 @@ $taskBkgColor: $mainBkg;
|
||||
$taskTextColor: $darkTextColor;
|
||||
$taskTextLightColor: $mainContrastColor;
|
||||
$taskTextOutsideColor: $taskTextLightColor;
|
||||
$taskTextClickableColor: #003163;
|
||||
$activeTaskBorderColor: rgba(255, 255, 255, 0.5);
|
||||
$activeTaskBkgColor: #81B1DB;
|
||||
$gridColor: $mainContrastColor;
|
||||
|
@@ -26,6 +26,7 @@ $signalTextColor: #333;
|
||||
$labelBoxBkgColor: $actorBkg;
|
||||
$labelBoxBorderColor: $actorBorder;
|
||||
$labelTextColor: $actorTextColor;
|
||||
$loopTextColor: $actorTextColor;
|
||||
$noteBorderColor: $border2;
|
||||
$noteBkgColor: #fff5ad;
|
||||
$activationBorderColor: #666;
|
||||
@@ -42,6 +43,7 @@ $taskTextLightColor: white;
|
||||
$taskTextColor: $taskTextLightColor;
|
||||
$taskTextDarkColor: black;
|
||||
$taskTextOutsideColor: $taskTextDarkColor;
|
||||
$taskTextClickableColor: #003163;
|
||||
$activeTaskBorderColor: #534fbc;
|
||||
$activeTaskBkgColor: #bfc7ff;
|
||||
$gridColor: lightgrey;
|
||||
|
@@ -30,9 +30,9 @@
|
||||
}
|
||||
|
||||
.cluster rect {
|
||||
fill: $secondBkg !important;
|
||||
stroke: $clusterBorder !important;
|
||||
stroke-width: 1px !important;
|
||||
fill: $secondBkg;
|
||||
stroke: $clusterBorder;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.cluster text {
|
||||
|
@@ -27,6 +27,7 @@ $signalTextColor: #333;
|
||||
$labelBoxBkgColor: $actorBkg;
|
||||
$labelBoxBorderColor: #326932;
|
||||
$labelTextColor: $actorTextColor;
|
||||
$loopTextColor: $actorTextColor;
|
||||
$noteBorderColor: $border2;
|
||||
$noteBkgColor: #fff5ad;
|
||||
$activationBorderColor: #666;
|
||||
@@ -43,6 +44,7 @@ $taskTextLightColor: white;
|
||||
$taskTextColor: $taskTextLightColor;
|
||||
$taskTextDarkColor: black;
|
||||
$taskTextOutsideColor: $taskTextDarkColor;
|
||||
$taskTextClickableColor: #003163;
|
||||
$activeTaskBorderColor: $taskBorderColor;
|
||||
$activeTaskBkgColor: $mainBkg;
|
||||
$gridColor: lightgrey;
|
||||
|
@@ -66,7 +66,6 @@
|
||||
|
||||
/* Task styling */
|
||||
|
||||
|
||||
/* Default task */
|
||||
|
||||
.task {
|
||||
@@ -90,6 +89,27 @@
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* Special case clickable */
|
||||
.task.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
.taskText.clickable {
|
||||
cursor: pointer;
|
||||
fill: $taskTextClickableColor !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.taskTextOutsideLeft.clickable {
|
||||
cursor: pointer;
|
||||
fill: $taskTextClickableColor !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.taskTextOutsideRight.clickable {
|
||||
cursor: pointer;
|
||||
fill: $taskTextClickableColor !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Specific task settings for the sections*/
|
||||
|
||||
@@ -109,7 +129,7 @@
|
||||
}
|
||||
|
||||
.taskTextOutside0,
|
||||
.taskTextOutside2,
|
||||
.taskTextOutside2
|
||||
{
|
||||
fill: $taskTextOutsideColor;
|
||||
}
|
||||
@@ -188,6 +208,13 @@
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.milestone {
|
||||
transform: rotate(45deg) scale(0.8,0.8);
|
||||
}
|
||||
|
||||
.milestoneText {
|
||||
font-style: italic;
|
||||
}
|
||||
.doneCritText0,
|
||||
.doneCritText1,
|
||||
.doneCritText2,
|
||||
|
@@ -30,7 +30,8 @@ $signalColor: $text;
|
||||
$signalTextColor: $text;
|
||||
$labelBoxBkgColor: $actorBkg;
|
||||
$labelBoxBorderColor: $actorBorder;
|
||||
$labelTextColor: white;
|
||||
$labelTextColor: $text;
|
||||
$loopTextColor: $text;
|
||||
$noteBorderColor: darken($note, 60%);
|
||||
$noteBkgColor: $note;
|
||||
$activationBorderColor: #666;
|
||||
@@ -47,6 +48,7 @@ $taskTextLightColor: white;
|
||||
$taskTextColor: $taskTextLightColor;
|
||||
$taskTextDarkColor: $text;
|
||||
$taskTextOutsideColor: $taskTextDarkColor;
|
||||
$taskTextClickableColor: #003163;
|
||||
$activeTaskBorderColor: $taskBorderColor;
|
||||
$activeTaskBkgColor: $mainBkg;
|
||||
$gridColor: lighten($border1, 30%);
|
||||
|
@@ -49,7 +49,7 @@ text.actor {
|
||||
}
|
||||
|
||||
.loopText {
|
||||
fill: $labelTextColor;
|
||||
fill: $loopTextColor;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user