#931 Updating code to align witn new code standard

This commit is contained in:
knsv
2019-09-12 12:57:36 -07:00
parent a2e3f3d900
commit cf05a8d8fa
4 changed files with 746 additions and 686 deletions

View File

@@ -1,217 +1,214 @@
import moment from 'moment-mini'
import { sanitizeUrl } from '@braintree/sanitize-url'
import { logger } from '../../logger'
import { getConfig } from '../../config'
import moment from 'moment-mini';
import { sanitizeUrl } from '@braintree/sanitize-url';
import { logger } from '../../logger';
import { getConfig } from '../../config';
const config = getConfig()
let dateFormat = ''
let axisFormat = ''
let excludes = []
let title = ''
let sections = []
let tasks = []
let currentSection = ''
const tags = ['active', 'done', 'crit', 'milestone']
let funs = []
let inclusiveEndDates = false
const config = getConfig();
let dateFormat = '';
let axisFormat = '';
let excludes = [];
let title = '';
let sections = [];
let tasks = [];
let currentSection = '';
const tags = ['active', 'done', 'crit', 'milestone'];
let funs = [];
let inclusiveEndDates = false;
export const clear = function () {
sections = []
tasks = []
currentSection = ''
funs = []
title = ''
taskCnt = 0
lastTask = undefined
lastTaskID = undefined
rawTasks = []
dateFormat = ''
axisFormat = ''
excludes = []
inclusiveEndDates = false
}
export const clear = function() {
sections = [];
tasks = [];
currentSection = '';
funs = [];
title = '';
taskCnt = 0;
lastTask = undefined;
lastTaskID = undefined;
rawTasks = [];
dateFormat = '';
axisFormat = '';
excludes = [];
inclusiveEndDates = false;
};
export const setAxisFormat = function (txt) {
axisFormat = txt
}
export const setAxisFormat = function(txt) {
axisFormat = txt;
};
export const getAxisFormat = function () {
return axisFormat
}
export const getAxisFormat = function() {
return axisFormat;
};
export const setDateFormat = function (txt) {
dateFormat = txt
}
export const setDateFormat = function(txt) {
dateFormat = txt;
};
export const enableInclusiveEndDates = function () {
inclusiveEndDates = true
}
export const enableInclusiveEndDates = function() {
inclusiveEndDates = true;
};
export const endDatesAreInclusive = function () {
return inclusiveEndDates
}
export const endDatesAreInclusive = function() {
return inclusiveEndDates;
};
export const getDateFormat = function () {
return dateFormat
}
export const getDateFormat = function() {
return dateFormat;
};
export const setExcludes = function (txt) {
excludes = txt.toLowerCase().split(/[\s,]+/)
}
export const setExcludes = function(txt) {
excludes = txt.toLowerCase().split(/[\s,]+/);
};
export const getExcludes = function () {
return excludes
}
export const getExcludes = function() {
return excludes;
};
export const setTitle = function (txt) {
title = txt
}
export const setTitle = function(txt) {
title = txt;
};
export const getTitle = function () {
return title
}
export const getTitle = function() {
return title;
};
export const addSection = function (txt) {
currentSection = txt
sections.push(txt)
}
export const addSection = function(txt) {
currentSection = txt;
sections.push(txt);
};
export const getSections = function () {
return sections
}
export const getSections = function() {
return sections;
};
export const getTasks = function () {
let allItemsPricessed = compileTasks()
const maxDepth = 10
let iterationCount = 0
while (!allItemsPricessed && (iterationCount < maxDepth)) {
allItemsPricessed = compileTasks()
iterationCount++
export const getTasks = function() {
let allItemsPricessed = compileTasks();
const maxDepth = 10;
let iterationCount = 0;
while (!allItemsPricessed && iterationCount < maxDepth) {
allItemsPricessed = compileTasks();
iterationCount++;
}
tasks = rawTasks
tasks = rawTasks;
return tasks
}
return tasks;
};
const isInvalidDate = function (date, dateFormat, excludes) {
const isInvalidDate = function(date, dateFormat, excludes) {
if (date.isoWeekday() >= 6 && excludes.indexOf('weekends') >= 0) {
return true
return true;
}
if (excludes.indexOf(date.format('dddd').toLowerCase()) >= 0) {
return true
return true;
}
return excludes.indexOf(date.format(dateFormat.trim())) >= 0
}
return excludes.indexOf(date.format(dateFormat.trim())) >= 0;
};
const checkTaskDates = function (task, dateFormat, excludes) {
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 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
const fixTaskDates = function(startTime, endTime, dateFormat, excludes) {
let invalid = false;
let renderEndTime = null;
while (startTime.date() <= endTime.date()) {
if (!invalid) {
renderEndTime = endTime.toDate()
renderEndTime = endTime.toDate();
}
invalid = isInvalidDate(startTime, dateFormat, excludes)
invalid = isInvalidDate(startTime, dateFormat, excludes);
if (invalid) {
endTime.add(1, 'd')
endTime.add(1, 'd');
}
startTime.add(1, 'd')
startTime.add(1, 'd');
}
return renderEndTime
}
return renderEndTime;
};
const getStartDate = function (prevTime, dateFormat, str) {
str = str.trim()
const getStartDate = function(prevTime, dateFormat, str) {
str = str.trim();
// Test for after
const re = /^after\s+([\d\w-]+)/
const afterStatement = re.exec(str.trim())
const re = /^after\s+([\d\w-]+)/;
const afterStatement = re.exec(str.trim());
if (afterStatement !== null) {
const task = findTaskById(afterStatement[1])
const task = findTaskById(afterStatement[1]);
if (typeof task === 'undefined') {
const dt = new Date()
dt.setHours(0, 0, 0, 0)
return dt
const dt = new Date();
dt.setHours(0, 0, 0, 0);
return dt;
}
return task.endTime
return task.endTime;
}
// Check for actual date set
let mDate = moment(str, dateFormat.trim(), true)
let mDate = moment(str, dateFormat.trim(), true);
if (mDate.isValid()) {
return mDate.toDate()
return mDate.toDate();
} else {
logger.debug('Invalid date:' + str)
logger.debug('With date format:' + dateFormat.trim())
logger.debug('Invalid date:' + str);
logger.debug('With date format:' + dateFormat.trim());
}
// Default date - now
return new Date()
}
return new Date();
};
const durationToDate = function (durationStatement, relativeTime) {
const durationToDate = function(durationStatement, relativeTime) {
if (durationStatement !== null) {
switch (durationStatement[2]) {
case 's':
relativeTime.add(durationStatement[1], 'seconds')
break
relativeTime.add(durationStatement[1], 'seconds');
break;
case 'm':
relativeTime.add(durationStatement[1], 'minutes')
break
relativeTime.add(durationStatement[1], 'minutes');
break;
case 'h':
relativeTime.add(durationStatement[1], 'hours')
break
relativeTime.add(durationStatement[1], 'hours');
break;
case 'd':
relativeTime.add(durationStatement[1], 'days')
break
relativeTime.add(durationStatement[1], 'days');
break;
case 'w':
relativeTime.add(durationStatement[1], 'weeks')
break
relativeTime.add(durationStatement[1], 'weeks');
break;
}
}
// Default date - now
return relativeTime.toDate()
}
return relativeTime.toDate();
};
const getEndDate = function (prevTime, dateFormat, str, inclusive) {
inclusive = inclusive || false
str = str.trim()
const getEndDate = function(prevTime, dateFormat, str, inclusive) {
inclusive = inclusive || false;
str = str.trim();
// Check for actual date
let mDate = moment(str, dateFormat.trim(), true)
let mDate = moment(str, dateFormat.trim(), true);
if (mDate.isValid()) {
if (inclusive) {
mDate.add(1, 'd')
mDate.add(1, 'd');
}
return mDate.toDate()
return mDate.toDate();
}
return durationToDate(
/^([\d]+)([wdhms])/.exec(str.trim()),
moment(prevTime)
)
}
return durationToDate(/^([\d]+)([wdhms])/.exec(str.trim()), moment(prevTime));
};
let taskCnt = 0
const parseId = function (idStr) {
let taskCnt = 0;
const parseId = function(idStr) {
if (typeof idStr === 'undefined') {
taskCnt = taskCnt + 1
return 'task' + taskCnt
taskCnt = taskCnt + 1;
return 'task' + taskCnt;
}
return idStr
}
return idStr;
};
// id, startDate, endDate
// id, startDate, length
// id, after x, endDate
@@ -223,116 +220,116 @@ const parseId = function (idStr) {
// endDate
// length
const compileData = function (prevTask, dataStr) {
let ds
const compileData = function(prevTask, dataStr) {
let ds;
if (dataStr.substr(0, 1) === ':') {
ds = dataStr.substr(1, dataStr.length)
ds = dataStr.substr(1, dataStr.length);
} else {
ds = dataStr
ds = dataStr;
}
const data = ds.split(',')
const data = ds.split(',');
const task = {}
const task = {};
// Get tags like active, done, crit and milestone
getTaskTags(data, task, tags)
getTaskTags(data, task, tags);
for (let i = 0; i < data.length; i++) {
data[i] = data[i].trim()
data[i] = data[i].trim();
}
let endTimeData = ''
let endTimeData = '';
switch (data.length) {
case 1:
task.id = parseId()
task.startTime = prevTask.endTime
endTimeData = data[0]
break
task.id = parseId();
task.startTime = prevTask.endTime;
endTimeData = data[0];
break;
case 2:
task.id = parseId()
task.startTime = getStartDate(undefined, dateFormat, data[0])
endTimeData = data[1]
break
task.id = parseId();
task.startTime = getStartDate(undefined, dateFormat, data[0]);
endTimeData = data[1];
break;
case 3:
task.id = parseId(data[0])
task.startTime = getStartDate(undefined, dateFormat, data[1])
endTimeData = data[2]
break
task.id = parseId(data[0]);
task.startTime = getStartDate(undefined, dateFormat, data[1]);
endTimeData = data[2];
break;
default:
}
if (endTimeData) {
task.endTime = getEndDate(task.startTime, dateFormat, endTimeData, inclusiveEndDates)
task.manualEndTime = moment(endTimeData, 'YYYY-MM-DD', true).isValid()
checkTaskDates(task, dateFormat, excludes)
task.endTime = getEndDate(task.startTime, dateFormat, endTimeData, inclusiveEndDates);
task.manualEndTime = moment(endTimeData, 'YYYY-MM-DD', true).isValid();
checkTaskDates(task, dateFormat, excludes);
}
return task
}
return task;
};
const parseData = function (prevTaskId, dataStr) {
let ds
const parseData = function(prevTaskId, dataStr) {
let ds;
if (dataStr.substr(0, 1) === ':') {
ds = dataStr.substr(1, dataStr.length)
ds = dataStr.substr(1, dataStr.length);
} else {
ds = dataStr
ds = dataStr;
}
const data = ds.split(',')
const data = ds.split(',');
const task = {}
const task = {};
// Get tags like active, done, crit and milestone
getTaskTags(data, task, tags)
getTaskTags(data, task, tags);
for (let i = 0; i < data.length; i++) {
data[i] = data[i].trim()
data[i] = data[i].trim();
}
switch (data.length) {
case 1:
task.id = parseId()
task.id = parseId();
task.startTime = {
type: 'prevTaskEnd',
id: prevTaskId
}
};
task.endTime = {
data: data[0]
}
break
};
break;
case 2:
task.id = parseId()
task.id = parseId();
task.startTime = {
type: 'getStartDate',
startData: data[0]
}
};
task.endTime = {
data: data[1]
}
break
};
break;
case 3:
task.id = parseId(data[0])
task.id = parseId(data[0]);
task.startTime = {
type: 'getStartDate',
startData: data[1]
}
};
task.endTime = {
data: data[2]
}
break
};
break;
default:
}
return task
}
return task;
};
let lastTask
let lastTaskID
let rawTasks = []
const taskDb = {}
export const addTask = function (descr, data) {
let lastTask;
let lastTaskID;
let rawTasks = [];
const taskDb = {};
export const addTask = function(descr, data) {
const rawTask = {
section: currentSection,
type: currentSection,
@@ -342,174 +339,187 @@ export const addTask = function (descr, data) {
raw: { data: data },
task: descr,
classes: []
}
const 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
rawTask.milestone = taskInfo.milestone
};
const 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;
rawTask.milestone = taskInfo.milestone;
const pos = rawTasks.push(rawTask)
const pos = rawTasks.push(rawTask);
lastTaskID = rawTask.id
lastTaskID = rawTask.id;
// Store cross ref
taskDb[rawTask.id] = pos - 1
}
taskDb[rawTask.id] = pos - 1;
};
export const findTaskById = function (id) {
const pos = taskDb[id]
return rawTasks[pos]
}
export const findTaskById = function(id) {
const pos = taskDb[id];
return rawTasks[pos];
};
export const addTaskOrg = function (descr, data) {
export const addTaskOrg = function(descr, data) {
const newTask = {
section: currentSection,
type: currentSection,
description: descr,
task: descr,
classes: []
}
const taskInfo = compileData(lastTask, data)
newTask.startTime = taskInfo.startTime
newTask.endTime = taskInfo.endTime
newTask.id = taskInfo.id
newTask.active = taskInfo.active
newTask.done = taskInfo.done
newTask.crit = taskInfo.crit
newTask.milestone = taskInfo.milestone
lastTask = newTask
tasks.push(newTask)
}
};
const taskInfo = compileData(lastTask, data);
newTask.startTime = taskInfo.startTime;
newTask.endTime = taskInfo.endTime;
newTask.id = taskInfo.id;
newTask.active = taskInfo.active;
newTask.done = taskInfo.done;
newTask.crit = taskInfo.crit;
newTask.milestone = taskInfo.milestone;
lastTask = newTask;
tasks.push(newTask);
};
const compileTasks = function () {
const compileTask = function (pos) {
const task = rawTasks[pos]
let startTime = ''
const compileTasks = function() {
const compileTask = function(pos) {
const task = rawTasks[pos];
let startTime = '';
switch (rawTasks[pos].raw.startTime.type) {
case 'prevTaskEnd':
const prevTask = findTaskById(task.prevTaskId)
task.startTime = prevTask.endTime
break
const prevTask = findTaskById(task.prevTaskId);
task.startTime = prevTask.endTime;
break;
case 'getStartDate':
startTime = getStartDate(undefined, dateFormat, rawTasks[pos].raw.startTime.startData)
startTime = getStartDate(undefined, dateFormat, rawTasks[pos].raw.startTime.startData);
if (startTime) {
rawTasks[pos].startTime = startTime
rawTasks[pos].startTime = startTime;
}
break
break;
}
if (rawTasks[pos].startTime) {
rawTasks[pos].endTime = getEndDate(rawTasks[pos].startTime, dateFormat, rawTasks[pos].raw.endTime.data, inclusiveEndDates)
rawTasks[pos].endTime = getEndDate(
rawTasks[pos].startTime,
dateFormat,
rawTasks[pos].raw.endTime.data,
inclusiveEndDates
);
if (rawTasks[pos].endTime) {
rawTasks[pos].processed = true
rawTasks[pos].manualEndTime = moment(rawTasks[pos].raw.endTime.data, 'YYYY-MM-DD', true).isValid()
checkTaskDates(rawTasks[pos], dateFormat, excludes)
rawTasks[pos].processed = true;
rawTasks[pos].manualEndTime = moment(
rawTasks[pos].raw.endTime.data,
'YYYY-MM-DD',
true
).isValid();
checkTaskDates(rawTasks[pos], dateFormat, excludes);
}
}
return rawTasks[pos].processed
}
return rawTasks[pos].processed;
};
let allProcessed = true
let allProcessed = true;
for (let i = 0; i < rawTasks.length; i++) {
compileTask(i)
compileTask(i);
allProcessed = allProcessed && rawTasks[i].processed
allProcessed = allProcessed && rawTasks[i].processed;
}
return allProcessed
}
return allProcessed;
};
/**
* Called by parser when a link is found. Adds the URL to the vertex data.
* @param ids Comma separated list of ids
* @param linkStr URL to create a link for
*/
export const setLink = function (ids, _linkStr) {
let linkStr = _linkStr
export const setLink = function(ids, _linkStr) {
let linkStr = _linkStr;
if (config.securityLevel !== 'loose') {
linkStr = sanitizeUrl(_linkStr)
linkStr = sanitizeUrl(_linkStr);
}
ids.split(',').forEach(function (id) {
let rawTask = findTaskById(id)
ids.split(',').forEach(function(id) {
let rawTask = findTaskById(id);
if (typeof rawTask !== 'undefined') {
pushFun(id, () => { window.open(linkStr, '_self') })
pushFun(id, () => {
window.open(linkStr, '_self');
});
}
})
setClass(ids, 'clickable')
}
});
setClass(ids, 'clickable');
};
/**
* Called by parser when a special node is found, e.g. a clickable element.
* @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)
export const setClass = function(ids, className) {
ids.split(',').forEach(function(id) {
let rawTask = findTaskById(id);
if (typeof rawTask !== 'undefined') {
rawTask.classes.push(className)
rawTask.classes.push(className);
}
})
}
});
};
const setClickFun = function (id, functionName, functionArgs) {
const setClickFun = function(id, functionName, functionArgs) {
if (config.securityLevel !== 'loose') {
return
return;
}
if (typeof functionName === 'undefined') {
return
return;
}
let argList = []
let argList = [];
if (typeof functionArgs === 'string') {
/* Splits functionArgs by ',', ignoring all ',' in double quoted strings */
argList = functionArgs.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/)
argList = functionArgs.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);
for (let i = 0; i < argList.length; i++) {
let item = argList[i].trim()
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)
item = item.substr(1, item.length - 2);
}
argList[i] = item
argList[i] = item;
}
}
let rawTask = findTaskById(id)
let rawTask = findTaskById(id);
if (typeof rawTask !== 'undefined') {
pushFun(id, () => { window[functionName](...argList) })
pushFun(id, () => {
window[functionName](...argList);
});
}
}
};
/**
* The callbackFunction is executed in a click event bound to the task with the specified id or the task's assigned text
* @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 pushFun = function(id, callbackFunction) {
funs.push(function(element) {
// const elem = d3.select(element).select(`[id="${id}"]`)
const elem = document.querySelector(`[id="${id}"]`)
const elem = document.querySelector(`[id="${id}"]`);
if (elem !== null) {
elem.addEventListener('click', function () {
callbackFunction()
})
elem.addEventListener('click', function() {
callbackFunction();
});
}
})
funs.push(function (element) {
});
funs.push(function(element) {
// const elem = d3.select(element).select(`[id="${id}-text"]`)
const elem = document.querySelector(`[id="${id}-text"]`)
const elem = document.querySelector(`[id="${id}-text"]`);
if (elem !== null) {
elem.addEventListener('click', function () {
callbackFunction()
})
elem.addEventListener('click', function() {
callbackFunction();
});
}
})
}
});
};
/**
* Called by parser when a click definition is found. Registers an event handler.
@@ -517,22 +527,22 @@ const pushFun = function (id, callbackFunction) {
* @param functionName Function to be called on click
* @param 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')
}
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 const bindFunctions = function(element) {
funs.forEach(function(fun) {
fun(element);
});
};
export default {
clear,
@@ -556,20 +566,20 @@ export default {
setLink,
bindFunctions,
durationToDate
}
};
function getTaskTags (data, task, tags) {
let matchFound = true
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)
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
task[t] = true;
data.shift(1);
matchFound = true;
}
})
});
}
}

View File

@@ -1,32 +1,32 @@
/* eslint-env jasmine */
import moment from 'moment-mini'
import ganttDb from './ganttDb'
import moment from 'moment-mini';
import ganttDb from './ganttDb';
describe('when using the ganttDb', function () {
beforeEach(function () {
ganttDb.clear()
})
describe('when using the ganttDb', function() {
beforeEach(function() {
ganttDb.clear();
});
describe('when using relative times', function () {
describe('when using relative times', function() {
it.each`
diff | date | expected
${' 1d'} | ${moment('2019-01-01')} | ${moment('2019-01-02').toDate()}
${' 1w'} | ${moment('2019-01-01')} | ${moment('2019-01-08').toDate()}
diff | date | expected
${' 1d'} | ${moment('2019-01-01')} | ${moment('2019-01-02').toDate()}
${' 1w'} | ${moment('2019-01-01')} | ${moment('2019-01-08').toDate()}
`('should add $diff to $date resulting in $expected', ({ diff, date, expected }) => {
expect(ganttDb.durationToDate(diff, date)).toEqual(expected)
})
})
expect(ganttDb.durationToDate(diff, date)).toEqual(expected);
});
});
describe('when calling the clear function', function () {
beforeEach(function () {
ganttDb.setDateFormat('YYYY-MM-DD')
ganttDb.enableInclusiveEndDates()
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.clear()
})
describe('when calling the clear function', function() {
beforeEach(function() {
ganttDb.setDateFormat('YYYY-MM-DD');
ganttDb.enableInclusiveEndDates();
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.clear();
});
it.each`
fn | expected
@@ -38,9 +38,9 @@ describe('when using the ganttDb', function () {
${'getSections'} | ${[]}
${'endDatesAreInclusive'} | ${false}
`('should clear $fn', ({ fn, expected }) => {
expect(ganttDb[ fn ]()).toEqual(expected)
})
})
expect(ganttDb[fn]()).toEqual(expected);
});
});
it.each`
testName | section | taskName | taskData | expStartDate | expEndDate | expId | expTask
@@ -52,135 +52,148 @@ describe('when using the ganttDb', function () {
${'should handle duration (weeks) instead of fixed date to determine end date'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2w'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 15)} | ${'id1'} | ${'test1'}
${'should handle 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)
})
`('$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.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)
})
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);
}
);
it('should handle relative start date based on id regardless of sections', function () {
ganttDb.setDateFormat('YYYY-MM-DD')
ganttDb.addSection('testa1')
ganttDb.addTask('test1', 'id1,2013-01-01,2w')
ganttDb.addTask('test2', 'id2,after id3,1d')
ganttDb.addSection('testa2')
ganttDb.addTask('test3', 'id3,after id1,2d')
it('should handle relative start date based on id regardless of sections', function() {
ganttDb.setDateFormat('YYYY-MM-DD');
ganttDb.addSection('testa1');
ganttDb.addTask('test1', 'id1,2013-01-01,2w');
ganttDb.addTask('test2', 'id2,after id3,1d');
ganttDb.addSection('testa2');
ganttDb.addTask('test3', 'id3,after id1,2d');
const tasks = ganttDb.getTasks()
const tasks = ganttDb.getTasks();
expect(tasks[1].startTime).toEqual(new Date(2013, 0, 17))
expect(tasks[1].endTime).toEqual(new Date(2013, 0, 18))
expect(tasks[1].id).toEqual('id2')
expect(tasks[1].task).toEqual('test2')
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(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')
expect(tasks[2].id).toEqual('id3');
expect(tasks[2].task).toEqual('test3');
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()
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[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[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[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[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[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[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')
})
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');
});
describe('when setting inclusive end dates', function () {
beforeEach(function () {
ganttDb.setDateFormat('YYYY-MM-DD')
ganttDb.enableInclusiveEndDates()
ganttDb.addTask('test1', 'id1,2019-02-01,1d')
ganttDb.addTask('test2', 'id2,2019-02-01,2019-02-03')
})
it('should automatically add one day to all end dates', function () {
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-02', 'YYYY-MM-DD').toDate())
expect(tasks[0].id).toEqual('id1')
expect(tasks[0].task).toEqual('test1')
describe('when setting inclusive end dates', function() {
beforeEach(function() {
ganttDb.setDateFormat('YYYY-MM-DD');
ganttDb.enableInclusiveEndDates();
ganttDb.addTask('test1', 'id1,2019-02-01,1d');
ganttDb.addTask('test2', 'id2,2019-02-01,2019-02-03');
});
it('should automatically add one day to all end dates', function() {
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-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-01', 'YYYY-MM-DD').toDate())
expect(tasks[1].endTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate())
expect(tasks[1].renderEndTime).toBeNull() // Fixed end
expect(tasks[1].manualEndTime).toBeTruthy()
expect(tasks[1].id).toEqual('id2')
expect(tasks[1].task).toEqual('test2')
})
})
})
expect(tasks[1].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate());
expect(tasks[1].endTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate());
expect(tasks[1].renderEndTime).toBeNull(); // Fixed end
expect(tasks[1].manualEndTime).toBeTruthy();
expect(tasks[1].id).toEqual('id2');
expect(tasks[1].task).toEqual('test2');
});
});
});

View File

@@ -1,9 +1,9 @@
import * as d3 from 'd3'
import * as d3 from 'd3';
import { parser } from './parser/gantt'
import ganttDb from './ganttDb'
import { parser } from './parser/gantt';
import ganttDb from './ganttDb';
parser.yy = ganttDb
parser.yy = ganttDb;
const conf = {
titleTopMargin: 25,
@@ -15,287 +15,316 @@ const conf = {
gridLineStartPadding: 35,
fontSize: 11,
fontFamily: '"Open-Sans", "sans-serif"'
}
export const setConf = function (cnf) {
const keys = Object.keys(cnf)
};
export const setConf = function(cnf) {
const keys = Object.keys(cnf);
keys.forEach(function (key) {
conf[key] = cnf[key]
})
}
let w
export const draw = function (text, id) {
parser.yy.clear()
parser.parse(text)
keys.forEach(function(key) {
conf[key] = cnf[key];
});
};
let w;
export const draw = function(text, id) {
parser.yy.clear();
parser.parse(text);
const elem = document.getElementById(id)
w = elem.parentElement.offsetWidth
const elem = document.getElementById(id);
w = elem.parentElement.offsetWidth;
if (typeof w === 'undefined') {
w = 1200
w = 1200;
}
if (typeof conf.useWidth !== 'undefined') {
w = conf.useWidth
w = conf.useWidth;
}
const taskArray = parser.yy.getTasks()
const taskArray = parser.yy.getTasks();
// Set height based on number of tasks
const h = taskArray.length * (conf.barHeight + conf.barGap) + 2 * conf.topPadding
const h = taskArray.length * (conf.barHeight + conf.barGap) + 2 * conf.topPadding;
elem.setAttribute('height', '100%')
elem.setAttribute('height', '100%');
// Set viewBox
elem.setAttribute('viewBox', '0 0 ' + w + ' ' + h)
const svg = d3.select(`[id="${id}"]`)
elem.setAttribute('viewBox', '0 0 ' + w + ' ' + h);
const svg = d3.select(`[id="${id}"]`);
// Set timescale
const timeScale = d3.scaleTime()
.domain([d3.min(taskArray, function (d) {
return d.startTime
}),
d3.max(taskArray, function (d) {
return d.endTime
})])
.rangeRound([0, w - conf.leftPadding - conf.rightPadding])
const timeScale = d3
.scaleTime()
.domain([
d3.min(taskArray, function(d) {
return d.startTime;
}),
d3.max(taskArray, function(d) {
return d.endTime;
})
])
.rangeRound([0, w - conf.leftPadding - conf.rightPadding]);
let categories = []
let categories = [];
for (let i = 0; i < taskArray.length; i++) {
categories.push(taskArray[i].type)
categories.push(taskArray[i].type);
}
const catsUnfiltered = categories // for vert labels
const catsUnfiltered = categories; // for vert labels
categories = checkUnique(categories)
categories = checkUnique(categories);
makeGant(taskArray, w, h)
makeGant(taskArray, w, h);
if (typeof conf.useWidth !== 'undefined') {
elem.setAttribute('width', w)
elem.setAttribute('width', w);
}
svg.append('text')
svg
.append('text')
.text(parser.yy.getTitle())
.attr('x', w / 2)
.attr('y', conf.titleTopMargin)
.attr('class', 'titleText')
.attr('class', 'titleText');
function makeGant (tasks, pageWidth, pageHeight) {
const barHeight = conf.barHeight
const gap = barHeight + conf.barGap
const topPadding = conf.topPadding
const leftPadding = conf.leftPadding
function makeGant(tasks, pageWidth, pageHeight) {
const barHeight = conf.barHeight;
const gap = barHeight + conf.barGap;
const topPadding = conf.topPadding;
const leftPadding = conf.leftPadding;
const colorScale = d3.scaleLinear()
const colorScale = d3
.scaleLinear()
.domain([0, categories.length])
.range(['#00B9FA', '#F95002'])
.interpolate(d3.interpolateHcl)
.interpolate(d3.interpolateHcl);
makeGrid(leftPadding, topPadding, pageWidth, pageHeight)
drawRects(tasks, gap, topPadding, leftPadding, barHeight, colorScale, pageWidth, pageHeight)
vertLabels(gap, topPadding, leftPadding, barHeight, colorScale)
drawToday(leftPadding, topPadding, pageWidth, pageHeight)
makeGrid(leftPadding, topPadding, pageWidth, pageHeight);
drawRects(tasks, gap, topPadding, leftPadding, barHeight, colorScale, pageWidth, pageHeight);
vertLabels(gap, topPadding, leftPadding, barHeight, colorScale);
drawToday(leftPadding, topPadding, pageWidth, pageHeight);
}
function drawRects (theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w, h) {
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')
svg
.append('g')
.selectAll('rect')
.data(theArray)
.enter()
.append('rect')
.attr('x', 0)
.attr('y', function (d, i) {
return i * theGap + theTopPad - 2
.attr('y', function(d, i) {
return i * theGap + theTopPad - 2;
})
.attr('width', function () {
return w - conf.rightPadding / 2
.attr('width', function() {
return w - conf.rightPadding / 2;
})
.attr('height', theGap)
.attr('class', function (d) {
.attr('class', function(d) {
for (let i = 0; i < categories.length; i++) {
if (d.type === categories[i]) {
return 'section section' + (i % conf.numberSectionStyles)
return 'section section' + (i % conf.numberSectionStyles);
}
}
return 'section section0'
})
return 'section section0';
});
// Draw the rects representing the tasks
const rectangles = svg.append('g')
const rectangles = svg
.append('g')
.selectAll('rect')
.data(theArray)
.enter()
.enter();
rectangles.append('rect')
.attr('id', function (d) { return d.id })
rectangles
.append('rect')
.attr('id', function(d) {
return d.id;
})
.attr('rx', 3)
.attr('ry', 3)
.attr('x', function (d) {
.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 +
0.5 * (timeScale(d.endTime) - timeScale(d.startTime)) -
0.5 * theBarHeight
);
}
return timeScale(d.startTime) + theSidePad
return timeScale(d.startTime) + theSidePad;
})
.attr('y', function (d, i) {
return i * theGap + theTopPad
.attr('y', function(d, i) {
return i * theGap + theTopPad;
})
.attr('width', function (d) {
.attr('width', function(d) {
if (d.milestone) {
return theBarHeight
return theBarHeight;
}
return (timeScale(d.renderEndTime || d.endTime) - timeScale(d.startTime))
return timeScale(d.renderEndTime || d.endTime) - timeScale(d.startTime);
})
.attr('height', theBarHeight)
.attr('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('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'
.attr('class', function(d) {
const res = 'task';
let classStr = ''
let classStr = '';
if (d.classes.length > 0) {
classStr = d.classes.join(' ')
classStr = d.classes.join(' ');
}
let secNum = 0
let secNum = 0;
for (let i = 0; i < categories.length; i++) {
if (d.type === categories[i]) {
secNum = (i % conf.numberSectionStyles)
secNum = i % conf.numberSectionStyles;
}
}
let taskClass = ''
let taskClass = '';
if (d.active) {
if (d.crit) {
taskClass += ' activeCrit'
taskClass += ' activeCrit';
} else {
taskClass = ' active'
taskClass = ' active';
}
} else if (d.done) {
if (d.crit) {
taskClass = ' doneCrit'
taskClass = ' doneCrit';
} else {
taskClass = ' done'
taskClass = ' done';
}
} else {
if (d.crit) {
taskClass += ' crit'
taskClass += ' crit';
}
}
if (taskClass.length === 0) {
taskClass = ' task'
taskClass = ' task';
}
if (d.milestone) {
taskClass = ' milestone ' + taskClass
taskClass = ' milestone ' + taskClass;
}
taskClass += secNum
taskClass += secNum;
taskClass += ' ' + classStr
taskClass += ' ' + classStr;
return res + taskClass
})
return res + taskClass;
});
// Append task labels
rectangles.append('text')
.attr('id', function (d) { return d.id + '-text' })
.text(function (d) {
return d.task
rectangles
.append('text')
.attr('id', function(d) {
return d.id + '-text';
})
.text(function(d) {
return d.task;
})
.attr('font-size', conf.fontSize)
.attr('x', function (d) {
let startX = timeScale(d.startTime)
let endX = timeScale(d.renderEndTime || d.endTime)
.attr('x', function(d) {
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)
startX += 0.5 * (timeScale(d.endTime) - timeScale(d.startTime)) - 0.5 * theBarHeight;
}
if (d.milestone) {
endX = startX + theBarHeight
endX = startX + theBarHeight;
}
const textWidth = this.getBBox().width
const textWidth = this.getBBox().width;
// Check id text width > width of rectangle
if (textWidth > (endX - startX)) {
if (textWidth > endX - startX) {
if (endX + textWidth + 1.5 * conf.leftPadding > w) {
return startX + theSidePad - 5
return startX + theSidePad - 5;
} else {
return endX + theSidePad + 5
return endX + theSidePad + 5;
}
} else {
return (endX - startX) / 2 + startX + theSidePad
return (endX - startX) / 2 + startX + theSidePad;
}
})
.attr('y', function (d, i) {
return i * theGap + (conf.barHeight / 2) + (conf.fontSize / 2 - 2) + theTopPad
.attr('y', function(d, i) {
return i * theGap + conf.barHeight / 2 + (conf.fontSize / 2 - 2) + theTopPad;
})
.attr('text-height', theBarHeight)
.attr('class', function (d) {
const startX = timeScale(d.startTime)
let endX = timeScale(d.endTime)
.attr('class', function(d) {
const startX = timeScale(d.startTime);
let endX = timeScale(d.endTime);
if (d.milestone) {
endX = startX + theBarHeight
endX = startX + theBarHeight;
}
const textWidth = this.getBBox().width
const textWidth = this.getBBox().width;
let classStr = ''
let classStr = '';
if (d.classes.length > 0) {
classStr = d.classes.join(' ')
classStr = d.classes.join(' ');
}
let secNum = 0
let secNum = 0;
for (let i = 0; i < categories.length; i++) {
if (d.type === categories[i]) {
secNum = (i % conf.numberSectionStyles)
secNum = i % conf.numberSectionStyles;
}
}
let taskType = ''
let taskType = '';
if (d.active) {
if (d.crit) {
taskType = 'activeCritText' + secNum
taskType = 'activeCritText' + secNum;
} else {
taskType = 'activeText' + secNum
taskType = 'activeText' + secNum;
}
}
if (d.done) {
if (d.crit) {
taskType = taskType + ' doneCritText' + secNum
taskType = taskType + ' doneCritText' + secNum;
} else {
taskType = taskType + ' doneText' + secNum
taskType = taskType + ' doneText' + secNum;
}
} else {
if (d.crit) {
taskType = taskType + ' critText' + secNum
taskType = taskType + ' critText' + secNum;
}
}
if (d.milestone) {
taskType += ' milestoneText'
taskType += ' milestoneText';
}
// Check id text width > width of rectangle
if (textWidth > (endX - startX)) {
if (textWidth > endX - startX) {
if (endX + textWidth + 1.5 * conf.leftPadding > w) {
return classStr + ' taskTextOutsideLeft taskTextOutside' + secNum + ' ' + taskType
return classStr + ' taskTextOutsideLeft taskTextOutside' + secNum + ' ' + taskType;
} else {
return classStr + ' taskTextOutsideRight taskTextOutside' + secNum + ' ' + taskType
return classStr + ' taskTextOutsideRight taskTextOutside' + secNum + ' ' + taskType;
}
} else {
return classStr + ' taskText taskText' + secNum + ' ' + taskType
return classStr + ' taskText taskText' + secNum + ' ' + taskType;
}
})
});
}
function makeGrid (theSidePad, theTopPad, w, h) {
let xAxis = d3.axisBottom(timeScale)
function makeGrid(theSidePad, theTopPad, w, h) {
let xAxis = d3
.axisBottom(timeScale)
.tickSize(-h + theTopPad + conf.gridLineStartPadding)
.tickFormat(d3.timeFormat(parser.yy.getAxisFormat() || conf.axisFormat || '%Y-%m-%d'))
.tickFormat(d3.timeFormat(parser.yy.getAxisFormat() || conf.axisFormat || '%Y-%m-%d'));
svg.append('g')
svg
.append('g')
.attr('class', 'grid')
.attr('transform', 'translate(' + theSidePad + ', ' + (h - 50) + ')')
.call(xAxis)
@@ -304,90 +333,92 @@ export const draw = function (text, id) {
.attr('fill', '#000')
.attr('stroke', 'none')
.attr('font-size', 10)
.attr('dy', '1em')
.attr('dy', '1em');
}
function vertLabels (theGap, theTopPad) {
const numOccurances = []
let prevGap = 0
function vertLabels(theGap, theTopPad) {
const numOccurances = [];
let prevGap = 0;
for (let i = 0; i < categories.length; i++) {
numOccurances[i] = [categories[i], getCount(categories[i], catsUnfiltered)]
numOccurances[i] = [categories[i], getCount(categories[i], catsUnfiltered)];
}
svg.append('g') // without doing this, impossible to put grid lines behind text
svg
.append('g') // without doing this, impossible to put grid lines behind text
.selectAll('text')
.data(numOccurances)
.enter()
.append('text')
.text(function (d) {
return d[0]
.text(function(d) {
return d[0];
})
.attr('x', 10)
.attr('y', function (d, i) {
.attr('y', function(d, i) {
if (i > 0) {
for (let j = 0; j < i; j++) {
prevGap += numOccurances[i - 1][1]
return d[1] * theGap / 2 + prevGap * theGap + theTopPad
prevGap += numOccurances[i - 1][1];
return (d[1] * theGap) / 2 + prevGap * theGap + theTopPad;
}
} else {
return d[1] * theGap / 2 + theTopPad
return (d[1] * theGap) / 2 + theTopPad;
}
})
.attr('class', function (d) {
.attr('class', function(d) {
for (let i = 0; i < categories.length; i++) {
if (d[0] === categories[i]) {
return 'sectionTitle sectionTitle' + (i % conf.numberSectionStyles)
return 'sectionTitle sectionTitle' + (i % conf.numberSectionStyles);
}
}
return 'sectionTitle'
})
return 'sectionTitle';
});
}
function drawToday (theSidePad, theTopPad, w, h) {
const todayG = svg.append('g')
.attr('class', 'today')
function drawToday(theSidePad, theTopPad, w, h) {
const todayG = svg.append('g').attr('class', 'today');
const today = new Date()
const today = new Date();
todayG.append('line')
todayG
.append('line')
.attr('x1', timeScale(today) + theSidePad)
.attr('x2', timeScale(today) + theSidePad)
.attr('y1', conf.titleTopMargin)
.attr('y2', h - conf.titleTopMargin)
.attr('class', 'today')
.attr('class', 'today');
}
// from this stackexchange question: http://stackoverflow.com/questions/1890203/unique-for-arrays-in-javascript
function checkUnique (arr) {
const hash = {}
const result = []
function checkUnique(arr) {
const hash = {};
const result = [];
for (let i = 0, l = arr.length; i < l; ++i) {
if (!hash.hasOwnProperty(arr[i])) { // it works with objects! in FF, at least
hash[arr[i]] = true
result.push(arr[i])
if (!hash.hasOwnProperty(arr[i])) {
// it works with objects! in FF, at least
hash[arr[i]] = true;
result.push(arr[i]);
}
}
return result
return result;
}
// from this stackexchange question: http://stackoverflow.com/questions/14227981/count-how-many-strings-in-an-array-have-duplicates-in-the-same-array
function getCounts (arr) {
let i = arr.length // const to loop over
const obj = {} // obj to store results
function getCounts(arr) {
let i = arr.length; // const to loop over
const obj = {}; // obj to store results
while (i) {
obj[arr[--i]] = (obj[arr[i]] || 0) + 1 // count occurrences
obj[arr[--i]] = (obj[arr[i]] || 0) + 1; // count occurrences
}
return obj
return obj;
}
// get specific from everything
function getCount (word, arr) {
return getCounts(arr)[word] || 0
function getCount(word, arr) {
return getCounts(arr)[word] || 0;
}
}
};
export default {
setConf,
draw
}
};

View File

@@ -1,50 +1,52 @@
/* eslint-env jasmine */
/* eslint-disable no-eval */
import { parser } from './gantt'
import ganttDb from '../ganttDb'
import { parser } from './gantt';
import ganttDb from '../ganttDb';
const parserFnConstructor = (str) => {
const parserFnConstructor = str => {
return () => {
parser.parse(str)
}
}
parser.parse(str);
};
};
describe('when parsing a gantt diagram it', function () {
beforeEach(function () {
parser.yy = ganttDb
parser.yy.clear()
})
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'
it('should handle a dateFormat definition', function() {
const str = 'gantt\ndateFormat yyyy-mm-dd';
expect(parserFnConstructor(str)).not.toThrow()
})
expect(parserFnConstructor(str)).not.toThrow();
});
it('should handle a inclusive end date definition', function () {
const str = 'gantt\ndateFormat yyyy-mm-dd\ninclusiveEndDates'
it('should handle a inclusive end date definition', function() {
const str = 'gantt\ndateFormat yyyy-mm-dd\ninclusiveEndDates';
expect(parserFnConstructor(str)).not.toThrow()
})
it('should handle a title definition', function () {
const str = 'gantt\ndateFormat yyyy-mm-dd\ntitle Adding gantt diagram functionality to mermaid'
expect(parserFnConstructor(str)).not.toThrow();
});
it('should handle a title definition', function() {
const str = 'gantt\ndateFormat yyyy-mm-dd\ntitle Adding gantt diagram functionality to mermaid';
expect(parserFnConstructor(str)).not.toThrow()
})
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'
expect(parserFnConstructor(str)).not.toThrow();
});
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';
expect(parserFnConstructor(str)).not.toThrow()
})
it('should handle a section definition', function () {
const str = 'gantt\n' +
expect(parserFnConstructor(str)).not.toThrow();
});
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'
'section Documentation';
expect(parserFnConstructor(str)).not.toThrow()
})
expect(parserFnConstructor(str)).not.toThrow();
});
/**
* Beslutsflöde inligt nedan. Obs bla bla bla
* ```
@@ -56,22 +58,23 @@ describe('when parsing a gantt diagram it', function () {
```
* params bapa - a unique bapap
*/
it('should handle a task definition', function () {
const str = 'gantt\n' +
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'
'Design jison grammar:des1, 2014-01-01, 2014-01-04';
expect(parserFnConstructor(str)).not.toThrow()
expect(parserFnConstructor(str)).not.toThrow();
const tasks = parser.yy.getTasks()
const tasks = parser.yy.getTasks();
expect(tasks[0].startTime).toEqual(new Date(2014, 0, 1))
expect(tasks[0].endTime).toEqual(new Date(2014, 0, 4))
expect(tasks[0].id).toEqual('des1')
expect(tasks[0].task).toEqual('Design jison grammar')
})
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}
@@ -79,25 +82,28 @@ describe('when parsing a gantt diagram it', function () {
${'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'
`('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']
const allowedTags = ['active', 'done', 'crit', 'milestone'];
expect(parserFnConstructor(str)).not.toThrow()
expect(parserFnConstructor(str)).not.toThrow();
const tasks = parser.yy.getTasks()
const tasks = parser.yy.getTasks();
allowedTags.forEach(function (t) {
if (eval(t)) {
expect(tasks[0][t]).toBeTruthy()
} else {
expect(tasks[0][t]).toBeFalsy()
}
})
})
})
allowedTags.forEach(function(t) {
if (eval(t)) {
expect(tasks[0][t]).toBeTruthy();
} else {
expect(tasks[0][t]).toBeFalsy();
}
});
});
});