From eff16d69a8fca45e36e41c56049a2290460cb37c Mon Sep 17 00:00:00 2001 From: Chesterkxng Date: Tue, 8 Jul 2025 21:02:55 +0200 Subject: [PATCH] feat: text censor (service updated to match censor patter instead of spliting input) --- src/pages/tools/string/censor/service.ts | 52 ++++++++++++------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/pages/tools/string/censor/service.ts b/src/pages/tools/string/censor/service.ts index 660cdcc..7aa4b95 100644 --- a/src/pages/tools/string/censor/service.ts +++ b/src/pages/tools/string/censor/service.ts @@ -4,46 +4,46 @@ export function censorText(input: string, options: InitialValuesType): string { if (!input) return ''; if (!options.wordsToCensor) return input; - if (options.censoredBySymbol && !isSymbol(options.censorSymbol[0])) { + if (options.censoredBySymbol && !isSymbol(options.censorSymbol)) { throw new Error('Enter a valid censor symbol (non-alphanumeric or emoji)'); } - // Split text into words and punctuation using Unicode-aware regex - const regex = /([\s\p{P}])/gu; - const textWords = input.split(regex); - - // Normalize censor words (trim and lowercase) const wordsToCensor = options.wordsToCensor .split('\n') - .map((word) => word.trim().toLowerCase()) + .map((word) => word.trim()) .filter((word) => word.length > 0); - const censoredText = textWords - .map((word) => { - const lowerWord = word.toLowerCase(); - if (wordsToCensor.includes(lowerWord)) { - if (options.censoredBySymbol) { - return options.eachLetter - ? options.censorSymbol.repeat(word.length) - : options.censorSymbol; - } else { - return options.censorWord; - } - } - return word; - }) - .join(''); + let censoredText = input; + + for (const word of wordsToCensor) { + const escapedWord = escapeRegex(word); + const pattern = new RegExp(`\\b${escapedWord}\\b`, 'giu'); + + const replacement = options.censoredBySymbol + ? options.eachLetter + ? options.censorSymbol.repeat(word.length) + : options.censorSymbol + : options.censorWord; + + censoredText = censoredText.replace(pattern, replacement); + } return censoredText; } /** - * Determines if a character is a symbol or emoji. - * Accepts single characters that are non-letter and non-number, - * or extended pictographic characters (like emojis). + * Escapes RegExp special characters in a string + */ +function escapeRegex(str: string): string { + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +/** + * Determines if a string is a valid symbol or emoji (multi-codepoint supported). */ function isSymbol(input: string): boolean { return ( - /^[^\p{L}\p{N}]+$/u.test(input) || /\p{Extended_Pictographic}/u.test(input) + /^[^\p{L}\p{N}]+$/u.test(input) || // Not a letter or number + /\p{Extended_Pictographic}/u.test(input) // Emoji or pictographic symbol ); }