js-awe
Version:
Awesome js utils including - plan: An Asynchronous control flow with a functional taste - Chrono: record and visualize timelines in the console
86 lines (85 loc) • 4.39 kB
JavaScript
import { arraySorter } from '../../jsUtils.js';
import { putCenteredValueAtPosIfFit, putValueAtPos } from '../tableUtils.js';
const LENGTH_IN_CHARS_OF_INI_END_TIMELINE = 80;
const START_OF_INTERVAL_CHAR = '|';
const END_OF_INTERVAL_CHAR = '|';
const INTERVAL_UNIT_CHAR = '-';
const LAST_SCALE_VALUE_MARGIN = 2;
//if from the oldest start to the newest end the difference in ms < the limit value then scale will be represented
// in this unit.
// The limit is calculated by applying:
//15% of miliseconds of two units above. ex: ms limit is 15% of one minute
const timelineScales = [
//1000*60*(15/100) 15% of 1 minute
{ limit: 9000, label: 'ms', valueInUnits: start => time => time - start },
//1000*60*60*(15/100) 15% of 1 hour
{ limit: 540000, label: 'sec', valueInUnits: start => time => Math.floor((time - start) / 1000).toString() },
//1000*60*60*24*(15/100)
{ limit: 12960000, label: 'min', valueInUnits: start => time => Math.floor((time - start) / 60000).toString() },
//1000*60*60*24*365*(15/100)
{ limit: 4730400000, label: 'hr', valueInUnits: start => time => Math.floor((time - start) / 3600000).toString() },
//1000*60*60*24*365*100*(15/100)
{ limit: 473040000000, label: 'day', valueInUnits: start => time => Math.floor((time - start) / 86400000).toString() },
//1000*60*60*24*365*100*10*(15/100)
{ limit: 4730400000000, label: 'year', valueInUnits: start => time => Math.floor((time - start) / 31536000000).toString() },
{ limit: Infinity, label: 'cen', valueInUnits: start => time => Math.floor((time - start) / 3153600000000).toString() },
];
function drawInterval(line, startInterval, endInterval) {
let value = START_OF_INTERVAL_CHAR +
''.padEnd(endInterval - startInterval - 1, INTERVAL_UNIT_CHAR) +
END_OF_INTERVAL_CHAR;
return putValueAtPos(line, value, startInterval);
}
function Timeline() {
let size;
let id;
let title;
let data;
let scalePoints;
let resolution;
let timelineScale;
function convertToResolution(point) {
return Math.floor((point - scalePoints[0]) / resolution);
}
return {
loadParams: paramId => paramTitle => {
id = paramId;
title = paramTitle;
return {
id,
load: columnData => {
data = columnData.map(intervals => intervals.map(interval => {
const start = new Date(interval.start).valueOf();
const end = new Date(interval.end).valueOf();
return {
start,
end
};
}));
scalePoints =
[...new Set(data.flatMap(el => el).reduce((scalePoints, point) => [...scalePoints, point.start, point.end], []).sort(arraySorter()))];
resolution = (scalePoints[scalePoints.length - 1] - scalePoints[0]) / LENGTH_IN_CHARS_OF_INI_END_TIMELINE;
let timelineScaleIndex = timelineScales.findIndex(el => (scalePoints[scalePoints.length - 1] - scalePoints[0]) < el.limit);
timelineScale = timelineScales[timelineScaleIndex];
size = timelineScale.label.length + 1 + LENGTH_IN_CHARS_OF_INI_END_TIMELINE + LAST_SCALE_VALUE_MARGIN;
},
getSize: () => size,
heading: {
nextValue: function* () {
yield scalePoints.reduce((line, scalePoint) => {
return putCenteredValueAtPosIfFit(line, timelineScale.valueInUnits(scalePoints[0])(scalePoint), convertToResolution(scalePoint) + 3, 1);
}, timelineScale.label + ' ' + ''.padEnd(LENGTH_IN_CHARS_OF_INI_END_TIMELINE + LAST_SCALE_VALUE_MARGIN));
}
},
row: {
nextValue: function* () {
for (let intervals of data) {
yield intervals.reduce((line, interval) => drawInterval(line, convertToResolution(interval.start) + timelineScale.label.length + 1, convertToResolution(interval.end) + timelineScale.label.length + 1), ''.padEnd(size));
}
}
}
};
}
};
}
export { Timeline };