From cf05a8d8fa06e64309af4acf599af55b29eff792 Mon Sep 17 00:00:00 2001 From: knsv Date: Thu, 12 Sep 2019 12:57:36 -0700 Subject: [PATCH] #931 Updating code to align witn new code standard --- src/diagrams/gantt/ganttDb.js | 634 ++++++++++++------------ src/diagrams/gantt/ganttDb.spec.js | 297 +++++------ src/diagrams/gantt/ganttRenderer.js | 375 +++++++------- src/diagrams/gantt/parser/gantt.spec.js | 126 ++--- 4 files changed, 746 insertions(+), 686 deletions(-) diff --git a/src/diagrams/gantt/ganttDb.js b/src/diagrams/gantt/ganttDb.js index 46ba09e04..9eb2b8c38 100644 --- a/src/diagrams/gantt/ganttDb.js +++ b/src/diagrams/gantt/ganttDb.js @@ -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; } - }) + }); } } diff --git a/src/diagrams/gantt/ganttDb.spec.js b/src/diagrams/gantt/ganttDb.spec.js index 1dad71ad6..433e3a495 100644 --- a/src/diagrams/gantt/ganttDb.spec.js +++ b/src/diagrams/gantt/ganttDb.spec.js @@ -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'); + }); + }); +}); diff --git a/src/diagrams/gantt/ganttRenderer.js b/src/diagrams/gantt/ganttRenderer.js index c9349f483..beee82e17 100644 --- a/src/diagrams/gantt/ganttRenderer.js +++ b/src/diagrams/gantt/ganttRenderer.js @@ -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 -} +}; diff --git a/src/diagrams/gantt/parser/gantt.spec.js b/src/diagrams/gantt/parser/gantt.spec.js index 7a28aeb6c..51d2c0884 100644 --- a/src/diagrams/gantt/parser/gantt.spec.js +++ b/src/diagrams/gantt/parser/gantt.spec.js @@ -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(); + } + }); + }); +});