mirror of
https://github.com/elder-plinius/P4RS3LT0NGV3.git
synced 2026-05-27 18:32:24 +02:00
refactor: migrate to modular tool-based architecture
- Implement tool registry system with individual tool modules - Reorganize transformers into categorized source modules - Remove emojiLibrary.js, consolidate into EmojiUtils and emojiData - Fix mobile close button and tooltip functionality - Add build system for transforms and emoji data - Migrate from Python backend to pure JavaScript - Add comprehensive documentation and testing - Improve code organization and maintainability - Ignore generated files (transforms-bundle.js, emojiData.js)
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
// ascii85 transform
|
||||
import BaseTransformer from '../BaseTransformer.js';
|
||||
|
||||
export default new BaseTransformer({
|
||||
name: 'ASCII85',
|
||||
priority: 290,
|
||||
// Detector: ASCII85 has distinctive <~ ~> wrapper
|
||||
detector: function(text) {
|
||||
return text.startsWith('<~') && text.endsWith('~>');
|
||||
},
|
||||
|
||||
func: function(text) {
|
||||
// Simple ASCII85 encoding implementation
|
||||
// Use TextEncoder to properly handle multi-byte UTF-8 characters
|
||||
const bytes = new TextEncoder().encode(text);
|
||||
let result = '<~';
|
||||
let buffer = 0;
|
||||
let bufferLength = 0;
|
||||
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
buffer = (buffer << 8) | bytes[i];
|
||||
bufferLength += 8;
|
||||
|
||||
if (bufferLength >= 32) {
|
||||
let value = buffer >>> (bufferLength - 32);
|
||||
buffer &= (1 << (bufferLength - 32)) - 1;
|
||||
bufferLength -= 32;
|
||||
|
||||
if (value === 0) {
|
||||
result += 'z';
|
||||
} else {
|
||||
for (let j = 4; j >= 0; j--) {
|
||||
const digit = (value / Math.pow(85, j)) % 85;
|
||||
result += String.fromCharCode(digit + 33);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle remaining bits
|
||||
if (bufferLength > 0) {
|
||||
buffer <<= (32 - bufferLength);
|
||||
let value = buffer;
|
||||
const bytes = Math.ceil(bufferLength / 8);
|
||||
|
||||
for (let j = 4; j >= (4 - bytes); j--) {
|
||||
const digit = (value / Math.pow(85, j)) % 85;
|
||||
result += String.fromCharCode(digit + 33);
|
||||
}
|
||||
}
|
||||
|
||||
return result + '~>';
|
||||
},
|
||||
preview: function(text) {
|
||||
if (!text) return '[ascii85]';
|
||||
const full = this.func(text);
|
||||
return full.substring(0, 16) + (full.length > 16 ? '...' : '');
|
||||
},
|
||||
reverse: function(text) {
|
||||
// Check if it's a valid ASCII85 string
|
||||
if (!text.startsWith('<~') || !text.endsWith('~>')) {
|
||||
return text;
|
||||
}
|
||||
|
||||
// Remove delimiters and whitespace
|
||||
text = text.substring(2, text.length - 2).replace(/\s+/g, '');
|
||||
|
||||
const bytes = [];
|
||||
let i = 0;
|
||||
|
||||
while (i < text.length) {
|
||||
// Handle 'z' special case (represents 4 zero bytes)
|
||||
if (text[i] === 'z') {
|
||||
bytes.push(0, 0, 0, 0);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Process a group of 5 characters
|
||||
if (i < text.length) {
|
||||
let value = 0;
|
||||
const groupSize = Math.min(5, text.length - i);
|
||||
|
||||
// Convert the group to a 32-bit value
|
||||
for (let j = 0; j < groupSize; j++) {
|
||||
value = value * 85 + (text.charCodeAt(i + j) - 33);
|
||||
}
|
||||
|
||||
// Pad with 'u' (84) if needed for partial groups
|
||||
for (let j = groupSize; j < 5; j++) {
|
||||
value = value * 85 + 84;
|
||||
}
|
||||
|
||||
// Extract bytes from the value
|
||||
// groupSize chars encodes (groupSize - 1) bytes
|
||||
const bytesToWrite = groupSize - 1;
|
||||
for (let j = 0; j < bytesToWrite; j++) {
|
||||
bytes.push((value >>> ((3 - j) * 8)) & 0xFF);
|
||||
}
|
||||
|
||||
i += groupSize;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Use TextDecoder to properly handle UTF-8 multi-byte characters
|
||||
return new TextDecoder().decode(new Uint8Array(bytes));
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,85 @@
|
||||
// base32 transform
|
||||
import BaseTransformer from '../BaseTransformer.js';
|
||||
|
||||
export default new BaseTransformer({
|
||||
name: 'Base32',
|
||||
priority: 280,
|
||||
// Detector: Only Base32 characters (A-Z, 2-7, =)
|
||||
detector: function(text) {
|
||||
const cleaned = text.trim().replace(/\s/g, '');
|
||||
return cleaned.length >= 8 && /^[A-Z2-7=]+$/.test(cleaned);
|
||||
},
|
||||
|
||||
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567',
|
||||
func: function(text) {
|
||||
if (!text) return '';
|
||||
|
||||
// Convert text to bytes
|
||||
const bytes = new TextEncoder().encode(text);
|
||||
let result = '';
|
||||
let bits = 0;
|
||||
let value = 0;
|
||||
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
value = (value << 8) | bytes[i];
|
||||
bits += 8;
|
||||
|
||||
while (bits >= 5) {
|
||||
bits -= 5;
|
||||
result += this.alphabet[(value >> bits) & 0x1F];
|
||||
}
|
||||
}
|
||||
|
||||
// Handle remaining bits
|
||||
if (bits > 0) {
|
||||
result += this.alphabet[(value << (5 - bits)) & 0x1F];
|
||||
}
|
||||
|
||||
// Add padding
|
||||
while (result.length % 8 !== 0) {
|
||||
result += '=';
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
preview: function(text) {
|
||||
if (!text) return '[base32]';
|
||||
const full = this.func(text);
|
||||
return full.substring(0, 16) + (full.length > 16 ? '...' : '');
|
||||
},
|
||||
reverse: function(text) {
|
||||
if (!text) return '';
|
||||
|
||||
// Remove padding and whitespace
|
||||
text = text.replace(/\s+/g, '').replace(/=+$/, '');
|
||||
|
||||
if (text.length === 0) return '';
|
||||
|
||||
// Create reverse map
|
||||
const revMap = {};
|
||||
for (let i = 0; i < this.alphabet.length; i++) {
|
||||
revMap[this.alphabet[i]] = i;
|
||||
}
|
||||
|
||||
const bytes = [];
|
||||
let bits = 0;
|
||||
let value = 0;
|
||||
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
const char = text[i].toUpperCase();
|
||||
if (revMap[char] === undefined) continue; // Skip invalid characters
|
||||
|
||||
value = (value << 5) | revMap[char];
|
||||
bits += 5;
|
||||
|
||||
while (bits >= 8) {
|
||||
bits -= 8;
|
||||
bytes.push((value >> bits) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
// Use TextDecoder to properly handle UTF-8 multi-byte characters
|
||||
return new TextDecoder().decode(new Uint8Array(bytes));
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
// base45 transform
|
||||
import BaseTransformer from '../BaseTransformer.js';
|
||||
|
||||
export default new BaseTransformer({
|
||||
|
||||
name: 'Base45',
|
||||
priority: 290,
|
||||
alphabet: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:',
|
||||
func: function(text) {
|
||||
const bytes = new TextEncoder().encode(text);
|
||||
const chars = [];
|
||||
for (let i=0;i<bytes.length;i+=2) {
|
||||
if (i+1 < bytes.length) {
|
||||
const x = 256*bytes[i] + bytes[i+1];
|
||||
const e = x % 45; const d = Math.floor(x/45) % 45; const c = Math.floor(x/45/45);
|
||||
chars.push(this.alphabet[e], this.alphabet[d], this.alphabet[c]);
|
||||
} else {
|
||||
const x = bytes[i];
|
||||
const e = x % 45; const d = Math.floor(x/45);
|
||||
chars.push(this.alphabet[e], this.alphabet[d]);
|
||||
}
|
||||
}
|
||||
return chars.join('');
|
||||
},
|
||||
preview: function(text) {
|
||||
if (!text) return 'QED8W';
|
||||
return this.func(text.slice(0,3));
|
||||
},
|
||||
reverse: function(text) {
|
||||
const index = {}; for (let i=0;i<this.alphabet.length;i++) index[this.alphabet[i]] = i;
|
||||
const codes = [...text].map(c => index[c]).filter(v => v !== undefined);
|
||||
const out = [];
|
||||
for (let i=0;i<codes.length;i+=3) {
|
||||
if (i+2 < codes.length) {
|
||||
const x = codes[i] + codes[i+1]*45 + codes[i+2]*45*45;
|
||||
out.push(x >> 8, x & 0xFF);
|
||||
} else if (i+1 < codes.length) {
|
||||
const x = codes[i] + codes[i+1]*45;
|
||||
out.push(x & 0xFF);
|
||||
}
|
||||
}
|
||||
return new TextDecoder().decode(Uint8Array.from(out));
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,61 @@
|
||||
// base58 transform
|
||||
import BaseTransformer from '../BaseTransformer.js';
|
||||
|
||||
export default new BaseTransformer({
|
||||
name: 'Base58',
|
||||
priority: 275,
|
||||
// Detector: Only Base58 characters (excludes 0, O, I, l)
|
||||
detector: function(text) {
|
||||
const cleaned = text.trim().replace(/\s/g, '');
|
||||
return cleaned.length >= 4 && /^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$/.test(cleaned);
|
||||
},
|
||||
|
||||
alphabet: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',
|
||||
func: function(text) {
|
||||
if (!text) return '';
|
||||
const bytes = new TextEncoder().encode(text);
|
||||
// Count leading zeros
|
||||
let zeros = 0;
|
||||
for (let b of bytes) { if (b === 0) zeros++; else break; }
|
||||
// Convert to BigInt
|
||||
let n = 0n;
|
||||
for (let b of bytes) { n = (n << 8n) + BigInt(b); }
|
||||
// Encode
|
||||
let out = '';
|
||||
while (n > 0n) {
|
||||
const rem = n % 58n;
|
||||
n = n / 58n;
|
||||
out = this.alphabet[Number(rem)] + out;
|
||||
}
|
||||
// Add leading zeros as '1'
|
||||
for (let i = 0; i < zeros; i++) out = '1' + out;
|
||||
return out || '1';
|
||||
},
|
||||
preview: function(text) {
|
||||
if (!text) return '[base58]';
|
||||
const full = this.func(text);
|
||||
return full.substring(0, 12) + (full.length > 12 ? '...' : '');
|
||||
},
|
||||
reverse: function(text) {
|
||||
if (!text) return '';
|
||||
// Count leading '1's
|
||||
let zeros = 0;
|
||||
for (let c of text) { if (c === '1') zeros++; else break; }
|
||||
// Convert to BigInt
|
||||
let n = 0n;
|
||||
for (let c of text) {
|
||||
const i = this.alphabet.indexOf(c);
|
||||
if (i < 0) continue;
|
||||
n = n * 58n + BigInt(i);
|
||||
}
|
||||
// Convert BigInt to bytes
|
||||
const bytes = [];
|
||||
while (n > 0n) {
|
||||
bytes.unshift(Number(n % 256n));
|
||||
n = n / 256n;
|
||||
}
|
||||
for (let i = 0; i < zeros; i++) bytes.unshift(0);
|
||||
return new TextDecoder().decode(Uint8Array.from(bytes));
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
// base62 transform
|
||||
import BaseTransformer from '../BaseTransformer.js';
|
||||
|
||||
export default new BaseTransformer({
|
||||
|
||||
name: 'Base62',
|
||||
priority: 290,
|
||||
alphabet: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
|
||||
func: function(text) {
|
||||
if (!text) return '';
|
||||
const bytes = new TextEncoder().encode(text);
|
||||
let n = 0n;
|
||||
for (let b of bytes) { n = (n << 8n) + BigInt(b); }
|
||||
if (n === 0n) return '0';
|
||||
let out = '';
|
||||
while (n > 0n) {
|
||||
const rem = n % 62n;
|
||||
n = n / 62n;
|
||||
out = this.alphabet[Number(rem)] + out;
|
||||
}
|
||||
return out;
|
||||
},
|
||||
preview: function(text) {
|
||||
if (!text) return '[base62]';
|
||||
return this.func(text.slice(0, 3)) + '...';
|
||||
},
|
||||
reverse: function(text) {
|
||||
if (!text) return '';
|
||||
let n = 0n;
|
||||
for (let c of text) {
|
||||
const i = this.alphabet.indexOf(c);
|
||||
if (i < 0) continue;
|
||||
n = n * 62n + BigInt(i);
|
||||
}
|
||||
const bytes = [];
|
||||
while (n > 0n) {
|
||||
bytes.unshift(Number(n % 256n));
|
||||
n = n / 256n;
|
||||
}
|
||||
if (bytes.length === 0) bytes.push(0);
|
||||
return new TextDecoder().decode(Uint8Array.from(bytes));
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
// base64 transform
|
||||
import BaseTransformer from '../BaseTransformer.js';
|
||||
|
||||
export default new BaseTransformer({
|
||||
name: 'Base64',
|
||||
priority: 270,
|
||||
// Detector: Only Base64 characters (A-Z, a-z, 0-9, +, /, =)
|
||||
detector: function(text) {
|
||||
const cleaned = text.trim().replace(/\s/g, '');
|
||||
return cleaned.length >= 4 && /^[A-Za-z0-9+\/=]+$/.test(cleaned);
|
||||
},
|
||||
|
||||
func: function(text) {
|
||||
try {
|
||||
// Properly encode UTF-8 text (including emojis) to Base64
|
||||
const encoder = new TextEncoder();
|
||||
const bytes = encoder.encode(text);
|
||||
let binaryString = '';
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
binaryString += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
return btoa(binaryString);
|
||||
} catch (e) {
|
||||
return '[Invalid input]';
|
||||
}
|
||||
},
|
||||
preview: function(text) {
|
||||
if (!text) return '[base64]';
|
||||
try {
|
||||
const full = this.func(text);
|
||||
return full.substring(0, 12) + (full.length > 12 ? '...' : '');
|
||||
} catch (e) {
|
||||
return '[Invalid input]';
|
||||
}
|
||||
},
|
||||
reverse: function(text) {
|
||||
try {
|
||||
// Properly decode Base64 to UTF-8 text (including emojis)
|
||||
const binaryString = atob(text);
|
||||
const bytes = new Uint8Array(binaryString.length);
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
return decoder.decode(bytes);
|
||||
} catch (e) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
// base64url transform
|
||||
import BaseTransformer from '../BaseTransformer.js';
|
||||
|
||||
export default new BaseTransformer({
|
||||
name: 'Base64 URL',
|
||||
priority: 270,
|
||||
// Detector: Only Base64 URL characters (A-Z, a-z, 0-9, -, _, =)
|
||||
detector: function(text) {
|
||||
const cleaned = text.trim().replace(/\s/g, '');
|
||||
return cleaned.length >= 4 && /^[A-Za-z0-9\-_=]+$/.test(cleaned);
|
||||
},
|
||||
|
||||
func: function(text) {
|
||||
if (!text) return '';
|
||||
try {
|
||||
// Properly encode UTF-8 text (including emojis) to Base64 URL
|
||||
const encoder = new TextEncoder();
|
||||
const bytes = encoder.encode(text);
|
||||
let binaryString = '';
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
binaryString += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
const std = btoa(binaryString);
|
||||
return std.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/,'');
|
||||
} catch (e) {
|
||||
return '[Invalid input]';
|
||||
}
|
||||
},
|
||||
preview: function(text) {
|
||||
if (!text) return '[b64url]';
|
||||
const full = this.func(text);
|
||||
return full.substring(0, 12) + (full.length > 12 ? '...' : '');
|
||||
},
|
||||
reverse: function(text) {
|
||||
if (!text) return '';
|
||||
let std = text.replace(/-/g, '+').replace(/_/g, '/');
|
||||
// pad
|
||||
while (std.length % 4 !== 0) std += '=';
|
||||
try {
|
||||
// Properly decode Base64 URL to UTF-8 text (including emojis)
|
||||
const binaryString = atob(std);
|
||||
const bytes = new Uint8Array(binaryString.length);
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
return decoder.decode(bytes);
|
||||
} catch (e) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
// binary transform
|
||||
import BaseTransformer from '../BaseTransformer.js';
|
||||
|
||||
export default new BaseTransformer({
|
||||
name: 'Binary',
|
||||
priority: 300,
|
||||
// Detector: Only 0s, 1s, and spaces
|
||||
detector: function(text) {
|
||||
const cleaned = text.trim();
|
||||
const noSpaces = cleaned.replace(/\s/g, '');
|
||||
return noSpaces.length >= 8 && /^[01\s]+$/.test(cleaned);
|
||||
},
|
||||
|
||||
func: function(text) {
|
||||
// Use TextEncoder to properly handle UTF-8 (including emoji)
|
||||
const encoder = new TextEncoder();
|
||||
const bytes = encoder.encode(text);
|
||||
return Array.from(bytes).map(b => b.toString(2).padStart(8, '0')).join(' ');
|
||||
},
|
||||
preview: function(text) {
|
||||
if (!text) return '[binary]';
|
||||
const full = this.func(text);
|
||||
return full.substring(0, 24) + (full.length > 24 ? '...' : '');
|
||||
},
|
||||
reverse: function(text) {
|
||||
// Remove spaces and ensure we have valid binary
|
||||
const binText = text.replace(/\s+/g, '');
|
||||
const bytes = [];
|
||||
|
||||
// Process 8 bits at a time
|
||||
for (let i = 0; i < binText.length; i += 8) {
|
||||
const byte = binText.substr(i, 8);
|
||||
if (byte.length === 8) {
|
||||
bytes.push(parseInt(byte, 2));
|
||||
}
|
||||
}
|
||||
|
||||
// Use TextDecoder to properly decode UTF-8
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
return decoder.decode(new Uint8Array(bytes));
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
// hex transform
|
||||
import BaseTransformer from '../BaseTransformer.js';
|
||||
|
||||
export default new BaseTransformer({
|
||||
name: 'Hexadecimal',
|
||||
priority: 290,
|
||||
// Detector: Only hex characters (0-9, A-F)
|
||||
detector: function(text) {
|
||||
const cleaned = text.trim().replace(/\s/g, '');
|
||||
return cleaned.length >= 4 && /^[0-9A-Fa-f]+$/.test(cleaned);
|
||||
},
|
||||
|
||||
func: function(text) {
|
||||
// Use TextEncoder to properly handle UTF-8 (including emoji)
|
||||
const encoder = new TextEncoder();
|
||||
const bytes = encoder.encode(text);
|
||||
return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join(' ');
|
||||
},
|
||||
preview: function(text) {
|
||||
if (!text) return '[hex]';
|
||||
const full = this.func(text);
|
||||
return full.substring(0, 20) + (full.length > 20 ? '...' : '');
|
||||
},
|
||||
reverse: function(text) {
|
||||
const hexText = text.replace(/\s+/g, '');
|
||||
const bytes = [];
|
||||
|
||||
for (let i = 0; i < hexText.length; i += 2) {
|
||||
const byte = hexText.substr(i, 2);
|
||||
if (byte.length === 2) {
|
||||
bytes.push(parseInt(byte, 16));
|
||||
}
|
||||
}
|
||||
|
||||
// Use TextDecoder to properly decode UTF-8
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
return decoder.decode(new Uint8Array(bytes));
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
// html transform
|
||||
import BaseTransformer from '../BaseTransformer.js';
|
||||
|
||||
export default new BaseTransformer({
|
||||
name: 'HTML Entities',
|
||||
priority: 40,
|
||||
// Detector: Look for &...; pattern (HTML entities)
|
||||
detector: function(text) {
|
||||
return text.includes('&') && text.includes(';') && /&[a-zA-Z0-9#]+;/.test(text);
|
||||
},
|
||||
|
||||
func: function(text) {
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
},
|
||||
preview: function(text) {
|
||||
return this.func(text);
|
||||
},
|
||||
reverse: function(text) {
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, '\'');
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
// invisible-text transform
|
||||
import BaseTransformer from '../BaseTransformer.js';
|
||||
|
||||
export default new BaseTransformer({
|
||||
|
||||
name: 'Invisible Text',
|
||||
priority: 100, // High confidence - uses exclusive Unicode Private Use Area (U+E0000-U+E00FF)
|
||||
func: function(text) {
|
||||
if (!text) return '';
|
||||
const bytes = new TextEncoder().encode(text);
|
||||
return Array.from(bytes)
|
||||
.map(byte => String.fromCodePoint(0xE0000 + byte))
|
||||
.join('');
|
||||
},
|
||||
preview: function(text) {
|
||||
return '[invisible]';
|
||||
},
|
||||
reverse: function(text) {
|
||||
if (!text) return '';
|
||||
const matches = [...text.matchAll(/[\u{E0000}-\u{E00FF}]/gu)];
|
||||
if (!matches.length) return '';
|
||||
|
||||
// Convert invisible characters back to bytes
|
||||
const bytes = new Uint8Array(
|
||||
matches.map(match => match[0].codePointAt(0) - 0xE0000)
|
||||
);
|
||||
|
||||
// Use TextDecoder to properly handle UTF-8 encoded bytes (including emoji)
|
||||
return new TextDecoder().decode(bytes);
|
||||
},
|
||||
// Detector: Check for at least one invisible Unicode character
|
||||
detector: function(text) {
|
||||
// Invisible text uses Unicode Private Use Area (U+E0000-U+E00FF for full byte range)
|
||||
const invisibleMatches = text.match(/[\u{E0000}-\u{E00FF}]/gu);
|
||||
// Return true if at least one invisible character is found
|
||||
return invisibleMatches && invisibleMatches.length > 0;
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
// url transform
|
||||
import BaseTransformer from '../BaseTransformer.js';
|
||||
|
||||
export default new BaseTransformer({
|
||||
name: 'URL Encode',
|
||||
priority: 40,
|
||||
// Detector: Look for %XX pattern (URL encoding)
|
||||
detector: function(text) {
|
||||
return text.includes('%') && /%[0-9A-Fa-f]{2}/.test(text);
|
||||
},
|
||||
|
||||
func: function(text) {
|
||||
try {
|
||||
return encodeURIComponent(text);
|
||||
} catch (e) {
|
||||
// Catch malformed Unicode or unpaired surrogates
|
||||
return '[Invalid input]';
|
||||
}
|
||||
},
|
||||
preview: function(text) {
|
||||
return this.func(text);
|
||||
},
|
||||
reverse: function(text) {
|
||||
try {
|
||||
return decodeURIComponent(text);
|
||||
} catch (e) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user