CyberChef/src/core/lib/Charts.mjs

208 lines
4.7 KiB
JavaScript
Raw Normal View History

/**
* @author tlwr [toby@toby.codes]
* @author Matt C [me@mitt.dev]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
2019-03-31 21:40:54 +01:00
/**
* @constant
* @default
*/
export const RECORD_DELIMITER_OPTIONS = ["Line feed", "CRLF"];
/**
* @constant
* @default
*/
2024-02-24 22:59:51 -05:00
export const FIELD_DELIMITER_OPTIONS = [
"Space",
"Comma",
"Semi-colon",
"Colon",
"Tab",
];
/**
* Default from colour
*
* @constant
* @default
*/
export const COLOURS = {
min: "white",
2024-02-24 22:59:51 -05:00
max: "black",
};
/**
* Gets values from input for a plot.
*
* @param {string} input
* @param {string} recordDelimiter
* @param {string} fieldDelimiter
* @param {boolean} columnHeadingsAreIncluded - whether we should skip the first record
2019-03-31 21:40:54 +01:00
* @param {number} length
* @returns {Object[]}
*/
2024-02-24 22:59:51 -05:00
export function getValues(
input,
recordDelimiter,
fieldDelimiter,
columnHeadingsAreIncluded,
length,
) {
let headings;
const values = [];
2024-02-24 22:59:51 -05:00
input.split(recordDelimiter).forEach((row, rowIndex) => {
const split = row.split(fieldDelimiter);
if (split.length !== length)
throw new OperationError(`Each row must have length ${length}.`);
if (columnHeadingsAreIncluded && rowIndex === 0) {
headings = split;
} else {
values.push(split);
}
});
2019-03-31 21:40:54 +01:00
return { headings, values };
}
/**
* Gets values from input for a scatter plot.
*
* @param {string} input
* @param {string} recordDelimiter
* @param {string} fieldDelimiter
* @param {boolean} columnHeadingsAreIncluded - whether we should skip the first record
* @returns {Object[]}
*/
2024-02-24 22:59:51 -05:00
export function getScatterValues(
input,
recordDelimiter,
fieldDelimiter,
columnHeadingsAreIncluded,
) {
let { headings, values } = getValues(
input,
2019-03-31 21:40:54 +01:00
recordDelimiter,
fieldDelimiter,
columnHeadingsAreIncluded,
2024-02-24 22:59:51 -05:00
2,
);
if (headings) {
2024-02-24 22:59:51 -05:00
headings = { x: headings[0], y: headings[1] };
}
2024-02-24 22:59:51 -05:00
values = values.map((row) => {
const x = parseFloat(row[0]),
y = parseFloat(row[1]);
2024-02-24 22:59:51 -05:00
if (Number.isNaN(x))
throw new OperationError("Values must be numbers in base 10.");
if (Number.isNaN(y))
throw new OperationError("Values must be numbers in base 10.");
return [x, y];
});
return { headings, values };
}
/**
* Gets values from input for a scatter plot with colour from the third column.
*
* @param {string} input
* @param {string} recordDelimiter
* @param {string} fieldDelimiter
* @param {boolean} columnHeadingsAreIncluded - whether we should skip the first record
* @returns {Object[]}
*/
2024-02-24 22:59:51 -05:00
export function getScatterValuesWithColour(
input,
recordDelimiter,
fieldDelimiter,
columnHeadingsAreIncluded,
) {
let { headings, values } = getValues(
input,
2024-02-24 22:59:51 -05:00
recordDelimiter,
fieldDelimiter,
columnHeadingsAreIncluded,
2024-02-24 22:59:51 -05:00
3,
);
if (headings) {
2024-02-24 22:59:51 -05:00
headings = { x: headings[0], y: headings[1] };
}
2024-02-24 22:59:51 -05:00
values = values.map((row) => {
const x = parseFloat(row[0]),
y = parseFloat(row[1]),
colour = row[2];
2024-02-24 22:59:51 -05:00
if (Number.isNaN(x))
throw new OperationError("Values must be numbers in base 10.");
if (Number.isNaN(y))
throw new OperationError("Values must be numbers in base 10.");
return [x, y, Utils.escapeHtml(colour)];
});
return { headings, values };
}
/**
* Gets values from input for a time series plot.
*
* @param {string} input
* @param {string} recordDelimiter
* @param {string} fieldDelimiter
* @param {boolean} columnHeadingsAreIncluded - whether we should skip the first record
* @returns {Object[]}
*/
2024-02-24 22:59:51 -05:00
export function getSeriesValues(
input,
recordDelimiter,
fieldDelimiter,
columnHeadingsAreIncluded,
) {
const { values } = getValues(
input,
2024-02-24 22:59:51 -05:00
recordDelimiter,
fieldDelimiter,
false,
2024-02-24 22:59:51 -05:00
3,
);
let xValues = new Set();
const series = {};
2024-02-24 22:59:51 -05:00
values.forEach((row) => {
const serie = row[0],
xVal = row[1],
val = parseFloat(row[2]);
2024-02-24 22:59:51 -05:00
if (Number.isNaN(val))
throw new OperationError("Values must be numbers in base 10.");
xValues.add(xVal);
if (typeof series[serie] === "undefined") series[serie] = {};
series[serie][xVal] = val;
});
xValues = new Array(...xValues);
const seriesList = [];
for (const seriesName in series) {
const serie = series[seriesName];
2024-02-24 22:59:51 -05:00
seriesList.push({ name: seriesName, data: serie });
}
return { xValues, series: seriesList };
}