Documentation and formatting
This commit is contained in:
550
utils/tui.js
550
utils/tui.js
@@ -4,134 +4,131 @@
|
|||||||
* @enum {string}
|
* @enum {string}
|
||||||
*/
|
*/
|
||||||
export const FrameType = {
|
export const FrameType = {
|
||||||
/**
|
/**
|
||||||
* ╔════════════╗
|
* ╔════════════╗
|
||||||
* ║ Hello, TUI ║
|
* ║ Hello, TUI ║
|
||||||
* ╚════════════╝
|
* ╚════════════╝
|
||||||
*
|
*
|
||||||
* @type {string} Double-lined frame
|
* @type {string} Double-lined frame
|
||||||
*/
|
*/
|
||||||
Double: "Double",
|
Double: "Double",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ┌────────────┐
|
* ┌────────────┐
|
||||||
* │ Hello, TUI │
|
* │ Hello, TUI │
|
||||||
* └────────────┘
|
* └────────────┘
|
||||||
*
|
*
|
||||||
* @type {string} Single-lined frame
|
* @type {string} Single-lined frame
|
||||||
*/
|
*/
|
||||||
Single: "Single",
|
Single: "Single",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Hello, TUI
|
* Hello, TUI
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @type {string} Double-lined frame
|
* @type {string} Double-lined frame
|
||||||
*/
|
*/
|
||||||
Invisible: "Invisible",
|
Invisible: "Invisible",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ( )
|
* ( )
|
||||||
* ( Hello, TUI )
|
* ( Hello, TUI )
|
||||||
* ( )
|
* ( )
|
||||||
*
|
*
|
||||||
* @type {string} Double-lined frame
|
* @type {string} Double-lined frame
|
||||||
*/
|
*/
|
||||||
Parentheses: "Parentheses",
|
Parentheses: "Parentheses",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* +------------+
|
* +------------+
|
||||||
* | Hello, TUI |
|
* | Hello, TUI |
|
||||||
* +------------+
|
* +------------+
|
||||||
*
|
*
|
||||||
* @type {string} Double-lined frame
|
* @type {string} Double-lined frame
|
||||||
*/
|
*/
|
||||||
Basic: "Basic",
|
Basic: "Basic",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @protected
|
* @protected
|
||||||
* Default values for the common frame types.
|
* Default values for the common frame types.
|
||||||
*
|
*
|
||||||
* [north, south, east, west, northwest, northeast, southwest, southeast]
|
* [north, south, east, west, northwest, northeast, southwest, southeast]
|
||||||
*/
|
*/
|
||||||
values: {
|
values: {
|
||||||
Basic: "--||++++",
|
Basic: "--||++++",
|
||||||
Double: "══║║╔╗╚╝",
|
Double: "══║║╔╗╚╝",
|
||||||
Invisible: " ",
|
Invisible: " ",
|
||||||
Parentheses: " () ",
|
Parentheses: " () ",
|
||||||
Single: "──││┌┐└┘",
|
Single: "──││┌┐└┘",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export class FramingOptions {
|
export class FramingOptions {
|
||||||
/** @type {number=0} Vertical Padding; number of vertical whitespace (newlines) between the text and the frame. */
|
/** @type {number=0} Vertical Padding; number of vertical whitespace (newlines) between the text and the frame. */
|
||||||
vPadding = 0;
|
vPadding = 0;
|
||||||
|
|
||||||
/** @type {number=0} Margin ; number of newlines to to insert before and after the framed text */
|
/** @type {number=0} Margin ; number of newlines to to insert before and after the framed text */
|
||||||
vMargin = 0;
|
vMargin = 0;
|
||||||
|
|
||||||
/** @type {number=0} Horizontal Padding; number of whitespace characters to insert between the text and the sides of the frame. */
|
/** @type {number=0} Horizontal Padding; number of whitespace characters to insert between the text and the sides of the frame. */
|
||||||
hPadding = 0;
|
hPadding = 0;
|
||||||
|
|
||||||
/** @type {number=0} Margin ; number of newlines to to insert before and after the text, but inside the frame */
|
/** @type {number=0} Margin ; number of newlines to to insert before and after the text, but inside the frame */
|
||||||
hMargin = 0;
|
hMargin = 0;
|
||||||
|
|
||||||
/** @type {FrameType=FrameType.Double} Type of frame to put around the text */
|
/** @type {FrameType=FrameType.Double} Type of frame to put around the text */
|
||||||
frameType = FrameType.Double;
|
frameType = FrameType.Double;
|
||||||
|
|
||||||
/** @type {number=0} Pad each line to become at least this long */
|
/** @type {number=0} Pad each line to become at least this long */
|
||||||
minLineWidth = 0;
|
minLineWidth = 0;
|
||||||
|
|
||||||
// Light block: ░ (U+2591)
|
// Light block: ░ (U+2591)
|
||||||
// Medium block: ▒ (U+2592)
|
// Medium block: ▒ (U+2592)
|
||||||
// Dark block: ▓ (U+2593)
|
// Dark block: ▓ (U+2593)
|
||||||
// Solid block: █ (U+2588)
|
// Solid block: █ (U+2588)
|
||||||
/** @type {string} Single character to use as filler inside the frame. */
|
/** @type {string} Single character to use as filler inside the frame. */
|
||||||
paddingChar = " "; // character used for padding inside the frame.
|
paddingChar = " ";
|
||||||
|
|
||||||
/** @type {string} Single character to use as filler outside the frame. */
|
/** @type {string} Single character to use as filler outside the frame. */
|
||||||
marginChar = " ";
|
marginChar = " ";
|
||||||
|
|
||||||
/** @type {string} The 8 characters that make up the frame elements */
|
/** @type {string} The 8 characters that make up the frame elements */
|
||||||
frameChars = FrameType.values.Double;
|
frameChars = FrameType.values.Double;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {object} o
|
* @param {object} o
|
||||||
* @returns {FramingOptions}
|
* @returns {FramingOptions}
|
||||||
*/
|
*/
|
||||||
static fromObject(o) {
|
static fromObject(o) {
|
||||||
const result = new FramingOptions();
|
const result = new FramingOptions();
|
||||||
|
|
||||||
result.vPadding = Math.max(0, Number.parseInt(o.vPadding) || 0);
|
result.vPadding = Math.max(0, Number.parseInt(o.vPadding) || 0);
|
||||||
result.hPadding = Math.max(0, Number.parseInt(o.hPadding) || 0);
|
result.hPadding = Math.max(0, Number.parseInt(o.hPadding) || 0);
|
||||||
result.vMargin = Math.max(0, Number.parseInt(o.vMargin) || 0);
|
result.vMargin = Math.max(0, Number.parseInt(o.vMargin) || 0);
|
||||||
result.hMargin = Math.max(0, Number.parseInt(o.hMargin) || 0);
|
result.hMargin = Math.max(0, Number.parseInt(o.hMargin) || 0);
|
||||||
result.minLineWidth = Math.max(0, Number.parseInt(o.hMargin) || 0);
|
result.minLineWidth = Math.max(0, Number.parseInt(o.hMargin) || 0);
|
||||||
|
|
||||||
result.paddingChar = String(o.paddingChar || " ")[0] || " ";
|
result.paddingChar = String(o.paddingChar || " ")[0] || " ";
|
||||||
result.marginChar = String(o.marginChar || " ")[0] || " ";
|
result.marginChar = String(o.marginChar || " ")[0] || " ";
|
||||||
|
|
||||||
//
|
//
|
||||||
// Do we have custom and valid frame chars?
|
// Do we have custom and valid frame chars?
|
||||||
if (
|
if (typeof o.frameChars === "string" && o.frameChars.length === FrameType.values.Double.length) {
|
||||||
typeof o.frameChars === "string" &&
|
result.frameChars = o.frameChars;
|
||||||
o.frameChars.length === FrameType.values.Double.length
|
|
||||||
) {
|
|
||||||
result.frameChars = o.frameChars;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// do we have document frame type instead ?
|
// do we have document frame type instead ?
|
||||||
} else if (o.frameType && FrameType.hasOwnProperty(o.frameType)) {
|
} else if (o.frameType && FrameType.hasOwnProperty(o.frameType)) {
|
||||||
result.frameChars = FrameType.values[o.frameType];
|
result.frameChars = FrameType.values[o.frameType];
|
||||||
|
|
||||||
// Fall back to using "Double" frame
|
// Fall back to using "Double" frame
|
||||||
} else {
|
} else {
|
||||||
result.frameChars = FrameType.values.Double;
|
result.frameChars = FrameType.values.Double;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -139,198 +136,187 @@ export class FramingOptions {
|
|||||||
* @param {FramingOptions} options
|
* @param {FramingOptions} options
|
||||||
*/
|
*/
|
||||||
export function frameText(text, options) {
|
export function frameText(text, options) {
|
||||||
if (!options) {
|
if (!options) {
|
||||||
options = new FramingOptions();
|
options = new FramingOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(options instanceof FramingOptions)) {
|
if (!(options instanceof FramingOptions)) {
|
||||||
options = FramingOptions.fromObject(options);
|
options = FramingOptions.fromObject(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
// There is a point to this; each element in the array may contain newlines,
|
// There is a point to this; each element in the array may contain newlines,
|
||||||
// so we have to combine everything into a long text and then split into
|
// so we have to combine everything into a long text and then split into
|
||||||
// individual lines afterwards.
|
// individual lines afterwards.
|
||||||
if (Array.isArray(text)) {
|
if (Array.isArray(text)) {
|
||||||
text = text.join("\n");
|
text = text.join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof text !== "string") {
|
if (typeof text !== "string") {
|
||||||
console.debug(text);
|
console.debug(text);
|
||||||
throw new Error(
|
throw new Error(`text argument was neither an array or a string, it was a ${typeof text}`);
|
||||||
`text argument was neither an array or a string, it was a ${typeof text}`,
|
}
|
||||||
|
|
||||||
|
/** @type {string[]} */
|
||||||
|
const lines = text.split("\n");
|
||||||
|
|
||||||
|
const innerLineLength = Math.max(
|
||||||
|
lines.reduce((accumulator, currentLine) => {
|
||||||
|
if (currentLine.length > accumulator) {
|
||||||
|
return currentLine.length;
|
||||||
|
}
|
||||||
|
return accumulator;
|
||||||
|
}, 0),
|
||||||
|
options.minLineWidth,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
/** @type {string[]} */
|
const frameThickness = 1; // always 1 for now.
|
||||||
const lines = text.split("\n");
|
|
||||||
|
|
||||||
const innerLineLength = Math.max(
|
const outerLineLength = 0 + innerLineLength + frameThickness * 2 + options.hPadding * 2 + options.hMargin * 2;
|
||||||
lines.reduce((accumulator, currentLine) => {
|
|
||||||
if (currentLine.length > accumulator) {
|
|
||||||
return currentLine.length;
|
|
||||||
}
|
|
||||||
return accumulator;
|
|
||||||
}, 0),
|
|
||||||
options.minLineWidth,
|
|
||||||
);
|
|
||||||
|
|
||||||
const frameThickness = 1; // always 1 for now.
|
// get the frame characters from the frameType.
|
||||||
|
let [
|
||||||
|
fNorth, // horizontal frame top lines
|
||||||
|
fSouth, // horizontal frame bottom lines
|
||||||
|
fWest, // vertical frame lines on the left side
|
||||||
|
fEast, // vertical frame lines on the right side
|
||||||
|
fNorthWest, // upper left frame corner
|
||||||
|
fNorthEast, // upper right frame corner
|
||||||
|
fSouthWest, // lower left frame corner
|
||||||
|
fSouthEast, // lower right frame corner
|
||||||
|
] = options.frameChars.split("");
|
||||||
|
if (fNorth === "§") {
|
||||||
|
fNorth = "";
|
||||||
|
}
|
||||||
|
if (fSouth === "§") {
|
||||||
|
fSouth = "";
|
||||||
|
}
|
||||||
|
if (fEast === "§") {
|
||||||
|
fEast = "";
|
||||||
|
}
|
||||||
|
if (fWest === "§") {
|
||||||
|
fWest = "";
|
||||||
|
}
|
||||||
|
if (fNorthEast === "§") {
|
||||||
|
fNorthEast = "";
|
||||||
|
}
|
||||||
|
if (fSouthEast === "§") {
|
||||||
|
fSouthEast = "";
|
||||||
|
}
|
||||||
|
if (fNorthWest === "§") {
|
||||||
|
fNorthWest = "";
|
||||||
|
}
|
||||||
|
if (fSouthWest === "§") {
|
||||||
|
fSouthWest = "";
|
||||||
|
}
|
||||||
|
|
||||||
const outerLineLength =
|
let output = "";
|
||||||
0 +
|
|
||||||
innerLineLength +
|
|
||||||
frameThickness * 2 +
|
|
||||||
options.hPadding * 2 +
|
|
||||||
options.hMargin * 2;
|
|
||||||
|
|
||||||
// get the frame characters from the frameType.
|
//
|
||||||
let [
|
// GENERATE THE MARGIN SPACE ABOVE THE FRAMED MsgType.TEXT
|
||||||
fNorth, // horizontal frame top lines
|
//
|
||||||
fSouth, // horizontal frame bottom lines
|
// ( we insert space characters even though )
|
||||||
fWest, // vertical frame lines on the left side
|
// ( they wouldn't normally be visible. But )
|
||||||
fEast, // vertical frame lines on the right side
|
// ( Some fonts might allow us to see blank )
|
||||||
fNorthWest, // upper left frame corner
|
// ( space, and what if we want to nest many )
|
||||||
fNorthEast, // upper right frame corner
|
// ( frames inside each other? )
|
||||||
fSouthWest, // lower left frame corner
|
//
|
||||||
fSouthEast, // lower right frame corner
|
output += (options.marginChar.repeat(outerLineLength) + "\n").repeat(options.vMargin);
|
||||||
] = options.frameChars.split("");
|
|
||||||
if (fNorth === "§") {
|
|
||||||
fNorth = "";
|
|
||||||
}
|
|
||||||
if (fSouth === "§") {
|
|
||||||
fSouth = "";
|
|
||||||
}
|
|
||||||
if (fEast === "§") {
|
|
||||||
fEast = "";
|
|
||||||
}
|
|
||||||
if (fWest === "§") {
|
|
||||||
fWest = "";
|
|
||||||
}
|
|
||||||
if (fNorthEast === "§") {
|
|
||||||
fNorthEast = "";
|
|
||||||
}
|
|
||||||
if (fSouthEast === "§") {
|
|
||||||
fSouthEast = "";
|
|
||||||
}
|
|
||||||
if (fNorthWest === "§") {
|
|
||||||
fNorthWest = "";
|
|
||||||
}
|
|
||||||
if (fSouthWest === "§") {
|
|
||||||
fSouthWest = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
let output = "";
|
//
|
||||||
|
// GENERATE THE TOP PART OF THE FRAME
|
||||||
//
|
// ╔════════════╗
|
||||||
// GENERATE THE MARGIN SPACE ABOVE THE FRAMED MsgType.TEXT
|
//
|
||||||
//
|
//
|
||||||
// ( we insert space characters even though )
|
|
||||||
// ( they wouldn't normally be visible. But )
|
|
||||||
// ( Some fonts might allow us to see blank )
|
|
||||||
// ( space, and what if we want to nest many )
|
|
||||||
// ( frames inside each other? )
|
|
||||||
//
|
|
||||||
output += (options.marginChar.repeat(outerLineLength) + "\n").repeat(
|
|
||||||
options.vMargin,
|
|
||||||
);
|
|
||||||
|
|
||||||
//
|
|
||||||
// GENERATE THE TOP PART OF THE FRAME
|
|
||||||
// ╔════════════╗
|
|
||||||
//
|
|
||||||
//
|
|
||||||
output +=
|
|
||||||
"" + // Make sure JS knows we're adding a string.
|
|
||||||
options.marginChar.repeat(options.hMargin) + // the margin before the frame starts
|
|
||||||
fNorthWest + // northwest frame corner
|
|
||||||
fNorth.repeat(innerLineLength + options.hPadding * 2) + // the long horizontal frame top bar
|
|
||||||
fNorthEast + // northeast frame corner
|
|
||||||
options.marginChar.repeat(options.hMargin) + // the margin after the frame ends
|
|
||||||
"\n";
|
|
||||||
//
|
|
||||||
// GENERATE UPPER PADDING
|
|
||||||
//
|
|
||||||
// ║ ║
|
|
||||||
//
|
|
||||||
// (the blank lines within the frame and above the text)
|
|
||||||
output += (
|
|
||||||
options.marginChar.repeat(options.hMargin) +
|
|
||||||
fWest +
|
|
||||||
options.paddingChar.repeat(innerLineLength + options.hPadding * 2) +
|
|
||||||
fEast +
|
|
||||||
options.marginChar.repeat(options.hMargin) +
|
|
||||||
"\n"
|
|
||||||
).repeat(options.vPadding);
|
|
||||||
|
|
||||||
//
|
|
||||||
// GENERATE FRAMED MsgType.TEXT SEGMENT
|
|
||||||
//
|
|
||||||
// ║ My pretty ║
|
|
||||||
// ║ text here ║
|
|
||||||
//
|
|
||||||
// ( this could be done with a reduce() )
|
|
||||||
//
|
|
||||||
for (const line of lines) {
|
|
||||||
output +=
|
output +=
|
||||||
"" + // Make sure JS knows we're adding a string.
|
"" + // Make sure JS knows we're adding a string.
|
||||||
options.marginChar.repeat(options.hMargin) + // margin before frame
|
options.marginChar.repeat(options.hMargin) + // the margin before the frame starts
|
||||||
fWest + // vertical frame char
|
fNorthWest + // northwest frame corner
|
||||||
options.paddingChar.repeat(options.hPadding) + // padding before text
|
fNorth.repeat(innerLineLength + options.hPadding * 2) + // the long horizontal frame top bar
|
||||||
line.padEnd(innerLineLength, " ") + // The actual text. Pad it with normal space character, NOT custom space.
|
fNorthEast + // northeast frame corner
|
||||||
options.paddingChar.repeat(options.hPadding) + // padding after text
|
options.marginChar.repeat(options.hMargin) + // the margin after the frame ends
|
||||||
fEast + // vertical frame bar
|
"\n";
|
||||||
options.marginChar.repeat(options.hMargin) + // margin after frame
|
//
|
||||||
"\n";
|
// GENERATE UPPER PADDING
|
||||||
}
|
//
|
||||||
|
// ║ ║
|
||||||
|
//
|
||||||
|
// (the blank lines within the frame and above the text)
|
||||||
|
output += (
|
||||||
|
options.marginChar.repeat(options.hMargin) +
|
||||||
|
fWest +
|
||||||
|
options.paddingChar.repeat(innerLineLength + options.hPadding * 2) +
|
||||||
|
fEast +
|
||||||
|
options.marginChar.repeat(options.hMargin) +
|
||||||
|
"\n"
|
||||||
|
).repeat(options.vPadding);
|
||||||
|
|
||||||
//
|
//
|
||||||
// GENERATE LOWER PADDING
|
// GENERATE FRAMED MsgType.TEXT SEGMENT
|
||||||
//
|
//
|
||||||
// ║ ║
|
// ║ My pretty ║
|
||||||
//
|
// ║ text here ║
|
||||||
// ( the blank lines within the )
|
//
|
||||||
// ( frame and below the text )
|
// ( this could be done with a reduce() )
|
||||||
//
|
//
|
||||||
// ( this code is a direct )
|
for (const line of lines) {
|
||||||
// ( repeat of the code that )
|
output +=
|
||||||
// ( generates top padding )
|
"" + // Make sure JS knows we're adding a string.
|
||||||
output += (
|
options.marginChar.repeat(options.hMargin) + // margin before frame
|
||||||
options.marginChar.repeat(options.hMargin) +
|
fWest + // vertical frame char
|
||||||
fWest +
|
options.paddingChar.repeat(options.hPadding) + // padding before text
|
||||||
options.paddingChar.repeat(innerLineLength + options.hPadding * 2) +
|
line.padEnd(innerLineLength, " ") + // The actual text. Pad it with normal space character, NOT custom space.
|
||||||
fEast +
|
options.paddingChar.repeat(options.hPadding) + // padding after text
|
||||||
options.marginChar.repeat(options.hMargin) +
|
fEast + // vertical frame bar
|
||||||
"\n"
|
options.marginChar.repeat(options.hMargin) + // margin after frame
|
||||||
).repeat(options.vPadding);
|
"\n";
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// GENERATE THE BOTTOM PART OF THE FRAME
|
// GENERATE LOWER PADDING
|
||||||
//
|
//
|
||||||
// ╚════════════╝
|
// ║ ║
|
||||||
//
|
//
|
||||||
output +=
|
// ( the blank lines within the )
|
||||||
"" + // Make sure JS knows we're adding a string.
|
// ( frame and below the text )
|
||||||
options.marginChar.repeat(options.hMargin) + // the margin before the frame starts
|
//
|
||||||
fSouthWest + // northwest frame corner
|
// ( this code is a direct )
|
||||||
fSouth.repeat(innerLineLength + options.hPadding * 2) + // the long horizontal frame top bar
|
// ( repeat of the code that )
|
||||||
fSouthEast + // northeast frame corner
|
// ( generates top padding )
|
||||||
options.marginChar.repeat(options.hMargin) + // the margin after the frame starts
|
output += (
|
||||||
"\n";
|
options.marginChar.repeat(options.hMargin) +
|
||||||
|
fWest +
|
||||||
|
options.paddingChar.repeat(innerLineLength + options.hPadding * 2) +
|
||||||
|
fEast +
|
||||||
|
options.marginChar.repeat(options.hMargin) +
|
||||||
|
"\n"
|
||||||
|
).repeat(options.vPadding);
|
||||||
|
|
||||||
//
|
//
|
||||||
// GENERATE THE MARGIN SPACE BELOW THE FRAMED MsgType.TEXT
|
// GENERATE THE BOTTOM PART OF THE FRAME
|
||||||
//
|
//
|
||||||
// ( we insert space characters even though )
|
// ╚════════════╝
|
||||||
// ( they wouldn't normally be visible. But )
|
//
|
||||||
// ( Some fonts might allow us to see blank )
|
output +=
|
||||||
// ( space, and what if we want to nest many )
|
"" + // Make sure JS knows we're adding a string.
|
||||||
// ( frames inside each other? )
|
options.marginChar.repeat(options.hMargin) + // the margin before the frame starts
|
||||||
//
|
fSouthWest + // northwest frame corner
|
||||||
output += (options.marginChar.repeat(outerLineLength) + "\n").repeat(
|
fSouth.repeat(innerLineLength + options.hPadding * 2) + // the long horizontal frame top bar
|
||||||
options.vMargin,
|
fSouthEast + // northeast frame corner
|
||||||
);
|
options.marginChar.repeat(options.hMargin) + // the margin after the frame starts
|
||||||
|
"\n";
|
||||||
|
|
||||||
return output;
|
//
|
||||||
|
// GENERATE THE MARGIN SPACE BELOW THE FRAMED MsgType.TEXT
|
||||||
|
//
|
||||||
|
// ( we insert space characters even though )
|
||||||
|
// ( they wouldn't normally be visible. But )
|
||||||
|
// ( Some fonts might allow us to see blank )
|
||||||
|
// ( space, and what if we want to nest many )
|
||||||
|
// ( frames inside each other? )
|
||||||
|
//
|
||||||
|
output += (options.marginChar.repeat(outerLineLength) + "\n").repeat(options.vMargin);
|
||||||
|
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow this script to be run directly from node as well as being included!
|
// Allow this script to be run directly from node as well as being included!
|
||||||
|
|||||||
Reference in New Issue
Block a user