Merge branch 'JustAnotherMark-totable-operation'
This commit is contained in:
commit
addb2b4448
@ -737,37 +737,43 @@ const Utils = {
|
||||
* Parses CSV data and returns it as a two dimensional array or strings.
|
||||
*
|
||||
* @param {string} data
|
||||
* @param {string[]} [cellDelims=[","]]
|
||||
* @param {string[]} [lineDelims=["\n", "\r"]]
|
||||
* @returns {string[][]}
|
||||
*
|
||||
* @example
|
||||
* // returns [["head1", "head2"], ["data1", "data2"]]
|
||||
* Utils.parseCSV("head1,head2\ndata1,data2");
|
||||
*/
|
||||
parseCSV: function(data) {
|
||||
|
||||
parseCSV: function(data, cellDelims=[","], lineDelims=["\n", "\r"]) {
|
||||
let b,
|
||||
ignoreNext = false,
|
||||
next,
|
||||
renderNext = false,
|
||||
inString = false,
|
||||
cell = "",
|
||||
line = [],
|
||||
lines = [];
|
||||
|
||||
// Remove BOM, often present in Excel CSV files
|
||||
if (data.length && data[0] === "\uFEFF") data = data.substr(1);
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
b = data[i];
|
||||
if (ignoreNext) {
|
||||
next = data[i+1] || "";
|
||||
if (renderNext) {
|
||||
cell += b;
|
||||
ignoreNext = false;
|
||||
renderNext = false;
|
||||
} else if (b === "\\") {
|
||||
cell += b;
|
||||
ignoreNext = true;
|
||||
renderNext = true;
|
||||
} else if (b === "\"" && !inString) {
|
||||
inString = true;
|
||||
} else if (b === "\"" && inString) {
|
||||
inString = false;
|
||||
} else if (b === "," && !inString) {
|
||||
if (next === "\"") renderNext = true;
|
||||
else inString = false;
|
||||
} else if (!inString && cellDelims.indexOf(b) >= 0) {
|
||||
line.push(cell);
|
||||
cell = "";
|
||||
} else if ((b === "\n" || b === "\r") && !inString) {
|
||||
} else if (!inString && lineDelims.indexOf(b) >= 0) {
|
||||
line.push(cell);
|
||||
cell = "";
|
||||
lines.push(line);
|
||||
|
@ -182,6 +182,7 @@ const Categories = [
|
||||
"To Lower case",
|
||||
"Add line numbers",
|
||||
"Remove line numbers",
|
||||
"To Table",
|
||||
"Reverse",
|
||||
"Sort",
|
||||
"Unique",
|
||||
|
@ -37,6 +37,7 @@ import SeqUtils from "../operations/SeqUtils.js";
|
||||
import Shellcode from "../operations/Shellcode.js";
|
||||
import StrUtils from "../operations/StrUtils.js";
|
||||
import Tidy from "../operations/Tidy.js";
|
||||
import ToTable from "../operations/ToTable.js";
|
||||
import Unicode from "../operations/Unicode.js";
|
||||
import URL_ from "../operations/URL.js";
|
||||
|
||||
@ -613,6 +614,34 @@ const OperationConfig = {
|
||||
}
|
||||
]
|
||||
},
|
||||
"To Table": {
|
||||
module: "Default",
|
||||
description: "Data can be split on different characters and rendered as an HTML or ASCII table with an optional header row.<br><br>Supports the CSV (Comma Separated Values) file format by default. Change the cell delimiter argument to <code>\\t</code> to support TSV (Tab Separated Values) or <code>|</code> for PSV (Pipe Separated Values).<br><br>You can enter as many delimiters as you like. Each character will be treat as a separate possible delimiter.",
|
||||
inputType: "string",
|
||||
outputType: "html",
|
||||
args: [
|
||||
{
|
||||
name: "Cell delimiters",
|
||||
type: "binaryShortString",
|
||||
value: ","
|
||||
},
|
||||
{
|
||||
name: "Row delimiters",
|
||||
type: "binaryShortString",
|
||||
value: "\\n\\r"
|
||||
},
|
||||
{
|
||||
name: "Make first row header",
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "Format",
|
||||
type: "option",
|
||||
value: ToTable.FORMATS
|
||||
}
|
||||
]
|
||||
},
|
||||
"From Hex": {
|
||||
module: "Default",
|
||||
description: "Converts a hexadecimal byte string back into its raw value.<br><br>e.g. <code>ce 93 ce b5 ce b9 ce ac 20 cf 83 ce bf cf 85 0a</code> becomes the UTF-8 encoded string <code>Γειά σου</code>",
|
||||
|
@ -27,6 +27,7 @@ import Rotate from "../../operations/Rotate.js";
|
||||
import SeqUtils from "../../operations/SeqUtils.js";
|
||||
import StrUtils from "../../operations/StrUtils.js";
|
||||
import Tidy from "../../operations/Tidy.js";
|
||||
import ToTable from "../../operations/ToTable.js";
|
||||
import Unicode from "../../operations/Unicode.js";
|
||||
import UUID from "../../operations/UUID.js";
|
||||
import XKCD from "../../operations/XKCD.js";
|
||||
@ -163,6 +164,7 @@ OpModules.Default = {
|
||||
"Mean": Arithmetic.runMean,
|
||||
"Median": Arithmetic.runMedian,
|
||||
"Standard Deviation": Arithmetic.runStdDev,
|
||||
"To Table": ToTable.runToTable,
|
||||
"Windows Filetime to UNIX Timestamp": Filetime.runFromFiletimeToUnix,
|
||||
"UNIX Timestamp to Windows Filetime": Filetime.runToFiletimeFromUnix,
|
||||
"XKCD Random Number": XKCD.runRandomNumber,
|
||||
|
164
src/core/operations/ToTable.js
Executable file
164
src/core/operations/ToTable.js
Executable file
@ -0,0 +1,164 @@
|
||||
/**
|
||||
* ToTable operations.
|
||||
*
|
||||
* @author Mark Jones [github.com/justanothermark]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*
|
||||
* @namespace
|
||||
*/
|
||||
import Utils from "../Utils.js";
|
||||
|
||||
const ToTable = {
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
FORMATS: [
|
||||
"ASCII",
|
||||
"HTML"
|
||||
],
|
||||
|
||||
|
||||
/**
|
||||
* To Table operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {html}
|
||||
*/
|
||||
runToTable: function (input, args) {
|
||||
const [cellDelims, rowDelims, firstRowHeader, format] = args;
|
||||
|
||||
// Process the input into a nested array of elements.
|
||||
const tableData = Utils.parseCSV(input, cellDelims.split(""), rowDelims.split(""));
|
||||
|
||||
if (!tableData.length) return "";
|
||||
|
||||
// Render the data in the requested format.
|
||||
switch (format) {
|
||||
case "ASCII":
|
||||
return asciiOutput(tableData);
|
||||
case "HTML":
|
||||
default:
|
||||
return htmlOutput(tableData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs an array of data as an ASCII table.
|
||||
*
|
||||
* @param {string[][]} tableData
|
||||
* @returns {string}
|
||||
*/
|
||||
function asciiOutput(tableData) {
|
||||
const horizontalBorder = "-";
|
||||
const verticalBorder = "|";
|
||||
const crossBorder = "+";
|
||||
|
||||
let output = "";
|
||||
let longestCells = [];
|
||||
|
||||
// Find longestCells value per column to pad cells equally.
|
||||
tableData.forEach(function(row, index) {
|
||||
row.forEach(function(cell, cellIndex) {
|
||||
if (longestCells[cellIndex] === undefined || cell.length > longestCells[cellIndex]) {
|
||||
longestCells[cellIndex] = cell.length;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Add the top border of the table to the output.
|
||||
output += outputHorizontalBorder(longestCells);
|
||||
|
||||
// If the first row is a header, remove the row from the data and
|
||||
// add it to the output with another horizontal border.
|
||||
if (firstRowHeader) {
|
||||
let row = tableData.shift();
|
||||
output += outputRow(row, longestCells);
|
||||
output += outputHorizontalBorder(longestCells);
|
||||
}
|
||||
|
||||
// Add the rest of the table rows.
|
||||
tableData.forEach(function(row, index) {
|
||||
output += outputRow(row, longestCells);
|
||||
});
|
||||
|
||||
// Close the table with a final horizontal border.
|
||||
output += outputHorizontalBorder(longestCells);
|
||||
|
||||
return output;
|
||||
|
||||
/**
|
||||
* Outputs a row of correctly padded cells.
|
||||
*/
|
||||
function outputRow(row, longestCells) {
|
||||
let rowOutput = verticalBorder;
|
||||
row.forEach(function(cell, index) {
|
||||
rowOutput += " " + cell + " ".repeat(longestCells[index] - cell.length) + " " + verticalBorder;
|
||||
});
|
||||
rowOutput += "\n";
|
||||
return rowOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a horizontal border with a different character where
|
||||
* the horizontal border meets a vertical border.
|
||||
*/
|
||||
function outputHorizontalBorder(longestCells) {
|
||||
let rowOutput = crossBorder;
|
||||
longestCells.forEach(function(cellLength) {
|
||||
rowOutput += horizontalBorder.repeat(cellLength + 2) + crossBorder;
|
||||
});
|
||||
rowOutput += "\n";
|
||||
return rowOutput;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a table of data as a HTML table.
|
||||
*
|
||||
* @param {string[][]} tableData
|
||||
* @returns {string}
|
||||
*/
|
||||
function htmlOutput(tableData) {
|
||||
// Start the HTML output with suitable classes for styling.
|
||||
let output = "<table class='table table-hover table-condensed table-bordered table-nonfluid'>";
|
||||
|
||||
// If the first row is a header then put it in <thead> with <th> cells.
|
||||
if (firstRowHeader) {
|
||||
let row = tableData.shift();
|
||||
output += "<thead>";
|
||||
output += outputRow(row, "th");
|
||||
output += "</thead>";
|
||||
}
|
||||
|
||||
// Output the rest of the rows in the <tbody>.
|
||||
output += "<tbody>";
|
||||
tableData.forEach(function(row, index) {
|
||||
output += outputRow(row, "td");
|
||||
});
|
||||
|
||||
// Close the body and table elements.
|
||||
output += "</tbody></table>";
|
||||
return output;
|
||||
|
||||
/**
|
||||
* Outputs a table row.
|
||||
*
|
||||
* @param {string[]} row
|
||||
* @param {string} cellType
|
||||
*/
|
||||
function outputRow(row, cellType) {
|
||||
let output = "<tr>";
|
||||
row.forEach(function(cell) {
|
||||
output += "<" + cellType + ">" + cell + "</" + cellType + ">";
|
||||
});
|
||||
output += "</tr>";
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default ToTable;
|
Loading…
x
Reference in New Issue
Block a user