Sync upstream and refine Translate/PromptCraft/Glitch UI

Made-with: Cursor
This commit is contained in:
Dustin Farley
2026-03-20 22:56:16 -07:00
parent bb42291a85
commit 8ff45957c8
101 changed files with 9508 additions and 177 deletions
+97
View File
@@ -0,0 +1,97 @@
// base122 encoding (more efficient than Base64)
import BaseTransformer from '../BaseTransformer.js';
export default new BaseTransformer({
name: 'Base122',
priority: 250,
category: 'encoding',
func: function(text) {
// Base122 uses UTF-8 bytes and encodes them more efficiently
// It uses 7-bit ASCII (0-127) plus some safe 2-byte UTF-8 sequences
const bytes = new TextEncoder().encode(text);
let result = '';
let i = 0;
while (i < bytes.length) {
const byte = bytes[i];
if (byte < 128) {
// Single byte ASCII
result += String.fromCharCode(byte);
i++;
} else if (i + 1 < bytes.length) {
// Try to encode as 2-byte sequence
const b1 = byte;
const b2 = bytes[i + 1];
// Check if it's a valid 2-byte UTF-8 sequence
if ((b1 & 0xE0) === 0xC0 && (b2 & 0xC0) === 0x80) {
result += String.fromCharCode(b1, b2);
i += 2;
} else {
// Fallback: encode as escaped sequence
result += String.fromCharCode(0xC2, 0x80 + (byte - 128));
i++;
}
} else {
// Last byte, encode as escaped
result += String.fromCharCode(0xC2, 0x80 + (byte - 128));
i++;
}
}
return result;
},
reverse: function(text) {
const bytes = [];
let i = 0;
while (i < text.length) {
const code = text.charCodeAt(i);
if (code < 128) {
bytes.push(code);
i++;
} else if (i + 1 < text.length) {
// Check for 2-byte sequence
const b1 = code;
const b2 = text.charCodeAt(i + 1);
if ((b1 & 0xE0) === 0xC0 && (b2 & 0xC0) === 0x80) {
// Extract original byte from escaped sequence
if (b1 === 0xC2 && b2 >= 0x80 && b2 < 0xC0) {
bytes.push(b2 - 0x80);
} else {
bytes.push(b1, b2);
}
i += 2;
} else {
bytes.push(code);
i++;
}
} else {
bytes.push(code);
i++;
}
}
try {
return new TextDecoder().decode(new Uint8Array(bytes));
} catch (e) {
return '';
}
},
preview: function(text) {
if (!text) return '[base122]';
const result = this.func(text.slice(0, 10));
return result.substring(0, 15) + '...';
},
detector: function(text) {
// Base122 produces text that's mostly ASCII with some UTF-8 sequences
// Hard to detect reliably, but check for mix of ASCII and UTF-8
const hasAscii = /[\x00-\x7F]/.test(text);
const hasUtf8 = /[\xC0-\xFF]/.test(text);
return hasAscii && text.length >= 8;
}
});
+60
View File
@@ -0,0 +1,60 @@
// base36 encoding transform
import BaseTransformer from '../BaseTransformer.js';
export default new BaseTransformer({
name: 'Base36',
priority: 270,
category: 'encoding',
alphabet: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ',
func: function(text) {
const bytes = new TextEncoder().encode(text);
let num = 0n;
for (let i = 0; i < bytes.length; i++) {
num = num * 256n + BigInt(bytes[i]);
}
if (num === 0n) return '0';
let result = '';
const base = 36n;
while (num > 0n) {
result = this.alphabet[Number(num % base)] + result;
num = num / base;
}
return result;
},
reverse: function(text) {
try {
let num = 0n;
const base = 36n;
for (let i = 0; i < text.length; i++) {
const char = text[i].toUpperCase();
const idx = this.alphabet.indexOf(char);
if (idx === -1) return text;
num = num * base + BigInt(idx);
}
// Convert back to bytes
const bytes = [];
while (num > 0n) {
bytes.unshift(Number(num % 256n));
num = num / 256n;
}
return new TextDecoder().decode(new Uint8Array(bytes));
} catch (e) {
return text;
}
},
preview: function(text) {
if (!text) return '[base36]';
const result = this.func(text.slice(0, 4));
return result.substring(0, 12) + '...';
},
detector: function(text) {
const cleaned = text.trim().replace(/\s/g, '').toUpperCase();
return cleaned.length >= 4 && /^[0-9A-Z]+$/.test(cleaned);
}
});
+59
View File
@@ -0,0 +1,59 @@
// base91 encoding transform
import BaseTransformer from '../BaseTransformer.js';
export default new BaseTransformer({
name: 'Base91',
priority: 270,
category: 'encoding',
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_`{|}~"',
func: function(text) {
const bytes = new TextEncoder().encode(text);
let num = 0n;
for (let i = 0; i < bytes.length; i++) {
num = num * 256n + BigInt(bytes[i]);
}
if (num === 0n) return this.alphabet[0];
let result = '';
const base = 91n;
while (num > 0n) {
result = this.alphabet[Number(num % base)] + result;
num = num / base;
}
return result;
},
reverse: function(text) {
try {
let num = 0n;
const base = 91n;
for (let i = 0; i < text.length; i++) {
const idx = this.alphabet.indexOf(text[i]);
if (idx === -1) return text;
num = num * base + BigInt(idx);
}
// Convert back to bytes
const bytes = [];
while (num > 0n) {
bytes.unshift(Number(num % 256n));
num = num / 256n;
}
return new TextDecoder().decode(new Uint8Array(bytes));
} catch (e) {
return text;
}
},
preview: function(text) {
if (!text) return '[base91]';
const result = this.func(text.slice(0, 4));
return result.substring(0, 12) + '...';
},
detector: function(text) {
const cleaned = text.trim().replace(/\s/g, '');
return cleaned.length >= 4 && /^[A-Za-z0-9!#$%&()*+,./:;<=>?@[\]^_`{|}~"]+$/.test(cleaned);
}
});
+151
View File
@@ -0,0 +1,151 @@
// baudot code / ITA2 encoding (teletype code)
import BaseTransformer from '../BaseTransformer.js';
export default new BaseTransformer({
name: 'Baudot Code (ITA2)',
priority: 250,
category: 'encoding',
// Baudot/ITA2 5-bit code (letters and figures shift)
letters: {
0b00000: ' ', // NULL/blank
0b00010: 'E',
0b00011: '\n', // Line feed
0b00100: 'A',
0b00101: ' ',
0b00110: 'S',
0b00111: 'I',
0b01000: 'U',
0b01001: '\r', // Carriage return
0b01010: 'D',
0b01011: 'R',
0b01100: 'J',
0b01101: 'N',
0b01110: 'F',
0b01111: 'C',
0b10000: 'K',
0b10001: 'T',
0b10010: 'Z',
0b10011: 'L',
0b10100: 'W',
0b10101: 'H',
0b10110: 'Y',
0b10111: 'P',
0b11000: 'Q',
0b11001: 'O',
0b11010: 'B',
0b11011: 'G',
0b11100: 'Figures', // Shift to figures
0b11101: 'M',
0b11110: 'X',
0b11111: 'V',
},
figures: {
0b00000: ' ',
0b00010: '3',
0b00011: '\n',
0b00100: '-',
0b00101: ' ',
0b00110: '\'',
0b00111: '8',
0b01000: '7',
0b01001: '\r',
0b01010: '\u0005', // ENQ
0b01011: '4',
0b01100: '\'', // Bell
0b01101: ',',
0b01110: '!',
0b01111: ':',
0b10000: '(',
0b10001: '5',
0b10010: '+',
0b10011: ')',
0b10100: '2',
0b10101: '$',
0b10110: '6',
0b10111: '0',
0b11000: '1',
0b11001: '9',
0b11010: '?',
0b11011: '&',
0b11100: 'Letters', // Shift to letters
0b11101: '.',
0b11110: '/',
0b11111: '=',
},
func: function(text) {
// Create reverse maps
const lettersToCode = {};
const figuresToCode = {};
for (const [code, char] of Object.entries(this.letters)) {
if (char !== 'Figures' && char !== 'Letters') {
lettersToCode[char] = parseInt(code);
}
}
for (const [code, char] of Object.entries(this.figures)) {
if (char !== 'Figures' && char !== 'Letters') {
figuresToCode[char] = parseInt(code);
}
}
let result = '';
let inFigures = false;
for (const char of text.toUpperCase()) {
// Check if we need to shift
const isFigure = /[0-9\-'():!$?&.\/+=]/.test(char);
if (isFigure && !inFigures) {
result += String.fromCharCode(0b11100); // Figures shift
inFigures = true;
} else if (!isFigure && inFigures) {
result += String.fromCharCode(0b11111); // Letters shift (approximate)
inFigures = false;
}
// Encode character
const code = inFigures ? figuresToCode[char] : lettersToCode[char];
if (code !== undefined) {
result += String.fromCharCode(code);
} else {
result += char; // Keep unmapped
}
}
return result;
},
reverse: function(text) {
let result = '';
let inFigures = false;
for (let i = 0; i < text.length; i++) {
const code = text.charCodeAt(i) & 0x1F; // 5 bits
if (code === 0b11100) {
inFigures = true;
continue;
} else if (code === 0b11111) {
inFigures = false;
continue;
}
const map = inFigures ? this.figures : this.letters;
const char = map[code];
if (char && char !== 'Figures' && char !== 'Letters') {
result += char;
}
}
return result;
},
preview: function(text) {
if (!text) return '[baudot]';
return this.func(text.slice(0, 5));
},
detector: function(text) {
// Baudot uses 5-bit codes (0-31)
// Check for characters in the 5-bit range
const has5Bit = /[\x00-\x1F]/.test(text);
return has5Bit && text.length >= 5;
}
});
+51
View File
@@ -0,0 +1,51 @@
// binary coded decimal (BCD) transform
import BaseTransformer from '../BaseTransformer.js';
export default new BaseTransformer({
name: 'Binary Coded Decimal',
priority: 300,
category: 'encoding',
func: function(text) {
return [...text].map(c => {
const code = c.charCodeAt(0);
// Convert each digit of the char code to BCD
return code.toString().split('').map(d => {
const digit = parseInt(d);
return digit.toString(2).padStart(4, '0');
}).join(' ');
}).join(' ');
},
reverse: function(text) {
try {
const bcdGroups = text.trim().split(/\s+/);
const chars = [];
let currentCode = '';
for (let i = 0; i < bcdGroups.length; i++) {
if (bcdGroups[i].length === 4 && /^[01]+$/.test(bcdGroups[i])) {
currentCode += parseInt(bcdGroups[i], 2).toString();
if (currentCode.length >= 3) {
const code = parseInt(currentCode);
if (code >= 0 && code <= 65535) {
chars.push(String.fromCharCode(code));
currentCode = '';
}
}
}
}
return chars.join('');
} catch (e) {
return text;
}
},
preview: function(text) {
if (!text) return '[bcd]';
return this.func(text.slice(0, 2));
},
detector: function(text) {
const cleaned = text.trim().replace(/\s/g, '');
return cleaned.length >= 4 && /^[01]+$/.test(cleaned) && cleaned.length % 4 === 0;
}
});
+157
View File
@@ -0,0 +1,157 @@
// EBCDIC encoding (IBM character encoding)
import BaseTransformer from '../BaseTransformer.js';
export default new BaseTransformer({
name: 'EBCDIC',
priority: 250,
category: 'encoding',
// EBCDIC to ASCII mapping (simplified - full EBCDIC has many variants)
ebcdicToAscii: {
0x40: 0x20, // Space
0x4A: 0x21, // !
0x4B: 0x22, // "
0x4C: 0x23, // #
0x4D: 0x24, // $
0x4E: 0x25, // %
0x4F: 0x26, // &
0x50: 0x27, // '
0x5A: 0x28, // (
0x5B: 0x29, // )
0x5C: 0x2A, // *
0x5D: 0x2B, // +
0x5E: 0x2C, // ,
0x5F: 0x2D, // -
0x60: 0x2E, // .
0x61: 0x2F, // /
0xF0: 0x30, // 0
0xF1: 0x31, // 1
0xF2: 0x32, // 2
0xF3: 0x33, // 3
0xF4: 0x34, // 4
0xF5: 0x35, // 5
0xF6: 0x36, // 6
0xF7: 0x37, // 7
0xF8: 0x38, // 8
0xF9: 0x39, // 9
0x7A: 0x3A, // :
0x7B: 0x3B, // ;
0x7C: 0x3C, // <
0x7D: 0x3D, // =
0x7E: 0x3E, // >
0x7F: 0x3F, // ?
0x81: 0x41, // A
0x82: 0x42, // B
0x83: 0x43, // C
0x84: 0x44, // D
0x85: 0x45, // E
0x86: 0x46, // F
0x87: 0x47, // G
0x88: 0x48, // H
0x89: 0x49, // I
0x91: 0x4A, // J
0x92: 0x4B, // K
0x93: 0x4C, // L
0x94: 0x4D, // M
0x95: 0x4E, // N
0x96: 0x4F, // O
0x97: 0x50, // P
0x98: 0x51, // Q
0x99: 0x52, // R
0xA2: 0x53, // S
0xA3: 0x54, // T
0xA4: 0x55, // U
0xA5: 0x56, // V
0xA6: 0x57, // W
0xA7: 0x58, // X
0xA8: 0x59, // Y
0xA9: 0x5A, // Z
},
func: function(text) {
// Convert ASCII to EBCDIC
const asciiToEbcdic = {};
for (const [ebcdic, ascii] of Object.entries(this.ebcdicToAscii)) {
asciiToEbcdic[ascii] = parseInt(ebcdic);
}
let result = '';
for (const char of text) {
const code = char.charCodeAt(0);
// Convert lowercase letters to uppercase before encoding (EBCDIC is uppercase-only)
if (code >= 0x61 && code <= 0x7A) { // a-z
const upperCode = code - 0x20; // Convert to A-Z
if (asciiToEbcdic[upperCode] !== undefined) {
result += String.fromCharCode(asciiToEbcdic[upperCode]);
} else {
result += char; // Keep unmapped characters
}
} else if (asciiToEbcdic[code] !== undefined) {
result += String.fromCharCode(asciiToEbcdic[code]);
} else {
result += char; // Keep unmapped characters
}
}
return result;
},
reverse: function(text) {
let result = '';
for (const char of text) {
const code = char.charCodeAt(0);
if (this.ebcdicToAscii[code] !== undefined) {
result += String.fromCharCode(this.ebcdicToAscii[code]);
} else {
result += char; // Keep unmapped characters
}
}
return result;
},
preview: function(text) {
if (!text) return '[ebcdic]';
return this.func(text.slice(0, 8)) + (text.length > 8 ? '...' : '');
},
detector: function(text) {
if (!text || text.length < 2) return false;
// EBCDIC uses specific byte ranges for letters and numbers
// Letters: 0x81-0xA9 (A-Z)
// Numbers: 0xF0-0xF9 (0-9)
// Punctuation: 0x40-0x7F range
// Check for EBCDIC-specific character codes (letters and numbers)
const hasEbcdicLetters = /[\x81-\x89\x91-\x99\xA2-\xA9]/.test(text); // A-Z in EBCDIC
const hasEbcdicNumbers = /[\xF0-\xF9]/.test(text); // 0-9 in EBCDIC
// Must have at least some EBCDIC-specific characters
if (!hasEbcdicLetters && !hasEbcdicNumbers) return false;
// Reject if text is already readable ASCII (common English words)
// This prevents false positives on plain text
const commonWords = /\b(the|and|for|are|but|not|you|all|can|her|was|one|our|out|day|get|has|him|his|how|man|new|now|old|see|two|way|who|boy|did|its|let|put|say|she|too|use)\b/i;
if (commonWords.test(text)) return false;
// Check if decoding produces text that looks like it was encoded
// EBCDIC-encoded text, when decoded, should have readable ASCII
// If the input is already readable ASCII, it's not EBCDIC
const readableAscii = /^[\x20-\x7E\s]*$/.test(text);
if (readableAscii && !hasEbcdicLetters && !hasEbcdicNumbers) {
// If it's all readable ASCII and has no EBCDIC-specific codes, reject
return false;
}
// Verify that at least some characters are in EBCDIC-specific ranges
// For short strings, require at least 1 EBCDIC character
// For longer strings, require at least 10% to be EBCDIC-specific
const ebcdicChars = (text.match(/[\x81-\x89\x91-\x99\xA2-\xA9\xF0-\xF9]/g) || []).length;
if (ebcdicChars === 0) return false;
// For short strings (<= 20 chars), just need at least 1 EBCDIC char
if (text.length <= 20) {
return ebcdicChars >= 1;
}
// For longer strings, require at least 10% to be EBCDIC-specific
const ebcdicRatio = ebcdicChars / text.length;
return ebcdicRatio >= 0.1; // At least 10% must be EBCDIC-specific
}
});
@@ -0,0 +1,79 @@
// emoji encoding transform
import BaseTransformer from '../BaseTransformer.js';
export default new BaseTransformer({
name: 'Emoji Encoding',
priority: 250,
category: 'encoding',
// Map bytes to emoji (using common emojis)
emojiMap: [
'😀', '😃', '😄', '😁', '😆', '😅', '🤣', '😂', '🙂', '🙃',
'😉', '😊', '😇', '🥰', '😍', '🤩', '😘', '😗', '😚', '😙',
'😋', '😛', '😜', '🤪', '😝', '🤑', '🤗', '🤭', '🤫', '🤔',
'🤐', '🤨', '😐', '😑', '😶', '😏', '😒', '🙄', '😬', '🤥',
'😌', '😔', '😪', '🤤', '😴', '😷', '🤒', '🤕', '🤢', '🤮',
'🤧', '🥵', '🥶', '😶‍🌫️', '😵', '😵‍💫', '🤯', '🤠', '🥳', '😎',
'🤓', '🧐', '😕', '😟', '🙁', '😮', '😯', '😲', '😳', '🥺',
'😦', '😧', '😨', '😰', '😥', '😢', '😭', '😱', '😖', '😣',
'😞', '😓', '😩', '😫', '🥱', '😤', '😡', '😠', '🤬', '😈',
'👿', '💀', '☠️', '💩', '🤡', '👹', '👺', '👻', '👽', '👾',
'🤖', '😺', '😸', '😹', '😻', '😼', '😽', '🙀', '😿', '😾',
'🙈', '🙉', '🙊', '💋', '💌', '💘', '💝', '💖', '💗', '💓',
'💞', '💕', '💟', '❣️', '💔', '❤️', '🧡', '💛', '💚', '💙',
'💜', '🖤', '🤍', '🤎', '💯', '💢', '💥', '💫', '💦', '💨',
'🕳️', '💣', '💬', '👁️‍🗨️', '🗨️', '🗯️', '💭', '💤', '👋', '🤚',
'🖐️', '✋', '🖖', '👌', '🤌', '🤏', '✌️', '🤞', '🤟', '🤘',
'🤙', '👈', '👉', '👆', '🖕', '👇', '☝️', '👍', '👎', '✊',
'👊', '🤛', '🤜', '👏', '🙌', '👐', '🤲', '🤝', '🙏', '✍️',
'💪', '🦾', '🦿', '🦵', '🦶', '👂', '🦻', '👃', '🧠', '🫀',
'🫁', '🦷', '🦴', '👀', '👁️', '👅', '👄', '💋', '🩸', '👶',
'🧒', '👦', '👧', '🧑', '👱', '👨', '🧔', '👨‍🦰', '👨‍🦱', '👨‍🦳',
'👨‍🦲', '👩', '👩‍🦰', '👩‍🦱', '👩‍🦳', '👩‍🦲', '🧓', '👴', '👵', '🙍',
'🙎', '🙅', '🙆', '💁', '🙋', '🧏', '🤦', '🤦‍♂️', '🤦‍♀️', '🤷',
'🤷‍♂️', '🤷‍♀️', '🙇', '🙇‍♂️', '🙇‍♀️', '🤦', '🤦‍♂️', '🤦‍♀️', '🤷', '🤷‍♂️'
],
func: function(text) {
const bytes = new TextEncoder().encode(text);
let result = '';
for (const byte of bytes) {
result += this.emojiMap[byte % this.emojiMap.length] + ' ';
}
return result.trim();
},
reverse: function(text) {
// Create reverse map
const reverseMap = {};
for (let i = 0; i < this.emojiMap.length; i++) {
reverseMap[this.emojiMap[i]] = i;
}
// Extract emojis (match any emoji, not just specific range)
const emojis = text.match(/[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/gu) || [];
const bytes = [];
for (const emoji of emojis) {
if (reverseMap[emoji] !== undefined) {
bytes.push(reverseMap[emoji]);
}
}
try {
return new TextDecoder().decode(new Uint8Array(bytes));
} catch (e) {
return '';
}
},
preview: function(text) {
if (!text) return '[emoji-encoding]';
return this.func(text.slice(0, 3));
},
detector: function(text) {
// Check for emoji patterns (broader range)
const emojiPattern = /[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/gu;
const matches = text.match(emojiPattern) || [];
return matches.length >= 3;
}
});
+56
View File
@@ -0,0 +1,56 @@
// gray code transform
import BaseTransformer from '../BaseTransformer.js';
export default new BaseTransformer({
name: 'Gray Code',
priority: 300,
category: 'encoding',
func: function(text) {
const bytes = new TextEncoder().encode(text);
const binary = Array.from(bytes)
.map(b => b.toString(2).padStart(8, '0'))
.join('');
// Convert to Gray code
let gray = binary[0];
for (let i = 1; i < binary.length; i++) {
gray += (parseInt(binary[i - 1]) ^ parseInt(binary[i])).toString();
}
return gray;
},
reverse: function(text) {
try {
// Convert from Gray code to binary
if (!/^[01]+$/.test(text)) return text;
let binary = text[0];
for (let i = 1; i < text.length; i++) {
binary += (parseInt(binary[i - 1]) ^ parseInt(text[i])).toString();
}
// Convert binary to bytes
const bytes = [];
for (let i = 0; i < binary.length; i += 8) {
const byte = binary.slice(i, i + 8);
if (byte.length === 8) {
bytes.push(parseInt(byte, 2));
}
}
return new TextDecoder().decode(new Uint8Array(bytes));
} catch (e) {
return text;
}
},
preview: function(text) {
if (!text) return '[gray]';
const result = this.func(text.slice(0, 2));
return result.substring(0, 16) + '...';
},
detector: function(text) {
const cleaned = text.trim().replace(/\s/g, '');
return cleaned.length >= 8 && /^[01]+$/.test(cleaned);
}
});
@@ -0,0 +1,78 @@
// quoted-printable encoding transform (RFC 2045)
import BaseTransformer from '../BaseTransformer.js';
export default new BaseTransformer({
name: 'Quoted-Printable',
priority: 70,
category: 'encoding',
func: function(text) {
const bytes = new TextEncoder().encode(text);
let result = '';
for (let i = 0; i < bytes.length; i++) {
const byte = bytes[i];
// Printable ASCII (33-126) except = (61) can be used as-is
// Space (32) can be used, but often encoded as =20
// = (61) must be encoded as =3D
if (byte >= 33 && byte <= 60 || byte >= 63 && byte <= 126) {
result += String.fromCharCode(byte);
} else if (byte === 32) {
// Space can be space or =20
result += ' ';
} else {
// Encode as =XX
result += '=' + byte.toString(16).toUpperCase().padStart(2, '0');
}
}
// Soft line breaks: lines should not exceed 76 chars (excluding CRLF)
// For simplicity, we'll add = at end of long lines
const lines = [];
let currentLine = '';
for (let i = 0; i < result.length; i++) {
if (currentLine.length >= 75) {
lines.push(currentLine + '=');
currentLine = result[i];
} else {
currentLine += result[i];
}
}
if (currentLine) lines.push(currentLine);
return lines.join('\r\n');
},
reverse: function(text) {
try {
// Remove soft line breaks (= at end of line)
let cleaned = text.replace(/=\r?\n/g, '').replace(/=\r/g, '');
let result = '';
for (let i = 0; i < cleaned.length; i++) {
if (cleaned[i] === '=' && i + 2 < cleaned.length) {
const hex = cleaned.substring(i + 1, i + 3);
const byte = parseInt(hex, 16);
if (!isNaN(byte)) {
result += String.fromCharCode(byte);
i += 2;
continue;
}
}
result += cleaned[i];
}
return new TextDecoder().decode(new TextEncoder().encode(result));
} catch (e) {
return text;
}
},
preview: function(text) {
if (!text) return '[qp]';
const result = this.func(text.slice(0, 10));
return result.substring(0, 20).replace(/\r?\n/g, ' ') + '...';
},
detector: function(text) {
// Check for quoted-printable patterns (=XX hex codes)
return /=([0-9A-F]{2})/i.test(text);
}
});
@@ -0,0 +1,40 @@
// unicode code points encoding transform
import BaseTransformer from '../BaseTransformer.js';
export default new BaseTransformer({
name: 'Unicode Code Points',
priority: 250,
category: 'encoding',
func: function(text) {
// Encode text as Unicode code points (U+XXXX format)
let result = '';
for (let i = 0; i < text.length; i++) {
const code = text.charCodeAt(i);
result += 'U+' + code.toString(16).toUpperCase().padStart(4, '0') + ' ';
}
return result.trim();
},
reverse: function(text) {
// Extract U+XXXX patterns and convert back to characters
const matches = text.match(/U\+([0-9A-Fa-f]{4,6})/g) || [];
let result = '';
for (const match of matches) {
const code = parseInt(match.substring(2), 16);
if (code >= 0 && code <= 0x10FFFF) {
result += String.fromCharCode(code);
}
}
return result;
},
preview: function(text) {
if (!text) return '[unicode-points]';
const result = this.func(text.slice(0, 3));
return result.substring(0, 20) + '...';
},
detector: function(text) {
// Check for U+XXXX pattern
const pattern = /U\+[0-9A-Fa-f]{4,6}/;
return pattern.test(text) && text.match(/U\+[0-9A-Fa-f]{4,6}/g).length >= 2;
}
});
+81
View File
@@ -0,0 +1,81 @@
// uuencoding transform (Unix-to-Unix encoding)
import BaseTransformer from '../BaseTransformer.js';
export default new BaseTransformer({
name: 'Uuencoding',
priority: 250,
category: 'encoding',
func: function(text) {
// Uuencoding encodes 3 bytes into 4 characters
// Each character represents 6 bits (0-63)
const uuChars = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_';
let result = '';
const bytes = new TextEncoder().encode(text);
for (let i = 0; i < bytes.length; i += 3) {
const b1 = bytes[i] || 0;
const b2 = bytes[i + 1] || 0;
const b3 = bytes[i + 2] || 0;
// Combine 3 bytes (24 bits) into 4 6-bit values
const val1 = (b1 >> 2) & 0x3F;
const val2 = ((b1 << 4) | (b2 >> 4)) & 0x3F;
const val3 = ((b2 << 2) | (b3 >> 6)) & 0x3F;
const val4 = b3 & 0x3F;
result += uuChars[val1] + uuChars[val2] + uuChars[val3] + uuChars[val4];
}
return result;
},
reverse: function(text) {
const uuChars = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_';
const bytes = [];
const totalChunks = Math.floor(text.length / 4);
for (let i = 0; i < totalChunks; i++) {
const chunk = text.substring(i * 4, (i + 1) * 4);
if (chunk.length < 4) break;
const val1 = uuChars.indexOf(chunk[0]);
const val2 = uuChars.indexOf(chunk[1]);
const val3 = uuChars.indexOf(chunk[2]);
const val4 = uuChars.indexOf(chunk[3]);
if (val1 === -1 || val2 === -1 || val3 === -1 || val4 === -1) continue;
// Reconstruct 3 bytes from 4 6-bit values
const b1 = (val1 << 2) | (val2 >> 4);
const b2 = ((val2 << 4) | (val3 >> 2)) & 0xFF;
const b3 = ((val3 << 6) | val4) & 0xFF;
bytes.push(b1);
bytes.push(b2);
bytes.push(b3);
}
// Remove trailing null bytes (padding)
while (bytes.length > 0 && bytes[bytes.length - 1] === 0) {
bytes.pop();
}
try {
return new TextDecoder().decode(new Uint8Array(bytes));
} catch (e) {
return '';
}
},
preview: function(text) {
if (!text) return '[uuencoding]';
const result = this.func(text.slice(0, 3));
return result.substring(0, 8) + '...';
},
detector: function(text) {
// Uuencoding uses specific character set: space through underscore (ASCII 32-95)
const uuPattern = /^[ !"#$%&'()*+,\-./0-9:;<=>?@A-Z[\\\]^_]+$/;
return text.length >= 8 && uuPattern.test(text) && text.length % 4 === 0;
}
});
+63
View File
@@ -0,0 +1,63 @@
// yenc encoding transform (Usenet binary encoding)
import BaseTransformer from '../BaseTransformer.js';
export default new BaseTransformer({
name: 'YEnc',
priority: 250,
category: 'encoding',
func: function(text) {
// YEnc encodes bytes by adding 42 (0x2A) and escaping special characters
const bytes = new TextEncoder().encode(text);
let result = '';
for (const byte of bytes) {
let encoded = (byte + 42) % 256;
// Escape special characters: NULL (0), LF (10), CR (13), = (61)
if (encoded === 0 || encoded === 10 || encoded === 13 || encoded === 61) {
result += '=' + String.fromCharCode((encoded + 64) % 256);
} else {
result += String.fromCharCode(encoded);
}
}
return result;
},
reverse: function(text) {
const bytes = [];
let i = 0;
while (i < text.length) {
if (text[i] === '=' && i + 1 < text.length) {
// Escaped character
const escaped = text.charCodeAt(i + 1);
const decoded = (escaped - 64) % 256;
bytes.push((decoded - 42 + 256) % 256);
i += 2;
} else {
// Normal character
const encoded = text.charCodeAt(i);
bytes.push((encoded - 42 + 256) % 256);
i++;
}
}
try {
return new TextDecoder().decode(new Uint8Array(bytes));
} catch (e) {
return '';
}
},
preview: function(text) {
if (!text) return '[yenc]';
const result = this.func(text.slice(0, 3));
return result.substring(0, 8) + '...';
},
detector: function(text) {
// YEnc produces binary-like data, hard to detect reliably
// Check for escape sequences (= followed by character)
const escapePattern = /=[\x00-\xFF]/;
return escapePattern.test(text) && text.length >= 8;
}
});
+92
View File
@@ -0,0 +1,92 @@
// z85 encoding (ZeroMQ Base85)
import BaseTransformer from '../BaseTransformer.js';
export default new BaseTransformer({
name: 'Z85',
priority: 250,
category: 'encoding',
// Z85 uses a different character set than standard Base85
charset: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#',
func: function(text) {
const bytes = new TextEncoder().encode(text);
let result = '';
const originalLength = bytes.length;
// Z85 encodes 4 bytes into 5 characters
for (let i = 0; i < bytes.length; i += 4) {
const chunk = Array.from(bytes.slice(i, i + 4));
const chunkLength = chunk.length;
while (chunk.length < 4) chunk.push(0);
// Convert 4 bytes to 32-bit integer
let value = 0;
for (let j = 0; j < 4; j++) {
value = (value << 8) + chunk[j];
}
// Convert to base 85 (5 digits)
const z85Chars = [];
for (let j = 0; j < 5; j++) {
z85Chars.unshift(this.charset[value % 85]);
value = Math.floor(value / 85);
}
result += z85Chars.join('');
}
// Store original length for decoding
this._z85OriginalLength = originalLength;
return result;
},
reverse: function(text) {
const bytes = [];
// Z85 decodes 5 characters into 4 bytes
for (let i = 0; i < text.length; i += 5) {
const chunk = text.substring(i, i + 5);
if (chunk.length < 5) break;
// Convert 5 base-85 digits to 32-bit integer
let value = 0;
for (const char of chunk) {
const idx = this.charset.indexOf(char);
if (idx === -1) return ''; // Invalid character
value = value * 85 + idx;
}
// Extract 4 bytes
bytes.push((value >> 24) & 0xFF);
bytes.push((value >> 16) & 0xFF);
bytes.push((value >> 8) & 0xFF);
bytes.push(value & 0xFF);
}
// Trim to original length if we stored it
if (this._z85OriginalLength !== undefined) {
bytes.length = Math.min(bytes.length, this._z85OriginalLength);
} else {
// Remove trailing null bytes (padding)
while (bytes.length > 0 && bytes[bytes.length - 1] === 0) {
bytes.pop();
}
}
try {
return new TextDecoder().decode(new Uint8Array(bytes));
} catch (e) {
return '';
}
},
preview: function(text) {
if (!text) return '[z85]';
const result = this.func(text.slice(0, 4));
return result.substring(0, 10) + '...';
},
detector: function(text) {
// Z85 uses specific character set
const z85Pattern = /^[0-9a-zA-Z.\-:+=^!\/\*\?&<>()\[\]{}@%$#]+$/;
return text.length >= 5 && z85Pattern.test(text) && text.length % 5 === 0;
}
});