Fix for issue #229, poor handling of activities in a gantt diagram where one activities follows a yet undefined activity.

This commit is contained in:
knsv 2015-10-24 12:44:47 +02:00
parent 1cd1423780
commit eecd56d6e0
8 changed files with 273 additions and 105 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

19
dist/www/index.html vendored
View File

@ -132,19 +132,20 @@
Future task in critical line :crit, 5d
Create tests for renderer :2d
Add to mermaid :1d</code></pre><p>Play with mermaid using this <a href="http://danielmschmidt.github.io/mermaid-demo/">editor</a> or this <a href="live_editor">live editor</a>.</p>
<h2 id="further-reading">Further reading</h2>
<ul>
<li><a href="http://knsv.github.io/mermaid/index.html#usage">Usage</a></li>
<li><a href="http://knsv.github.io/mermaid/index.html#flowcharts-basic-syntax">Flowchart syntax</a></li>
<li><a href="http://knsv.github.io/mermaid/index.html#sequence-diagrams">Sequence diagram syntax</a></li>
<li><a href="http://knsv.github.io/mermaid/index.html#gant-diagrams">Gantt chart syntax</a></li>
<li><a href="http://knsv.github.io/mermaid/index.html#flowcharts-basic-syntax">Mermaid client</a></li>
<li><a href="http://knsv.github.io/mermaid/live_editor">Editor</a></li>
</ul>
<h2 id="credits">Credits</h2>
<p>Many thanks to the <a href="http://d3js.org/">d3</a> and <a href="https://github.com/cpettitt/dagre-d3">dagre-d3</a> projects for providing<br>the graphical layout and drawing libraries! Thanks also to the<br><a href="http://bramp.github.io/js-sequence-diagrams">js-sequence-diagram</a> project for usage of the grammar for the<br>sequence diagrams.</p>
<p><em>Mermaid was created by Knut Sveidqvist for easier documentation.</em></p>
<p>Knut has not done all work by himself, here is the full list of the projects <a href="https://github.com/knsv/mermaid/graphs/contributors">contributors</a>.</p>
<h1 id="online-live-editor">Online live editor</h1>
<p>An editor is available for creating diagrams. With it you can quickly start writing mermaid diagrams. It is possible to:</p>
<ul>
<li>save the result as a svg</li>
<li>get a link to a viewer of the diagram </li>
<li><p>get a link to edit of the diagram to share a diagram so that someone else can tweak it and send a new link back</p>
</li>
<li><p><a href="http://knsv.github.io/mermaid/live_editor">Editor</a></p>
</li>
</ul>
</div>

View File

@ -30658,7 +30658,7 @@ module.exports = '1.0.7';
},{}],83:[function(require,module,exports){
module.exports={
"name": "mermaid",
"version": "0.5.4",
"version": "0.5.5",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams and gantt charts.",
"main": "src/mermaid.js",
"keywords": [
@ -34762,6 +34762,8 @@ exports.clear = function () {
title = '';
taskCnt = 0;
lastTask = undefined;
lastTaskID = undefined;
rawTasks = [];
};
exports.setDateFormat = function (txt) {
@ -34784,26 +34786,28 @@ exports.addSection = function (txt) {
sections.push(txt);
};
exports.findTaskById = function (id) {
var i;
for (i = 0; i < tasks.length; i++) {
if (tasks[i].id === id) {
return tasks[i];
}
}
};
exports.getTasks = function () {
var i;
for (i = 10000; i < tasks.length; i++) {
tasks[i].startTime = moment(tasks[i].startTime).format(dateFormat);
tasks[i].endTime = moment(tasks[i].endTime).format(dateFormat);
var allItemsPricessed = compileTasks();
var maxDepth = 10;
var iterationCount = 0;
while (!allItemsPricessed && iterationCount < maxDepth) {
allItemsPricessed = compileTasks();
iterationCount++;
}
tasks = rawTasks;
//var i;
//for(i=10000;i<tasks.length;i++){
// tasks[i].startTime = moment(tasks[i].startTime).format(dateFormat);
// tasks[i].endTime = moment(tasks[i].endTime).format(dateFormat);
//}
return tasks;
};
var getStartDate = function getStartDate(prevTime, dateFormat, str) {
//console.log('Deciding start date:'+JSON.stringify(str));
//log.debug('Deciding start date:'+str);
//log.debug('with dateformat:'+dateFormat);
@ -34812,12 +34816,15 @@ var getStartDate = function getStartDate(prevTime, dateFormat, str) {
// Test for after
var re = /^after\s+([\d\w\-]+)/;
var afterStatement = re.exec(str.trim());
if (afterStatement !== null) {
var task = exports.findTaskById(afterStatement[1]);
if (typeof task === 'undefined') {
var dt = new Date();
dt.setHours(0, 0, 0, 0);
return dt;
//return undefined;
}
return task.endTime;
}
@ -34954,8 +34961,108 @@ var compileData = function compileData(prevTask, dataStr) {
return task;
};
var parseData = function parseData(prevTaskId, dataStr) {
var ds;
if (dataStr.substr(0, 1) === ':') {
ds = dataStr.substr(1, dataStr.length);
} else {
ds = dataStr;
}
var data = ds.split(',');
var task = {};
// Get tags like active, done cand crit
var matchFound = true;
while (matchFound) {
matchFound = false;
if (data[0].match(/^\s*active\s*$/)) {
task.active = true;
data.shift(1);
matchFound = true;
}
if (data[0].match(/^\s*done\s*$/)) {
task.done = true;
data.shift(1);
matchFound = true;
}
if (data[0].match(/^\s*crit\s*$/)) {
task.crit = true;
data.shift(1);
matchFound = true;
}
}
var i;
for (i = 0; i < data.length; i++) {
data[i] = data[i].trim();
}
switch (data.length) {
case 1:
task.id = parseId();
task.startTime = { type: 'prevTaskEnd', id: prevTaskId };
task.endTime = { data: data[0] };
break;
case 2:
task.id = parseId();
task.startTime = { type: 'getStartDate', startData: data[0] };
task.endTime = { data: data[1] };
break;
case 3:
task.id = parseId(data[0]);
task.startTime = { type: 'getStartDate', startData: data[1] };
task.endTime = { data: data[2] };
break;
default:
}
return task;
};
var lastTask;
var lastTaskID;
var rawTasks = [];
var taskDb = {};
exports.addTask = function (descr, data) {
var rawTask = {
section: currentSection,
type: currentSection,
processed: false,
raw: { data: data },
task: descr
};
var taskInfo = parseData(lastTaskID, data);
rawTask.raw.startTime = taskInfo.startTime;
rawTask.raw.endTime = taskInfo.endTime;
rawTask.id = taskInfo.id;
rawTask.prevTaskId = lastTaskID;
rawTask.active = taskInfo.active;
rawTask.done = taskInfo.done;
rawTask.crit = taskInfo.crit;
var pos = rawTasks.push(rawTask);
lastTaskID = rawTask.id;
// Store cross ref
taskDb[rawTask.id] = pos - 1;
};
exports.findTaskById = function (id) {
//var i;
//for(i=0;i<tasks.length;i++){
// if(tasks[i].id === id){
// return tasks[i];
// }
//}
var pos = taskDb[id];
return rawTasks[pos];
};
exports.addTaskOrg = function (descr, data) {
var newTask = {
section: currentSection,
@ -34974,6 +35081,45 @@ exports.addTask = function (descr, data) {
tasks.push(newTask);
};
var compileTasks = function compileTasks() {
var df = exports.getDateFormat();
var compileTask = function compileTask(pos) {
var task = rawTasks[pos];
var startTime = '';
switch (rawTasks[pos].raw.startTime.type) {
case 'prevTaskEnd':
var prevTask = exports.findTaskById(task.prevTaskId);
task.startTime = prevTask.endTime;
break;
case 'getStartDate':
startTime = getStartDate(undefined, df, rawTasks[pos].raw.startTime.startData);
if (startTime) {
rawTasks[pos].startTime = startTime;
}
break;
}
if (rawTasks[pos].startTime) {
rawTasks[pos].endTime = getEndDate(rawTasks[pos].startTime, df, rawTasks[pos].raw.endTime.data);
if (rawTasks[pos].endTime) {
rawTasks[pos].processed = true;
}
}
return rawTasks[pos].processed;
};
var i;
var allProcessed = true;
for (i = 0; i < rawTasks.length; i++) {
compileTask(i);
allProcessed = allProcessed && rawTasks[i].processed;
}
return allProcessed;
};
exports.parseError = function (err, hash) {
global.mermaidAPI.parseError(err, hash);
};
@ -37184,7 +37330,7 @@ var drawMessage = function drawMessage(elem, startx, stopx, verticalPos, msg) {
line.attr('stroke', 'black');
line.style('fill', 'none'); // remove any fill colour
if (msg.type === sq.yy.LINETYPE.SOLID || msg.type === sq.yy.LINETYPE.DOTTED) {
line.attr('marker-end', 'url(' + url + '#crosshead)');
line.attr('marker-end', 'url(' + url + '#arrowhead)');
}
if (msg.type === sq.yy.LINETYPE.SOLID_CROSS || msg.type === sq.yy.LINETYPE.DOTTED_CROSS) {

View File

@ -61,15 +61,6 @@ gantt
Play with mermaid using this [editor](http://danielmschmidt.github.io/mermaid-demo/) or this [live editor](live_editor).
## Further reading
* [Usage](http://knsv.github.io/mermaid/index.html#usage)
* [Flowchart syntax](http://knsv.github.io/mermaid/index.html#flowcharts-basic-syntax)
* [Sequence diagram syntax](http://knsv.github.io/mermaid/index.html#sequence-diagrams)
* [Gantt chart syntax](http://knsv.github.io/mermaid/index.html#gant-diagrams)
* [Mermaid client](http://knsv.github.io/mermaid/index.html#flowcharts-basic-syntax)
* [Editor](http://knsv.github.io/mermaid/live_editor)
## Credits
Many thanks to the [d3](http://d3js.org/) and [dagre-d3](https://github.com/cpettitt/dagre-d3) projects for providing
the graphical layout and drawing libraries! Thanks also to the
@ -79,3 +70,13 @@ sequence diagrams.
*Mermaid was created by Knut Sveidqvist for easier documentation.*
Knut has not done all work by himself, here is the full list of the projects [contributors](https://github.com/knsv/mermaid/graphs/contributors).
# Online live editor
An editor is available for creating diagrams. With it you can quickly start writing mermaid diagrams. It is possible to:
* save the result as a svg
* get a link to a viewer of the diagram
* get a link to edit of the diagram to share a diagram so that someone else can tweak it and send a new link back
* [Editor](http://knsv.github.io/mermaid/live_editor)

View File

@ -19,6 +19,8 @@ exports.clear = function(){
title = '';
taskCnt = 0;
lastTask = undefined;
lastTaskID = undefined;
rawTasks = [];
};
exports.setDateFormat = function(txt){
@ -41,23 +43,24 @@ exports.addSection = function(txt){
sections.push(txt);
};
exports.findTaskById = function(id) {
var i;
for(i=0;i<tasks.length;i++){
if(tasks[i].id === id){
return tasks[i];
}
}
};
exports.getTasks=function(){
//compileTasks();
var i;
for(i=10000;i<tasks.length;i++){
tasks[i].startTime = moment(tasks[i].startTime).format(dateFormat);
tasks[i].endTime = moment(tasks[i].endTime).format(dateFormat);
var allItemsPricessed = compileTasks();
var maxDepth = 10;
var iterationCount = 0;
while(!allItemsPricessed && (iterationCount < maxDepth)){
allItemsPricessed = compileTasks();
iterationCount++;
}
tasks = rawTasks;
//var i;
//for(i=10000;i<tasks.length;i++){
// tasks[i].startTime = moment(tasks[i].startTime).format(dateFormat);
// tasks[i].endTime = moment(tasks[i].endTime).format(dateFormat);
//}
return tasks;
};
@ -75,7 +78,7 @@ var getStartDate = function(prevTime, dateFormat, str){
if(afterStatement!==null){
var task = exports.findTaskById(afterStatement[1]);
//console.log('xxx'+JSON.stringify(task));
if(typeof task === 'undefined'){
var dt = new Date();
dt.setHours(0,0,0,0);
@ -222,7 +225,7 @@ var compileData = function(prevTask, dataStr){
return task;
};
var parseData = function(dataStr){
var parseData = function(prevTaskId, dataStr){
var ds;
if(dataStr.substr(0,1) === ':'){
@ -236,8 +239,6 @@ var parseData = function(dataStr){
var task = {};
var df = exports.getDateFormat();
// Get tags like active, done cand crit
var matchFound = true;
@ -269,7 +270,7 @@ var parseData = function(dataStr){
switch(data.length){
case 1:
task.id = parseId();
task.startTime = {type: 'prevTaskEnd'};
task.startTime = {type: 'prevTaskEnd', id:prevTaskId};
task.endTime = {data: data[0]};
break;
case 2:
@ -295,15 +296,15 @@ var lastTask;
var lastTaskID;
var rawTasks = [];
var taskDb = {};
exports.addTaskNew = function(descr,data){
exports.addTask = function(descr,data){
var rawTask = {
section:currentSection,
type:currentSection,
description:descr,
processed:false,
raw:{data:data}
raw:{data:data},
task:descr
};
var taskInfo = parseData(data);
var taskInfo = parseData(lastTaskID, data);
rawTask.raw.startTime = taskInfo.startTime;
rawTask.raw.endTime = taskInfo.endTime;
rawTask.id = taskInfo.id;
@ -311,15 +312,29 @@ exports.addTaskNew = function(descr,data){
rawTask.active = taskInfo.active;
rawTask.done = taskInfo.done;
rawTask.crit = taskInfo.crit;
var pos = rawTasks.push(rawTask);
lastTaskID = rawTask.id;
// Store cross ref
taskDb[rawTask.id]= pos;
taskDb[rawTask.id]= pos-1;
};
exports.addTask = function(descr,data){
exports.findTaskById = function(id) {
//var i;
//for(i=0;i<tasks.length;i++){
// if(tasks[i].id === id){
// return tasks[i];
// }
//}
var pos = taskDb[id];
return rawTasks[pos];
};
exports.addTaskOrg = function(descr,data){
var newTask = {
section:currentSection,
@ -339,8 +354,6 @@ exports.addTask = function(descr,data){
};
var compileTasks=function(){
console.log('Compiling tasks'+rawTasks.length);
var df = exports.getDateFormat();
var compileTask = function(pos){
@ -348,10 +361,11 @@ var compileTasks=function(){
var startTime = '';
switch(rawTasks[pos].raw.startTime.type){
case 'prevTaskEnd':
rawTasks[pos].startTime = rawTasks[taskDb[pos].prevTaskId].endTime;
var prevTask = exports.findTaskById(task.prevTaskId);
task.startTime = prevTask.endTime;
break;
case 'getStartDate':
var startTime = getStartDate(undefined, df, rawTasks[pos].raw.startTime.startData);
startTime = getStartDate(undefined, df, rawTasks[pos].raw.startTime.startData);
if(startTime){
rawTasks[pos].startTime = startTime;
}
@ -360,17 +374,23 @@ var compileTasks=function(){
if(rawTasks[pos].startTime){
rawTasks[pos].endTime = getEndDate(rawTasks[pos].startTime, df, rawTasks[pos].raw.endTime.data);
if(rawTasks[pos].endTime){
rawTasks[pos].processed = true;
}
}
return rawTasks[pos].processed;
};
var i;
var allProcessed = true;
for(i=0;i<rawTasks.length;i++){
console.log('Pre ompiling: '+JSON.stringify(rawTasks[i]));
compileTask(i);
//console.log('Compiling: '+rawTasks[taskDb[i]].id);
console.log('Compiling: '+JSON.stringify(rawTasks[i],null,2));
allProcessed = allProcessed && rawTasks[i].processed;
}
return allProcessed;
};
exports.parseError = function(err,hash){

View File

@ -21,7 +21,7 @@ describe('when using the ganttDb',function() {
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].description).toEqual('test1');
expect(tasks[0].task).toEqual('test1');
});
it('should handle duration (days) instead of fixed date to determine end date', function () {
gDb.setDateFormat('YYYY-MM-DD');
@ -31,7 +31,7 @@ describe('when using the ganttDb',function() {
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].description).toEqual('test1');
expect(tasks[0].task).toEqual('test1');
});
it('should handle duration (hours) instead of fixed date to determine end date', function () {
gDb.setDateFormat('YYYY-MM-DD');
@ -41,7 +41,7 @@ describe('when using the ganttDb',function() {
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].description).toEqual('test1');
expect(tasks[0].task).toEqual('test1');
});
it('should handle duration (minutes) instead of fixed date to determine end date', function () {
gDb.setDateFormat('YYYY-MM-DD');
@ -51,7 +51,7 @@ describe('when using the ganttDb',function() {
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].description).toEqual('test1');
expect(tasks[0].task).toEqual('test1');
});
it('should handle duration (seconds) instead of fixed date to determine end date', function () {
gDb.setDateFormat('YYYY-MM-DD');
@ -61,7 +61,7 @@ describe('when using the ganttDb',function() {
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].description).toEqual('test1');
expect(tasks[0].task).toEqual('test1');
});
it('should handle duration (weeks) instead of fixed date to determine end date', function () {
gDb.setDateFormat('YYYY-MM-DD');
@ -71,7 +71,7 @@ describe('when using the ganttDb',function() {
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].description).toEqual('test1');
expect(tasks[0].task).toEqual('test1');
});
it('should handle relative start date based on id', function () {
@ -84,7 +84,7 @@ describe('when using the ganttDb',function() {
expect(tasks[1].startTime ).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate());
expect(tasks[1].id ).toEqual('id2');
expect(tasks[1].description).toEqual('test2');
expect(tasks[1].task).toEqual('test2');
});
it('should handle relative start date based on id when id is invalid', function () {
@ -95,7 +95,7 @@ describe('when using the ganttDb',function() {
var tasks = gDb.getTasks();
expect(tasks[1].startTime).toEqual(new Date((new Date()).setHours(0,0,0,0)));
expect(tasks[1].id ).toEqual('id2');
expect(tasks[1].description).toEqual('test2');
expect(tasks[1].task).toEqual('test2');
});
it('should handle fixed dates without id', function () {
@ -106,7 +106,7 @@ describe('when using the ganttDb',function() {
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].description).toEqual('test1');
expect(tasks[0].task).toEqual('test1');
});
it('should handle duration instead of a fixed date to determine end date without id', function () {
@ -117,7 +117,7 @@ describe('when using the ganttDb',function() {
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].description).toEqual('test1');
expect(tasks[0].task).toEqual('test1');
});
it('should handle relative start date of a fixed date to determine end date without id', function () {
@ -130,7 +130,7 @@ describe('when using the ganttDb',function() {
expect(tasks[1].startTime ).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate());
expect(tasks[1].id ).toEqual('task1');
expect(tasks[1].description).toEqual('test2');
expect(tasks[1].task).toEqual('test2');
});
it('should handle a new task with only an end date as definition', function () {
gDb.setDateFormat('YYYY-MM-DD');
@ -143,7 +143,7 @@ describe('when using the ganttDb',function() {
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].description).toEqual('test2');
expect(tasks[1].task).toEqual('test2');
});
it('should handle a new task with only an end date as definition', function () {
gDb.setDateFormat('YYYY-MM-DD');
@ -156,9 +156,9 @@ describe('when using the ganttDb',function() {
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].description).toEqual('test2');
expect(tasks[1].task).toEqual('test2');
});
xit('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 () {
gDb.setDateFormat('YYYY-MM-DD');
gDb.addSection('testa1');
gDb.addTask('test1','id1,2013-01-01,2w');
@ -168,13 +168,15 @@ describe('when using the ganttDb',function() {
var tasks = gDb.getTasks();
expect(tasks[1].startTime ).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate());
expect(tasks[1].startTime ).toEqual(moment('2013-01-17', 'YYYY-MM-DD').toDate());
expect(tasks[1].endTime ).toEqual(moment('2013-01-18', 'YYYY-MM-DD').toDate());
expect(tasks[1].id ).toEqual('id2');
expect(tasks[1].description).toEqual('test2');
expect(tasks[1].task).toEqual('test2');
expect(tasks[2].id ).toEqual('id3');
expect(tasks[2].description).toEqual('test3');
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());
});
});