import esrever from "esrever";
import {
type SentimentResult,
type ReadabilityResult,
type TextDiffResult,
type LanguageDetectionResult,
SentimentAnalyzer,
TextSummarizer,
TextStatistics,
LanguageDetector,
TextDiff,
} from "./extensions";
/**
* @namespace Tools
* @description A TypeScript module providing text analysis functionalities through various operations like removing punctuations, numbers, alphabets, special characters, extracting URLs, and performing case transformations. It also includes functions for character and alphanumeric counting.
*/
export namespace Tools {
/**
* @class ToolsConstant
* @summary A collection of regular expressions for different character patterns.
* @readonly
* @property {Object} regex - An object containing regex patterns.
* @property {RegExp} regex.alphabets - Matches all alphabetical characters (both uppercase and lowercase).
* @property {RegExp} regex.numbers - Matches all numeric digits (0-9).
* @property {RegExp} regex.punctuations - Matches common punctuation characters.
* @property {RegExp} regex.specialCharacters - Matches any special characters that are not alphanumeric or common punctuation.
* @property {RegExp} regex.urls - Matches URLs that start with "http" or "https".
* @property {RegExp} regex.newlines - Matches empty lines (newlines with only whitespace).
* @property {RegExp} regex.extraSpaces - Matches multiple consecutive spaces.
* @property {RegExp} regex.character - Matches whitespace characters.
*/
export class ToolsConstant {
static readonly regex = {
alphabets: /[a-zA-Z]/g,
numbers: /\d/g,
punctuations: /[!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~]/g, // Simplified syntax
specialCharacters: /[^a-zA-Z0-9\s!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~]/g,
urls: /https?:\/\/\S+/gi,
newlines: /^\s*$(?:\r\n?|\n)/gm,
extraSpaces: / +/g,
character: /[^\s\p{Cf}]/gu,
// Enhanced patterns
email: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
phoneNumber: /(?:\+\d{1,3}[-\s]?)?\(?\d{3}\)?[-\s]?\d{3}[-\s]?\d{4}/g,
hashtags: /#[a-zA-Z0-9_]+/g,
mentions: /@[a-zA-Z0-9_]+/g,
};
}
/**
* @enum {string} Operations
* @description Enum representing various text analysis and manipulation operations. Each operation corresponds to a specific functionality within the `Analyser` class.
*/
export enum Operations {
RemovePunctuations = "removepunc",
RemoveNumbers = "removenum",
RemoveAlphabets = "removealpha",
RemoveSpecialChars = "removespecialchar",
RemoveNewlines = "newlineremover",
RemoveExtraSpaces = "extraspaceremover",
ExtractUrls = "extractUrls",
ExtractEmails = "extractEmails",
ExtractPhoneNumbers = "extractPhoneNumbers",
ExtractHashtags = "extractHashtags",
ExtractMentions = "extractMentions",
ConvertToUppercase = "fullcaps",
ConvertToLowercase = "lowercaps",
ConvertToTitleCase = "titlecase",
CountCharacters = "charcount",
CountAlphabets = "alphacount",
CountNumbers = "numcount",
CountAlphanumeric = "alphanumericcount",
CountWords = "wordcount",
CountSentences = "sentencecount",
ReverseText = "reversetext",
Truncate = "truncate",
AnalyzeSentiment = "analyzeSentiment",
SummarizeText = "summarizeText",
CalculateReadability = "calculateReadability",
DetectLanguage = "detectLanguage",
CompareTexts = "compareTexts",
}
/** Types for Analyser options and built-in operations */
export type AnalyserBuiltInOptions = Partial<
Record<Operations | string, boolean | any>
>;
type BuiltInOptions = keyof typeof Operations;
/** Type for operation results */
export interface AnalyserResult {
purpose: string;
output: string;
metadata: {
counts: {
characterCount: number;
alphabetCount: number;
numericCount: number;
wordCount?: number;
sentenceCount?: number;
};
urls?: string[];
emails?: string[];
phoneNumbers?: string[];
hashtags?: string[];
mentions?: string[];
custom?: {
[key: string]: any;
};
};
[key: string]: any;
operations: string[];
builtInOperations: string[];
customOperations: string[];
executionTime?: number;
}
export interface AnalyserExtendedResult extends AnalyserResult {
sentiment?: SentimentResult;
summary?: string;
readability?: ReadabilityResult;
languageDetection?: LanguageDetectionResult;
textComparison?: TextDiffResult;
}
/** Configuration type for Truncate operation */
export interface TruncateConfig {
maxLength: number;
suffix?: string;
}
/**
* @class Analyser
* @summary A class to analyze and manipulate strings based on user-provided options.
*/
export class Analyser {
public raw_text: string;
public count: number = 0;
public alphacount: number = 0;
public numericcount: number = 0;
public wordCount: number = 0;
public sentenceCount: number = 0;
// Enhanced extraction features
public urls: string[] = [];
public emails: string[] = [];
public phoneNumbers: string[] = [];
public hashtags: string[] = [];
public mentions: string[] = [];
private metadata: { [key: string]: { [key: string]: any } } = {};
public operations: string[] = [];
public customOperationsList: string[] = [];
public builtInOperationsList: string[] = [];
private executionStartTime: number = 0;
private executionEndTime: number = 0;
private customOperations: { [key: string]: () => Promise<void> } = {};
private builtInOptions: AnalyserBuiltInOptions = {};
/**
* @constructor
* @param {string} raw_text - The input text to process.
* @param {AnalyserBuiltInOptions} options - The options object defining operations to perform.
* @throws {Error} If raw_text is not a string
*/
constructor(raw_text: string, options: AnalyserBuiltInOptions = {}) {
if (typeof raw_text !== "string") {
throw new Error("Input text must be a string");
}
this.raw_text = raw_text || ""; // Fallback to empty string if raw_text is empty
this.builtInOptions = options;
}
/** @summary Get all available operations */
public get availableOperations(): Record<string, string> {
const customOps = Object.keys(this.customOperations).reduce(
(acc, key) => ({ ...acc, [key]: key }),
{}
);
return {
...Operations,
...customOps,
};
}
/** @summary Get current options */
public get options(): AnalyserBuiltInOptions {
return this.builtInOptions;
}
/** @summary Set new options */
public set options(newOptions: AnalyserBuiltInOptions) {
this.builtInOptions = { ...this.builtInOptions, ...newOptions };
}
/**
* @description Operation handlers mapping
* @private
*/
private get operationHandlers(): Record<string, () => Promise<void>> {
return {
[Operations.RemovePunctuations]: this.removePunctuations.bind(this),
[Operations.RemoveNumbers]: this.removeNumbers.bind(this),
[Operations.RemoveAlphabets]: this.removeAlphabets.bind(this),
[Operations.RemoveSpecialChars]:
this.removeSpecialCharacters.bind(this),
[Operations.RemoveNewlines]: this.newLineRemover.bind(this),
[Operations.RemoveExtraSpaces]: this.extraSpaceRemover.bind(this),
[Operations.ExtractUrls]: this.extractURL.bind(this),
[Operations.ExtractEmails]: this.extractEmails.bind(this),
[Operations.ExtractPhoneNumbers]: this.extractPhoneNumbers.bind(this),
[Operations.ExtractHashtags]: this.extractHashtags.bind(this),
[Operations.ExtractMentions]: this.extractMentions.bind(this),
[Operations.ConvertToUppercase]: this.toFullUppercase.bind(this),
[Operations.ConvertToLowercase]: this.toFullLowercase.bind(this),
[Operations.ConvertToTitleCase]: this.toTitleCase.bind(this),
[Operations.CountCharacters]: this.countCharacters.bind(this),
[Operations.CountAlphabets]: this.countAlphas.bind(this),
[Operations.CountNumbers]: this.countNums.bind(this),
[Operations.CountAlphanumeric]: this.countAlphaNumeric.bind(this),
[Operations.CountWords]: this.countWords.bind(this),
[Operations.CountSentences]: this.countSentences.bind(this),
[Operations.ReverseText]: this.reverseText.bind(this),
[Operations.Truncate]: this.truncateText.bind(this),
[Operations.AnalyzeSentiment]: this.analyzeSentiment.bind(this),
[Operations.SummarizeText]: this.summarizeText.bind(this),
[Operations.CalculateReadability]: this.calculateReadability.bind(this),
[Operations.DetectLanguage]: this.detectLanguage.bind(this),
[Operations.CompareTexts]: this.compareTexts.bind(this),
...this.customOperations,
};
}
/**
* @private
* @async
* @function removeAlphabets
* @summary Removes all alphabetic characters from the input text.
*/
private async removeAlphabets(): Promise<void> {
this.raw_text = this.raw_text.replace(ToolsConstant.regex.alphabets, "");
this.logOperation("Removed Alphabets");
}
/**
* @private
* @async
* @function removeNumbers
* @summary Removes all numeric characters from the input text.
*/
private async removeNumbers(): Promise<void> {
this.raw_text = this.raw_text.replace(ToolsConstant.regex.numbers, "");
this.logOperation("Removed Numbers");
}
/**
* @private
* @async
* @function removePunctuations
* @summary Removes all punctuation characters from the input text.
*/
private async removePunctuations(): Promise<void> {
this.raw_text = this.raw_text.replace(
ToolsConstant.regex.punctuations,
""
);
this.logOperation("Removed Punctuations");
}
/**
* @private
* @async
* @function removeSpecialCharacters
* @summary Removes all special characters from the input text.
*/
private async removeSpecialCharacters(): Promise<void> {
this.raw_text = this.raw_text.replace(
ToolsConstant.regex.specialCharacters,
""
);
this.logOperation("Removed Special Characters");
}
/**
* @private
* @async
* @function extraSpaceRemover
* @summary Removes extra spaces and trims the input text.
*/
private async extraSpaceRemover(): Promise<void> {
this.raw_text = this.raw_text
.replace(ToolsConstant.regex.extraSpaces, " ")
.trim();
this.logOperation("Removed Extra Spaces");
}
/**
* @private
* @async
* @function newLineRemover
* @summary Removes newline characters from the input text.
*/
private async newLineRemover(): Promise<void> {
this.raw_text = this.raw_text
.replace(ToolsConstant.regex.newlines, "\n")
.trim();
this.logOperation("Removed New Line Characters");
}
/**
* @private
* @async
* @function extractURL
* @summary Extracts all URLs from the input text.
*/
private async extractURL(): Promise<void> {
this.urls = this.raw_text.match(ToolsConstant.regex.urls) || [];
this.logOperation("Extracted URLs");
}
/**
* @private
* @async
* @function extractEmails
* @summary Extracts all email addresses from the input text.
*/
private async extractEmails(): Promise<void> {
this.emails = this.raw_text.match(ToolsConstant.regex.email) || [];
this.logOperation("Extracted Emails");
}
/**
* @private
* @async
* @function extractPhoneNumbers
* @summary Extracts all phone numbers from the input text.
*/
private async extractPhoneNumbers(): Promise<void> {
this.phoneNumbers =
this.raw_text.match(ToolsConstant.regex.phoneNumber) || [];
this.logOperation("Extracted Phone Numbers");
}
/**
* @private
* @async
* @function extractHashtags
* @summary Extracts all hashtags from the input text.
*/
private async extractHashtags(): Promise<void> {
this.hashtags = this.raw_text.match(ToolsConstant.regex.hashtags) || [];
this.logOperation("Extracted Hashtags");
}
/**
* @private
* @async
* @function extractMentions
* @summary Extracts all mentions from the input text.
*/
private async extractMentions(): Promise<void> {
this.mentions = this.raw_text.match(ToolsConstant.regex.mentions) || [];
this.logOperation("Extracted Mentions");
}
/**
* @private
* @async
* @function toFullUppercase
* @summary Converts all characters in the input text to uppercase.
*/
private async toFullUppercase(): Promise<void> {
this.raw_text = this.raw_text.toUpperCase();
this.logOperation("Changed to Uppercase");
}
/**
* @private
* @async
* @function toFullLowercase
* @summary Converts all characters in the input text to lowercase.
*/
private async toFullLowercase(): Promise<void> {
this.raw_text = this.raw_text.toLowerCase();
this.logOperation("Changed to Lowercase");
}
/**
* @private
* @async
* @function toTitleCase
* @summary Converts the input text to title case (first letter of each word capitalized).
*/
private async toTitleCase(): Promise<void> {
this.raw_text = this.raw_text.replace(/\w\S*/g, (txt) => {
return txt.charAt(0).toUpperCase() + txt.substring(1).toLowerCase();
});
this.logOperation("Changed to Title Case");
}
/**
* @private
* @async
* @function countCharacters
* @summary Counts the number of non-whitespace characters in the input text.
*/
private async countCharacters(): Promise<void> {
this.count = (
this.raw_text.match(ToolsConstant.regex.character) ?? []
).length;
this.logOperation("Counted Characters");
}
/**
* @private
* @async
* @function countAlphas
* @summary Counts the number of alphabets in the input text.
*/
private async countAlphas(): Promise<void> {
this.alphacount = (
this.raw_text.match(ToolsConstant.regex.alphabets) || []
).length;
this.logOperation("Counted Alphabets");
}
/**
* @private
* @async
* @function countNums
* @summary Counts the number of numeric characters in the input text.
*/
private async countNums(): Promise<void> {
this.numericcount = (
this.raw_text.match(ToolsConstant.regex.numbers) || []
).length;
this.logOperation("Counted Numbers");
}
/**
* @private
* @async
* @function countAlphaNumeric
* @summary Counts the number of alphabetic and numeric characters in the input text.
*/
private async countAlphaNumeric(): Promise<void> {
this.alphacount = (
this.raw_text.match(ToolsConstant.regex.alphabets) || []
).length;
this.numericcount = (
this.raw_text.match(ToolsConstant.regex.numbers) || []
).length;
this.logOperation("Counted Alphabets and Numbers");
}
/**
* @private
* @async
* @function countWords
* @summary Counts the number of words in the input text.
*/
private async countWords(): Promise<void> {
// Count words by splitting on whitespace and filtering out empty strings
const words = this.raw_text
.trim()
.split(/\s+/)
.filter((word) => word.length > 0);
this.wordCount = words.length;
this.logOperation("Counted Words");
}
/**
* @private
* @async
* @function countSentences
* @summary Counts the number of sentences in the input text.
*/
private async countSentences(): Promise<void> {
// Count sentences by splitting on period, exclamation, or question mark followed by whitespace
const sentences = this.raw_text
.split(/[.!?]+\s+/)
.filter((sentence) => sentence.length > 0);
this.sentenceCount = sentences.length > 0 ? sentences.length : 0;
// If text ends with a sentence-ending punctuation but no trailing space, count the last sentence
if (/[.!?]$/.test(this.raw_text.trim())) {
this.sentenceCount += 1;
}
this.logOperation("Counted Sentences");
}
/**
* @private
* @async
* @function reverseText
* @summary Reverses the input text.
*/
private async reverseText(): Promise<void> {
this.raw_text = esrever.reverse(this.raw_text);
this.logOperation("Reversed Text");
}
/**
* @private
* @async
* @function truncateText
* @summary Truncates the input text to a specified length.
*/
private async truncateText(): Promise<void> {
const config = this.builtInOptions[Operations.Truncate] as TruncateConfig;
if (!config || typeof config !== "object" || !config.maxLength) {
throw new Error(
"Truncate operation requires a valid configuration with maxLength"
);
}
const { maxLength, suffix = "..." } = config;
if (this.raw_text.length <= maxLength) {
return; // No truncation needed
}
this.raw_text = this.raw_text.substring(0, maxLength) + suffix;
this.logOperation(`Truncated Text to ${maxLength} characters`);
}
/**
* @private
* @async
* @function analyzeSentiment
* @summary Analyzes the sentiment of the input text.
*/
private async analyzeSentiment(): Promise<void> {
const analyzer = new SentimentAnalyzer();
// Apply custom lexicon if provided in options
const lexiconOption = this.builtInOptions[Operations.AnalyzeSentiment];
if (
lexiconOption &&
typeof lexiconOption === "object" &&
("positive" in lexiconOption || "negative" in lexiconOption)
) {
analyzer.addCustomLexicon(
lexiconOption as { positive?: string[]; negative?: string[] }
);
}
const result = analyzer.analyze(this.raw_text);
// Store in metadata
if (!this.metadata.sentiment) {
this.metadata.sentiment = {};
}
this.metadata.sentiment = result;
this.logOperation("Analyzed Sentiment");
}
/**
* @private
* @async
* @function summarizeText
* @summary Summarizes the input text.
*/
private async summarizeText(): Promise<void> {
const summarizer = new TextSummarizer();
// Get configuration options
const summaryOptions = this.builtInOptions[Operations.SummarizeText];
let sentenceCount = 3; // Default sentence count
// Apply custom options if provided
if (summaryOptions && typeof summaryOptions === "object") {
if (
"sentenceCount" in summaryOptions &&
typeof summaryOptions.sentenceCount === "number"
) {
sentenceCount = summaryOptions.sentenceCount;
}
if (
"stopWords" in summaryOptions &&
Array.isArray(summaryOptions.stopWords)
) {
summarizer.addStopWords(summaryOptions.stopWords);
}
}
const summary = summarizer.extractiveSummarize(
this.raw_text,
sentenceCount
);
// Store in metadata
if (!this.metadata.summary) {
this.metadata.summary = {};
}
this.metadata.summary = {
text: summary,
originalLength: this.raw_text.length,
summaryLength: summary.length,
compressionRatio: summary.length / Math.max(this.raw_text.length, 1),
};
this.logOperation(`Summarized Text (${sentenceCount} sentences)`);
}
/**
* @private
* @async
* @function calculateReadability
* @summary Calculates the readability of the input text.
*/
private async calculateReadability(): Promise<void> {
const textStats = new TextStatistics();
const readabilityResult = textStats.fleschKincaidReadability(
this.raw_text
);
// Store in metadata
if (!this.metadata.readability) {
this.metadata.readability = {};
}
this.metadata.readability = readabilityResult;
this.logOperation("Calculated Readability Metrics");
}
/**
* @private
* @async
* @function detectLanguage
* @summary Detects the language of the input text.
*/
private async detectLanguage(): Promise<void> {
const languageDetector = new LanguageDetector();
// Apply custom language profiles if provided
const languageOptions = this.builtInOptions[Operations.DetectLanguage];
if (
languageOptions &&
typeof languageOptions === "object" &&
"customLanguages" in languageOptions
) {
const customLanguages = languageOptions.customLanguages;
if (customLanguages && typeof customLanguages === "object") {
for (const [language, profile] of Object.entries(customLanguages)) {
if (typeof profile === "object") {
languageDetector.addCustomLanguage(
language,
profile as Record<string, number>
);
}
}
}
}
const detectionResult = languageDetector.detect(this.raw_text);
// Store in metadata
if (!this.metadata.language) {
this.metadata.language = {};
}
this.metadata.language = detectionResult;
this.logOperation(
`Detected Language: ${detectionResult.detectedLanguage}`
);
}
/**
* @private
* @async
* @function compareTexts
* @summary Compares two texts and returns the differences.
*/
private async compareTexts(): Promise<void> {
const textDiff = new TextDiff();
// Get comparison text from options
const compareOptions = this.builtInOptions[Operations.CompareTexts];
if (
!compareOptions ||
typeof compareOptions !== "object" ||
!("compareWith" in compareOptions)
) {
throw new Error(
"CompareTexts operation requires a 'compareWith' text to compare against"
);
}
const compareWith = compareOptions.compareWith as string;
if (typeof compareWith !== "string") {
throw new Error("The 'compareWith' option must be a string");
}
const comparisonResult = textDiff.compare(this.raw_text, compareWith);
// Store in metadata
if (!this.metadata.comparison) {
this.metadata.comparison = {};
}
this.metadata.comparison = comparisonResult;
this.logOperation(
`Compared Texts (${comparisonResult.similarity.toFixed(2)}% similarity)`
);
}
/**
* @function addCustomOperation
* @summary Adds a custom text operation to the analyser dynamically.
* @param {string} commandName - The name of the custom operation to be registered.
* @param {string} logName - The logging name of the custom operation to be registered.
* @param {(text: string) => string} operation - A function that performs the custom operation on the text.
* @param {Object} config - Configuration object for the custom operation.
* @param {(text: string) => string} config.operation - A function that performs the custom operation on the text.
* @param {boolean} [config.isEnabled=false] - Whether to enable the operation immediately.
* @param {Object} [config.metadata] - Optional metadata to be added to the result.
* @param {(text: string) => any} [config.metadataExtractor] - Optional function to extract additional metadata.
*
* @returns {Promise<void>} Resolves when the custom operation is successfully added.
* @throws {Error} If the commandName already exists or if parameters are invalid.
*/
public async addCustomOperation(
commandName: string,
logName: string,
config: {
operation: (text: string) => string;
isEnabled?: boolean;
metadata?: { [key: string]: any };
metadataExtractor?: (text: string) => any;
}
): Promise<void> {
if (!commandName || typeof commandName !== "string") {
throw new Error("Command name must be a non-empty string");
}
if (!logName || typeof logName !== "string") {
throw new Error("Log name must be a non-empty string");
}
if (typeof config.operation !== "function") {
throw new Error("Operation must be a function");
}
// Check if command already exists
if (
Operations[commandName as BuiltInOptions] ||
this.customOperations[commandName]
) {
throw new Error(`Operation "${commandName}" already exists`);
}
// Add the custom operation
this.customOperations[commandName] = async () => {
try {
// Store the original text to potentially use in metadata extraction
const originalText = this.raw_text;
// Apply the operation
this.raw_text = config.operation(this.raw_text);
// Log the operation
this.logOperation(`${logName}`, true);
// Initialize custom metadata if not exists
if (!this.metadata.custom) {
this.metadata = {};
}
// If metadata is provided, add it to the custom metadata
if (config.metadata) {
this.metadata[commandName] = {
...(this.metadata[commandName] || {}), // Preserve existing metadata
...config.metadata, // Add new metadata
};
}
// If a metadata extractor is provided, extract and add metadata
if (config.metadataExtractor) {
const extractedMetadata = config.metadataExtractor(originalText);
this.metadata[commandName] = {
...(this.metadata[commandName] || {}), // Preserve existing metadata
...extractedMetadata, // Add extracted metadata
};
}
} catch (error) {
this.logOperation(
`Error in Custom Operation: ${logName} - ${error}`,
true
);
throw error;
}
};
// Enable the operation if requested
this.builtInOptions[commandName] = config.isEnabled || false;
}
/**
* @function toggleOperation
* @summary Toggles an operation on or off.
* @param {string} commandName - The name of the operation to toggle.
* @param {boolean} isEnabled - Whether to enable the operation.
* @returns {Promise<void>} Resolves when the operation is successfully toggled.
* @throws {Error} If the operation does not exist or is already in the requested state.
*/
public async toggleOperation(
commandName: string,
isEnabled: boolean
): Promise<void> {
// Check if operation exists
if (
!(commandName in this.builtInOptions) &&
!(commandName in Operations) &&
!(commandName in this.customOperations)
) {
throw new Error(
`Operation "${commandName}" not found. Please add it first.`
);
}
// Check if already in requested state
if (this.builtInOptions[commandName] === isEnabled) {
return; // Operation is already in the requested state, do nothing
}
// Toggle the operation
this.builtInOptions[commandName] = isEnabled;
}
/**
* @function enableAllOperations
* @summary Enables all available operations.
* @returns {Promise<void>} Resolves when all operations are enabled.
*/
public async enableAllOperations(): Promise<void> {
// Enable all built-in operations
for (const operation in Operations) {
if (isNaN(Number(operation))) {
// Skip numeric enum keys
this.builtInOptions[Operations[operation as BuiltInOptions]] = true;
}
}
// Enable all custom operations
for (const operation in this.customOperations) {
this.builtInOptions[operation] = true;
}
}
/**
* @function disableAllOperations
* @summary Disables all operations.
* @returns {Promise<void>} Resolves when all operations are disabled.
*/
public async disableAllOperations(): Promise<void> {
for (const operation in this.builtInOptions) {
this.builtInOptions[operation] = false;
}
}
/**
* @function resetText
* @summary Resets the text to the original value or a new value.
* @param {string} [newText] - Optional new text to set.
* @returns {Promise<void>} Resolves when the text is reset.
*/
public async resetText(newText?: string): Promise<void> {
if (newText !== undefined) {
if (typeof newText !== "string") {
throw new Error("New text must be a string");
}
this.raw_text = newText;
}
// Reset all counters and extracted data
this.count = 0;
this.alphacount = 0;
this.numericcount = 0;
this.wordCount = 0;
this.sentenceCount = 0;
this.urls = [];
this.emails = [];
this.phoneNumbers = [];
this.hashtags = [];
this.mentions = [];
// Reset operations log
this.operations = [];
this.logOperation("Text Reset");
}
/**
* @private
* @function logOperation
* @summary Logs the performed operation.
* @param {string} operation - The operation performed on the text.
* @param {string} isCustom - Whether the operation is a custom function.
*/
private logOperation(operation: string, isCustom: boolean = false): void {
this.operations.push(operation);
if (isCustom) {
this.customOperationsList.push(operation);
} else {
this.builtInOperationsList.push(operation);
}
}
/**
* @private
* @async
* @function getResults
* @summary Retrieves the results of the operations performed on the text.
* @returns {Promise<AnalyserResult>} An object containing the results of the analysis.
*/
private async getResults(): Promise<AnalyserExtendedResult> {
const executionTime = this.executionEndTime - this.executionStartTime;
const result: AnalyserExtendedResult = {
purpose: this.operations.join(","),
output: this.raw_text,
operations: [...this.operations],
builtInOperations: [...this.builtInOperationsList],
customOperations: [...this.customOperationsList],
executionTime,
metadata: {
counts: {
characterCount: this.count,
alphabetCount: this.alphacount,
numericCount: this.numericcount,
wordCount: this.wordCount,
sentenceCount: this.sentenceCount,
},
urls: this.urls,
emails: this.emails,
phoneNumbers: this.phoneNumbers,
hashtags: this.hashtags,
mentions: this.mentions,
custom: { ...this.metadata },
},
};
if (this.metadata.sentiment) {
result.sentiment = this.metadata.sentiment as SentimentResult;
}
if (this.metadata.summary) {
result.summary = (this.metadata.summary as any).text as string;
}
if (this.metadata.readability) {
result.readability = this.metadata.readability as ReadabilityResult;
}
if (this.metadata.language) {
result.languageDetection = this.metadata
.language as LanguageDetectionResult;
}
if (this.metadata.comparison) {
result.textComparison = this.metadata.comparison as TextDiffResult;
}
return result;
}
/**
* @async
* @function main
* @summary Executes the text analysis operations based on the provided options.
* @returns {Promise<AnalyserResult>} An object containing the analysis results.
* @throws {Error} If an operation fails.
*/
public async main(): Promise<AnalyserResult> {
this.executionStartTime = performance.now();
try {
for (const [operation, enabled] of Object.entries(
this.builtInOptions
)) {
if (enabled) {
const handler = this.operationHandlers[operation];
if (handler) {
await handler();
} else {
console.warn(`No handler found for operation: ${operation}`);
}
}
}
} catch (error) {
this.logOperation(
`Error: ${error instanceof Error ? error.message : String(error)}`
);
throw error;
} finally {
this.executionEndTime = performance.now();
}
return this.getResults();
}
/**
* @static
* @function createWithEnabledOperations
* @summary Factory method to create an Analyser instance with specific operations enabled.
* @param {string} text - The text to analyze.
* @param {(keyof typeof Operations)[]} operations - The operations to enable.
* @returns {Analyser} A new Analyser instance with the specified operations enabled.
*/
public static createWithEnabledOperations(
text: string,
operations: (keyof typeof Operations)[]
): Analyser {
const options: AnalyserBuiltInOptions = {};
for (const operation of operations) {
if (Operations[operation]) {
options[Operations[operation]] = true;
}
}
return new Analyser(text, options);
}
/**
* @static
* @function batch
* @summary Process multiple strings with the same operations.
* @param {string[]} texts - Array of strings to process.
* @param {AnalyserBuiltInOptions} options - Operations to apply to all texts.
* @returns {Promise<AnalyserResult[]>} Array of results for each text.
*/
public static async batch(
texts: string[],
options: AnalyserBuiltInOptions
): Promise<AnalyserResult[]> {
if (!Array.isArray(texts)) {
throw new Error("Texts must be an array of strings");
}
const results: AnalyserResult[] = [];
for (const text of texts) {
const analyser = new Analyser(text, options);
const result = await analyser.main();
results.push(result);
}
return results;
}
}
}
/**
* @class SentimentAnalyzer
* @summary A utility class for analyzing text sentiment
*/
export class SentimentAnalyzer {
private positiveWords: Set<string>;
private negativeWords: Set<string>;
constructor() {
// Initialize with basic sentiment lexicons
this.positiveWords = new Set([
"good",
"great",
"excellent",
"amazing",
"awesome",
"wonderful",
"happy",
"joy",
"love",
"positive",
"beautiful",
"nice",
"perfect",
"best",
"fantastic",
"delightful",
"pleased",
"glad",
"superb",
]);
this.negativeWords = new Set([
"bad",
"terrible",
"awful",
"horrible",
"poor",
"negative",
"sad",
"angry",
"hate",
"dislike",
"worst",
"disappointing",
"frustrated",
"annoyed",
"miserable",
"unhappy",
"awful",
"dreadful",
]);
}
/**
* @function analyze
* @summary Analyzes the sentiment of the provided text
* @param {string} text - The text to analyze
* @returns {SentimentResult} Object containing sentiment score and classification
*/
public analyze(text: string): SentimentResult {
if (!text || typeof text !== "string") {
throw new Error("Input must be a non-empty string");
}
const words = text.toLowerCase().match(/\b\w+\b/g) || [];
let positiveCount = 0;
let negativeCount = 0;
words.forEach((word) => {
if (this.positiveWords.has(word)) positiveCount++;
if (this.negativeWords.has(word)) negativeCount++;
});
const score = (positiveCount - negativeCount) / words.length;
return {
score,
positiveWordCount: positiveCount,
negativeWordCount: negativeCount,
totalWords: words.length,
classification: this.classifySentiment(score),
};
}
/**
* @private
* @function classifySentiment
* @summary Classifies sentiment based on score
* @param {number} score - The sentiment score
* @returns {SentimentClassification} The classification of sentiment
*/
private classifySentiment(score: number): SentimentClassification {
if (score > 0.15) return "positive";
if (score < -0.05) return "negative";
return "neutral";
}
/**
* @function addCustomLexicon
* @summary Adds custom words to the sentiment lexicons
* @param {Object} lexicon - Object containing positive and negative word arrays
* @param {string[]} lexicon.positive - Array of positive words
* @param {string[]} lexicon.negative - Array of negative words
*/
public addCustomLexicon(lexicon: {
positive?: string[];
negative?: string[];
}): void {
if (lexicon.positive && Array.isArray(lexicon.positive)) {
lexicon.positive.forEach((word) =>
this.positiveWords.add(word.toLowerCase())
);
}
if (lexicon.negative && Array.isArray(lexicon.negative)) {
lexicon.negative.forEach((word) =>
this.negativeWords.add(word.toLowerCase())
);
}
}
}
/**
* @interface SentimentResult
* @summary Result of sentiment analysis
*/
export interface SentimentResult {
score: number;
positiveWordCount: number;
negativeWordCount: number;
totalWords: number;
classification: SentimentClassification;
}
/**
* @type SentimentClassification
* @summary Classifications for sentiment analysis
*/
export type SentimentClassification = "positive" | "negative" | "neutral";
/**
* @class TextSummarizer
* @summary A utility class for summarizing text
*/
export class TextSummarizer {
private stopWords: Set<string>;
constructor() {
// Initialize with common English stop words
this.stopWords = new Set([
"a",
"an",
"the",
"and",
"or",
"but",
"if",
"because",
"as",
"what",
"which",
"this",
"that",
"these",
"those",
"then",
"just",
"so",
"than",
"such",
"when",
"while",
"to",
"of",
"at",
"by",
"for",
"with",
"about",
"against",
"between",
"into",
"through",
"during",
"before",
"after",
"above",
"below",
"from",
"up",
"down",
"in",
"out",
"on",
"off",
"over",
"under",
"again",
"further",
"then",
"once",
"here",
"there",
"all",
"any",
"both",
"each",
"few",
"more",
"most",
"other",
"some",
"such",
"no",
"nor",
"not",
"only",
"own",
"same",
"so",
"than",
"too",
"very",
"can",
"will",
]);
}
/**
* @function extractiveSummarize
* @summary Generates an extractive summary of the text
* @param {string} text - The text to summarize
* @param {number} [sentenceCount=3] - Number of sentences in the summary
* @returns {string} Summarized text
*/
public extractiveSummarize(text: string, sentenceCount: number = 3): string {
if (!text || typeof text !== "string") {
throw new Error("Input must be a non-empty string");
}
// Split into sentences
const sentences = text.match(/[^.!?]+[.!?]+/g) || [];
if (sentences.length <= sentenceCount) {
return text; // Return original if it's already shorter than requested summary
}
// Calculate word frequency
const wordFrequency = this.calculateWordFrequency(text);
// Score sentences based on word importance
const sentenceScores = sentences.map((sentence) => {
const words = sentence.toLowerCase().match(/\b\w+\b/g) || [];
const score = words.reduce((total, word) => {
if (!this.stopWords.has(word)) {
return total + (wordFrequency[word] || 0);
}
return total;
}, 0);
return score / Math.max(words.length, 1);
});
// Get highest scoring sentences
const indexScorePairs = sentenceScores.map((score, index) => ({
index,
score,
}));
const topSentences = indexScorePairs
.sort((a, b) => b.score - a.score)
.slice(0, sentenceCount)
.sort((a, b) => a.index - b.index); // Maintain original order
// Build summary
return topSentences.map((pair) => sentences[pair.index].trim()).join(" ");
}
/**
* @private
* @function calculateWordFrequency
* @summary Calculates frequency of each word in text
* @param {string} text - Input text
* @returns {Record<string, number>} Word frequency map
*/
private calculateWordFrequency(text: string): Record<string, number> {
const words = text.toLowerCase().match(/\b\w+\b/g) || [];
const frequency: Record<string, number> = {};
words.forEach((word) => {
if (!this.stopWords.has(word)) {
frequency[word] = (frequency[word] || 0) + 1;
}
});
return frequency;
}
/**
* @function addStopWords
* @summary Adds custom stop words to the analyzer
* @param {string[]} words - Array of stop words to add
*/
public addStopWords(words: string[]): void {
if (!Array.isArray(words)) {
throw new Error("Words must be an array of strings");
}
words.forEach((word) => this.stopWords.add(word.toLowerCase()));
}
}
/**
* @class TextStatistics
* @summary A utility class for computing readability metrics
*/
export class TextStatistics {
/**
* @function fleschKincaidReadability
* @summary Calculate Flesch-Kincaid readability score
* @param {string} text - Text to analyze
* @returns {ReadabilityResult} Readability scores and metrics
*/
public fleschKincaidReadability(text: string): ReadabilityResult {
if (!text || typeof text !== "string") {
throw new Error("Input must be a non-empty string");
}
const words = text.match(/\b\w+\b/g) || [];
const sentences = text.match(/[^.!?]+[.!?]+/g) || [];
const syllables = this.countSyllables(text);
const wordCount = words.length;
const sentenceCount = sentences.length;
// Prevent division by zero
if (wordCount === 0 || sentenceCount === 0) {
return {
readabilityScore: 0,
gradeLevel: 0,
wordCount,
sentenceCount,
syllableCount: syllables,
avgWordsPerSentence: 0,
avgSyllablesPerWord: 0,
complexity: "unknown",
};
}
const avgWordsPerSentence = wordCount / sentenceCount;
const avgSyllablesPerWord = syllables / wordCount;
// Flesch Reading Ease score
const readabilityScore =
206.835 - 1.015 * avgWordsPerSentence - 84.6 * avgSyllablesPerWord;
// Flesch-Kincaid Grade Level
const gradeLevel =
0.39 * avgWordsPerSentence + 11.8 * avgSyllablesPerWord - 15.59;
return {
readabilityScore: Math.round(readabilityScore * 10) / 10,
gradeLevel: Math.round(gradeLevel * 10) / 10,
wordCount,
sentenceCount,
syllableCount: syllables,
avgWordsPerSentence: Math.round(avgWordsPerSentence * 10) / 10,
avgSyllablesPerWord: Math.round(avgSyllablesPerWord * 100) / 100,
complexity: this.getComplexityLabel(readabilityScore),
};
}
/**
* @private
* @function countSyllables
* @summary Estimates syllable count in text
* @param {string} text - Input text
* @returns {number} Estimated syllable count
*/
private countSyllables(text: string): number {
const words = text.toLowerCase().match(/\b\w+\b/g) || [];
return words.reduce((count, word) => {
// Basic syllable counting heuristics
// This is a simplified approach - a comprehensive implementation would be more complex
return count + this.estimateSyllablesInWord(word);
}, 0);
}
/**
* @private
* @function estimateSyllablesInWord
* @summary Estimates syllables in a word using heuristics
* @param {string} word - Word to analyze
* @returns {number} Estimated syllable count
*/
private estimateSyllablesInWord(word: string): number {
// Remove trailing e
word = word.replace(/e$/, "");
// Count vowel groups
const vowelGroups = word.match(/[aeiouy]+/g);
if (!vowelGroups) return 1; // Minimum one syllable
return vowelGroups.length;
}
/**
* @private
* @function getComplexityLabel
* @summary Get descriptive label for readability score
* @param {number} score - Flesch readability score
* @returns {string} Descriptive complexity label
*/
private getComplexityLabel(score: number): string {
if (score >= 90) return "very easy";
if (score >= 80) return "easy";
if (score >= 70) return "fairly easy";
if (score >= 60) return "standard";
if (score >= 50) return "fairly difficult";
if (score >= 30) return "difficult";
return "very difficult";
}
}
/**
* @interface ReadabilityResult
* @summary Result of readability analysis
*/
export interface ReadabilityResult {
readabilityScore: number;
gradeLevel: number;
wordCount: number;
sentenceCount: number;
syllableCount: number;
avgWordsPerSentence: number;
avgSyllablesPerWord: number;
complexity: string;
}
/**
* @class LanguageDetector
* @summary Detects probable language of text
*/
export class LanguageDetector {
private languageProfiles: Map<string, Map<string, number>>;
constructor() {
this.languageProfiles = new Map();
this.initializeLanguageProfiles();
}
/**
* @private
* @function initializeLanguageProfiles
* @summary Initialize frequency profiles for common languages
*/
private initializeLanguageProfiles(): void {
// English profile (common trigrams)
const englishProfile = new Map<string, number>();
englishProfile.set("the", 1.0);
englishProfile.set("and", 0.95);
englishProfile.set("ing", 0.94);
englishProfile.set("ion", 0.92);
englishProfile.set("ent", 0.9);
englishProfile.set("her", 0.89);
englishProfile.set("tha", 0.88);
englishProfile.set("for", 0.87);
englishProfile.set("ere", 0.85);
englishProfile.set("tio", 0.84);
// Spanish profile (common trigrams)
const spanishProfile = new Map<string, number>();
spanishProfile.set("que", 1.0);
spanishProfile.set("ent", 0.96);
spanishProfile.set("ade", 0.94);
spanishProfile.set("cion", 0.93);
spanishProfile.set("esta", 0.92);
spanishProfile.set("para", 0.9);
spanishProfile.set("los", 0.88);
spanishProfile.set("por", 0.86);
spanishProfile.set("as", 0.84);
spanishProfile.set("es", 0.82);
// French profile (common trigrams)
const frenchProfile = new Map<string, number>();
frenchProfile.set("les", 1.0);
frenchProfile.set("ent", 0.96);
frenchProfile.set("que", 0.94);
frenchProfile.set("tion", 0.92);
frenchProfile.set("pour", 0.9);
frenchProfile.set("dans", 0.88);
frenchProfile.set("une", 0.86);
frenchProfile.set("des", 0.85);
frenchProfile.set("par", 0.84);
frenchProfile.set("est", 0.82);
// German profile (common trigrams)
const germanProfile = new Map<string, number>();
germanProfile.set("der", 1.0);
germanProfile.set("die", 0.97);
germanProfile.set("und", 0.95);
germanProfile.set("sch", 0.93);
germanProfile.set("ein", 0.92);
germanProfile.set("ich", 0.9);
germanProfile.set("den", 0.88);
germanProfile.set("zu", 0.86);
germanProfile.set("das", 0.84);
germanProfile.set("gen", 0.82);
this.languageProfiles.set("english", englishProfile);
this.languageProfiles.set("spanish", spanishProfile);
this.languageProfiles.set("french", frenchProfile);
this.languageProfiles.set("german", germanProfile);
}
/**
* @function detect
* @summary Detects the most likely language of the text
* @param {string} text - The text to analyze
* @returns {LanguageDetectionResult} The detected language and confidence scores
*/
public detect(text: string): LanguageDetectionResult {
if (!text || typeof text !== "string") {
throw new Error("Input must be a non-empty string");
}
const textProfile = this.createTextProfile(text);
const scores = new Map<string, number>();
// Calculate similarity scores for each language
this.languageProfiles.forEach((langProfile, language) => {
const score = this.calculateSimilarity(textProfile, langProfile);
scores.set(language, score);
});
// Find best match
let bestLanguage = "unknown";
let highestScore = -1;
scores.forEach((score, language) => {
if (score > highestScore) {
highestScore = score;
bestLanguage = language;
}
});
// Create result object with all scores
const result: LanguageDetectionResult = {
detectedLanguage: bestLanguage,
confidence: highestScore,
scores: {},
};
scores.forEach((score, language) => {
result.scores[language] = Math.round(score * 100) / 100;
});
return result;
}
/**
* @private
* @function createTextProfile
* @summary Creates a profile of ngrams for the input text
* @param {string} text - Input text
* @returns {Map<string, number>} Frequency map of ngrams
*/
private createTextProfile(text: string): Map<string, number> {
const profile = new Map<string, number>();
const words = text.toLowerCase().match(/\b\w+\b/g) || [];
// Count word frequencies
words.forEach((word) => {
if (word.length >= 2) {
profile.set(word, (profile.get(word) || 0) + 1);
}
// Also count trigrams for more accuracy
if (word.length >= 3) {
for (let i = 0; i <= word.length - 3; i++) {
const trigram = word.substring(i, i + 3);
profile.set(trigram, (profile.get(trigram) || 0) + 1);
}
}
});
return profile;
}
/**
* @private
* @function calculateSimilarity
* @summary Calculates similarity between two profiles
* @param {Map<string, number>} textProfile - Profile of analyzed text
* @param {Map<string, number>} langProfile - Profile of reference language
* @returns {number} Similarity score (0-1)
*/
private calculateSimilarity(
textProfile: Map<string, number>,
langProfile: Map<string, number>
): number {
let matches = 0;
let total = 0;
langProfile.forEach((value, key) => {
if (textProfile.has(key)) {
matches += value * (textProfile.get(key) || 0);
}
total += value;
});
return matches / total;
}
/**
* @function addCustomLanguage
* @summary Adds a new language profile
* @param {string} language - Name of the language
* @param {Record<string, number>} profile - Language profile as object
*/
public addCustomLanguage(
language: string,
profile: Record<string, number>
): void {
if (!language || typeof language !== "string") {
throw new Error("Language name must be a non-empty string");
}
const languageProfile = new Map<string, number>();
Object.entries(profile).forEach(([key, value]) => {
languageProfile.set(key, value);
});
this.languageProfiles.set(language.toLowerCase(), languageProfile);
}
}
/**
* @interface LanguageDetectionResult
* @summary Result of language detection
*/
export interface LanguageDetectionResult {
detectedLanguage: string;
confidence: number;
scores: Record<string, number>;
}
/**
* @class TextDiff
* @summary Compare text and generate difference information
*/
export class TextDiff {
/**
* @function compare
* @summary Compare two texts and calculate their similarity
* @param {string} text1 - First text to compare
* @param {string} text2 - Second text to compare
* @returns {TextDiffResult} Comparison results
*/
public compare(text1: string, text2: string): TextDiffResult {
if (typeof text1 !== "string" || typeof text2 !== "string") {
throw new Error("Both inputs must be strings");
}
// Calculate Levenshtein distance
const distance = this.levenshteinDistance(text1, text2);
// Calculate similarity percentage
const maxLength = Math.max(text1.length, text2.length);
const similarity =
maxLength === 0 ? 100 : ((maxLength - distance) / maxLength) * 100;
// Find common substrings
const commonSubstrings = this.findCommonSubstrings(text1, text2);
// Count added/removed words
const words1 = text1.split(/\s+/).filter((w) => w.length > 0);
const words2 = text2.split(/\s+/).filter((w) => w.length > 0);
const wordDiff = this.getWordDifference(words1, words2);
return {
similarity: Math.round(similarity * 100) / 100,
editDistance: distance,
commonSubstrings: commonSubstrings.slice(0, 5), // Return top 5 common substrings
wordDifference: {
added: wordDiff.added,
removed: wordDiff.removed,
unchanged: wordDiff.unchanged,
addedCount: wordDiff.added.length,
removedCount: wordDiff.removed.length,
unchangedCount: wordDiff.unchanged.length,
},
};
}
/**
* @private
* @function levenshteinDistance
* @summary Calculate Levenshtein distance between two strings
* @param {string} s1 - First string
* @param {string} s2 - Second string
* @returns {number} Edit distance
*/
private levenshteinDistance(s1: string, s2: string): number {
const m = s1.length;
const n = s2.length;
// Create matrix
const dp: number[][] = [];
for (let i = 0; i <= m; i++) {
dp[i] = [];
dp[i][0] = i;
}
for (let j = 0; j <= n; j++) {
dp[0][j] = j;
}
// Fill matrix
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
const cost = s1[i - 1] === s2[j - 1] ? 0 : 1;
dp[i][j] = Math.min(
dp[i - 1][j] + 1, // deletion
dp[i][j - 1] + 1, // insertion
dp[i - 1][j - 1] + cost // substitution
);
}
}
return dp[m][n];
}
/**
* @private
* @function findCommonSubstrings
* @summary Find common substrings between two texts
* @param {string} s1 - First string
* @param {string} s2 - Second string
* @returns {Array<{substring: string, length: number}>} Common substrings
*/
private findCommonSubstrings(
s1: string,
s2: string
): Array<{ substring: string; length: number }> {
const result: Array<{ substring: string; length: number }> = [];
const minLength = 4; // Minimum length for common substring
for (let i = 0; i < s1.length; i++) {
for (let j = 0; j < s2.length; j++) {
let k = 0;
// Find length of common substring
while (
i + k < s1.length &&
j + k < s2.length &&
s1[i + k] === s2[j + k]
) {
k++;
}
if (k >= minLength) {
const substring = s1.substring(i, i + k);
result.push({
substring,
length: k,
});
}
}
}
// Sort by length descending
return result.sort((a, b) => b.length - a.length);
}
/**
* @private
* @function getWordDifference
* @summary Compare word arrays and find differences
* @param {string[]} words1 - First array of words
* @param {string[]} words2 - Second array of words
* @returns {Object} Word difference analysis
*/
private getWordDifference(
words1: string[],
words2: string[]
): {
added: string[];
removed: string[];
unchanged: string[];
} {
const added: string[] = [];
const removed: string[] = [];
const unchanged: string[] = [];
// Count occurrences of each word
const countWords = (words: string[]): Map<string, number> => {
const counts = new Map<string, number>();
words.forEach((word) => {
counts.set(word, (counts.get(word) || 0) + 1);
});
return counts;
};
const counts1 = countWords(words1);
const counts2 = countWords(words2);
// Find unchanged words
counts1.forEach((count, word) => {
const count2 = counts2.get(word) || 0;
const minCount = Math.min(count, count2);
for (let i = 0; i < minCount; i++) {
unchanged.push(word);
}
if (count > count2) {
for (let i = 0; i < count - count2; i++) {
removed.push(word);
}
}
});
// Find added words
counts2.forEach((count, word) => {
const count1 = counts1.get(word) || 0;
if (count > count1) {
for (let i = 0; i < count - count1; i++) {
added.push(word);
}
}
});
return { added, removed, unchanged };
}
}
/**
* @interface TextDiffResult
* @summary Result of text comparison
*/
export interface TextDiffResult {
similarity: number;
editDistance: number;
commonSubstrings: Array<{ substring: string; length: number }>;
wordDifference: {
added: string[];
removed: string[];
unchanged: string[];
addedCount: number;
removedCount: number;
unchangedCount: number;
};
}
/* Copyright (C) 2019 Robert Gerlach
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
export interface Tools {
flip: {
init: () => void;
encode: (text: string) => string;
decode: (text: string) => string;
map: { [key: string]: string };
};
mirror: {
init: () => void;
encode: (text: string) => string;
decode: (text: string) => string;
map: { [key: string]: string };
};
creepify: {
init: () => void;
encode: (text: any) => string;
decode: (text: any) => string;
diacriticsTop: any;
diacriticsMiddle: any;
diacriticsBottom: any;
options: { [key: string]: any };
};
bubbles: {
init: () => void;
encode: (text: any) => string;
decode: (text: any) => string;
map: any;
mapInverse: any;
};
squares: {
init: () => void;
encode: (text: any) => string;
decode: (text: any) => string;
};
roundsquares: {
init: () => void;
encode: (text: any) => string;
decode: (text: any) => string;
};
bent: {
init: () => void;
encode: (text: string) => string;
decode: (text: string) => string;
map: { [key: string]: string };
};
tiny: {
init: () => void;
encode: (text: string) => string;
decode: (text: string) => string;
map: { [key: string]: string };
};
}
export class Lunicode {
tools: Tools;
highSurrogate: number;
codepoint: number;
constructor() {
this.tools = {
// Flip/rotate Text by 180°
flip: {
init: function () {
// invert the map
for (const key in this.map) {
this.map[this.map[key]] = key;
}
},
encode: function (text: string): string {
const ret: string[] = [];
let ch: string;
for (let i = 0, len = text.length; i < len; i++) {
ch = text.charAt(i);
// combining diacritical marks: combine with previous character for ä,ö,ü,...
if (
i > 0 &&
(ch === "\u0324" ||
ch === "\u0317" ||
ch === "\u0316" ||
ch === "\u032e")
) {
ch = this.map[text.charAt(i - 1) + ch];
ret.pop();
} else {
ch = this.map[ch];
if (typeof ch === "undefined") {
ch = text.charAt(i);
}
}
ret.push(ch);
}
return ret.reverse().join("");
},
decode: function (text: string): string {
const ret: string[] = [];
let ch: string;
for (let i = 0, len = text.length; i < len; i++) {
ch = text.charAt(i);
// combining diacritical marks: combine with previous character for ä,ö,ü,...
if (
i > 0 &&
(ch === "\u0324" ||
ch === "\u0317" ||
ch === "\u0316" ||
ch === "\u032e")
) {
ch = this.map[text.charAt(i - 1) + ch];
ret.pop();
} else {
ch = this.map[ch];
if (typeof ch === "undefined") {
ch = text.charAt(i);
}
}
ret.push(ch);
}
return ret.reverse().join("");
},
map: {
// Thanks to
// - David Faden: http://www.revfad.com/flip.html
// - http://en.wikipedia.org/wiki/Transformation_of_text
a: "\u0250",
b: "q",
c: "\u0254",
d: "p",
e: "\u01DD",
f: "\u025F",
g: "\u0253",
h: "\u0265",
i: "\u0131",
j: "\u027E",
k: "\u029E",
l: "\u006C",
m: "\u026F",
n: "u",
r: "\u0279",
t: "\u0287",
v: "\u028C",
w: "\u028D",
y: "\u028E",
A: "\u2200",
B: "ᙠ",
C: "\u0186",
D: "ᗡ",
E: "\u018e",
F: "\u2132",
G: "\u2141",
J: "\u017f",
K: "\u22CA",
L: "\u02e5",
M: "W",
P: "\u0500",
Q: "\u038C",
R: "\u1D1A",
T: "\u22a5",
U: "\u2229",
V: "\u039B",
Y: "\u2144",
"1": "\u21c2",
"2": "\u1105",
"3": "\u0190",
"4": "\u3123",
"5": "\u078e",
"6": "9",
"7": "\u3125",
"&": "\u214b",
".": "\u02D9",
'"': "\u201e",
";": "\u061b",
"[": "]",
"(": ")",
"{": "}",
"?": "\u00BF",
"!": "\u00A1",
"'": ",",
"<": ">",
"\u203E": "_",
"\u00AF": "_",
"\u203F": "\u2040",
"\u2045": "\u2046",
"\u2234": "\u2235",
"\r": "\n",
ß: "ᙠ",
"\u0308": "\u0324",
ä: "ɐ" + "\u0324",
ö: "o" + "\u0324",
ü: "n" + "\u0324",
Ä: "\u2200" + "\u0324",
Ö: "O" + "\u0324",
Ü: "\u2229" + "\u0324",
"´": " \u0317",
é: "\u01DD" + "\u0317",
á: "\u0250" + "\u0317",
ó: "o" + "\u0317",
ú: "n" + "\u0317",
É: "\u018e" + "\u0317",
Á: "\u2200" + "\u0317",
Ó: "O" + "\u0317",
Ú: "\u2229" + "\u0317",
"`": " \u0316",
è: "\u01DD" + "\u0316",
à: "\u0250" + "\u0316",
ò: "o" + "\u0316",
ù: "n" + "\u0316",
È: "\u018e" + "\u0316",
À: "\u2200" + "\u0316",
Ò: "O" + "\u0316",
Ù: "\u2229" + "\u0316",
"^": " \u032E",
ê: "\u01DD" + "\u032e",
â: "\u0250" + "\u032e",
ô: "o" + "\u032e",
û: "n" + "\u032e",
Ê: "\u018e" + "\u032e",
Â: "\u2200" + "\u032e",
Ô: "O" + "\u032e",
Û: "\u2229" + "\u032e",
// TODO: flip more letters with stuff around them. See http://en.wikipedia.org/wiki/Combining_character
},
},
// Mirror text (flip horizontally)
mirror: {
init: function () {
// invert the map
for (const key in this.map) {
this.map[this.map[key]] = key;
}
},
encode: function (text: string): string {
const ret: string[] = [];
let ch: string;
for (let i = 0, len = text.length; i < len; i++) {
ch = text.charAt(i);
// combining diacritical marks: combine with previous character for ä,ö,ü,...
if (
i > 0 &&
(ch === "\u0324" ||
ch === "\u0317" ||
ch === "\u0316" ||
ch === "\u032e")
) {
ch = this.map[text.charAt(i - 1) + ch];
ret.pop();
} else {
ch = this.map[ch];
if (typeof ch === "undefined") {
ch = text.charAt(i);
}
}
ret.push(ch);
}
return ret.reverse().join("");
},
decode: function (text: string): string {
const ret: string[] = [];
let ch: string;
for (let i = 0, len = text.length; i < len; i++) {
ch = text.charAt(i);
// combining diacritical marks: combine with previous character for ä,ö,ü,...
if (
i > 0 &&
(ch === "\u0324" ||
ch === "\u0317" ||
ch === "\u0316" ||
ch === "\u032e")
) {
ch = this.map[text.charAt(i - 1) + ch];
ret.pop();
} else {
ch = this.map[ch];
if (typeof ch === "undefined") {
ch = text.charAt(i);
}
}
ret.push(ch);
}
return ret.reverse().join("");
},
// Thanks to http://www.macchiato.com/unicode/mirrored-ascii
map: {
a: "ɒ",
b: "d",
c: "ɔ",
e: "ɘ",
f: "Ꮈ",
g: "ǫ",
h: "ʜ",
j: "ꞁ",
k: "ʞ",
l: "|",
n: "ᴎ",
p: "q",
r: "ɿ",
s: "ꙅ",
t: "ƚ",
y: "ʏ",
z: "ƹ",
B: "ᙠ",
C: "Ɔ",
D: "ᗡ",
E: "Ǝ",
F: "ꟻ",
G: "Ꭾ",
J: "Ⴑ",
K: "⋊",
L: "⅃",
N: "Ͷ",
P: "ꟼ",
Q: "Ọ",
R: "Я",
S: "Ꙅ",
Z: "Ƹ",
"1": "",
"2": "",
"3": "",
"4": "",
"5": "",
"6": "",
"7": "",
"&": "",
";": "",
"[": "]",
"(": ")",
"{": "}",
"?": "⸮",
"<": ">",
ä: "ɒ" + "\u0308",
ß: "ᙠ",
"´": "`",
é: "ɘ" + "\u0300",
á: "ɒ" + "\u0300",
ó: "ò",
ú: "ù",
É: "Ǝ" + "\u0300",
Á: "À",
Ó: "Ò",
Ú: "Ù",
"`": "´",
è: "ɘ" + "\u0301",
à: "ɒ" + "\u0301",
È: "Ǝ" + "\u0301",
ê: "ɘ" + "\u0302",
â: "ɒ" + "\u0302",
Ê: "Ǝ" + "\u0302",
Ø: "ᴓ",
ø: "ᴓ",
},
},
// Thanks to Michael S. Kaplan: http://blogs.msdn.com/b/michkap/archive/2006/02/17/533929.aspx
// Creepify.
creepify: {
init: function () {
// Sort diacritics in top, bottom or middle
for (let i = 768; i <= 789; i++) {
this.diacriticsTop.push(String.fromCharCode(i));
}
for (let i = 790; i <= 819; i++) {
if (i != 794 && i != 795) {
this.diacriticsBottom.push(String.fromCharCode(i));
}
}
this.diacriticsTop.push(String.fromCharCode(794));
this.diacriticsTop.push(String.fromCharCode(795));
for (let i = 820; i <= 824; i++) {
this.diacriticsMiddle.push(String.fromCharCode(i));
}
for (let i = 825; i <= 828; i++) {
this.diacriticsBottom.push(String.fromCharCode(i));
}
for (let i = 829; i <= 836; i++) {
this.diacriticsTop.push(String.fromCharCode(i));
}
this.diacriticsTop.push(String.fromCharCode(836));
this.diacriticsBottom.push(String.fromCharCode(837));
this.diacriticsTop.push(String.fromCharCode(838));
this.diacriticsBottom.push(String.fromCharCode(839));
this.diacriticsBottom.push(String.fromCharCode(840));
this.diacriticsBottom.push(String.fromCharCode(841));
this.diacriticsTop.push(String.fromCharCode(842));
this.diacriticsTop.push(String.fromCharCode(843));
this.diacriticsTop.push(String.fromCharCode(844));
this.diacriticsBottom.push(String.fromCharCode(845));
this.diacriticsBottom.push(String.fromCharCode(846));
// 847 (U+034F) is invisible http://en.wikipedia.org/wiki/Combining_grapheme_joiner
this.diacriticsTop.push(String.fromCharCode(848));
this.diacriticsTop.push(String.fromCharCode(849));
this.diacriticsTop.push(String.fromCharCode(850));
this.diacriticsBottom.push(String.fromCharCode(851));
this.diacriticsBottom.push(String.fromCharCode(852));
this.diacriticsBottom.push(String.fromCharCode(853));
this.diacriticsBottom.push(String.fromCharCode(854));
this.diacriticsTop.push(String.fromCharCode(855));
this.diacriticsTop.push(String.fromCharCode(856));
this.diacriticsBottom.push(String.fromCharCode(857));
this.diacriticsBottom.push(String.fromCharCode(858));
this.diacriticsTop.push(String.fromCharCode(859));
this.diacriticsBottom.push(String.fromCharCode(860));
this.diacriticsTop.push(String.fromCharCode(861));
this.diacriticsTop.push(String.fromCharCode(861));
this.diacriticsBottom.push(String.fromCharCode(863));
this.diacriticsTop.push(String.fromCharCode(864));
this.diacriticsTop.push(String.fromCharCode(865));
},
encode: function (text: any) {
let newText = "",
newChar;
for (let i in text) {
newChar = text[i];
// Middle
// Put just one of the middle characters there, or it gets crowded
if (this.options.middle) {
newChar +=
this.diacriticsMiddle[
Math.floor(Math.random() * this.diacriticsMiddle.length)
];
}
// Top
if (this.options.top) {
// Put up to this.options.maxHeight random diacritics on top.
// optionally fluctuate the number via the randomization value (0-100%)
// randomization 100%: 0 to maxHeight
// 30%: 70% of maxHeight to maxHeight
// x%: 100-x% of maxHeight to maxHeight
var diacriticsTopLength = this.diacriticsTop.length - 1;
for (
let count = 0,
len =
this.options.maxHeight -
Math.random() *
((this.options.randomization / 100) *
this.options.maxHeight);
count < len;
count++
) {
newChar +=
this.diacriticsTop[
Math.floor(Math.random() * diacriticsTopLength)
];
}
}
// Bottom
if (this.options.bottom) {
var diacriticsBottomLength = this.diacriticsBottom.length - 1;
for (
let count = 0,
len =
this.options.maxHeight -
Math.random() *
((this.options.randomization / 100) *
this.options.maxHeight);
count < len;
count++
) {
newChar +=
this.diacriticsBottom[
Math.floor(Math.random() * diacriticsBottomLength)
];
}
}
newText += newChar;
}
return newText;
},
decode: function (text: any) {
let newText = "",
charCode;
for (let i in text) {
charCode = text[i].charCodeAt(0);
if (charCode < 768 || charCode > 865) {
newText += text[i];
}
}
return newText;
},
diacriticsTop: [],
diacriticsMiddle: [],
diacriticsBottom: [],
options: {
top: true,
middle: true,
bottom: true,
maxHeight: 15, // How many diacritic marks shall we put on top/bottom?
randomization: 100, // 0-100%. maxHeight 100 and randomization 20%: the height goes from 80 to 100. randomization 70%, height goes from 30 to 100.
},
},
// Circles around Letters. Uses special circle characters for some letters and combining characters for the rest
// Thanks to
// - Alan Wood: http://www.alanwood.net/unicode/enclosed_alphanumerics.html
bubbles: {
init: function () {
// Numbers
for (let i = 49; i <= 57; i++) {
this.map[String.fromCharCode(i)] = String.fromCharCode(i + 9263);
}
this.map["0"] = "\u24ea";
// Capital letters
for (let i = 65; i <= 90; i++) {
this.map[String.fromCharCode(i)] = String.fromCharCode(i + 9333);
}
// Lower letters
for (let i = 97; i <= 122; i++) {
this.map[String.fromCharCode(i)] = String.fromCharCode(i + 9327);
}
// invert the map
for (let i in this.map) {
this.mapInverse[this.map[i]] = i;
}
},
encode: function (text: any) {
let ret: any = "",
ch,
first = true;
for (let i in text) {
ch = this.map[text[i]];
// No dedicated circled character available? Use a Combining Diacritical Mark surrounded
// with non-breaking spaces, so it doesn't overlap
if (typeof ch == "undefined") {
if (text[i].charCodeAt(0) >= 33) {
ch = text[i] + String.fromCharCode(8413);
if (!first) {
ch =
String.fromCharCode(8239) +
String.fromCharCode(160) +
String.fromCharCode(160) +
String.fromCharCode(8239) +
ch;
}
} else {
ch = text[i];
}
}
ret += ch;
first = ch == "\n";
}
return ret;
},
decode: function (text: any) {
let ret: any = "",
ch,
newRet = "";
for (let i in text) {
ch = this.mapInverse[text[i]];
ret += typeof ch == "undefined" ? text[i] : ch;
}
for (let i in ret) {
ch = ret[i].charCodeAt(0);
if (ch != 160 && ch != 8239 && ch != 8413) {
newRet += ret[i];
}
}
return newRet;
},
map: {},
mapInverse: {},
},
// Puts a Square Combining Character after a letter, thus ensquaring it, squarily.
squares: {
init: function () {},
encode: function (text: any) {
let ret = "",
ch,
first = true;
for (let i in text) {
if (text[i].charCodeAt(0) >= 33) {
ch = text[i] + String.fromCharCode(8414);
if (!first) {
ch =
String.fromCharCode(8239) +
String.fromCharCode(160) +
String.fromCharCode(160) +
String.fromCharCode(8239) +
ch;
}
} else {
ch = text[i];
}
ret += ch;
first = ch == "\n";
}
return ret;
},
decode: function (text: any) {
let ret = "",
ch;
for (let i in text) {
ch = text[i].charCodeAt(0);
if (ch != 160 && ch != 8239 && ch != 8414) {
ret += text[i];
}
}
return ret;
},
},
// Same as squares, just round.
roundsquares: {
init: function () {},
encode: function (text: any) {
let ret = "",
ch,
first = true;
for (let i in text) {
if (text[i].charCodeAt(0) >= 33) {
ch = text[i] + String.fromCharCode(8419);
if (!first) {
ch =
String.fromCharCode(160) +
String.fromCharCode(160) +
String.fromCharCode(160) +
ch;
}
} else {
ch = text[i];
}
ret += ch;
first = ch == "\n";
}
return ret;
},
decode: function (text: any) {
let ret = "",
ch;
for (let i in text) {
ch = text[i].charCodeAt(0);
if (ch != 160 && ch != 8239 && ch != 8419) {
ret += text[i];
}
}
return ret;
},
},
// Weird looking alternatives to most characters
bent: {
init: function () {
// invert the map
for (const i in this.map) {
this.map[this.map[i]] = i;
}
},
encode: function (text: any) {
let ret = "",
ch;
for (let i = 0, len = text.length; i < len; i++) {
ch = this.map[text.charAt(i)];
if (typeof ch == "undefined") {
ch = text.charAt(i);
}
ret += ch;
}
return ret;
},
decode: function (text: any) {
let ret = "",
ch;
for (let i = 0, len = text.length; i < len; i++) {
ch = this.map[text.charAt(i)];
if (typeof ch == "undefined") {
ch = text.charAt(i);
}
ret += ch;
}
return ret;
},
// Thanks to Eddie Ringle for most lowercase letters: http://funicode.com
map: {
a: "ą",
b: "ҍ",
c: "ç",
d: "ժ",
e: "ҽ",
f: "ƒ",
g: "ց",
h: "հ",
i: "ì",
j: "ʝ",
k: "ҟ",
l: "Ӏ",
m: "ʍ",
n: "ղ",
o: "օ",
p: "ք",
q: "զ",
r: "ɾ",
s: "ʂ",
t: "է",
u: "մ",
v: "ѵ",
w: "ա",
x: "×",
y: "վ",
z: "Հ",
A: "Ⱥ",
B: "β",
C: "↻",
D: "Ꭰ",
E: "Ɛ",
F: "Ƒ",
G: "Ɠ",
H: "Ƕ",
I: "į",
J: "ل",
K: "Ҡ",
L: "Ꝉ",
M: "Ɱ",
N: "ហ",
O: "ට",
P: "φ",
Q: "Ҩ",
R: "འ",
S: "Ϛ",
T: "Ͳ",
U: "Ա",
V: "Ỽ",
W: "చ",
X: "ჯ",
Y: "Ӌ",
Z: "ɀ",
"0": "⊘",
"1": "������",
"2": "ϩ",
"3": "Ӡ",
"4": "५",
"5": "Ƽ",
"6": "Ϭ",
"7": "7",
"8": "������",
"9": "९",
"&": "⅋",
"(": "{",
")": "}",
"{": "(",
"}": ")",
ä: "ą" + "\u0308",
ö: "օ" + "\u0308",
ü: "մ" + "\u0308",
Ä: "Ⱥ" + "\u0308",
Ö: "ට" + "\u0308",
Ü: "Ա" + "\u0308",
é: "ҽ" + "\u0301",
á: "ą" + "\u0301",
ó: "օ" + "\u0301",
ú: "մ" + "\u0301",
É: "Ɛ" + "\u0301",
Á: "Ⱥ" + "\u0301",
Ó: "ට" + "\u0301",
Ú: "Ա" + "\u0301",
è: "ҽ" + "\u0300",
à: "ą" + "\u0300",
ò: "օ" + "\u0300",
ù: "մ" + "\u0300",
È: "Ɛ" + "\u0300",
À: "Ⱥ" + "\u0300",
Ò: "ට" + "\u0300",
Ù: "Ա" + "\u0300",
ê: "ҽ" + "\u0302",
â: "ą" + "\u0302",
ô: "օ" + "\u0302",
û: "մ" + "\u0302",
Ê: "Ɛ" + "\u0302",
Â: "Ⱥ" + "\u0302",
Ô: "ට" + "\u0302",
Û: "Ա" + "\u0302",
},
},
// Tiny Capitals
tiny: {
init: function () {
// invert the map
for (const i in this.map) {
this.map[this.map[i]] = i;
}
},
encode: function (text: any) {
let ret = "",
ch;
text = text.toUpperCase();
for (let i = 0, len = text.length; i < len; i++) {
ch = this.map[text.charAt(i)];
if (typeof ch == "undefined") {
ch = text.charAt(i);
}
ret += ch;
}
return ret;
},
decode: function (text: any) {
let ret = "",
ch;
for (let i = 0, len = text.length; i < len; i++) {
ch = this.map[text.charAt(i)];
if (typeof ch == "undefined") {
ch = text.charAt(i);
}
ret += ch;
}
return ret;
},
// TODO: Find small lower case letters
map: {
A: "ᴀ",
B: "ʙ",
C: "ᴄ",
D: "ᴅ",
E: "ᴇ",
F: "ꜰ",
G: "ɢ",
H: "ʜ",
I: "ɪ",
J: "ᴊ",
K: "ᴋ",
L: "ʟ",
M: "ᴍ",
N: "ɴ",
O: "ᴏ",
P: "ᴘ",
Q: "Q",
R: "ʀ",
S: "ꜱ",
T: "ᴛ",
U: "ᴜ",
V: "ᴠ",
W: "ᴡ",
X: "x",
Y: "ʏ",
Z: "ᴢ",
},
},
};
this.highSurrogate = 0;
this.codepoint = 0;
// Initialize each tool
for (const i in this.tools) {
this.tools[i as keyof Tools].init();
}
}
// Encode every character: U+00A0 ->   etc.
getHTML(text: string): string {
let html = "";
let ch: number;
let lastSpaceWasNonBreaking = true; // for alternating [non-breaking] spaces
for (let i = 0, len = text.length; i < len; i++) {
ch = text.charCodeAt(i);
// line break: add <br>\n
if (ch === 10 || ch === 13) {
html += "<br>\n";
lastSpaceWasNonBreaking = true;
// space: add alternating space and non-breaking space (U+00A0). Otherwise
// a series of normal spaces would collapse to one in the browser
} else if (ch === 32) {
if (lastSpaceWasNonBreaking) {
html += " ";
lastSpaceWasNonBreaking = false;
} else {
html += " ";
lastSpaceWasNonBreaking = true;
}
// Normal character: Decode. Special cases for higher numbers:
// http://en.wikipedia.org/wiki/Mapping_of_Unicode_characters#Surrogates
} else {
// Character is high surrogate: Remember and continue
if (ch >= 0xd800 && ch <= 0xdbff) {
this.highSurrogate = ch;
this.codepoint = 0;
// last character was high surrogate: Combine with low surrogate
} else if (this.highSurrogate > 0) {
// If char is low surrogate:
if (ch >= 0xdc00 && ch <= 0xdfff) {
this.codepoint =
(this.highSurrogate - 0xd800) * 1024 + (ch - 0xdc00) + 0x10000;
}
this.highSurrogate = 0;
// no surrogates: Just take the character
} else {
this.codepoint = ch;
}
if (this.codepoint !== 0) {
html += "&#x" + this.codepoint.toString(16) + ";";
lastSpaceWasNonBreaking = true;
}
}
}
return html;
}
}
// IMPORTS
import { Lunicode } from "./lunicode";
import esrever from "esrever";
import $ from "jquery";
/*
Author : Rohit Kumar
Version : 0.0.1
App Name : Fancy Text Generator
Website: https://www.iamrohit.in
Youtube: https://youtube.com/AskRohit
*/
// Primary Declaration
let reverseIsDisabled: boolean = false;
const luni = new Lunicode();
luni.tools.creepify.options.maxHeight = 10;
// Main Code
function createMap(chars: string): string {
const alphanum = [
"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
"m",
"n",
"o",
"p",
"q",
"r",
"s",
"t",
"u",
"v",
"w",
"x",
"y",
"z",
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
];
let i: number = 0;
let map: { [key: string]: string } = {};
for (const c of chars) {
map[alphanum[i]] = c;
i++;
}
return JSON.stringify(map);
}
function crazyWithFlourishOrSymbols(text: string) {
if (Math.random() < 0.7) return wrapInSymbols(crazifyText(text), 2);
else return wrapInFlourish(crazifyText(text));
}
function forward(text: string) {
text = text.trim();
if (text === "") {
return "";
}
var finalText = "";
finalText += applyCharMap(oldEnglishCharMap, text) + "\n\n";
finalText += applyCharMap(medievalCharMap, text) + "\n\n";
finalText += fullCrazy(text) + "\n\n";
finalText += applyCharMap(cursiveCharMap, text) + "\n\n";
finalText += scriptify(text) + "\n\n";
finalText += applyCharMap(doubleStruckCharMap, text) + "\n\n";
finalText += applyCharMap(wideTextCharMap, text) + "\n\n";
finalText += cuteText(text) + "\n\n";
finalText += luni.tools.tiny.encode(text) + "\n\n";
finalText += luni.tools.flip.encode(text) + "\n\n";
finalText += luni.tools.roundsquares.encode(text) + "\n\n";
finalText += luni.tools.squares.encode(text) + "\n\n";
finalText += applyCharMap(squaresCharMap, text) + "\n\n";
finalText += luni.tools.mirror.encode(text) + "\n\n";
finalText += luni.tools.creepify.encode(text) + "\n\n";
finalText += applyCharMap(invertedSquaresCharMap, text) + "\n\n";
finalText += applyCharMap(subscriptCharMap, text) + "\n\n";
finalText += applyCharMap(superscriptCharMap, text) + "\n\n";
finalText += luni.tools.bubbles.encode(text) + "\n\n";
finalText += applyCharMap(squiggleCharMap, text) + "\n\n";
finalText += applyCharMap(squiggle2CharMap, text) + "\n\n";
finalText += applyCharMap(squiggle3CharMap, text) + "\n\n";
finalText += applyCharMap(squiggle4CharMap, text) + "\n\n";
finalText += applyCharMap(squiggle5CharMap, text) + "\n\n";
finalText += applyCharMap(squiggle6CharMap, text) + "\n\n";
finalText += applyCharMap(boldCharMap, text) + "\n\n";
finalText += applyCharMap(boldSansCharMap, text) + "\n\n";
finalText += applyCharMap(italicCharMap, text) + "\n\n";
finalText += applyCharMap(boldItalicCharMap, text) + "\n\n";
finalText += applyCharMap(monospaceCharMap, text) + "\n\n";
finalText += applyCharMap(upperAnglesCharMap, text) + "\n\n";
finalText += applyCharMap(greekCharMap, text) + "\n\n";
finalText += applyCharMap(symbolsCharMap, text) + "\n\n";
finalText += applyCharMap(currencyCharMap, text) + "\n\n";
finalText += applyCharMap(asianStyleCharMap, text) + "\n\n";
finalText += applyCharMap(asianStyle2CharMap, text) + "\n\n";
finalText += thickBlockFramed(text) + "\n\n";
finalText += diametricAngleFrame(text) + "\n\n";
finalText += wavyJoiner(text) + "\n\n";
finalText += dottyJoiner(text) + "\n\n";
finalText += kirbyHug(text) + "\n\n";
finalText += vaporwaveText(text) + "\n\n";
finalText += littleSparkles(text) + "\n\n";
finalText += weirdBox(text) + "\n\n";
finalText += firework(text) + "\n\n";
finalText += applyCharMap(bentTextCharMap, text) + "\n\n";
finalText += applyCharMap(neonCharMap, text) + "\n\n";
finalText += applyCharMap(futureAlienCharMap, text) + "\n\n";
finalText += strikeThrough(text) + "\n\n";
finalText += tildeStrikeThrough(text) + "\n\n";
finalText += slashThrough(text) + "\n\n";
finalText += underline(text) + "\n\n";
finalText += doubleUnderline(text) + "\n\n";
finalText += stinky(text) + "\n\n";
finalText += heartsBetween(text) + "\n\n";
finalText += arrowBelow(text) + "\n\n";
finalText += crossAboveBelow(text) + "\n\n";
finalText += "Wingdings: " + wingdings(text) + "\n\n";
finalText += cuteText(text) + "\n\n";
finalText += cuteText(text) + "\n\n";
finalText += cuteText(text) + "\n\n";
finalText += cuteText(text) + "\n\n";
finalText += cuteText(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += crazyWithFlourishOrSymbols(text) + "\n\n";
finalText += cuteText(text) + "\n\n";
finalText += cuteText(text) + "\n\n";
finalText += cuteText(text) + "\n\n";
return finalText.trim();
}
// CHAR MAP
const AsianChars: string =
"リサフランク現代のコンピュ竹内 まりや若者が履く流行のスニーカー真夜中のドアホットドッグマスターストライカーソニーブギ新しい日の誕生ライフ - ヒスイ蒸気波 無線゠ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶヷヸヹヺ・ーヽヾヿぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんゔゕゖ゙゚゛゜ゝゞゟ亜哀挨愛曖悪握圧扱宛嵐安案暗以衣位囲医依委威為畏胃尉異移萎偉椅彙意違維慰遺緯域育壱逸茨芋引印因咽姻員院淫陰飲隠韻右宇羽雨唄鬱畝浦運雲永泳英映栄営詠影鋭衛易疫益液駅悦越謁閲円延沿炎怨宴媛援園煙猿遠鉛塩演縁艶汚王凹央応往押旺欧殴桜翁奥横岡屋億憶臆虞乙俺卸音恩温穏下化火加可仮何花佳価果河苛科";
const wingdingsCharMap: [string, string][] = [
["0", "📁︎"],
["1", "📂︎"],
["2", "📄︎"],
["3", "🗏︎"],
["4", "🗐︎"],
["5", "🗄︎"],
["6", "⌛︎"],
["7", "🖮︎"],
["8", "🖰︎"],
["9", "🖲︎"],
["!", "✏︎"],
['"', "✂︎"],
["#", "✁︎"],
["$", "👓︎"],
["%", "🕭︎"],
["&", "🕮︎"],
["'", "🕯︎"],
["(", "🕿︎"],
[")", "✆︎"],
["*", "🖂︎"],
["+", "🖃︎"],
[",", "📪︎"],
["-", "📫︎"],
[".", "📬︎"],
["/", "📭︎"],
[":", "🖳︎"],
[";", "🖴︎"],
["<", "🖫︎"],
["=", "🖬︎"],
[">", "✇︎"],
["?", "✍︎"],
["A", "✌︎"],
["B", "👌︎"],
["C", "👍︎"],
["D", "👎︎"],
["E", "☜︎"],
["F", "☞︎"],
["G", "☝︎"],
["H", "☟︎"],
["I", "✋︎"],
["J", "☺︎"],
["K", "😐︎"],
["L", "☹︎"],
["M", "💣︎"],
["N", "☠︎"],
["O", "⚐︎"],
["P", "🏱︎"],
["Q", "✈︎"],
["R", "☼︎"],
["S", "💧︎"],
["T", "❄︎"],
["U", "🕆︎"],
["V", "✞︎"],
["W", "🕈︎"],
["X", "✠︎"],
["Y", "✡︎"],
["Z", "☪︎"],
["[", "☯︎"],
["\\", "ॐ︎"],
["]", "☸︎"],
["^", "♈︎"],
["_", "♉︎"],
["`", "♊︎"],
["a", "♋︎"],
["b", "♌︎"],
["c", "♍︎"],
["d", "♎︎"],
["e", "♏︎"],
["f", "♐︎"],
["g", "♑︎"],
["h", "♒︎"],
["i", "♓︎"],
["j", "🙰"],
["k", "🙵"],
["l", "●︎"],
["m", "❍︎"],
["n", "■︎"],
["o", "□︎"],
["p", "◻︎"],
["q", "❑︎"],
["r", "❒︎"],
["s", "⬧︎"],
["t", "⧫︎"],
["u", "◆︎"],
["v", "❖︎"],
["w", "⬥︎"],
["x", "⌧︎"],
["y", "⍓︎"],
["z", "⌘︎"],
["{", "❀︎"],
["|", "✿︎"],
["}", "❝︎"],
["~", "❞︎"],
[" ", "▯︎"],
["€", "⓪︎"],
[" ", "①︎"],
["‚", "②︎"],
["ƒ", "③︎"],
["„", "④︎"],
["…", "⑤︎"],
["†", "⑥︎"],
["‡", "⑦︎"],
["ˆ", "⑧︎"],
["‰", "⑨︎"],
["Š", "⑩︎"],
["‹", "⓿︎"],
["Œ", "❶︎"],
[" ", "❷︎"],
["Ž", "❸︎"],
[" ", "❹︎"],
[" ", "❺︎"],
["‘", "❻︎"],
["’", "❼︎"],
["“", "❽︎"],
["”", "❾︎"],
["•", "❿︎"],
["–", "◻︎"],
["—", "◻︎"],
["˜", "◻︎"],
["™", "◻︎"],
["š", "◻︎"],
["›", "◻︎"],
["œ", "◻︎"],
[" ", "◻︎"],
["ž", "·︎"],
["Ÿ", "•︎"],
["¡", "○︎"],
["¢", "⭕︎"],
["£", "◻︎"],
["¤", "◉︎"],
["¥", "◎︎"],
["¦", "◻︎"],
["§", "▪︎"],
["¨", "◻︎"],
["©", "◻︎"],
["ª", "✦︎"],
["«", "★︎"],
["¬", "✶︎"],
["®", "✹︎"],
["¯", "✵︎"],
["°", "◻︎"],
["±", "⌖︎"],
["²", "⟡︎"],
["³", "⌑︎"],
["´", "◻︎"],
["µ", "✪︎"],
["¶", "✰︎"],
["·", "🕐︎"],
["¸", "🕑︎"],
["¹", "🕒︎"],
["º", "🕓︎"],
["»", "🕔︎"],
["¼", "🕕︎"],
["½", "🕖︎"],
["¾", "🕗︎"],
["¿", "🕘︎"],
["À", "🕙︎"],
["Á", "🕚︎"],
["Â", "🕛︎"],
["Ã", "◻︎"],
["Ä", "◻︎"],
["Å", "◻︎"],
["Æ", "◻︎"],
["Ç", "◻︎"],
["È", "◻︎"],
["É", "◻︎"],
["Ê", "◻︎"],
["Ë", "◻︎"],
["Ì", "◻︎"],
["Í", "◻︎"],
["Î", "◻︎"],
["Ï", "◻︎"],
["Ð", "◻︎"],
["Ñ", "◻︎"],
["Ò", "◻︎"],
["Ó", "◻︎"],
["Ô", "◻︎"],
["Õ", "⌫︎"],
["Ö", "⌦︎"],
["×", "◻︎"],
["Ø", "➢︎"],
["Ù", "◻︎"],
["Ú", "◻︎"],
["Û", "◻︎"],
["Ü", "➲︎"],
["Ý", "◻︎"],
["Þ", "◻︎"],
["ß", "◻︎"],
["à", "◻︎"],
["á", "◻︎"],
["â", "◻︎"],
["ã", "◻︎"],
["ä", "◻︎"],
["å", "◻︎"],
["æ", "◻︎"],
["ç", "◻︎"],
["è", "➔︎"],
["é", "◻︎"],
["ê", "◻︎"],
["ë", "◻︎"],
["ì", "◻︎"],
["í", "◻︎"],
["î", "◻︎"],
["ï", "⇦︎"],
["ð", "⇨︎"],
["ñ", "⇧︎"],
["ò", "⇩︎"],
["ó", "⬄︎"],
["ô", "⇳︎"],
["õ", "⬀︎"],
["ö", "⬁︎"],
["÷", "⬃︎"],
["ø", "⬂︎"],
["ù", "▭︎"],
["ú", "▫︎"],
["û", "✗︎"],
["ü", "✓︎"],
["ý", "☒︎"],
["þ", "☑︎"],
["ÿ", "◻︎"],
];
const vaporwaveCharMap: { [key: string]: string } = {
" ": " ",
"`": "`",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
"0": "0",
"-": "-",
"=": "=",
"~": "~",
"!": "!",
"@": "@",
"#": "#",
$: "$",
"%": "%",
"^": "^",
"&": "&",
"*": "*",
"(": "(",
")": ")",
_: "_",
"+": "+",
q: "q",
w: "w",
e: "e",
r: "r",
t: "t",
y: "y",
u: "u",
i: "i",
o: "o",
p: "p",
"[": "[",
"]": "]",
"\\": "\\",
Q: "Q",
W: "W",
E: "E",
R: "R",
T: "T",
Y: "Y",
U: "U",
I: "I",
O: "O",
P: "P",
"{": "{",
"}": "}",
"|": "|",
a: "a",
s: "s",
d: "d",
f: "f",
g: "g",
h: "h",
j: "j",
k: "k",
l: "l",
";": ";",
"'": "'",
A: "A",
S: "S",
D: "D",
F: "F",
G: "G",
H: "H",
J: "J",
K: "K",
L: "L",
":": ":",
'"': '"',
z: "z",
x: "x",
c: "c",
v: "v",
b: "b",
n: "n",
m: "m",
",": ",",
".": ".",
"/": "/",
Z: "Z",
X: "X",
C: "C",
V: "V",
B: "B",
N: "N",
M: "M",
"<": "<",
">": ">",
"?": "?",
};
const flourishArray: string[] = [
"★·.·´¯`·.·★ [[text]] ★·.·´¯`·.·★",
"▁ ▂ ▄ ▅ ▆ ▇ █ [[text]] █ ▇ ▆ ▅ ▄ ▂ ▁",
"°°°·.°·..·°¯°·._.· [[text]] ·._.·°¯°·.·° .·°°°",
"¸,ø¤º°`°º¤ø,¸¸,ø¤º° [[text]] °º¤ø,¸¸,ø¤º°`°º¤ø,¸",
"ıllıllı [[text]] ıllıllı",
"•?((¯°·._.• [[text]] •._.·°¯))؟•",
"▌│█║▌║▌║ [[text]] ║▌║▌║█│▌",
"׺°”˜`”°º× [[text]] ׺°”˜`”°º×",
"•]••´º´•» [[text]] «•´º´••[•",
"*•.¸♡ [[text]] ♡¸.•*",
"╰☆☆ [[text]] ☆☆╮",
".•°¤*(¯`★´¯)*¤° [[text]] °¤*(¯´★`¯)*¤°•.",
"(¯´•._.• [[text]] •._.•´¯)",
"¸„.-•~¹°”ˆ˜¨ [[text]] ¨˜ˆ”°¹~•-.„¸",
"░▒▓█ [[text]] █▓▒░",
"░▒▓█►─═ [[text]] ═─◄█▓▒░",
"★彡 [[text]] 彡★",
"•´¯`•. [[text]] .•´¯`•",
"§.•´¨'°÷•..× [[text]] ×,.•´¨'°÷•..§",
"•°¯`•• [[text]] ••´¯°•",
"(¯`*•.¸,¤°´✿.。.:* [[text]] *.:。.✿`°¤,¸.•*´¯)",
"|!¤*'~``~'*¤!| [[text]] |!¤*'~``~'*¤!|",
"•._.••´¯``•.¸¸.•` [[text]] `•.¸¸.•´´¯`••._.•",
"¸„.-•~¹°”ˆ˜¨ [[text]] ¨˜ˆ”°¹~•-.„¸",
"(¯´•._.• [[text]] •._.•´¯)",
"••¤(`×[¤ [[text]] ¤]×´)¤••",
"•´¯`•» [[text]] «•´¯`•",
" .o0×X×0o. [[text]] .o0×X×0o.",
"¤¸¸.•´¯`•¸¸.•..>> [[text]] <<..•.¸¸•´¯`•.¸¸¤",
"—(••÷[ [[text]] ]÷••)—",
"¸,ø¤º°`°º¤ø,¸ [[text]] ¸,ø¤º°`°º¤ø,¸",
"`•.¸¸.•´´¯`••._.• [[text]] •._.••`¯´´•.¸¸.•`",
",-*' ^ '~*-.,_,.-*~ [[text]] ~*-.,_,.-*~' ^ '*-,",
"`•.,¸¸,.•´¯ [[text]] ¯`•.,¸¸,.•´",
"↤↤↤↤↤ [[text]] ↦↦↦↦↦",
"➶➶➶➶➶ [[text]] ➷➷➷➷➷",
"↫↫↫↫↫ [[text]] ↬↬↬↬↬",
"·.¸¸.·♩♪♫ [[text]] ♫♪♩·.¸¸.·",
"【。_。】 [[text]] 【。_。】",
"]|I{•------» [[text]] «------•}I|[",
"▀▄▀▄▀▄ [[text]] ▄▀▄▀▄▀",
"(-_-) [[text]] (-_-)",
"•´¯`•. [[text]] .•´¯`•",
"-漫~*'¨¯¨'*·舞~ [[text]] ~舞*'¨¯¨'*·~漫-",
"๑۞๑,¸¸,ø¤º°`°๑۩ [[text]] ๑۩ ,¸¸,ø¤º°`°๑۞๑",
".•°¤*(¯`★´¯)*¤° [[text]] °¤*(¯´★`¯)*¤°•.",
"••.•´¯`•.•• [[text]] ••.•´¯`•.••",
"¤¸¸.•´¯`•¸¸.•..>> [[text]] <<..•.¸¸•´¯`•.¸¸¤",
"◦•●◉✿ [[text]] ✿◉●•◦",
"╚»★«╝ [[text]] ╚»★«╝",
"-·=»‡«=·- [[text]] -·=»‡«=·-",
"∙∙·▫▫ᵒᴼᵒ▫ₒₒ▫ᵒᴼᵒ▫ₒₒ▫ᵒᴼᵒ [[text]] ᵒᴼᵒ▫ₒₒ▫ᵒᴼᵒ▫ₒₒ▫ᵒᴼᵒ▫▫·∙∙",
"¸¸♬·¯·♩¸¸♪·¯·♫¸¸ [[text]] ¸¸♫·¯·♪¸¸♩·¯·♬¸¸",
"ஜ۩۞۩ஜ [[text]] ஜ۩۞۩ஜ",
"¤ (¯´☆✭.¸_)¤ [[text]] ¤(_¸.✭☆´¯) ¤",
"(¯`·.¸¸.·´¯`·.¸¸.-> [[text]] <-.¸¸.·´¯`·.¸¸.·´¯)",
"✿.。.:* ☆:**:. [[text]] .:**:.☆*.:。.✿",
".•♫•♬• [[text]] •♬•♫•.",
"ღ(¯`◕‿◕´¯) ♫ ♪ ♫ [[text]] ♫ ♪ ♫ (¯`◕‿◕´¯)ღ",
"«-(¯`v´¯)-« [[text]] »-(¯`v´¯)-»",
];
const futureAlienCharMap: { [key: string]: string } = {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
a: "ᗩ",
b: "ᗷ",
c: "ᑢ",
d: "ᕲ",
e: "ᘿ",
f: "ᖴ",
g: "ᘜ",
h: "ᕼ",
i: "ᓰ",
j: "ᒚ",
k: "ᖽᐸ",
l: "ᒪ",
m: "ᘻ",
n: "ᘉ",
o: "ᓍ",
p: "ᕵ",
q: "ᕴ",
r: "ᖇ",
s: "S",
t: "ᖶ",
u: "ᑘ",
v: "ᐺ",
w: "ᘺ",
x: "᙭",
y: "ᖻ",
z: "ᗱ",
A: "ᗩ",
B: "ᗷ",
C: "ᑢ",
D: "ᕲ",
E: "ᘿ",
F: "ᖴ",
G: "ᘜ",
H: "ᕼ",
I: "ᓰ",
J: "ᒚ",
K: "ᖽᐸ",
L: "ᒪ",
M: "ᘻ",
N: "ᘉ",
O: "ᓍ",
P: "ᕵ",
Q: "ᕴ",
R: "ᖇ",
S: "S",
T: "ᖶ",
U: "ᑘ",
V: "ᐺ",
W: "ᘺ",
X: "᙭",
Y: "ᖻ",
Z: "ᗱ",
};
const squiggle6CharMap: { [key: string]: string } = {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
a: "ค",
b: "๖",
c: "¢",
d: "໓",
e: "ē",
f: "f",
g: "ງ",
h: "h",
i: "i",
j: "ว",
k: "k",
l: "l",
m: "๓",
n: "ຖ",
o: "໐",
p: "p",
q: "๑",
r: "r",
s: "Ş",
t: "t",
u: "น",
v: "ง",
w: "ຟ",
x: "x",
y: "ฯ",
z: "ຊ",
A: "ค",
B: "๖",
C: "¢",
D: "໓",
E: "ē",
F: "f",
G: "ງ",
H: "h",
I: "i",
J: "ว",
K: "k",
L: "l",
M: "๓",
N: "ຖ",
O: "໐",
P: "p",
Q: "๑",
R: "r",
S: "Ş",
T: "t",
U: "น",
V: "ง",
W: "ຟ",
X: "x",
Y: "ฯ",
Z: "ຊ",
};
const squiggle5CharMap: { [key: string]: string } = {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
a: "ą",
b: "ც",
c: "ƈ",
d: "ɖ",
e: "ɛ",
f: "ʄ",
g: "ɠ",
h: "ɧ",
i: "ı",
j: "ʝ",
k: "ƙ",
l: "Ɩ",
m: "ɱ",
n: "ŋ",
o: "ơ",
p: "℘",
q: "զ",
r: "ཞ",
s: "ʂ",
t: "ɬ",
u: "ų",
v: "۷",
w: "ῳ",
x: "ҳ",
y: "ყ",
z: "ʑ",
A: "ą",
B: "ც",
C: "ƈ",
D: "ɖ",
E: "ɛ",
F: "ʄ",
G: "ɠ",
H: "ɧ",
I: "ı",
J: "ʝ",
K: "ƙ",
L: "Ɩ",
M: "ɱ",
N: "ŋ",
O: "ơ",
P: "℘",
Q: "զ",
R: "ཞ",
S: "ʂ",
T: "ɬ",
U: "ų",
V: "۷",
W: "ῳ",
X: "ҳ",
Y: "ყ",
Z: "ʑ",
};
const asianStyle2CharMap: { [key: string]: string } = {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
a: "ム",
b: "乃",
c: "ᄃ",
d: "り",
e: "乇",
f: "キ",
g: "ム",
h: "ん",
i: "ノ",
j: "フ",
k: "ズ",
l: "レ",
m: "ᄊ",
n: "刀",
o: "の",
p: "ア",
q: "ゐ",
r: "尺",
s: "丂",
t: "イ",
u: "ひ",
v: "√",
w: "W",
x: "メ",
y: "リ",
z: "乙",
A: "ム",
B: "乃",
C: "ᄃ",
D: "り",
E: "乇",
F: "キ",
G: "ム",
H: "ん",
I: "ノ",
J: "フ",
K: "ズ",
L: "レ",
M: "ᄊ",
N: "刀",
O: "の",
P: "ア",
Q: "ゐ",
R: "尺",
S: "丂",
T: "イ",
U: "ひ",
V: "√",
W: "W",
X: "メ",
Y: "リ",
Z: "乙",
};
const asianStyleCharMap: { [key: string]: string } = {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
a: "卂",
b: "乃",
c: "匚",
d: "ᗪ",
e: "乇",
f: "千",
g: "Ꮆ",
h: "卄",
i: "丨",
j: "フ",
k: "Ҝ",
l: "ㄥ",
m: "爪",
n: "几",
o: "ㄖ",
p: "卩",
q: "Ɋ",
r: "尺",
s: "丂",
t: "ㄒ",
u: "ㄩ",
v: "ᐯ",
w: "山",
x: "乂",
y: "ㄚ",
z: "乙",
A: "卂",
B: "乃",
C: "匚",
D: "ᗪ",
E: "乇",
F: "千",
G: "Ꮆ",
H: "卄",
I: "丨",
J: "フ",
K: "Ҝ",
L: "ㄥ",
M: "爪",
N: "几",
O: "ㄖ",
P: "卩",
Q: "Ɋ",
R: "尺",
S: "丂",
T: "ㄒ",
U: "ㄩ",
V: "ᐯ",
W: "山",
X: "乂",
Y: "ㄚ",
Z: "乙",
};
const squaresCharMap: { [key: string]: string } = {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
a: "🄰",
b: "🄱",
c: "🄲",
d: "🄳",
e: "🄴",
f: "🄵",
g: "🄶",
h: "🄷",
i: "🄸",
j: "🄹",
k: "🄺",
l: "🄻",
m: "🄼",
n: "🄽",
o: "🄾",
p: "🄿",
q: "🅀",
r: "🅁",
s: "🅂",
t: "🅃",
u: "🅄",
v: "🅅",
w: "🅆",
x: "🅇",
y: "🅈",
z: "🅉",
A: "🄰",
B: "🄱",
C: "🄲",
D: "🄳",
E: "🄴",
F: "🄵",
G: "🄶",
H: "🄷",
I: "🄸",
J: "🄹",
K: "🄺",
L: "🄻",
M: "🄼",
N: "🄽",
O: "🄾",
P: "🄿",
Q: "🅀",
R: "🅁",
S: "🅂",
T: "🅃",
U: "🅄",
V: "🅅",
W: "🅆",
X: "🅇",
Y: "🅈",
Z: "🅉",
};
const squiggle4CharMap: { [key: string]: string } = {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
a: "Ꮧ",
b: "Ᏸ",
c: "ፈ",
d: "Ꮄ",
e: "Ꮛ",
f: "Ꭶ",
g: "Ꮆ",
h: "Ꮒ",
i: "Ꭵ",
j: "Ꮰ",
k: "Ꮶ",
l: "Ꮭ",
m: "Ꮇ",
n: "Ꮑ",
o: "Ꭷ",
p: "Ꭾ",
q: "Ꭴ",
r: "Ꮢ",
s: "Ꮥ",
t: "Ꮦ",
u: "Ꮼ",
v: "Ꮙ",
w: "Ꮗ",
x: "ጀ",
y: "Ꭹ",
z: "ፚ",
A: "Ꮧ",
B: "Ᏸ",
C: "ፈ",
D: "Ꮄ",
E: "Ꮛ",
F: "Ꭶ",
G: "Ꮆ",
H: "Ꮒ",
I: "Ꭵ",
J: "Ꮰ",
K: "Ꮶ",
L: "Ꮭ",
M: "Ꮇ",
N: "Ꮑ",
O: "Ꭷ",
P: "Ꭾ",
Q: "Ꭴ",
R: "Ꮢ",
S: "Ꮥ",
T: "Ꮦ",
U: "Ꮼ",
V: "Ꮙ",
W: "Ꮗ",
X: "ጀ",
Y: "Ꭹ",
Z: "ፚ",
};
const neonCharMap: { [key: string]: string } = {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
a: "ᗩ",
b: "ᗷ",
c: "ᑕ",
d: "ᗪ",
e: "E",
f: "ᖴ",
g: "G",
h: "ᕼ",
i: "I",
j: "ᒍ",
k: "K",
l: "ᒪ",
m: "ᗰ",
n: "ᑎ",
o: "O",
p: "ᑭ",
q: "ᑫ",
r: "ᖇ",
s: "ᔕ",
t: "T",
u: "ᑌ",
v: "ᐯ",
w: "ᗯ",
x: "᙭",
y: "Y",
z: "ᘔ",
A: "ᗩ",
B: "ᗷ",
C: "ᑕ",
D: "ᗪ",
E: "E",
F: "ᖴ",
G: "G",
H: "ᕼ",
I: "I",
J: "ᒍ",
K: "K",
L: "ᒪ",
M: "ᗰ",
N: "ᑎ",
O: "O",
P: "ᑭ",
Q: "ᑫ",
R: "ᖇ",
S: "ᔕ",
T: "T",
U: "ᑌ",
V: "ᐯ",
W: "ᗯ",
X: "᙭",
Y: "Y",
Z: "ᘔ",
};
const squiggle3CharMap: { [key: string]: string } = {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
a: "ǟ",
b: "ɮ",
c: "ƈ",
d: "ɖ",
e: "ɛ",
f: "ʄ",
g: "ɢ",
h: "ɦ",
i: "ɨ",
j: "ʝ",
k: "ӄ",
l: "ʟ",
m: "ʍ",
n: "ռ",
o: "օ",
p: "ք",
q: "զ",
r: "ʀ",
s: "ֆ",
t: "ȶ",
u: "ʊ",
v: "ʋ",
w: "ա",
x: "Ӽ",
y: "ʏ",
z: "ʐ",
A: "ǟ",
B: "ɮ",
C: "ƈ",
D: "ɖ",
E: "ɛ",
F: "ʄ",
G: "ɢ",
H: "ɦ",
I: "ɨ",
J: "ʝ",
K: "ӄ",
L: "ʟ",
M: "ʍ",
N: "ռ",
O: "օ",
P: "ք",
Q: "զ",
R: "ʀ",
S: "ֆ",
T: "ȶ",
U: "ʊ",
V: "ʋ",
W: "ա",
X: "Ӽ",
Y: "ʏ",
Z: "ʐ",
};
const monospaceCharMap: { [key: string]: string } = {
"0": "𝟶",
"1": "𝟷",
"2": "𝟸",
"3": "𝟹",
"4": "𝟺",
"5": "𝟻",
"6": "𝟼",
"7": "𝟽",
"8": "𝟾",
"9": "𝟿",
a: "𝚊",
b: "𝚋",
c: "𝚌",
d: "𝚍",
e: "𝚎",
f: "𝚏",
g: "𝚐",
h: "𝚑",
i: "𝚒",
j: "𝚓",
k: "𝚔",
l: "𝚕",
m: "𝚖",
n: "𝚗",
o: "𝚘",
p: "𝚙",
q: "𝚚",
r: "𝚛",
s: "𝚜",
t: "𝚝",
u: "𝚞",
v: "𝚟",
w: "𝚠",
x: "𝚡",
y: "𝚢",
z: "𝚣",
A: "𝙰",
B: "𝙱",
C: "𝙲",
D: "𝙳",
E: "𝙴",
F: "𝙵",
G: "𝙶",
H: "𝙷",
I: "𝙸",
J: "𝙹",
K: "𝙺",
L: "𝙻",
M: "𝙼",
N: "𝙽",
O: "𝙾",
P: "𝙿",
Q: "𝚀",
R: "𝚁",
S: "𝚂",
T: "𝚃",
U: "𝚄",
V: "𝚅",
W: "𝚆",
X: "𝚇",
Y: "𝚈",
Z: "𝚉",
};
const boldItalicCharMap: { [key: string]: string } = {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
a: "𝙖",
b: "𝙗",
c: "𝙘",
d: "𝙙",
e: "𝙚",
f: "𝙛",
g: "𝙜",
h: "𝙝",
i: "𝙞",
j: "𝙟",
k: "𝙠",
l: "𝙡",
m: "𝙢",
n: "𝙣",
o: "𝙤",
p: "𝙥",
q: "𝙦",
r: "𝙧",
s: "𝙨",
t: "𝙩",
u: "𝙪",
v: "𝙫",
w: "𝙬",
x: "𝙭",
y: "𝙮",
z: "𝙯",
A: "𝘼",
B: "𝘽",
C: "𝘾",
D: "𝘿",
E: "𝙀",
F: "𝙁",
G: "𝙂",
H: "𝙃",
I: "𝙄",
J: "𝙅",
K: "𝙆",
L: "𝙇",
M: "𝙈",
N: "𝙉",
O: "𝙊",
P: "𝙋",
Q: "𝙌",
R: "𝙍",
S: "𝙎",
T: "𝙏",
U: "𝙐",
V: "𝙑",
W: "𝙒",
X: "𝙓",
Y: "𝙔",
Z: "𝙕",
};
const boldCharMap: { [key: string]: string } = {
"0": "𝟎",
"1": "𝟏",
"2": "𝟐",
"3": "𝟑",
"4": "𝟒",
"5": "𝟓",
"6": "𝟔",
"7": "𝟕",
"8": "𝟖",
"9": "𝟗",
a: "𝐚",
b: "𝐛",
c: "𝐜",
d: "𝐝",
e: "𝐞",
f: "𝐟",
g: "𝐠",
h: "𝐡",
i: "𝐢",
j: "𝐣",
k: "𝐤",
l: "𝐥",
m: "𝐦",
n: "𝐧",
o: "𝐨",
p: "𝐩",
q: "𝐪",
r: "𝐫",
s: "𝐬",
t: "𝐭",
u: "𝐮",
v: "𝐯",
w: "𝐰",
x: "𝐱",
y: "𝐲",
z: "𝐳",
A: "𝐀",
B: "𝐁",
C: "𝐂",
D: "𝐃",
E: "𝐄",
F: "𝐅",
G: "𝐆",
H: "𝐇",
I: "𝐈",
J: "𝐉",
K: "𝐊",
L: "𝐋",
M: "𝐌",
N: "𝐍",
O: "𝐎",
P: "𝐏",
Q: "𝐐",
R: "𝐑",
S: "𝐒",
T: "𝐓",
U: "𝐔",
V: "𝐕",
W: "𝐖",
X: "𝐗",
Y: "𝐘",
Z: "𝐙",
};
const boldSansCharMap: { [key: string]: string } = {
"0": "𝟬",
"1": "𝟭",
"2": "𝟮",
"3": "𝟯",
"4": "𝟰",
"5": "𝟱",
"6": "𝟲",
"7": "𝟳",
"8": "𝟴",
"9": "𝟵",
a: "𝗮",
b: "𝗯",
c: "𝗰",
d: "𝗱",
e: "𝗲",
f: "𝗳",
g: "𝗴",
h: "𝗵",
i: "𝗶",
j: "𝗷",
k: "𝗸",
l: "𝗹",
m: "𝗺",
n: "𝗻",
o: "𝗼",
p: "𝗽",
q: "𝗾",
r: "𝗿",
s: "𝘀",
t: "𝘁",
u: "𝘂",
v: "𝘃",
w: "𝘄",
x: "𝘅",
y: "𝘆",
z: "𝘇",
A: "𝗔",
B: "𝗕",
C: "𝗖",
D: "𝗗",
E: "𝗘",
F: "𝗙",
G: "𝗚",
H: "𝗛",
I: "𝗜",
J: "𝗝",
K: "𝗞",
L: "𝗟",
M: "𝗠",
N: "𝗡",
O: "𝗢",
P: "𝗣",
Q: "𝗤",
R: "𝗥",
S: "𝗦",
T: "𝗧",
U: "𝗨",
V: "𝗩",
W: "𝗪",
X: "𝗫",
Y: "𝗬",
Z: "𝗭",
};
const italicCharMap: { [key: string]: string } = {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
a: "𝘢",
b: "𝘣",
c: "𝘤",
d: "𝘥",
e: "𝘦",
f: "𝘧",
g: "𝘨",
h: "𝘩",
i: "𝘪",
j: "𝘫",
k: "𝘬",
l: "𝘭",
m: "𝘮",
n: "𝘯",
o: "𝘰",
p: "𝘱",
q: "𝘲",
r: "𝘳",
s: "𝘴",
t: "𝘵",
u: "𝘶",
v: "𝘷",
w: "𝘸",
x: "𝘹",
y: "𝘺",
z: "𝘻",
A: "𝘈",
B: "𝘉",
C: "𝘊",
D: "𝘋",
E: "𝘌",
F: "𝘍",
G: "𝘎",
H: "𝘏",
I: "𝘐",
J: "𝘑",
K: "𝘒",
L: "𝘓",
M: "𝘔",
N: "𝘕",
O: "𝘖",
P: "𝘗",
Q: "𝘘",
R: "𝘙",
S: "𝘚",
T: "𝘛",
U: "𝘜",
V: "𝘝",
W: "𝘞",
X: "𝘟",
Y: "𝘠",
Z: "𝘡",
};
const squiggle2CharMap: { [key: string]: string } = {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
a: "α",
b: "Ⴆ",
c: "ƈ",
d: "ԃ",
e: "ҽ",
f: "ϝ",
g: "ɠ",
h: "ԋ",
i: "ι",
j: "ʝ",
k: "ƙ",
l: "ʅ",
m: "ɱ",
n: "ɳ",
o: "σ",
p: "ρ",
q: "ϙ",
r: "ɾ",
s: "ʂ",
t: "ƚ",
u: "υ",
v: "ʋ",
w: "ɯ",
x: "x",
y: "ყ",
z: "ȥ",
A: "A",
B: "B",
C: "C",
D: "D",
E: "E",
F: "F",
G: "G",
H: "H",
I: "I",
J: "J",
K: "K",
L: "L",
M: "M",
N: "N",
O: "O",
P: "P",
Q: "Q",
R: "R",
S: "S",
T: "T",
U: "U",
V: "V",
W: "W",
X: "X",
Y: "Y",
Z: "Z",
};
const currencyCharMap: { [key: string]: string } = {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
a: "₳",
b: "฿",
c: "₵",
d: "Đ",
e: "Ɇ",
f: "₣",
g: "₲",
h: "Ⱨ",
i: "ł",
j: "J",
k: "₭",
l: "Ⱡ",
m: "₥",
n: "₦",
o: "Ø",
p: "₱",
q: "Q",
r: "Ɽ",
s: "₴",
t: "₮",
u: "Ʉ",
v: "V",
w: "₩",
x: "Ӿ",
y: "Ɏ",
z: "Ⱬ",
A: "₳",
B: "฿",
C: "₵",
D: "Đ",
E: "Ɇ",
F: "₣",
G: "₲",
H: "Ⱨ",
I: "ł",
J: "J",
K: "₭",
L: "Ⱡ",
M: "₥",
N: "₦",
O: "Ø",
P: "₱",
Q: "Q",
R: "Ɽ",
S: "₴",
T: "₮",
U: "Ʉ",
V: "V",
W: "₩",
X: "Ӿ",
Y: "Ɏ",
Z: "Ⱬ",
};
const symbolsCharMap: { [key: string]: string } = {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
a: "å",
b: "ß",
c: "¢",
d: "Ð",
e: "ê",
f: "£",
g: "g",
h: "h",
i: "ï",
j: "j",
k: "k",
l: "l",
m: "m",
n: "ñ",
o: "ð",
p: "þ",
q: "q",
r: "r",
s: "§",
t: "†",
u: "µ",
v: "v",
w: "w",
x: "x",
y: "¥",
z: "z",
A: "Ä",
B: "ß",
C: "Ç",
D: "Ð",
E: "È",
F: "£",
G: "G",
H: "H",
I: "Ì",
J: "J",
K: "K",
L: "L",
M: "M",
N: "ñ",
O: "Ö",
P: "þ",
Q: "Q",
R: "R",
S: "§",
T: "†",
U: "Ú",
V: "V",
W: "W",
X: "×",
Y: "¥",
Z: "Z",
};
const greekCharMap: { [key: string]: string } = {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
a: "α",
b: "в",
c: "¢",
d: "∂",
e: "є",
f: "ƒ",
g: "g",
h: "н",
i: "ι",
j: "נ",
k: "к",
l: "ℓ",
m: "м",
n: "η",
o: "σ",
p: "ρ",
q: "q",
r: "я",
s: "ѕ",
t: "т",
u: "υ",
v: "ν",
w: "ω",
x: "χ",
y: "у",
z: "z",
A: "α",
B: "в",
C: "¢",
D: "∂",
E: "є",
F: "ƒ",
G: "g",
H: "н",
I: "ι",
J: "נ",
K: "к",
L: "ℓ",
M: "м",
N: "η",
O: "σ",
P: "ρ",
Q: "q",
R: "я",
S: "ѕ",
T: "т",
U: "υ",
V: "ν",
W: "ω",
X: "χ",
Y: "у",
Z: "z",
};
const bentTextCharMap: { [key: string]: string } = {
"0": "⊘",
"1": "𝟙",
"2": "ϩ",
"3": "Ӡ",
"4": "५",
"5": "Ƽ",
"6": "Ϭ",
"7": "7",
"8": "𝟠",
"9": "९",
a: "ą",
b: "ҍ",
c: "ç",
d: "ժ",
e: "ҽ",
f: "ƒ",
g: "ց",
h: "հ",
i: "ì",
j: "ʝ",
k: "ҟ",
l: "Ӏ",
m: "ʍ",
n: "ղ",
o: "օ",
p: "ք",
q: "զ",
r: "ɾ",
s: "ʂ",
t: "է",
u: "մ",
v: "ѵ",
w: "ա",
x: "×",
y: "վ",
z: "Հ",
A: "Ⱥ",
B: "β",
C: "↻",
D: "Ꭰ",
E: "Ɛ",
F: "Ƒ",
G: "Ɠ",
H: "Ƕ",
I: "į",
J: "ل",
K: "Ҡ",
L: "Ꝉ",
M: "Ɱ",
N: "ហ",
O: "ට",
P: "φ",
Q: "Ҩ",
R: "འ",
S: "Ϛ",
T: "Ͳ",
U: "Ա",
V: "Ỽ",
W: "చ",
X: "ჯ",
Y: "Ӌ",
Z: "ɀ",
};
const upperAnglesCharMap: { [key: string]: string } = {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
a: "Λ",
b: "B",
c: "ᄃ",
d: "D",
e: "Σ",
f: "F",
g: "G",
h: "Ή",
i: "I",
j: "J",
k: "K",
l: "ᄂ",
m: "M",
n: "П",
o: "Ө",
p: "P",
q: "Q",
r: "Я",
s: "Ƨ",
t: "Ƭ",
u: "Ц",
v: "V",
w: "Щ",
x: "X",
y: "Y",
z: "Z",
A: "Λ",
B: "B",
C: "ᄃ",
D: "D",
E: "Σ",
F: "F",
G: "G",
H: "Ή",
I: "I",
J: "J",
K: "K",
L: "ᄂ",
M: "M",
N: "П",
O: "Ө",
P: "P",
Q: "Q",
R: "Я",
S: "Ƨ",
T: "Ƭ",
U: "Ц",
V: "V",
W: "Щ",
X: "X",
Y: "Y",
Z: "Z",
};
const subscriptCharMap: { [key: string]: string } = {
"0": "₀",
"1": "₁",
"2": "₂",
"3": "₃",
"4": "₄",
"5": "₅",
"6": "₆",
"7": "₇",
"8": "₈",
"9": "₉",
a: "ₐ",
b: "b",
c: "c",
d: "d",
e: "ₑ",
f: "f",
g: "g",
h: "ₕ",
i: "ᵢ",
j: "ⱼ",
k: "ₖ",
l: "ₗ",
m: "ₘ",
n: "ₙ",
o: "ₒ",
p: "ₚ",
q: "q",
r: "ᵣ",
s: "ₛ",
t: "ₜ",
u: "ᵤ",
v: "ᵥ",
w: "w",
x: "ₓ",
y: "y",
z: "z",
A: "ₐ",
B: "B",
C: "C",
D: "D",
E: "ₑ",
F: "F",
G: "G",
H: "ₕ",
I: "ᵢ",
J: "ⱼ",
K: "ₖ",
L: "ₗ",
M: "ₘ",
N: "ₙ",
O: "ₒ",
P: "ₚ",
Q: "Q",
R: "ᵣ",
S: "ₛ",
T: "ₜ",
U: "ᵤ",
V: "ᵥ",
W: "W",
X: "ₓ",
Y: "Y",
Z: "Z",
"+": "₊",
"-": "₋",
"=": "₌",
"(": "₍",
")": "₎",
};
const superscriptCharMap: { [key: string]: string } = {
"0": "⁰",
"1": "¹",
"2": "²",
"3": "³",
"4": "⁴",
"5": "⁵",
"6": "⁶",
"7": "⁷",
"8": "⁸",
"9": "⁹",
a: "ᵃ",
b: "ᵇ",
c: "ᶜ",
d: "ᵈ",
e: "ᵉ",
f: "ᶠ",
g: "ᵍ",
h: "ʰ",
i: "ⁱ",
j: "ʲ",
k: "ᵏ",
l: "ˡ",
m: "ᵐ",
n: "ⁿ",
o: "ᵒ",
p: "ᵖ",
q: "q",
r: "ʳ",
s: "ˢ",
t: "ᵗ",
u: "ᵘ",
v: "ᵛ",
w: "ʷ",
x: "ˣ",
y: "ʸ",
z: "ᶻ",
A: "ᴬ",
B: "ᴮ",
C: "ᶜ",
D: "ᴰ",
E: "ᴱ",
F: "ᶠ",
G: "ᴳ",
H: "ᴴ",
I: "ᴵ",
J: "ᴶ",
K: "ᴷ",
L: "ᴸ",
M: "ᴹ",
N: "ᴺ",
O: "ᴼ",
P: "ᴾ",
Q: "Q",
R: "ᴿ",
S: "ˢ",
T: "ᵀ",
U: "ᵁ",
V: "ⱽ",
W: "ᵂ",
X: "ˣ",
Y: "ʸ",
Z: "ᶻ",
"+": "⁺",
"-": "⁻",
"=": "⁼",
"(": "⁽",
")": "⁾",
};
const squiggleCharMap: { [key: string]: string } = {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
a: "ค",
b: "๒",
c: "ς",
d: "๔",
e: "є",
f: "Ŧ",
g: "ﻮ",
h: "ђ",
i: "เ",
j: "ן",
k: "к",
l: "ɭ",
m: "๓",
n: "ภ",
o: "๏",
p: "ק",
q: "ợ",
r: "г",
s: "ร",
t: "Շ",
u: "ย",
v: "ש",
w: "ฬ",
x: "א",
y: "ץ",
z: "չ",
A: "ค",
B: "๒",
C: "ς",
D: "๔",
E: "є",
F: "Ŧ",
G: "ﻮ",
H: "ђ",
I: "เ",
J: "ן",
K: "к",
L: "ɭ",
M: "๓",
N: "ภ",
O: "๏",
P: "ק",
Q: "ợ",
R: "г",
S: "ร",
T: "Շ",
U: "ย",
V: "ש",
W: "ฬ",
X: "א",
Y: "ץ",
Z: "չ",
};
const doubleStruckCharMap: { [key: string]: string } = {
"0": "𝟘",
"1": "𝟙",
"2": "𝟚",
"3": "𝟛",
"4": "𝟜",
"5": "𝟝",
"6": "𝟞",
"7": "𝟟",
"8": "𝟠",
"9": "𝟡",
a: "𝕒",
b: "𝕓",
c: "𝕔",
d: "𝕕",
e: "𝕖",
f: "𝕗",
g: "𝕘",
h: "𝕙",
i: "𝕚",
j: "𝕛",
k: "𝕜",
l: "𝕝",
m: "𝕞",
n: "𝕟",
o: "𝕠",
p: "𝕡",
q: "𝕢",
r: "𝕣",
s: "𝕤",
t: "𝕥",
u: "𝕦",
v: "𝕧",
w: "𝕨",
x: "𝕩",
y: "𝕪",
z: "𝕫",
A: "𝔸",
B: "𝔹",
C: "ℂ",
D: "𝔻",
E: "𝔼",
F: "𝔽",
G: "𝔾",
H: "ℍ",
I: "𝕀",
J: "𝕁",
K: "𝕂",
L: "𝕃",
M: "𝕄",
N: "ℕ",
O: "𝕆",
P: "ℙ",
Q: "ℚ",
R: "ℝ",
S: "𝕊",
T: "𝕋",
U: "𝕌",
V: "𝕍",
W: "𝕎",
X: "𝕏",
Y: "𝕐",
Z: "ℤ",
};
const medievalCharMap: { [key: string]: string } = {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
a: "𝖆",
b: "𝖇",
c: "𝖈",
d: "𝖉",
e: "𝖊",
f: "𝖋",
g: "𝖌",
h: "𝖍",
i: "𝖎",
j: "𝖏",
k: "𝖐",
l: "𝖑",
m: "𝖒",
n: "𝖓",
o: "𝖔",
p: "𝖕",
q: "𝖖",
r: "𝖗",
s: "𝖘",
t: "𝖙",
u: "𝖚",
v: "𝖛",
w: "𝖜",
x: "𝖝",
y: "𝖞",
z: "𝖟",
A: "𝕬",
B: "𝕭",
C: "𝕮",
D: "𝕯",
E: "𝕰",
F: "𝕱",
G: "𝕲",
H: "𝕳",
I: "𝕴",
J: "𝕵",
K: "𝕶",
L: "𝕷",
M: "𝕸",
N: "𝕹",
O: "𝕺",
P: "𝕻",
Q: "𝕼",
R: "𝕽",
S: "𝕾",
T: "𝕿",
U: "𝖀",
V: "𝖁",
W: "𝖂",
X: "𝖃",
Y: "𝖄",
Z: "𝖅",
};
const invertedSquaresCharMap: { [key: string]: string } = {
q: "🆀",
w: "🆆",
e: "🅴",
r: "🆁",
t: "🆃",
y: "🆈",
u: "🆄",
i: "🅸",
o: "🅾",
p: "🅿",
a: "🅰",
s: "🆂",
d: "🅳",
f: "🅵",
g: "🅶",
h: "🅷",
j: "🅹",
k: "🅺",
l: "🅻",
z: "🆉",
x: "🆇",
c: "🅲",
v: "🆅",
b: "🅱",
n: "🅽",
m: "🅼",
};
const cursiveCharMap: { [key: string]: string } = {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
a: "𝓪",
b: "𝓫",
c: "𝓬",
d: "𝓭",
e: "𝓮",
f: "𝓯",
g: "𝓰",
h: "𝓱",
i: "𝓲",
j: "𝓳",
k: "𝓴",
l: "𝓵",
m: "𝓶",
n: "𝓷",
o: "𝓸",
p: "𝓹",
q: "𝓺",
r: "𝓻",
s: "𝓼",
t: "𝓽",
u: "𝓾",
v: "𝓿",
w: "𝔀",
x: "𝔁",
y: "𝔂",
z: "𝔃",
A: "𝓐",
B: "𝓑",
C: "𝓒",
D: "𝓓",
E: "𝓔",
F: "𝓕",
G: "𝓖",
H: "𝓗",
I: "𝓘",
J: "𝓙",
K: "𝓚",
L: "𝓛",
M: "𝓜",
N: "𝓝",
O: "𝓞",
P: "𝓟",
Q: "𝓠",
R: "𝓡",
S: "𝓢",
T: "𝓣",
U: "𝓤",
V: "𝓥",
W: "𝓦",
X: "𝓧",
Y: "𝓨",
Z: "𝓩",
};
const oldEnglishCharMap: { [key: string]: string } = {
a: "𝔞",
b: "𝔟",
c: "𝔠",
d: "𝔡",
e: "𝔢",
f: "𝔣",
g: "𝔤",
h: "𝔥",
i: "𝔦",
j: "𝔧",
k: "𝔨",
l: "𝔩",
m: "𝔪",
n: "𝔫",
o: "𝔬",
p: "𝔭",
q: "𝔮",
r: "𝔯",
s: "𝔰",
t: "𝔱",
u: "𝔲",
v: "𝔳",
w: "𝔴",
x: "𝔵",
y: "𝔶",
z: "𝔷",
A: "𝔄",
B: "𝔅",
C: "ℭ",
D: "𝔇",
E: "𝔈",
F: "𝔉",
G: "𝔊",
H: "ℌ",
I: "ℑ",
J: "𝔍",
K: "𝔎",
L: "𝔏",
M: "𝔐",
N: "𝔑",
O: "𝔒",
P: "𝔓",
Q: "𝔔",
R: "ℜ",
S: "𝔖",
T: "𝔗",
U: "𝔘",
V: "𝔙",
W: "𝔚",
X: "𝔛",
Y: "𝔜",
Z: "ℨ",
};
const wideTextCharMap: { [key: string]: string } = {
"`": "`",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
"0": "0",
"-": "-",
"=": "=",
"~": "~",
"!": "!",
"@": "@",
"#": "#",
$: "$",
"%": "%",
"^": "^",
"&": "&",
"*": "*",
"(": "(",
")": ")",
_: "_",
"+": "+",
q: "q",
w: "w",
e: "e",
r: "r",
t: "t",
y: "y",
u: "u",
i: "i",
o: "o",
p: "p",
"[": "[",
"]": "]",
"\\": "\\",
Q: "Q",
W: "W",
E: "E",
R: "R",
T: "T",
Y: "Y",
U: "U",
I: "I",
O: "O",
P: "P",
"{": "{",
"}": "}",
"|": "|",
a: "a",
s: "s",
d: "d",
f: "f",
g: "g",
h: "h",
j: "j",
k: "k",
l: "l",
";": ";",
"'": "'",
A: "A",
S: "S",
D: "D",
F: "F",
G: "G",
H: "H",
J: "J",
K: "K",
L: "L",
":": ":",
'"': '"',
z: "z",
x: "x",
c: "c",
v: "v",
b: "b",
n: "n",
m: "m",
",": ",",
".": ".",
"/": "/",
Z: "Z",
X: "X",
C: "C",
V: "V",
B: "B",
N: "N",
M: "M",
"<": "<",
">": ">",
"?": "?",
};
const emoji = {
":)": [
"٩(˘◡˘)۶",
"(•◡•)",
"(ɔ◔‿◔)ɔ",
"❀◕‿◕❀",
"(。◕‿◕。)",
"●‿●",
"◕‿◕",
"😍",
"ツ",
"(✿ヘᴥヘ)",
"🌘‿🌘",
"(づ。◕‿‿◕。)づ",
"(/◔◡◔)/",
"s(^‿^)-b",
"(人◕‿◕)",
"(✿╹◡╹)",
"◔◡◔",
],
"^^": [
"(^▽^)",
"(✿^▽^)",
"ᵔ⌣ᵔ",
"ᵔᴥᵔ",
"(≧◡≦)",
"^ㅅ^",
"^ㅂ^",
"(⌒‿⌒)",
"◠◡◠",
"⁀‿⁀",
"",
],
":p": [":Þ", ""],
":D": ["(ノ◕ヮ◕)ノ*:・゚✧", "(✿◕ᗜ◕)━♫.*・。゚", "ᕕ(ᐛ)ᕗ"],
"<3": [
"♡",
"❤",
"♥",
"❥",
"💘",
"💙",
"💗",
"💖",
"💕",
"💓",
"💞",
"💝",
"💟",
],
"(y)": ["👍", "👌"],
};
const round = [
"❁",
"🌺",
"❀",
"💮",
"🏵",
"🌸",
"☯",
"😍",
"♡",
"🍩",
"🍬",
"💙",
"🌞",
"💍",
"🍪",
"❤",
"💗",
"🍑",
"💞",
];
const food = [
"🍬",
"🍭",
"🍒",
"🍎",
"🍉",
"🍇",
"🍓",
"🍌",
"🍑",
"🍰",
"🎂",
"🍩",
"🍪",
"🍧",
"🍦",
"🍫",
"🍡",
];
const flowers = [
"✿",
"⚘",
"❦",
"❧",
"✾",
"❁",
"❀",
"💐",
"🌸",
"💮",
"🏵",
"🌹",
"🌺",
"🌻",
"🌷",
"☘",
"🌱",
"🌲",
"🌳",
"🌴",
"🌿",
"🍂",
];
const twinkles = [
"⚛",
"🌌",
"🌠",
"*",
":",
"。",
"・゚",
"✧",
"✮",
"★",
"✩",
"⋆",
".",
"°",
"`",
"✴",
"。",
"✴",
"⋆",
"✳",
"✶",
"✷",
"❈",
];
const animals = [
"🐯",
"🐎",
"🐖",
"🐷",
"🐏",
"🐑",
"🐐",
"🐫",
"🐘",
"🐭",
"🐁",
"🐀",
"🐹",
"🐰",
"🐇",
"🐿",
"🐻",
"🐨",
"🐼",
"🐾",
"🐔",
"🐓",
"🐣",
"🐤",
"🐥",
"🐦",
"🐧",
"🕊",
"🐸",
"🐢",
"🐳",
"🐋",
"🐬",
"🐟",
"🐠",
"🐡",
"🐙",
"🐚",
"🐌",
"🐞",
];
const plants = [
"✿",
"⚘",
"❦",
"❧",
"✾",
"❁",
"❀",
"💐",
"🌸",
"💮",
"🏵",
"🌹",
"🌺",
"🌻",
"🌷",
"☘",
"🌱",
"🌲",
"🌳",
"🌴",
"🌿",
"🍂",
];
const misc = [
"🌌",
"🌠",
"🎉",
"🎊",
"🎈",
"💌",
"🎎",
"🎁",
"🎀",
"🕯",
"🔮",
"🛀",
"🎖",
"🏆",
"🏅",
"👑",
"💍",
"👶",
"👼",
"👸",
"👯",
"👒",
"👻",
];
const music = ["♫", "♬", "♪", "♩", "°", "。", "✧", "🎻", "🎺", "🎸", "🎷", "📯"];
const flourish = [
"•?((¯°·._.• ",
"ıllıllı ",
"¸,ø¤º°`°º¤ø,¸¸,ø¤º° ",
"°°°·.°·..·°¯°·._.· ",
"•´¯`•. ",
"׺°”˜`”°º× ",
"•]••´º´•» ",
"]|I{•------» ",
"§.•´¨'°÷•..× ",
"•°¯`•• ",
"(¯`·.¸¸.·´¯`·.¸¸.-> ",
"*´¯`*.¸¸.*´¯`* ",
"(¯`·.¸¸.-> °º ",
"°·.¸.·°¯°·.¸.·°¯°·.¸.-> ",
"•._.••´¯``•.¸¸.•` ",
"¸„.-•~¹°”ˆ˜¨ ",
"(¯´•._.• ",
"••¤(`×",
"•´¯`•» ",
"`•.,¸¸,.•´¯ ",
"¸,ø¤º°`°º¤ø,¸ ",
".o0×X×0o. ",
",-*'^'~*-.,_,.-*~ ",
"`•.¸¸.•´´¯`••._.• ",
"—(••÷",
"¤¸¸.•´¯`•¸¸.•..>> ",
"••.•´¯`•.•• ",
".•°¤*(¯`★´¯)*¤° ",
"๑۞๑,¸¸,ø¤º°`°๑۩ ",
"-漫~*'¨¯¨'*·舞~ ",
"★·.·´¯`·.·★ ",
"▁ ▂ ▄ ▅ ▆ ▇ █ ",
"▀▄▀▄▀▄ ",
"▌│█║▌║▌║ ",
];
const symbolsRandom = [
"🐙",
"🐉",
"🐊",
"🐒",
"🐝",
"🐜",
"🐚",
"🐲",
"🐳",
"🐸",
"👑",
"👹",
"👺",
"👤",
"💲",
"💣",
"💙",
"💚",
"💛",
"💜",
"💝",
"💗",
"💘",
"💞",
"💔",
"💥",
"🐯",
"🐼",
"🐻",
"🐺",
"👌",
"🐍",
"🐧",
"🐟",
"🐠",
"🐨",
"🎯",
"🏆",
"🎁",
"🎀",
"🎉",
"🎈",
"🍮",
"🍭",
"🍬",
"🍫",
"🍪",
"🍧",
"🌷",
"🍓",
"😺",
"😾",
"✎",
"😎",
"😝",
"😂",
"😈",
"😡",
"😲",
"😳",
"🍔",
"🍟",
"🍩",
"🎃",
"🎄",
"🎅",
"🐣",
"🐤",
"👍",
"👊",
"👻",
"👽",
"👮",
"💎",
"💋",
"👣",
"💀",
"💢",
"🔥",
"♔",
"♕",
"♖",
"♗",
"♘",
"♙",
"♚",
"♛",
"♜",
"♝",
"♞",
"♟",
"♠",
"♡",
"♢",
"♣",
"♤",
"♥",
"♦",
"♧",
"♨",
"♩",
"♪",
"♬",
"★",
"☆",
"☺",
"☹",
"☯",
"☮",
"☢",
"☠",
"☟",
"☞",
"☝",
"☜",
"✌",
"✋",
"✊",
"⛵",
"ൠ",
"✌",
"ඏ",
];
// FUNCTIONS
function applyCharMap(map: Record<string, string>, text: string) {
let out = "";
for (let c of text.split("")) {
if (map[c] !== undefined) out += map[c];
else if (map[c.toLowerCase()] !== undefined) out += map[c.toLowerCase()];
else out += c;
}
return out;
}
function strikeThrough(text: string): string {
return text.split("").join("̶") + "̶";
}
function tildeStrikeThrough(text: string): string {
return text.split("").join("̴") + "̴";
}
function underline(text: string): string {
return text.split("").join("̲") + "̲";
}
function doubleUnderline(text: string): string {
return text.split("").join("̳") + "̳";
}
function slashThrough(text: string): string {
return text.split("").join("̷") + "̷";
}
function stinky(text: string): string {
return text.split("").join("̾") + "̾";
}
function heartsBetween(text: string): string {
return text.split("").join("♥");
}
function arrowBelow(text: string): string {
return text.split("").join("͎") + "͎";
}
function crossAboveBelow(text: string): string {
return text.split("").join("͓̽") + "͓̽";
}
function getAsianChars(n: number) {
if (!n) n = 1;
let str: string = "";
for (let i = 0; i < n; i++) {
str += AsianChars[Math.floor(Math.random() * AsianChars.length)];
}
return str;
}
function wrapInFlourish(text: string) {
return flourishArray[
Math.floor(Math.random() * flourishArray.length)
].replace("[[text]]", text);
}
function vaporwaveText(text: string) {
const numSpaces = text.split(" ").length;
text = applyCharMap(vaporwaveCharMap, text);
let asianChars = getAsianChars(Math.max(3, numSpaces));
if (numSpaces > 6)
asianChars = asianChars
.split("")
.map((c) => c + ["", " "][Math.round(Math.random() * 0.6)])
.join("");
let outputs = [];
outputs.push(text + " " + asianChars);
outputs.push(
text
.replace(/ /g, "░")
.replace(/ae/, "æ")
.replace(/A/g, "Λ")
.replace(/E/g, function () {
return Math.random() > 0.5 ? "Ξ" : "Σ";
})
.replace(/O/g, "♢") +
" (" +
asianChars +
")"
);
outputs.push("【" + text + "】");
return outputs.join("\n\n");
}
function wingdings(text: string): string {
return text
.split("")
.map(function (a) {
const match = wingdingsCharMap.find(([key]) => key === a);
return match ? match[1] : a;
})
.join("");
}
function wrapInSymbols(text: string, number: number) {
return randomSymbols(number) + " " + text + " " + randomSymbols(number);
}
function firework(text: string): string {
return text.split("").join("҉") + "҉";
}
function weirdBox(text: string): string {
return text.replace(/([^\s])/g, "[̲̅$1]");
}
function littleSparkles(text: string): string {
return "˜”*°•.˜”*°• " + text + " •°*”˜.•°*”˜";
}
function kirbyHug(text: string): string {
return "(っ◔◡◔)っ ♥ " + text + " ♥";
}
function dottyJoiner(text: string): string {
return "░" + text.split("").join("░") + "░";
}
function wavyJoiner(text: string): string {
return "≋" + text.split("").join("≋") + "≋";
}
function diametricAngleFrame(text: string): string {
return text.replace(/([^\s])/g, "『$1』");
}
function thickBlockFramed(text: string): string {
return text.replace(/([^\s])/g, "【$1】");
}
function scriptify(text: string) {
const map: { [key: string]: string } = {
"0": "𝟢",
"1": "𝟣",
"2": "𝟤",
"3": "𝟥",
"4": "𝟦",
"5": "𝟧",
"6": "𝟨",
"7": "𝟩",
"8": "𝟪",
"9": "𝟫",
a: "𝒶",
b: "𝒷",
c: "𝒸",
d: "𝒹",
e: "𝑒",
f: "𝒻",
g: "𝑔",
h: "𝒽",
i: "𝒾",
j: "𝒿",
k: "𝓀",
l: "𝓁",
m: "𝓂",
n: "𝓃",
o: "𝑜",
p: "𝓅",
q: "𝓆",
r: "𝓇",
s: "𝓈",
t: "𝓉",
u: "𝓊",
v: "𝓋",
w: "𝓌",
x: "𝓍",
y: "𝓎",
z: "𝓏",
A: "𝒜",
B: "𝐵",
C: "𝒞",
D: "𝒟",
E: "𝐸",
F: "𝐹",
G: "𝒢",
H: "𝐻",
I: "𝐼",
J: "𝒥",
K: "𝒦",
L: "𝐿",
M: "𝑀",
N: "𝒩",
O: "𝒪",
P: "𝒫",
Q: "𝒬",
R: "𝑅",
S: "𝒮",
T: "𝒯",
U: "𝒰",
V: "𝒱",
W: "𝒲",
X: "𝒳",
Y: "𝒴",
Z: "𝒵",
};
let charArray = text.split("");
for (let i = 0; i < charArray.length; i++) {
if (map[charArray[i].toLowerCase()]) {
charArray[i] = map[charArray[i]];
}
}
text = charArray.join("");
return text;
}
function shuffleArray<T>(array: T[]): T[] {
let currentIndex = array.length;
let randomIndex: number;
// While there remain elements to shuffle...
while (currentIndex !== 0) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
// Swap it with the current element.
[array[currentIndex], array[randomIndex]] = [
array[randomIndex],
array[currentIndex],
];
}
return array;
}
function randomElement<T>(a: T[]): T {
if (Array.isArray(a)) {
return a[Math.floor(Math.random() * a.length)];
}
return a;
}
function randomSymbols(n: number) {
const symbols = [
"🐙",
"🐉",
"🐊",
"🐒",
"🐝",
"🐜",
"🐚",
"🐲",
"🐳",
"🐸",
"👑",
"👹",
"👺",
"👤",
"💲",
"💣",
"💙",
"💚",
"💛",
"💜",
"💝",
"💗",
"💘",
"💞",
"💔",
"💥",
"🐯",
"🐼",
"🐻",
"🐺",
"👌",
"🐍",
"🐧",
"🐟",
"🐠",
"🐨",
"🎯",
"🏆",
"🎁",
"🎀",
"🎉",
"🎈",
"🍮",
"🍭",
"🍬",
"🍫",
"🍪",
"🍧",
"🌷",
"🍓",
"😺",
"😾",
"✎",
"😎",
"😝",
"😂",
"😈",
"😡",
"😲",
"😳",
"🍔",
"🍟",
"🍩",
"🎃",
"🎄",
"🎅",
"🐣",
"🐤",
"👍",
"👊",
"👻",
"👽",
"👮",
"💎",
"💋",
"👣",
"💀",
"💢",
"🔥",
"♔",
"♕",
"♖",
"♗",
"♘",
"♙",
"♚",
"♛",
"♜",
"♝",
"♞",
"♟",
"♠",
"♡",
"♢",
"♣",
"♤",
"♥",
"♦",
"♧",
"♨",
"♩",
"♪",
"♬",
"★",
"☆",
"☺",
"☹",
"☯",
"☮",
"☢",
"☠",
"☟",
"☞",
"☝",
"☜",
"✌",
"✋",
"✊",
"⛵",
"ൠ",
"✌",
"ඏ",
];
let s = [];
for (let i = 0; i < n; i++) s.push(randomElement(symbols));
return s.join("");
}
function randInt(min: number, max: number) {
return min + Math.floor(Math.random() * (max - min + 1));
}
function boundingString(n: number) {
return randomElement([
foodString,
twinkleString,
animalString,
flourishString,
])(n);
}
function foodString(n: number) {
return new Array(n + 1)
.join("0")
.split("")
.map(function (a) {
return randomElement(food);
})
.join(" ⋆ ");
}
function twinkleString(n: number) {
return new Array(n + 1)
.join("0")
.split("")
.map(function () {
return randomElement(twinkles);
})
.join("");
}
function animalString(n: number) {
return new Array(n + 1)
.join("0")
.split("")
.map(function (a) {
return randomElement(animals);
})
.join(" ⋆ ");
}
function flourishString(n: number) {
return randomElement(flourish);
}
// CUTE TEXT:
function cuteText(text: string) {
if (text.trim() === "") return "";
qi = -1;
const bounds = boundingString(Math.floor(Math.random() * 2) + 1);
return (
bounds +
" 🎀 " +
text
.split(/([!?.]+)/gi)
.map(cuteSentence)
.join("") +
" 🎀 " +
esrever.reverse(bounds)
);
}
function cuteSentence(sentence: string) {
return sentence
.split(/([\s,]+)/gi)
.map(cuteWord)
.join("");
}
function cuteWord(word: string) {
//remember to handle exclamations, empty strings, commas etc. etc.!!
if (word === "" || /[[!?.\s,]+]/gi.test(word)) return word;
//TODO: random turn o's into flowers, turn quotes into unicode and ! and ?
word = roundReplace(word);
word = punctReplace(word);
word = emojiReplace(word);
return scriptify(word);
}
let qi = -1;
let qa = ["❝", "❞"];
function quotes() {
qi++;
if (qi === 2) qi = 0;
return qa[qi];
}
function punctReplace(word: string) {
return word
.split("")
.map(function (a) {
if (a === "!") return randomElement(["❣", "❢"]);
else if (a === "?") return randomElement(["¿", "?"]);
else if (a === '"') return quotes();
else return a;
})
.join("");
}
function emojiReplace(word: string) {
return word
.replace(":)", randomElement(emoji[":)"]))
.replace("(:", randomElement(emoji[":)"]))
.replace("^^", randomElement(emoji["^^"]))
.replace(":P", randomElement(emoji[":p"]))
.replace(":p", randomElement(emoji[":p"]))
.replace(":D", randomElement(emoji[":D"]))
.replace("<3", randomElement(emoji["<3"]))
.replace("(y)", randomElement(emoji["(y)"]))
.replace("(Y)", randomElement(emoji["(y)"]));
}
function roundReplace(text: string): string {
return text
.split("")
.map(function (a) {
if (a.toLowerCase() === "o" || a === "0") {
return randomElement(round);
} else {
return a;
}
})
.join("");
}
// CRAZY TEXT
function fullCrazy(text: string) {
if (text.trim() === "") return "";
return randomSymbols(2) + " " + crazifyText(text) + " " + randomSymbols(2);
}
function crazifyText(text: string): string {
// Convert text into an array of characters
const characters = text.split("");
// Iterate through each character and apply crazifyCharacter
for (let i = 0; i < characters.length; i++) {
characters[i] = crazifyCharacter(characters[i]);
}
// Rejoin the characters into a string and return
return characters.join("");
}
function crazifyCharacter(c: string): string {
c = c.toLowerCase();
const map: { [key: string]: string | string[] } = {
"&": "⅋",
"%": ["⅍", "℀", "℁", "℆", "℅"],
"0": ["0", "Ѳ", "ʘ"],
"1": ["➀", "❶", "1"],
"2": ["2", "❷", "➁"],
"3": ["3", "❸", "➂"],
"4": ["4", "❹", "➃"],
"5": ["❺", "➄", "5"],
"6": ["6", "❻", "➅"],
"7": ["7", "❼", "➆"],
"8": ["8", "➇", "❽"],
"9": ["➈", "❾", "9"],
"<": [
"≼",
"≺",
"≪",
"☾",
"≾",
"⋜",
"⋞",
"⋐",
"⊂",
"⊏",
"⊑",
"《",
"<",
"❮",
"❰",
"⫷",
],
">": "☽≫≻≽≿⋝⋟⋑⊃⊐⊒⫸》>❯❱",
"[": "【〖〘〚[",
"]": "】〗〙〛]",
"*": "✨✩✪✫✬✭✮✯✰✦✱✲✳✴✵✶✷֍֎✸✹✺✻✼✽✾✿❀❁❂❃❄★☆*",
a: [
"Ⓐ",
"ⓐ",
"α",
"A",
"a",
"ᗩ",
"卂",
"Δ",
"ค",
"α",
"ά",
"Ã",
"𝔞",
"𝓪",
"𝒶",
"𝓐",
"𝐀",
"𝐚",
"𝔸",
"𝕒",
"ᵃ",
],
b: [
"Ⓑ",
"ⓑ",
"в",
"B",
"乃",
"b",
"ᗷ",
"β",
"๒",
"в",
"в",
"β",
"𝔟",
"𝓫",
"𝒷",
"𝓑",
"𝐁",
"𝐛",
"𝔹",
"𝕓",
"ᵇ",
],
c: [
"Ⓒ",
"ⓒ",
"匚",
"¢",
"C",
"c",
"ᑕ",
"Ć",
"ς",
"c",
"ς",
"Č",
"℃",
"𝔠",
"𝓬",
"𝒸",
"𝓒",
"𝐂",
"𝐜",
"ℂ",
"𝕔",
"ᶜ",
],
d: [
"Ⓓ",
"ⓓ",
"∂",
"D",
"d",
"ᗪ",
"Đ",
"๔",
"∂",
"đ",
"Ď",
"𝔡",
"𝓭",
"𝒹",
"𝓓",
"𝐃",
"ᗪ",
"𝐝",
"𝔻",
"𝕕",
"ᵈ",
],
e: [
"Ⓔ",
"乇",
"ⓔ",
"є",
"E",
"e",
"ᗴ",
"€",
"є",
"ε",
"έ",
"Ẹ",
"𝔢",
"𝒆",
"𝑒",
"𝓔",
"𝐄",
"𝐞",
"𝔼",
"𝕖",
"ᵉ",
],
f: [
"Ⓕ",
"ⓕ",
"ƒ",
"F",
"f",
"千",
"ᖴ",
"ℱ",
"Ŧ",
"ғ",
"ғ",
"Ƒ",
"𝔣",
"𝒇",
"𝒻",
"𝓕",
"𝐅",
"𝐟",
"𝔽",
"𝕗",
"ᶠ",
],
g: [
"Ⓖ",
"ⓖ",
"ق",
"g",
"G",
"g",
"Ǥ",
"Ꮆ",
"ﻮ",
"g",
"ģ",
"Ğ",
"𝔤",
"𝓰",
"𝑔",
"𝓖",
"𝐆",
"𝐠",
"𝔾",
"𝕘",
"ᵍ",
"Ꮆ",
],
h: [
"Ⓗ",
"卄",
"ⓗ",
"н",
"H",
"h",
"ᕼ",
"Ħ",
"ђ",
"н",
"ħ",
"Ĥ",
"𝔥",
"𝓱",
"𝒽",
"𝓗",
"𝐇",
"𝐡",
"ℍ",
"𝕙",
"ʰ",
],
i: [
"Ⓘ",
"ⓘ",
"ι",
"I",
"i",
"Ꭵ",
"丨",
"Ɨ",
"เ",
"ι",
"ί",
"Į",
"𝔦",
"𝓲",
"𝒾",
"𝓘",
"𝐈",
"𝐢",
"𝕀",
"𝕚",
"ᶤ",
],
j: [
"Ⓙ",
"ⓙ",
"נ",
"J",
"ڶ",
"j",
"ᒎ",
"Ĵ",
"ן",
"נ",
"ј",
"Ĵ",
"𝔧",
"𝓳",
"𝒿",
"𝓙",
"𝐉",
"𝐣",
"𝕁",
"𝕛",
"ʲ",
],
k: [
"Ⓚ",
"ⓚ",
"к",
"K",
"k",
"ᛕ",
"Ҝ",
"к",
"к",
"ķ",
"Ќ",
"𝔨",
"𝓴",
"𝓀",
"𝓚",
"𝐊",
"𝐤",
"𝕂",
"𝕜",
"ᵏ",
"Ҝ",
],
l: [
"Ⓛ",
"ⓛ",
"ℓ",
"ㄥ",
"L",
"l",
"ᒪ",
"Ł",
"l",
"ℓ",
"Ļ",
"Ĺ",
"𝔩",
"𝓵",
"𝓁",
"𝓛",
"𝐋",
"𝐥",
"𝕃",
"𝕝",
"ˡ",
],
m: [
"Ⓜ",
"ⓜ",
"м",
"M",
"m",
"ᗰ",
"Μ",
"๓",
"м",
"м",
"ϻ",
"𝔪",
"𝓶",
"𝓂",
"𝓜",
"𝐌",
"𝐦",
"𝕄",
"𝕞",
"ᵐ",
"爪",
],
n: [
"Ⓝ",
"几",
"ⓝ",
"η",
"N",
"n",
"ᑎ",
"Ň",
"ภ",
"η",
"ή",
"Ň",
"𝔫",
"𝓷",
"𝓃",
"𝓝",
"𝐍",
"𝐧",
"ℕ",
"𝕟",
"ᶰ",
],
o: [
"Ⓞ",
"ㄖ",
"ⓞ",
"σ",
"O",
"o",
"ᗝ",
"Ø",
"๏",
"σ",
"ό",
"Ỗ",
"𝔬",
"𝓸",
"𝑜",
"𝓞",
"𝐎",
"𝐨",
"𝕆",
"𝕠",
"ᵒ",
],
p: [
"Ⓟ",
"ⓟ",
"ρ",
"P",
"p",
"卩",
"ᑭ",
"Ƥ",
"ק",
"ρ",
"ρ",
"Ƥ",
"𝔭",
"𝓹",
"𝓅",
"𝓟",
"𝐏",
"𝐩",
"ℙ",
"𝕡",
"ᵖ",
],
q: [
"Ⓠ",
"ⓠ",
"q",
"Q",
"q",
"Ɋ",
"Ω",
"ợ",
"q",
"q",
"Ǫ",
"𝔮",
"𝓺",
"𝓆",
"𝓠",
"𝐐",
"𝐪",
"ℚ",
"𝕢",
"ᵠ",
],
r: [
"Ⓡ",
"ⓡ",
"я",
"尺",
"R",
"r",
"ᖇ",
"Ř",
"г",
"я",
"ŕ",
"Ř",
"𝔯",
"𝓻",
"𝓇",
"𝓡",
"𝐑",
"𝐫",
"ℝ",
"𝕣",
"ʳ",
],
s: [
"Ⓢ",
"ⓢ",
"ѕ",
"S",
"丂",
"s",
"ᔕ",
"Ş",
"ร",
"s",
"ş",
"Ŝ",
"𝔰",
"𝓼",
"𝓈",
"𝓢",
"𝐒",
"𝐬",
"𝕊",
"𝕤",
"ˢ",
],
t: [
"Ⓣ",
"ⓣ",
"т",
"T",
"t",
"丅",
"Ŧ",
"t",
"т",
"ţ",
"Ť",
"𝔱",
"𝓽",
"𝓉",
"𝓣",
"𝐓",
"𝐭",
"𝕋",
"𝕥",
"ᵗ",
],
u: [
"Ⓤ",
"ⓤ",
"υ",
"U",
"u",
"ᑌ",
"Ữ",
"ย",
"υ",
"ù",
"Ǘ",
"𝔲",
"𝓾",
"𝓊",
"𝓤",
"𝐔",
"𝐮",
"𝕌",
"𝕦",
"ᵘ",
],
v: [
"Ⓥ",
"ⓥ",
"ν",
"V",
"v",
"ᐯ",
"V",
"ש",
"v",
"ν",
"Ѷ",
"𝔳",
"𝓿",
"𝓋",
"𝓥",
"𝐕",
"𝐯",
"𝕍",
"𝕧",
"ᵛ",
],
w: [
"Ⓦ",
"ⓦ",
"ω",
"W",
"w",
"ᗯ",
"Ŵ",
"ฬ",
"ω",
"ώ",
"Ŵ",
"𝔴",
"𝔀",
"𝓌",
"𝓦",
"𝐖",
"𝐰",
"𝕎",
"𝕨",
"ʷ",
"山",
],
x: [
"Ⓧ",
"ⓧ",
"χ",
"X",
"乂",
"x",
"᙭",
"Ж",
"א",
"x",
"x",
"Ж",
"𝔵",
"𝔁",
"𝓍",
"𝓧",
"𝐗",
"𝐱",
"𝕏",
"𝕩",
"ˣ",
],
y: [
"Ⓨ",
"ㄚ",
"ⓨ",
"у",
"Y",
"y",
"Ƴ",
"¥",
"ץ",
"ү",
"ч",
"Ў",
"𝔶",
"𝔂",
"𝓎",
"𝓨",
"𝐘",
"𝐲",
"𝕐",
"𝕪",
"ʸ",
],
z: [
"Ⓩ",
"ⓩ",
"z",
"乙",
"Z",
"z",
"Ƶ",
"Ž",
"z",
"z",
"ž",
"Ż",
"𝔷",
"𝔃",
"𝓏",
"𝓩",
"𝐙",
"𝐳",
"ℤ",
"𝕫",
"ᶻ",
],
};
// If the character exists in the map, return a random replacement
if (map[c]) {
// Use the helper function only if `map[c]` is an array
if (Array.isArray(map[c])) {
return randomElement(map[c] as string[]); // Type assertion to string[]
} else {
return map[c] as string; // Directly return the string value
}
} else {
// Otherwise, return the character as-is
return c;
}
}
reverseIsDisabled = true;
function backward(text: string) {
return $("#english-text").val();
}
let phrases1;
let phrases2;
let words1;
let words2;
let intraword1;
let intraword2;
let prefixes1;
let prefixes2;
let suffixes1;
let suffixes2;
let regex1;
let regex2;
let rev_regex1;
let rev_regex2;
let ordering1;
let ordering2;
try {
const jsonData = {
phrases1: "",
phrases2: "",
words1: "",
words2: "",
intraword1: "",
intraword2: "",
prefixes1: "",
prefixes2: "",
suffixes1: "",
suffixes2: "",
regex1: "",
regex2: "",
rev_regex1: "",
rev_regex2: "",
ordering1: "",
ordering2: "",
};
phrases1 = jsonData.phrases1.split("\n");
phrases2 = jsonData.phrases2.split("\n");
words1 = jsonData.words1.split("\n");
words2 = jsonData.words2.split("\n");
intraword1 = jsonData.intraword1.split("\n");
intraword2 = jsonData.intraword2.split("\n");
prefixes1 = jsonData.prefixes1.split("\n");
prefixes2 = jsonData.prefixes2.split("\n");
suffixes1 = jsonData.suffixes1.split("\n");
suffixes2 = jsonData.suffixes2.split("\n");
regex1 = jsonData.regex1.split("\n");
regex2 = jsonData.regex2.split("\n");
rev_regex1 = jsonData.rev_regex1.split("\n");
rev_regex2 = jsonData.rev_regex2.split("\n");
ordering1 = jsonData.ordering1.split("\n");
ordering2 = jsonData.ordering2.split("\n");
} catch (err: any) {
alert(
"Ahh an error! Please contact me via hello@josephrocca.com and I'll fix it asap. Error log: " +
err.message
);
}
//fix for mysql trailing newline deletion problem
function evenUpSizes(a: any, b: any) {
if (a.length > b.length) {
while (a.length > b.length) b.push("");
} else if (b.length > a.length) {
while (b.length > a.length) a.push("");
}
}
evenUpSizes(phrases1, phrases2);
evenUpSizes(words1, words2);
evenUpSizes(intraword1, intraword2);
evenUpSizes(prefixes1, prefixes2);
evenUpSizes(suffixes1, suffixes2);
export { forward, crazyWithFlourishOrSymbols };