Skip to content

Instantly share code, notes, and snippets.

@devanshbatham
Created February 27, 2025 18:26
Show Gist options
  • Save devanshbatham/6007f443f119435cc6d390dccf870d53 to your computer and use it in GitHub Desktop.
Save devanshbatham/6007f443f119435cc6d390dccf870d53 to your computer and use it in GitHub Desktop.
function findUnicodeVariants(input) {
const results = [];
const inputLower = input.toLowerCase();
const inputUpper = input.toUpperCase();
const startSubstrLower = inputLower.slice(0, 2); // First 2 chars for substring check
const startSubstrUpper = inputUpper.slice(0, 2);
// Function to test a full-string variant
function testFullVariant(original, variant, pos, unicodeChar, codePoint) {
const upper = variant.toUpperCase();
const lower = variant.toLowerCase();
if (upper === inputUpper) {
results.push({
type: "full",
original: input,
variant: variant,
substitutedChar: unicodeChar,
position: pos,
unicode: `U+${codePoint.toString(16).padStart(4, "0")}`,
conversion: "toUpperCase",
result: upper,
description: `Substituted '${input[pos]}' at position ${pos} with '${unicodeChar}' (${`U+${codePoint.toString(16).padStart(4, "0")}`}), uppercased to '${upper}'`
});
}
if (lower === inputLower && lower !== upper) {
results.push({
type: "full",
original: input,
variant: variant,
substitutedChar: unicodeChar,
position: pos,
unicode: `U+${codePoint.toString(16).padStart(4, "0")}`,
conversion: "toLowerCase",
result: lower,
description: `Substituted '${input[pos]}' at position ${pos} with '${unicodeChar}' (${`U+${codePoint.toString(16).padStart(4, "0")}`}), lowercased to '${lower}'`
});
}
}
// Function to test a starting substring match
function testSubstringMatch(unicodeChar, codePoint) {
const upper = unicodeChar.toUpperCase();
const lower = unicodeChar.toLowerCase();
if (upper === startSubstrUpper && upper.length === 2) {
results.push({
type: "substring",
original: input,
variant: unicodeChar,
substitutedChar: unicodeChar,
position: "start (0-1)",
unicode: `U+${codePoint.toString(16).padStart(4, "0")}`,
conversion: "toUpperCase",
result: upper,
description: `Single character '${unicodeChar}' (${`U+${codePoint.toString(16).padStart(4, "0")}`}) uppercased to '${upper}', matches start of '${input}'`
});
}
if (lower === startSubstrLower && lower !== upper && lower.length === 2) {
results.push({
type: "substring",
original: input,
variant: unicodeChar,
position: "start (0-1)",
unicode: `U+${codePoint.toString(16).padStart(4, "0")}`,
conversion: "toLowerCase",
result: lower,
description: `Single character '${unicodeChar}' (${`U+${codePoint.toString(16).padStart(4, "0")}`}) lowercased to '${lower}', matches start of '${input}'`
});
}
}
// Scan Unicode characters, skipping ASCII (U+0000 to U+007F)
for (let i = 0x0080; i <= 0xFFFF; i++) {
const unicodeChar = String.fromCharCode(i);
const charUpper = unicodeChar.toUpperCase();
const charLower = unicodeChar.toLowerCase();
if (charUpper === charLower && charUpper === unicodeChar) continue;
// Check for full-string substitution
for (let pos = 0; pos < input.length; pos++) {
const prefix = input.slice(0, pos);
const suffix = input.slice(pos + 1);
const variant = prefix + unicodeChar + suffix;
testFullVariant(input, variant, pos, unicodeChar, i);
}
// Check for starting substring match
testSubstringMatch(unicodeChar, i);
}
return results;
}
// Function to display results without the JS file prefix
function displayResults(input) {
const variants = findUnicodeVariants(input);
if (variants.length === 0) {
console.log(`Found 0 non-ASCII Unicode variants for "${input}":`);
return;
}
console.log(`Found ${variants.length} non-ASCII Unicode variants for "${input}":`);
variants.forEach((result, index) => {
console.log(``); // Empty line before each variant
console.log(`Variant ${index + 1}:`);
console.log(`- Original: "${result.original}"`);
console.log(`- Variant: "${result.variant}"`);
console.log(`- Substituted: '${result.substitutedChar}' (${result.unicode}) at position ${result.position}`);
console.log(`- Conversion: ${result.conversion} → "${result.result}"`);
console.log(`- Description: ${result.description}`);
});
console.log(``); // Empty line after last variant
}
// Test with some examples
const testStrings = ["script", "style", "div"];
testStrings.forEach(str => {
displayResults(str);
console.log("-----------------------------------");
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment