diff --git a/src/core/config/Categories.js b/src/core/config/Categories.js index b2c404f..33ec4a9 100755 --- a/src/core/config/Categories.js +++ b/src/core/config/Categories.js @@ -67,6 +67,7 @@ const Categories = [ "Encode text", "Decode text", "Swap endianness", + "To Table", ] }, { diff --git a/src/core/config/OperationConfig.js b/src/core/config/OperationConfig.js index bbe80a0..5d2ca47 100644 --- a/src/core/config/OperationConfig.js +++ b/src/core/config/OperationConfig.js @@ -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,38 @@ const OperationConfig = { } ] }, + "To Table": { + module: "Default", + description: "Renders data as a table. Data can be split on different characters and output as a HTML or ASCII table with optional header row.", + inputType: "string", + outputType: "html", + highlight: false, + highlightReverse: false, + manualBake: false, + args: [ + { + name: "Select separator", + type: "populateOption", + value: ToTable.SEPARATORS, + target: 1 + }, + { + name: "Separator", + type: "string", + value: "," + }, + { + name: "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.

e.g. ce 93 ce b5 ce b9 ce ac 20 cf 83 ce bf cf 85 0a becomes the UTF-8 encoded string Γειά σου", diff --git a/src/core/config/modules/Default.js b/src/core/config/modules/Default.js index e5f070c..6b9f60f 100644 --- a/src/core/config/modules/Default.js +++ b/src/core/config/modules/Default.js @@ -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, diff --git a/src/core/operations/ToTable.js b/src/core/operations/ToTable.js new file mode 100755 index 0000000..3422719 --- /dev/null +++ b/src/core/operations/ToTable.js @@ -0,0 +1,189 @@ +import Utils from "../Utils.js"; + +/** + * ToTable operations. + * + * @author Mark Jones [github.com/justanothermark] + * @namespace + */ + const ToTable = { + /** + * @constant + * @default + */ + SEPARATORS: [ + {name: "Comma", value:","}, + {name: "Tab", value: escape("\t")}, + {name: "Pipe", value: "|"}, + {name: "Custom", value: ""} + ], + + /** + * @constant + * @default + */ + FORMATS: [ + 'ASCII', + 'HTML' + ], + + /** + * To Table operation. + * + * @param {string} input + * @param {Object[]} args + * @returns {html} + */ + runToTable: function (input, args) { + let separator = args[1]; + let firstRowHeader = args[2]; + let format = args[3]; + let tableData = []; + + // If the separator contains any tabs, convert them to tab characters. + separator = separator.replace('\\t', '\t'); + + // Process the input into a nested array of elements. + let rows = input.split('\n'); + rows.forEach(function(element) { + if (separator == '') { + tableData.push([element]); + } + else { + tableData.push(element.split(separator)); + } + }); + + // Render the data in the requested format. + let output = ''; + switch (format) { + case 'ASCII': + output = asciiOutput(tableData); + break; + + default: + output = htmlOutput(tableData); + break; + } + + return output; + + /** + * Outputs an array of data as an ASCII table. + * + * @param {Array[]} 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; + } + }); + }); + + // Calculate the complete row length. This is the length of the + // longest cell for each column plus 3 characters per cell + // (1 padding each side of the value and 1 for the cell border) + // plus 1 for the final cell border. + let rowLength = (longestCells.length * 3) + 1; + longestCells.forEach(function(celllongestCells) { + rowLength += celllongestCells; + }); + + // 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. + */ + function htmlOutput(tableData) { + // Start the HTML output with suitable classes for styling. + let output = ""; + + // If the first row is a header then put it in with "; + output += outputRow(row, 'th'); + output += ""; + } + + // Output the rest of the rows in the . + output += ""; + tableData.forEach(function(row, index) { + output += outputRow(row, 'td'); + }); + + // Close the body and table elements. + output += "
cells. + if (firstRowHeader) { + let row = tableData.shift(); + output += "
"; + return output; + + function outputRow(row, cellType) { + let output = ""; + row.forEach(function(cell) { + output += "<" + cellType + ">" + cell + ""; + }); + output += ""; + return output; + } + } + + return output; + } +}; + +export default ToTable; \ No newline at end of file