49aa969572
- updates html background -update instructions.txt
1480 lines
42 KiB
JavaScript
1480 lines
42 KiB
JavaScript
/*
|
|
FIGlet.js (a FIGDriver for FIGlet fonts)
|
|
Written by https://github.com/patorjk/figlet.js/graphs/contributors
|
|
Originally Written For: http://patorjk.com/software/taag/
|
|
License: MIT (with this header staying intact)
|
|
|
|
This JavaScript code aims to fully implement the FIGlet spec.
|
|
Full FIGlet spec: http://patorjk.com/software/taag/docs/figfont.txt
|
|
|
|
FIGlet fonts are actually kind of complex, which is why you will see
|
|
a lot of code about parsing and interpreting rules. The actual generation
|
|
code is pretty simple and is done near the bottom of the code.
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
const figlet = (() => {
|
|
// ---------------------------------------------------------------------
|
|
// Private static variables
|
|
|
|
const FULL_WIDTH = 0,
|
|
FITTING = 1,
|
|
SMUSHING = 2,
|
|
CONTROLLED_SMUSHING = 3;
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Variable that will hold information about the fonts
|
|
|
|
const figFonts = {}; // What stores all of the FIGlet font data
|
|
const figDefaults = {
|
|
font: "Standard",
|
|
fontPath: "./fonts",
|
|
};
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Private static methods
|
|
|
|
/*
|
|
This method takes in the oldLayout and newLayout data from the FIGfont header file and returns
|
|
the layout information.
|
|
*/
|
|
function getSmushingRules(oldLayout, newLayout) {
|
|
let rules = {};
|
|
let val, index, len, code;
|
|
let codes = [
|
|
[16384, "vLayout", SMUSHING],
|
|
[8192, "vLayout", FITTING],
|
|
[4096, "vRule5", true],
|
|
[2048, "vRule4", true],
|
|
[1024, "vRule3", true],
|
|
[512, "vRule2", true],
|
|
[256, "vRule1", true],
|
|
[128, "hLayout", SMUSHING],
|
|
[64, "hLayout", FITTING],
|
|
[32, "hRule6", true],
|
|
[16, "hRule5", true],
|
|
[8, "hRule4", true],
|
|
[4, "hRule3", true],
|
|
[2, "hRule2", true],
|
|
[1, "hRule1", true],
|
|
];
|
|
|
|
val = newLayout !== null ? newLayout : oldLayout;
|
|
index = 0;
|
|
len = codes.length;
|
|
while (index < len) {
|
|
code = codes[index];
|
|
if (val >= code[0]) {
|
|
val = val - code[0];
|
|
rules[code[1]] =
|
|
typeof rules[code[1]] === "undefined" ? code[2] : rules[code[1]];
|
|
} else if (code[1] !== "vLayout" && code[1] !== "hLayout") {
|
|
rules[code[1]] = false;
|
|
}
|
|
index++;
|
|
}
|
|
|
|
if (typeof rules["hLayout"] === "undefined") {
|
|
if (oldLayout === 0) {
|
|
rules["hLayout"] = FITTING;
|
|
} else if (oldLayout === -1) {
|
|
rules["hLayout"] = FULL_WIDTH;
|
|
} else {
|
|
if (
|
|
rules["hRule1"] ||
|
|
rules["hRule2"] ||
|
|
rules["hRule3"] ||
|
|
rules["hRule4"] ||
|
|
rules["hRule5"] ||
|
|
rules["hRule6"]
|
|
) {
|
|
rules["hLayout"] = CONTROLLED_SMUSHING;
|
|
} else {
|
|
rules["hLayout"] = SMUSHING;
|
|
}
|
|
}
|
|
} else if (rules["hLayout"] === SMUSHING) {
|
|
if (
|
|
rules["hRule1"] ||
|
|
rules["hRule2"] ||
|
|
rules["hRule3"] ||
|
|
rules["hRule4"] ||
|
|
rules["hRule5"] ||
|
|
rules["hRule6"]
|
|
) {
|
|
rules["hLayout"] = CONTROLLED_SMUSHING;
|
|
}
|
|
}
|
|
|
|
if (typeof rules["vLayout"] === "undefined") {
|
|
if (
|
|
rules["vRule1"] ||
|
|
rules["vRule2"] ||
|
|
rules["vRule3"] ||
|
|
rules["vRule4"] ||
|
|
rules["vRule5"]
|
|
) {
|
|
rules["vLayout"] = CONTROLLED_SMUSHING;
|
|
} else {
|
|
rules["vLayout"] = FULL_WIDTH;
|
|
}
|
|
} else if (rules["vLayout"] === SMUSHING) {
|
|
if (
|
|
rules["vRule1"] ||
|
|
rules["vRule2"] ||
|
|
rules["vRule3"] ||
|
|
rules["vRule4"] ||
|
|
rules["vRule5"]
|
|
) {
|
|
rules["vLayout"] = CONTROLLED_SMUSHING;
|
|
}
|
|
}
|
|
|
|
return rules;
|
|
}
|
|
|
|
/* The [vh]Rule[1-6]_Smush functions return the smushed character OR false if the two characters can't be smushed */
|
|
|
|
/*
|
|
Rule 1: EQUAL CHARACTER SMUSHING (code value 1)
|
|
|
|
Two sub-characters are smushed into a single sub-character
|
|
if they are the same. This rule does not smush
|
|
hardblanks. (See rule 6 on hardblanks below)
|
|
*/
|
|
function hRule1_Smush(ch1, ch2, hardBlank) {
|
|
if (ch1 === ch2 && ch1 !== hardBlank) {
|
|
return ch1;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Rule 2: UNDERSCORE SMUSHING (code value 2)
|
|
|
|
An underscore ("_") will be replaced by any of: "|", "/",
|
|
"\", "[", "]", "{", "}", "(", ")", "<" or ">".
|
|
*/
|
|
function hRule2_Smush(ch1, ch2) {
|
|
let rule2Str = "|/\\[]{}()<>";
|
|
if (ch1 === "_") {
|
|
if (rule2Str.indexOf(ch2) !== -1) {
|
|
return ch2;
|
|
}
|
|
} else if (ch2 === "_") {
|
|
if (rule2Str.indexOf(ch1) !== -1) {
|
|
return ch1;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Rule 3: HIERARCHY SMUSHING (code value 4)
|
|
|
|
A hierarchy of six classes is used: "|", "/\", "[]", "{}",
|
|
"()", and "<>". When two smushing sub-characters are
|
|
from different classes, the one from the latter class
|
|
will be used.
|
|
*/
|
|
function hRule3_Smush(ch1, ch2) {
|
|
let rule3Classes = "| /\\ [] {} () <>";
|
|
let r3_pos1 = rule3Classes.indexOf(ch1);
|
|
let r3_pos2 = rule3Classes.indexOf(ch2);
|
|
if (r3_pos1 !== -1 && r3_pos2 !== -1) {
|
|
if (r3_pos1 !== r3_pos2 && Math.abs(r3_pos1 - r3_pos2) !== 1) {
|
|
const startPos = Math.max(r3_pos1, r3_pos2);
|
|
const endPos = startPos + 1;
|
|
return rule3Classes.substring(startPos, endPos);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Rule 4: OPPOSITE PAIR SMUSHING (code value 8)
|
|
|
|
Smushes opposing brackets ("[]" or "]["), braces ("{}" or
|
|
"}{") and parentheses ("()" or ")(") together, replacing
|
|
any such pair with a vertical bar ("|").
|
|
*/
|
|
function hRule4_Smush(ch1, ch2) {
|
|
let rule4Str = "[] {} ()";
|
|
let r4_pos1 = rule4Str.indexOf(ch1);
|
|
let r4_pos2 = rule4Str.indexOf(ch2);
|
|
if (r4_pos1 !== -1 && r4_pos2 !== -1) {
|
|
if (Math.abs(r4_pos1 - r4_pos2) <= 1) {
|
|
return "|";
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Rule 5: BIG X SMUSHING (code value 16)
|
|
|
|
Smushes "/\" into "|", "\/" into "Y", and "><" into "X".
|
|
Note that "<>" is not smushed in any way by this rule.
|
|
The name "BIG X" is historical; originally all three pairs
|
|
were smushed into "X".
|
|
*/
|
|
function hRule5_Smush(ch1, ch2) {
|
|
let rule5Str = "/\\ \\/ ><";
|
|
let rule5Hash = { 0: "|", 3: "Y", 6: "X" };
|
|
let r5_pos1 = rule5Str.indexOf(ch1);
|
|
let r5_pos2 = rule5Str.indexOf(ch2);
|
|
if (r5_pos1 !== -1 && r5_pos2 !== -1) {
|
|
if (r5_pos2 - r5_pos1 === 1) {
|
|
return rule5Hash[r5_pos1];
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Rule 6: HARDBLANK SMUSHING (code value 32)
|
|
|
|
Smushes two hardblanks together, replacing them with a
|
|
single hardblank. (See "Hardblanks" below.)
|
|
*/
|
|
function hRule6_Smush(ch1, ch2, hardBlank) {
|
|
if (ch1 === hardBlank && ch2 === hardBlank) {
|
|
return hardBlank;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Rule 1: EQUAL CHARACTER SMUSHING (code value 256)
|
|
|
|
Same as horizontal smushing rule 1.
|
|
*/
|
|
function vRule1_Smush(ch1, ch2) {
|
|
if (ch1 === ch2) {
|
|
return ch1;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Rule 2: UNDERSCORE SMUSHING (code value 512)
|
|
|
|
Same as horizontal smushing rule 2.
|
|
*/
|
|
function vRule2_Smush(ch1, ch2) {
|
|
let rule2Str = "|/\\[]{}()<>";
|
|
if (ch1 === "_") {
|
|
if (rule2Str.indexOf(ch2) !== -1) {
|
|
return ch2;
|
|
}
|
|
} else if (ch2 === "_") {
|
|
if (rule2Str.indexOf(ch1) !== -1) {
|
|
return ch1;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Rule 3: HIERARCHY SMUSHING (code value 1024)
|
|
|
|
Same as horizontal smushing rule 3.
|
|
*/
|
|
function vRule3_Smush(ch1, ch2) {
|
|
let rule3Classes = "| /\\ [] {} () <>";
|
|
let r3_pos1 = rule3Classes.indexOf(ch1);
|
|
let r3_pos2 = rule3Classes.indexOf(ch2);
|
|
if (r3_pos1 !== -1 && r3_pos2 !== -1) {
|
|
if (r3_pos1 !== r3_pos2 && Math.abs(r3_pos1 - r3_pos2) !== 1) {
|
|
const startPos = Math.max(r3_pos1, r3_pos2);
|
|
const endPos = startPos + 1;
|
|
return rule3Classes.substring(startPos, endPos);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Rule 4: HORIZONTAL LINE SMUSHING (code value 2048)
|
|
|
|
Smushes stacked pairs of "-" and "_", replacing them with
|
|
a single "=" sub-character. It does not matter which is
|
|
found above the other. Note that vertical smushing rule 1
|
|
will smush IDENTICAL pairs of horizontal lines, while this
|
|
rule smushes horizontal lines consisting of DIFFERENT
|
|
sub-characters.
|
|
*/
|
|
function vRule4_Smush(ch1, ch2) {
|
|
if ((ch1 === "-" && ch2 === "_") || (ch1 === "_" && ch2 === "-")) {
|
|
return "=";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Rule 5: VERTICAL LINE SUPERSMUSHING (code value 4096)
|
|
|
|
This one rule is different from all others, in that it
|
|
"supersmushes" vertical lines consisting of several
|
|
vertical bars ("|"). This creates the illusion that
|
|
FIGcharacters have slid vertically against each other.
|
|
Supersmushing continues until any sub-characters other
|
|
than "|" would have to be smushed. Supersmushing can
|
|
produce impressive results, but it is seldom possible,
|
|
since other sub-characters would usually have to be
|
|
considered for smushing as soon as any such stacked
|
|
vertical lines are encountered.
|
|
*/
|
|
function vRule5_Smush(ch1, ch2) {
|
|
if (ch1 === "|" && ch2 === "|") {
|
|
return "|";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Universal smushing simply overrides the sub-character from the
|
|
earlier FIGcharacter with the sub-character from the later
|
|
FIGcharacter. This produces an "overlapping" effect with some
|
|
FIGfonts, wherin the latter FIGcharacter may appear to be "in
|
|
front".
|
|
*/
|
|
function uni_Smush(ch1, ch2, hardBlank) {
|
|
if (ch2 === " " || ch2 === "") {
|
|
return ch1;
|
|
} else if (ch2 === hardBlank && ch1 !== " ") {
|
|
return ch1;
|
|
} else {
|
|
return ch2;
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// main vertical smush routines (excluding rules)
|
|
|
|
/*
|
|
txt1 - A line of text
|
|
txt2 - A line of text
|
|
opts - FIGlet options array
|
|
|
|
About: Takes in two lines of text and returns one of the following:
|
|
"valid" - These lines can be smushed together given the current smushing rules
|
|
"end" - The lines can be smushed, but we're at a stopping point
|
|
"invalid" - The two lines cannot be smushed together
|
|
*/
|
|
function canVerticalSmush(txt1, txt2, opts) {
|
|
if (opts.fittingRules.vLayout === FULL_WIDTH) {
|
|
return "invalid";
|
|
}
|
|
let ii,
|
|
len = Math.min(txt1.length, txt2.length),
|
|
ch1,
|
|
ch2,
|
|
endSmush = false,
|
|
validSmush;
|
|
if (len === 0) {
|
|
return "invalid";
|
|
}
|
|
|
|
for (ii = 0; ii < len; ii++) {
|
|
ch1 = txt1.substring(ii, ii + 1);
|
|
ch2 = txt2.substring(ii, ii + 1);
|
|
if (ch1 !== " " && ch2 !== " ") {
|
|
if (opts.fittingRules.vLayout === FITTING) {
|
|
return "invalid";
|
|
} else if (opts.fittingRules.vLayout === SMUSHING) {
|
|
return "end";
|
|
} else {
|
|
if (vRule5_Smush(ch1, ch2)) {
|
|
endSmush = endSmush || false;
|
|
continue;
|
|
} // rule 5 allow for "super" smushing, but only if we're not already ending this smush
|
|
validSmush = false;
|
|
validSmush = opts.fittingRules.vRule1
|
|
? vRule1_Smush(ch1, ch2)
|
|
: validSmush;
|
|
validSmush =
|
|
!validSmush && opts.fittingRules.vRule2
|
|
? vRule2_Smush(ch1, ch2)
|
|
: validSmush;
|
|
validSmush =
|
|
!validSmush && opts.fittingRules.vRule3
|
|
? vRule3_Smush(ch1, ch2)
|
|
: validSmush;
|
|
validSmush =
|
|
!validSmush && opts.fittingRules.vRule4
|
|
? vRule4_Smush(ch1, ch2)
|
|
: validSmush;
|
|
endSmush = true;
|
|
if (!validSmush) {
|
|
return "invalid";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (endSmush) {
|
|
return "end";
|
|
} else {
|
|
return "valid";
|
|
}
|
|
}
|
|
|
|
function getVerticalSmushDist(lines1, lines2, opts) {
|
|
let maxDist = lines1.length;
|
|
let len1 = lines1.length;
|
|
let len2 = lines2.length;
|
|
let subLines1, subLines2, slen;
|
|
let curDist = 1;
|
|
let ii, ret, result;
|
|
while (curDist <= maxDist) {
|
|
subLines1 = lines1.slice(Math.max(0, len1 - curDist), len1);
|
|
subLines2 = lines2.slice(0, Math.min(maxDist, curDist));
|
|
|
|
slen = subLines2.length; //TODO:check this
|
|
result = "";
|
|
for (ii = 0; ii < slen; ii++) {
|
|
ret = canVerticalSmush(subLines1[ii], subLines2[ii], opts);
|
|
if (ret === "end") {
|
|
result = ret;
|
|
} else if (ret === "invalid") {
|
|
result = ret;
|
|
break;
|
|
} else {
|
|
if (result === "") {
|
|
result = "valid";
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result === "invalid") {
|
|
curDist--;
|
|
break;
|
|
}
|
|
if (result === "end") {
|
|
break;
|
|
}
|
|
if (result === "valid") {
|
|
curDist++;
|
|
}
|
|
}
|
|
|
|
return Math.min(maxDist, curDist);
|
|
}
|
|
|
|
function verticallySmushLines(line1, line2, opts) {
|
|
let ii,
|
|
len = Math.min(line1.length, line2.length);
|
|
let ch1,
|
|
ch2,
|
|
result = "",
|
|
validSmush;
|
|
|
|
for (ii = 0; ii < len; ii++) {
|
|
ch1 = line1.substring(ii, ii + 1);
|
|
ch2 = line2.substring(ii, ii + 1);
|
|
if (ch1 !== " " && ch2 !== " ") {
|
|
if (opts.fittingRules.vLayout === FITTING) {
|
|
result += uni_Smush(ch1, ch2);
|
|
} else if (opts.fittingRules.vLayout === SMUSHING) {
|
|
result += uni_Smush(ch1, ch2);
|
|
} else {
|
|
validSmush = false;
|
|
validSmush = opts.fittingRules.vRule5
|
|
? vRule5_Smush(ch1, ch2)
|
|
: validSmush;
|
|
validSmush =
|
|
!validSmush && opts.fittingRules.vRule1
|
|
? vRule1_Smush(ch1, ch2)
|
|
: validSmush;
|
|
validSmush =
|
|
!validSmush && opts.fittingRules.vRule2
|
|
? vRule2_Smush(ch1, ch2)
|
|
: validSmush;
|
|
validSmush =
|
|
!validSmush && opts.fittingRules.vRule3
|
|
? vRule3_Smush(ch1, ch2)
|
|
: validSmush;
|
|
validSmush =
|
|
!validSmush && opts.fittingRules.vRule4
|
|
? vRule4_Smush(ch1, ch2)
|
|
: validSmush;
|
|
result += validSmush;
|
|
}
|
|
} else {
|
|
result += uni_Smush(ch1, ch2);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function verticalSmush(lines1, lines2, overlap, opts) {
|
|
let len1 = lines1.length;
|
|
let len2 = lines2.length;
|
|
let piece1 = lines1.slice(0, Math.max(0, len1 - overlap));
|
|
let piece2_1 = lines1.slice(Math.max(0, len1 - overlap), len1);
|
|
let piece2_2 = lines2.slice(0, Math.min(overlap, len2));
|
|
let ii,
|
|
len,
|
|
line,
|
|
piece2 = [],
|
|
piece3,
|
|
result = [];
|
|
|
|
len = piece2_1.length;
|
|
for (ii = 0; ii < len; ii++) {
|
|
if (ii >= len2) {
|
|
line = piece2_1[ii];
|
|
} else {
|
|
line = verticallySmushLines(piece2_1[ii], piece2_2[ii], opts);
|
|
}
|
|
piece2.push(line);
|
|
}
|
|
|
|
piece3 = lines2.slice(Math.min(overlap, len2), len2);
|
|
|
|
return result.concat(piece1, piece2, piece3);
|
|
}
|
|
|
|
function padLines(lines, numSpaces) {
|
|
let ii,
|
|
len = lines.length,
|
|
padding = "";
|
|
for (ii = 0; ii < numSpaces; ii++) {
|
|
padding += " ";
|
|
}
|
|
for (ii = 0; ii < len; ii++) {
|
|
lines[ii] += padding;
|
|
}
|
|
}
|
|
|
|
function smushVerticalFigLines(output, lines, opts) {
|
|
let len1 = output[0].length;
|
|
let len2 = lines[0].length;
|
|
let overlap;
|
|
if (len1 > len2) {
|
|
padLines(lines, len1 - len2);
|
|
} else if (len2 > len1) {
|
|
padLines(output, len2 - len1);
|
|
}
|
|
overlap = getVerticalSmushDist(output, lines, opts);
|
|
return verticalSmush(output, lines, overlap, opts);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Main horizontal smush routines (excluding rules)
|
|
|
|
function getHorizontalSmushLength(txt1, txt2, opts) {
|
|
if (opts.fittingRules.hLayout === FULL_WIDTH) {
|
|
return 0;
|
|
}
|
|
let ii,
|
|
len1 = txt1.length,
|
|
len2 = txt2.length;
|
|
let maxDist = len1;
|
|
let curDist = 1;
|
|
let breakAfter = false;
|
|
let validSmush = false;
|
|
let seg1, seg2, ch1, ch2;
|
|
if (len1 === 0) {
|
|
return 0;
|
|
}
|
|
|
|
distCal: while (curDist <= maxDist) {
|
|
const seg1StartPos = len1 - curDist;
|
|
seg1 = txt1.substring(seg1StartPos, seg1StartPos + curDist);
|
|
seg2 = txt2.substring(0, Math.min(curDist, len2));
|
|
for (ii = 0; ii < Math.min(curDist, len2); ii++) {
|
|
ch1 = seg1.substring(ii, ii + 1);
|
|
ch2 = seg2.substring(ii, ii + 1);
|
|
if (ch1 !== " " && ch2 !== " ") {
|
|
if (opts.fittingRules.hLayout === FITTING) {
|
|
curDist = curDist - 1;
|
|
break distCal;
|
|
} else if (opts.fittingRules.hLayout === SMUSHING) {
|
|
if (ch1 === opts.hardBlank || ch2 === opts.hardBlank) {
|
|
curDist = curDist - 1; // universal smushing does not smush hardblanks
|
|
}
|
|
break distCal;
|
|
} else {
|
|
breakAfter = true; // we know we need to break, but we need to check if our smushing rules will allow us to smush the overlapped characters
|
|
validSmush = false; // the below checks will let us know if we can smush these characters
|
|
|
|
validSmush = opts.fittingRules.hRule1
|
|
? hRule1_Smush(ch1, ch2, opts.hardBlank)
|
|
: validSmush;
|
|
validSmush =
|
|
!validSmush && opts.fittingRules.hRule2
|
|
? hRule2_Smush(ch1, ch2, opts.hardBlank)
|
|
: validSmush;
|
|
validSmush =
|
|
!validSmush && opts.fittingRules.hRule3
|
|
? hRule3_Smush(ch1, ch2, opts.hardBlank)
|
|
: validSmush;
|
|
validSmush =
|
|
!validSmush && opts.fittingRules.hRule4
|
|
? hRule4_Smush(ch1, ch2, opts.hardBlank)
|
|
: validSmush;
|
|
validSmush =
|
|
!validSmush && opts.fittingRules.hRule5
|
|
? hRule5_Smush(ch1, ch2, opts.hardBlank)
|
|
: validSmush;
|
|
validSmush =
|
|
!validSmush && opts.fittingRules.hRule6
|
|
? hRule6_Smush(ch1, ch2, opts.hardBlank)
|
|
: validSmush;
|
|
|
|
if (!validSmush) {
|
|
curDist = curDist - 1;
|
|
break distCal;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (breakAfter) {
|
|
break;
|
|
}
|
|
curDist++;
|
|
}
|
|
return Math.min(maxDist, curDist);
|
|
}
|
|
|
|
function horizontalSmush(textBlock1, textBlock2, overlap, opts) {
|
|
let ii,
|
|
jj,
|
|
outputFig = [],
|
|
overlapStart,
|
|
piece1,
|
|
piece2,
|
|
piece3,
|
|
len1,
|
|
len2,
|
|
txt1,
|
|
txt2;
|
|
|
|
for (ii = 0; ii < opts.height; ii++) {
|
|
txt1 = textBlock1[ii];
|
|
txt2 = textBlock2[ii];
|
|
len1 = txt1.length;
|
|
len2 = txt2.length;
|
|
overlapStart = len1 - overlap;
|
|
piece1 = txt1.substr(0, Math.max(0, overlapStart));
|
|
piece2 = "";
|
|
|
|
// determine overlap piece
|
|
const seg1StartPos = Math.max(0, len1 - overlap);
|
|
var seg1 = txt1.substring(seg1StartPos, seg1StartPos + overlap);
|
|
var seg2 = txt2.substring(0, Math.min(overlap, len2));
|
|
|
|
for (jj = 0; jj < overlap; jj++) {
|
|
var ch1 = jj < len1 ? seg1.substring(jj, jj + 1) : " ";
|
|
var ch2 = jj < len2 ? seg2.substring(jj, jj + 1) : " ";
|
|
|
|
if (ch1 !== " " && ch2 !== " ") {
|
|
if (opts.fittingRules.hLayout === FITTING) {
|
|
piece2 += uni_Smush(ch1, ch2, opts.hardBlank);
|
|
} else if (opts.fittingRules.hLayout === SMUSHING) {
|
|
piece2 += uni_Smush(ch1, ch2, opts.hardBlank);
|
|
} else {
|
|
// Controlled Smushing
|
|
var nextCh = "";
|
|
nextCh =
|
|
!nextCh && opts.fittingRules.hRule1
|
|
? hRule1_Smush(ch1, ch2, opts.hardBlank)
|
|
: nextCh;
|
|
nextCh =
|
|
!nextCh && opts.fittingRules.hRule2
|
|
? hRule2_Smush(ch1, ch2, opts.hardBlank)
|
|
: nextCh;
|
|
nextCh =
|
|
!nextCh && opts.fittingRules.hRule3
|
|
? hRule3_Smush(ch1, ch2, opts.hardBlank)
|
|
: nextCh;
|
|
nextCh =
|
|
!nextCh && opts.fittingRules.hRule4
|
|
? hRule4_Smush(ch1, ch2, opts.hardBlank)
|
|
: nextCh;
|
|
nextCh =
|
|
!nextCh && opts.fittingRules.hRule5
|
|
? hRule5_Smush(ch1, ch2, opts.hardBlank)
|
|
: nextCh;
|
|
nextCh =
|
|
!nextCh && opts.fittingRules.hRule6
|
|
? hRule6_Smush(ch1, ch2, opts.hardBlank)
|
|
: nextCh;
|
|
nextCh = nextCh || uni_Smush(ch1, ch2, opts.hardBlank);
|
|
piece2 += nextCh;
|
|
}
|
|
} else {
|
|
piece2 += uni_Smush(ch1, ch2, opts.hardBlank);
|
|
}
|
|
}
|
|
|
|
if (overlap >= len2) {
|
|
piece3 = "";
|
|
} else {
|
|
piece3 = txt2.substring(overlap, overlap + Math.max(0, len2 - overlap));
|
|
}
|
|
outputFig[ii] = piece1 + piece2 + piece3;
|
|
}
|
|
return outputFig;
|
|
}
|
|
|
|
/*
|
|
Creates new empty ASCII placeholder of give len
|
|
- len - number
|
|
*/
|
|
function newFigChar(len) {
|
|
let outputFigText = [],
|
|
row;
|
|
for (row = 0; row < len; row++) {
|
|
outputFigText[row] = "";
|
|
}
|
|
return outputFigText;
|
|
}
|
|
|
|
/*
|
|
Return max line of the ASCII Art
|
|
- text is array of lines for text
|
|
- char is next character
|
|
*/
|
|
const figLinesWidth = function (textLines) {
|
|
return Math.max.apply(
|
|
Math,
|
|
textLines.map(function (line, i) {
|
|
return line.length;
|
|
})
|
|
);
|
|
};
|
|
|
|
/*
|
|
join words or single characaters into single Fig line
|
|
- array - array of ASCII words or single characters: {fig: array, overlap: number}
|
|
- len - height of the Characters (number of rows)
|
|
- opt - options object
|
|
*/
|
|
function joinFigArray(array, len, opts) {
|
|
return array.reduce(function (acc, data) {
|
|
return horizontalSmush(acc, data.fig, data.overlap, opts);
|
|
}, newFigChar(len));
|
|
}
|
|
|
|
/*
|
|
break long word return leftover characters and line before the break
|
|
- figChars - list of single ASCII characters in form {fig, overlap}
|
|
- len - number of rows
|
|
- opt - options object
|
|
*/
|
|
function breakWord(figChars, len, opts) {
|
|
const result = {};
|
|
for (let i = figChars.length; --i; ) {
|
|
let w = joinFigArray(figChars.slice(0, i), len, opts);
|
|
if (figLinesWidth(w) <= opts.width) {
|
|
result.outputFigText = w;
|
|
if (i < figChars.length) {
|
|
result.chars = figChars.slice(i);
|
|
} else {
|
|
result.chars = [];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function generateFigTextLines(txt, figChars, opts) {
|
|
let charIndex,
|
|
figChar,
|
|
overlap = 0,
|
|
row,
|
|
outputFigText,
|
|
len,
|
|
height = opts.height,
|
|
outputFigLines = [],
|
|
maxWidth,
|
|
nextFigChars,
|
|
figWords = [],
|
|
char,
|
|
isSpace,
|
|
textFigWord,
|
|
textFigLine,
|
|
tmpBreak;
|
|
|
|
outputFigText = newFigChar(height);
|
|
if (opts.width > 0 && opts.whitespaceBreak) {
|
|
// list of characters is used to break in the middle of the word when word is logner
|
|
// chars is array of characters with {fig, overlap} and overlap is for whole word
|
|
nextFigChars = {
|
|
chars: [],
|
|
overlap: overlap,
|
|
};
|
|
}
|
|
if (opts.printDirection === 1) {
|
|
txt = txt.split("").reverse().join("");
|
|
}
|
|
len = txt.length;
|
|
for (charIndex = 0; charIndex < len; charIndex++) {
|
|
char = txt.substring(charIndex, charIndex + 1);
|
|
isSpace = char.match(/\s/);
|
|
figChar = figChars[char.charCodeAt(0)];
|
|
textFigLine = null;
|
|
if (figChar) {
|
|
if (opts.fittingRules.hLayout !== FULL_WIDTH) {
|
|
overlap = 10000; // a value too high to be the overlap
|
|
for (row = 0; row < opts.height; row++) {
|
|
overlap = Math.min(
|
|
overlap,
|
|
getHorizontalSmushLength(outputFigText[row], figChar[row], opts)
|
|
);
|
|
}
|
|
overlap = overlap === 10000 ? 0 : overlap;
|
|
}
|
|
if (opts.width > 0) {
|
|
if (opts.whitespaceBreak) {
|
|
// next character in last word (figChars have same data as words)
|
|
textFigWord = joinFigArray(
|
|
nextFigChars.chars.concat([
|
|
{
|
|
fig: figChar,
|
|
overlap: overlap,
|
|
},
|
|
]),
|
|
height,
|
|
opts
|
|
);
|
|
textFigLine = joinFigArray(
|
|
figWords.concat([
|
|
{
|
|
fig: textFigWord,
|
|
overlap: nextFigChars.overlap,
|
|
},
|
|
]),
|
|
height,
|
|
opts
|
|
);
|
|
maxWidth = figLinesWidth(textFigLine);
|
|
} else {
|
|
textFigLine = horizontalSmush(
|
|
outputFigText,
|
|
figChar,
|
|
overlap,
|
|
opts
|
|
);
|
|
maxWidth = figLinesWidth(textFigLine);
|
|
}
|
|
if (maxWidth >= opts.width && charIndex > 0) {
|
|
if (opts.whitespaceBreak) {
|
|
outputFigText = joinFigArray(figWords.slice(0, -1), height, opts);
|
|
if (figWords.length > 1) {
|
|
outputFigLines.push(outputFigText);
|
|
outputFigText = newFigChar(height);
|
|
}
|
|
figWords = [];
|
|
} else {
|
|
outputFigLines.push(outputFigText);
|
|
outputFigText = newFigChar(height);
|
|
}
|
|
}
|
|
}
|
|
if (opts.width > 0 && opts.whitespaceBreak) {
|
|
if (!isSpace || charIndex === len - 1) {
|
|
nextFigChars.chars.push({ fig: figChar, overlap: overlap });
|
|
}
|
|
if (isSpace || charIndex === len - 1) {
|
|
// break long words
|
|
tmpBreak = null;
|
|
while (true) {
|
|
textFigLine = joinFigArray(nextFigChars.chars, height, opts);
|
|
maxWidth = figLinesWidth(textFigLine);
|
|
if (maxWidth >= opts.width) {
|
|
tmpBreak = breakWord(nextFigChars.chars, height, opts);
|
|
nextFigChars = { chars: tmpBreak.chars };
|
|
outputFigLines.push(tmpBreak.outputFigText);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
// any leftovers
|
|
if (maxWidth > 0) {
|
|
if (tmpBreak) {
|
|
figWords.push({ fig: textFigLine, overlap: 1 });
|
|
} else {
|
|
figWords.push({
|
|
fig: textFigLine,
|
|
overlap: nextFigChars.overlap,
|
|
});
|
|
}
|
|
}
|
|
// save space character and current overlap for smush in joinFigWords
|
|
if (isSpace) {
|
|
figWords.push({ fig: figChar, overlap: overlap });
|
|
outputFigText = newFigChar(height);
|
|
}
|
|
if (charIndex === len - 1) {
|
|
// last line
|
|
outputFigText = joinFigArray(figWords, height, opts);
|
|
}
|
|
nextFigChars = {
|
|
chars: [],
|
|
overlap: overlap,
|
|
};
|
|
continue;
|
|
}
|
|
}
|
|
outputFigText = horizontalSmush(outputFigText, figChar, overlap, opts);
|
|
}
|
|
}
|
|
// special case when last line would be empty
|
|
// this may happen if text fit exactly opt.width
|
|
if (figLinesWidth(outputFigText) > 0) {
|
|
outputFigLines.push(outputFigText);
|
|
}
|
|
// remove hardblanks
|
|
if (opts.showHardBlanks !== true) {
|
|
outputFigLines.forEach(function (outputFigText) {
|
|
len = outputFigText.length;
|
|
for (row = 0; row < len; row++) {
|
|
outputFigText[row] = outputFigText[row].replace(
|
|
new RegExp("\\" + opts.hardBlank, "g"),
|
|
" "
|
|
);
|
|
}
|
|
});
|
|
}
|
|
return outputFigLines;
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Parsing and Generation methods
|
|
|
|
const getHorizontalFittingRules = function (layout, options) {
|
|
let props = [
|
|
"hLayout",
|
|
"hRule1",
|
|
"hRule2",
|
|
"hRule3",
|
|
"hRule4",
|
|
"hRule5",
|
|
"hRule6",
|
|
],
|
|
params = {},
|
|
ii;
|
|
if (layout === "default") {
|
|
for (ii = 0; ii < props.length; ii++) {
|
|
params[props[ii]] = options.fittingRules[props[ii]];
|
|
}
|
|
} else if (layout === "full") {
|
|
params = {
|
|
hLayout: FULL_WIDTH,
|
|
hRule1: false,
|
|
hRule2: false,
|
|
hRule3: false,
|
|
hRule4: false,
|
|
hRule5: false,
|
|
hRule6: false,
|
|
};
|
|
} else if (layout === "fitted") {
|
|
params = {
|
|
hLayout: FITTING,
|
|
hRule1: false,
|
|
hRule2: false,
|
|
hRule3: false,
|
|
hRule4: false,
|
|
hRule5: false,
|
|
hRule6: false,
|
|
};
|
|
} else if (layout === "controlled smushing") {
|
|
params = {
|
|
hLayout: CONTROLLED_SMUSHING,
|
|
hRule1: true,
|
|
hRule2: true,
|
|
hRule3: true,
|
|
hRule4: true,
|
|
hRule5: true,
|
|
hRule6: true,
|
|
};
|
|
} else if (layout === "universal smushing") {
|
|
params = {
|
|
hLayout: SMUSHING,
|
|
hRule1: false,
|
|
hRule2: false,
|
|
hRule3: false,
|
|
hRule4: false,
|
|
hRule5: false,
|
|
hRule6: false,
|
|
};
|
|
} else {
|
|
return;
|
|
}
|
|
return params;
|
|
};
|
|
|
|
const getVerticalFittingRules = function (layout, options) {
|
|
let props = ["vLayout", "vRule1", "vRule2", "vRule3", "vRule4", "vRule5"],
|
|
params = {},
|
|
ii;
|
|
if (layout === "default") {
|
|
for (ii = 0; ii < props.length; ii++) {
|
|
params[props[ii]] = options.fittingRules[props[ii]];
|
|
}
|
|
} else if (layout === "full") {
|
|
params = {
|
|
vLayout: FULL_WIDTH,
|
|
vRule1: false,
|
|
vRule2: false,
|
|
vRule3: false,
|
|
vRule4: false,
|
|
vRule5: false,
|
|
};
|
|
} else if (layout === "fitted") {
|
|
params = {
|
|
vLayout: FITTING,
|
|
vRule1: false,
|
|
vRule2: false,
|
|
vRule3: false,
|
|
vRule4: false,
|
|
vRule5: false,
|
|
};
|
|
} else if (layout === "controlled smushing") {
|
|
params = {
|
|
vLayout: CONTROLLED_SMUSHING,
|
|
vRule1: true,
|
|
vRule2: true,
|
|
vRule3: true,
|
|
vRule4: true,
|
|
vRule5: true,
|
|
};
|
|
} else if (layout === "universal smushing") {
|
|
params = {
|
|
vLayout: SMUSHING,
|
|
vRule1: false,
|
|
vRule2: false,
|
|
vRule3: false,
|
|
vRule4: false,
|
|
vRule5: false,
|
|
};
|
|
} else {
|
|
return;
|
|
}
|
|
return params;
|
|
};
|
|
|
|
/*
|
|
Generates the ASCII Art
|
|
- fontName: Font to use
|
|
- option: Options to override the defaults
|
|
- txt: The text to make into ASCII Art
|
|
*/
|
|
const generateText = function (fontName, options, txt) {
|
|
txt = txt.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
let lines = txt.split("\n");
|
|
let figLines = [];
|
|
let ii, len, output;
|
|
len = lines.length;
|
|
for (ii = 0; ii < len; ii++) {
|
|
figLines = figLines.concat(
|
|
generateFigTextLines(lines[ii], figFonts[fontName], options)
|
|
);
|
|
}
|
|
len = figLines.length;
|
|
output = figLines[0];
|
|
for (ii = 1; ii < len; ii++) {
|
|
output = smushVerticalFigLines(output, figLines[ii], options);
|
|
}
|
|
|
|
return output ? output.join("\n") : "";
|
|
};
|
|
|
|
/*
|
|
takes assigned options and merges them with the default options from the choosen font
|
|
*/
|
|
function _reworkFontOpts(fontOpts, options) {
|
|
let myOpts = JSON.parse(JSON.stringify(fontOpts)), // make a copy because we may edit this (see below)
|
|
params,
|
|
prop;
|
|
|
|
/*
|
|
If the user is chosing to use a specific type of layout (e.g., 'full', 'fitted', etc etc)
|
|
Then we need to override the default font options.
|
|
*/
|
|
if (typeof options.horizontalLayout !== "undefined") {
|
|
params = getHorizontalFittingRules(options.horizontalLayout, fontOpts);
|
|
for (prop in params) {
|
|
if (params.hasOwnProperty(prop)) {
|
|
myOpts.fittingRules[prop] = params[prop];
|
|
}
|
|
}
|
|
}
|
|
if (typeof options.verticalLayout !== "undefined") {
|
|
params = getVerticalFittingRules(options.verticalLayout, fontOpts);
|
|
for (prop in params) {
|
|
if (params.hasOwnProperty(prop)) {
|
|
myOpts.fittingRules[prop] = params[prop];
|
|
}
|
|
}
|
|
}
|
|
myOpts.printDirection =
|
|
typeof options.printDirection !== "undefined"
|
|
? options.printDirection
|
|
: fontOpts.printDirection;
|
|
myOpts.showHardBlanks = options.showHardBlanks || false;
|
|
myOpts.width = options.width || -1;
|
|
myOpts.whitespaceBreak = options.whitespaceBreak || false;
|
|
|
|
return myOpts;
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Public methods
|
|
|
|
/*
|
|
A short-cut for the figlet.text method
|
|
|
|
Parameters:
|
|
- txt (string): The text to make into ASCII Art
|
|
- options (object/string - optional): Options that will override the current font's default options.
|
|
If a string is provided instead of an object, it is assumed to be the font name.
|
|
|
|
* font
|
|
* horizontalLayout
|
|
* verticalLayout
|
|
* showHardBlanks - Wont remove hardblank characters
|
|
|
|
- next (function): A callback function, it will contained the outputted ASCII Art.
|
|
*/
|
|
const me = function (txt, options, next) {
|
|
return me.text(txt, options, next);
|
|
};
|
|
me.text = async function (txt, options, next) {
|
|
let fontName = "";
|
|
|
|
// Validate inputs
|
|
txt = txt + "";
|
|
|
|
if (typeof arguments[1] === "function") {
|
|
next = options;
|
|
options = {};
|
|
options.font = figDefaults.font; // default font
|
|
}
|
|
|
|
if (typeof options === "string") {
|
|
fontName = options;
|
|
options = {};
|
|
} else {
|
|
options = options || {};
|
|
fontName = options.font || figDefaults.font;
|
|
}
|
|
|
|
return await new Promise((resolve, reject) => {
|
|
/*
|
|
Load the font. If it loads, it's data will be contained in the figFonts object.
|
|
The callback will recieve a fontsOpts object, which contains the default
|
|
options of the font (its fitting rules, etc etc).
|
|
*/
|
|
me.loadFont(fontName, function (err, fontOpts) {
|
|
if (err) {
|
|
reject(err);
|
|
if (next) next(err);
|
|
return;
|
|
}
|
|
|
|
const generatedTxt = generateText(
|
|
fontName,
|
|
_reworkFontOpts(fontOpts, options),
|
|
txt
|
|
);
|
|
|
|
resolve(generatedTxt);
|
|
if (next) next(null, generatedTxt);
|
|
});
|
|
});
|
|
};
|
|
|
|
/*
|
|
Synchronous version of figlet.text.
|
|
Accepts the same parameters.
|
|
*/
|
|
me.textSync = function (txt, options) {
|
|
let fontName = "";
|
|
|
|
// Validate inputs
|
|
txt = txt + "";
|
|
|
|
if (typeof options === "string") {
|
|
fontName = options;
|
|
options = {};
|
|
} else {
|
|
options = options || {};
|
|
fontName = options.font || figDefaults.font;
|
|
}
|
|
|
|
var fontOpts = _reworkFontOpts(me.loadFontSync(fontName), options);
|
|
return generateText(fontName, fontOpts, txt);
|
|
};
|
|
|
|
/*
|
|
Returns metadata about a specfic FIGlet font.
|
|
|
|
Returns:
|
|
next(err, options, headerComment)
|
|
- err: The error if an error occurred, otherwise null/falsey.
|
|
- options (object): The options defined for the font.
|
|
- headerComment (string): The font's header comment.
|
|
*/
|
|
me.metadata = function (fontName, next) {
|
|
fontName = fontName + "";
|
|
|
|
/*
|
|
Load the font. If it loads, it's data will be contained in the figFonts object.
|
|
The callback will recieve a fontsOpts object, which contains the default
|
|
options of the font (its fitting rules, etc etc).
|
|
*/
|
|
me.loadFont(fontName, function (err, fontOpts) {
|
|
if (err) {
|
|
next(err);
|
|
return;
|
|
}
|
|
|
|
next(null, fontOpts, figFonts[fontName].comment);
|
|
});
|
|
};
|
|
|
|
/*
|
|
Allows you to override defaults. See the definition of the figDefaults object up above
|
|
to see what properties can be overridden.
|
|
Returns the options for the font.
|
|
*/
|
|
me.defaults = function (opts) {
|
|
if (typeof opts === "object" && opts !== null) {
|
|
for (var prop in opts) {
|
|
if (opts.hasOwnProperty(prop)) {
|
|
figDefaults[prop] = opts[prop];
|
|
}
|
|
}
|
|
}
|
|
return JSON.parse(JSON.stringify(figDefaults));
|
|
};
|
|
|
|
/*
|
|
Parses data from a FIGlet font file and places it into the figFonts object.
|
|
*/
|
|
me.parseFont = function (fontName, data) {
|
|
data = data.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
figFonts[fontName] = {};
|
|
|
|
var lines = data.split("\n");
|
|
var headerData = lines.splice(0, 1)[0].split(" ");
|
|
var figFont = figFonts[fontName];
|
|
var opts = {};
|
|
|
|
opts.hardBlank = headerData[0].substr(5, 1);
|
|
opts.height = parseInt(headerData[1], 10);
|
|
opts.baseline = parseInt(headerData[2], 10);
|
|
opts.maxLength = parseInt(headerData[3], 10);
|
|
opts.oldLayout = parseInt(headerData[4], 10);
|
|
opts.numCommentLines = parseInt(headerData[5], 10);
|
|
opts.printDirection =
|
|
headerData.length >= 6 ? parseInt(headerData[6], 10) : 0;
|
|
opts.fullLayout =
|
|
headerData.length >= 7 ? parseInt(headerData[7], 10) : null;
|
|
opts.codeTagCount =
|
|
headerData.length >= 8 ? parseInt(headerData[8], 10) : null;
|
|
opts.fittingRules = getSmushingRules(opts.oldLayout, opts.fullLayout);
|
|
|
|
figFont.options = opts;
|
|
|
|
// error check
|
|
if (
|
|
opts.hardBlank.length !== 1 ||
|
|
isNaN(opts.height) ||
|
|
isNaN(opts.baseline) ||
|
|
isNaN(opts.maxLength) ||
|
|
isNaN(opts.oldLayout) ||
|
|
isNaN(opts.numCommentLines)
|
|
) {
|
|
throw new Error("FIGlet header contains invalid values.");
|
|
}
|
|
|
|
/*
|
|
All FIGlet fonts must contain chars 32-126, 196, 214, 220, 228, 246, 252, 223
|
|
*/
|
|
|
|
let charNums = [],
|
|
ii;
|
|
for (ii = 32; ii <= 126; ii++) {
|
|
charNums.push(ii);
|
|
}
|
|
charNums = charNums.concat(196, 214, 220, 228, 246, 252, 223);
|
|
|
|
// error check - validate that there are enough lines in the file
|
|
if (lines.length < opts.numCommentLines + opts.height * charNums.length) {
|
|
throw new Error("FIGlet file is missing data.");
|
|
}
|
|
|
|
/*
|
|
Parse out the context of the file and put it into our figFont object
|
|
*/
|
|
|
|
let cNum,
|
|
endCharRegEx,
|
|
parseError = false;
|
|
|
|
figFont.comment = lines.splice(0, opts.numCommentLines).join("\n");
|
|
figFont.numChars = 0;
|
|
|
|
while (lines.length > 0 && figFont.numChars < charNums.length) {
|
|
cNum = charNums[figFont.numChars];
|
|
figFont[cNum] = lines.splice(0, opts.height);
|
|
// remove end sub-chars
|
|
for (ii = 0; ii < opts.height; ii++) {
|
|
if (typeof figFont[cNum][ii] === "undefined") {
|
|
figFont[cNum][ii] = "";
|
|
} else {
|
|
endCharRegEx = new RegExp(
|
|
"\\" +
|
|
figFont[cNum][ii].substr(figFont[cNum][ii].length - 1, 1) +
|
|
"+$"
|
|
);
|
|
figFont[cNum][ii] = figFont[cNum][ii].replace(endCharRegEx, "");
|
|
}
|
|
}
|
|
figFont.numChars++;
|
|
}
|
|
|
|
/*
|
|
Now we check to see if any additional characters are present
|
|
*/
|
|
|
|
while (lines.length > 0) {
|
|
cNum = lines.splice(0, 1)[0].split(" ")[0];
|
|
if (/^0[xX][0-9a-fA-F]+$/.test(cNum)) {
|
|
cNum = parseInt(cNum, 16);
|
|
} else if (/^0[0-7]+$/.test(cNum)) {
|
|
cNum = parseInt(cNum, 8);
|
|
} else if (/^[0-9]+$/.test(cNum)) {
|
|
cNum = parseInt(cNum, 10);
|
|
} else if (/^-0[xX][0-9a-fA-F]+$/.test(cNum)) {
|
|
cNum = parseInt(cNum, 16);
|
|
} else {
|
|
if (cNum === "") {
|
|
break;
|
|
}
|
|
// something's wrong
|
|
console.log("Invalid data:" + cNum);
|
|
parseError = true;
|
|
break;
|
|
}
|
|
|
|
figFont[cNum] = lines.splice(0, opts.height);
|
|
// remove end sub-chars
|
|
for (ii = 0; ii < opts.height; ii++) {
|
|
if (typeof figFont[cNum][ii] === "undefined") {
|
|
figFont[cNum][ii] = "";
|
|
} else {
|
|
endCharRegEx = new RegExp(
|
|
"\\" +
|
|
figFont[cNum][ii].substr(figFont[cNum][ii].length - 1, 1) +
|
|
"+$"
|
|
);
|
|
figFont[cNum][ii] = figFont[cNum][ii].replace(endCharRegEx, "");
|
|
}
|
|
}
|
|
figFont.numChars++;
|
|
}
|
|
|
|
// error check
|
|
if (parseError === true) {
|
|
throw new Error("Error parsing data.");
|
|
}
|
|
|
|
return opts;
|
|
};
|
|
|
|
/*
|
|
Loads a font.
|
|
*/
|
|
me.loadFont = function (fontName, next) {
|
|
if (figFonts[fontName]) {
|
|
next(null, figFonts[fontName].options);
|
|
return;
|
|
}
|
|
|
|
if (typeof fetch !== "function") {
|
|
console.error(
|
|
"figlet.js requires the fetch API or a fetch polyfill such as https://cdnjs.com/libraries/fetch"
|
|
);
|
|
throw new Error("fetch is required for figlet.js to work.");
|
|
}
|
|
|
|
fetch(figDefaults.fontPath + "/" + fontName + ".flf")
|
|
.then(function (response) {
|
|
if (response.ok) {
|
|
return response.text();
|
|
}
|
|
|
|
console.log("Unexpected response", response);
|
|
throw new Error("Network response was not ok.");
|
|
})
|
|
.then(function (text) {
|
|
next(null, me.parseFont(fontName, text));
|
|
})
|
|
.catch(next);
|
|
};
|
|
|
|
/*
|
|
loads a font synchronously, not implemented for the browser
|
|
*/
|
|
me.loadFontSync = function (name) {
|
|
if (figFonts[name]) {
|
|
return figFonts[name].options;
|
|
}
|
|
throw new Error(
|
|
"synchronous font loading is not implemented for the browser"
|
|
);
|
|
};
|
|
|
|
/*
|
|
preloads a list of fonts prior to using textSync
|
|
- fonts: an array of font names (i.e. ["Standard","Soft"])
|
|
- next: callback function
|
|
*/
|
|
me.preloadFonts = function (fonts, next) {
|
|
let fontData = [];
|
|
|
|
fonts
|
|
.reduce(function (promise, name) {
|
|
return promise.then(function () {
|
|
return fetch(figDefaults.fontPath + "/" + name + ".flf")
|
|
.then((response) => {
|
|
return response.text();
|
|
})
|
|
.then(function (data) {
|
|
fontData.push(data);
|
|
});
|
|
});
|
|
}, Promise.resolve())
|
|
.then(function (res) {
|
|
for (var i in fonts) {
|
|
if (fonts.hasOwnProperty(i)) {
|
|
me.parseFont(fonts[i], fontData[i]);
|
|
}
|
|
}
|
|
|
|
if (next) {
|
|
next();
|
|
}
|
|
});
|
|
};
|
|
|
|
me.figFonts = figFonts;
|
|
|
|
return me;
|
|
})();
|
|
|
|
// for node.js
|
|
if (typeof module !== "undefined") {
|
|
if (typeof module.exports !== "undefined") {
|
|
module.exports = figlet;
|
|
}
|
|
}
|