document.addEventListener('DOMContentLoaded', function() { // DOM Elements const inputText = document.getElementById('inputText'); const outputContainer = document.getElementById('outputContainer'); const formatBtn = document.getElementById('formatBtn'); const copyBtn = document.getElementById('copyBtn'); const downloadBtn = document.getElementById('downloadBtn'); const clearBtn = document.getElementById('clearBtn'); const languageSelect = document.getElementById('languageSelect'); const uppercaseCheck = document.getElementById('uppercaseCheck'); // Tabu words for each language (expanded lists) const tabuWords = { PT: ['o', 'a', 'os', 'as', 'um', 'uma', 'me', 'te', 'se', 'lhe', 'nos', 'de', 'do', 'da', 'em', 'no', 'na', 'por', 'para', 'com', 'ao', 'e', 'ou', 'mas', 'que', 'como', 'porque', 'quando', 'então', 'só', 'já', 'também', 'tipo', 'nosso', 'nossa', 'até', 'sem', 'sob', 'sobre', 'trás', 'ante', 'após', 'entre', 'durante', 'desde', 'perante', 'mal', 'bem', 'muito', 'pouco', 'tão', 'mais', 'menos', 'tal', 'qual', 'seu', 'sua', 'seus', 'suas', 'meu', 'minha', 'meus', 'minhas', 'teu', 'tua', 'teus', 'tuas', 'nosso', 'nossa', 'nossos', 'nossas', 'vosso', 'vossa', 'vossos', 'vossas', 'este', 'esta', 'estes', 'estas', 'isto', 'esse', 'essa', 'esses', 'essas', 'isso', 'aquele', 'aquela', 'aqueles', 'aquelas', 'aquilo', 'cada', 'todo', 'toda', 'todos', 'todas', 'outro', 'outra', 'outros', 'outras', 'mesmo', 'mesma', 'mesmos', 'mesmas', 'próprio', 'própria', 'próprios', 'próprias', 'demais', 'demais', 'certo', 'certa', 'certos', 'certas', 'diverso', 'diversa', 'diversos', 'diversas', 'algum', 'alguma', 'alguns', 'algumas', 'nenhum', 'nenhuma', 'nenhuns', 'nenhumas', 'muito', 'muita', 'muitos', 'muitas', 'pouco', 'pouca', 'poucos', 'poucas', 'tanto', 'tanta', 'tantos', 'tantas', 'quanto', 'quanta', 'quantos', 'quantas', 'varios', 'varias', 'bastante', 'suficiente', 'demais', 'demasiado', 'menos', 'mais', 'melhor', 'pior', 'maior', 'menor', 'talvez', 'acaso', 'caso', 'vez', 'vezes', 'hora', 'horas', 'dia', 'dias', 'noite', 'noites', 'manhã', 'tarde', 'tarde', 'ano', 'anos', 'mês', 'meses', 'semana', 'semanas', 'hoje', 'ontem', 'amanhã', 'antes', 'depois', 'agora', 'sempre', 'nunca', 'ainda', 'já', 'logo', 'depois', 'cedo', 'tarde', 'cedo', 'tarde', 'longe', 'perto', 'acima', 'abaixo', 'dentro', 'fora', 'atrás', 'diante', 'frente', 'meio', 'lado', 'outro', 'mesmo', 'próximo', 'distante', 'junto', 'separado', 'certo', 'duro', 'fácil', 'difícil', 'possível', 'impossível', 'necessário', 'importante', 'grande', 'pequeno', 'alto', 'baixo', 'comprido', 'curto', 'largo', 'estreito', 'grosso', 'fino', 'cheio', 'vazio', 'claro', 'escuro', 'quente', 'frio', 'seco', 'molhado', 'limpo', 'sujo', 'novo', 'velho', 'antigo', 'moderno', 'bonito', 'feio', 'bom', 'mau', 'melhor', 'pior', 'caro', 'barato', 'rápido', 'lento', 'certo', 'errado', 'verdadeiro', 'falso', 'real', 'fictício', 'verdade', 'mentira', 'verdadeiro', 'falso', 'certo', 'incerto', 'seguro', 'perigoso', 'tranquilo', 'agitado', 'calmo', 'nervoso', 'alegre', 'triste', 'feliz', 'infeliz', 'contente', 'descontente', 'satisfeito', 'insatisfeito', 'orgulhoso', 'envergonhado', 'surpreso', 'indiferente', 'interessado', 'entediado', 'cansado', 'descansado', 'faminto', 'satisfeito', 'sede', 'satisfeito', 'com fome', 'com sede', 'com sono', 'acordado', 'dormindo', 'vivo', 'morto', 'nascendo', 'morrendo', 'crescendo', 'diminuindo', 'aumentando', 'diminuindo', 'subindo', 'descendo', 'entrando', 'saindo', 'chegando', 'partindo', 'indo', 'vindo', 'ficando', 'saindo', 'voltando', 'retornando', 'continuando', 'parando', 'começando', 'terminando', 'iniciando', 'finalizando', 'abrindo', 'fechando', 'ligando', 'desligando', 'acendendo', 'apagando', 'acendendo', 'apagando', 'aquecendo', 'esfriando', 'secando', 'molhando', 'limpando', 'sujando', 'enchendo', 'esvaziando', 'cheio', 'vazio', 'claro', 'escuro', 'quente', 'frio', 'seco', 'molhado', 'limpo', 'sujo', 'novo', 'velho', 'antigo', 'moderno', 'bonito', 'feio', 'bom', 'mau', 'melhor', 'pior', 'caro', 'barato', 'rápido', 'lento', 'certo', 'errado', 'verdadeiro', 'falso', 'real', 'fictício', 'verdade', 'mentira', 'verdadeiro', 'falso', 'certo', 'incerto', 'seguro', 'perigoso', 'tranquilo', 'agitado', 'calmo', 'nervoso', 'alegre', 'triste', 'feliz', 'infeliz', 'contente', 'descontente', 'satisfeito', 'insatisfeito', 'orgulhoso', 'envergonhado', 'surpreso', 'indiferente', 'interessado', 'entediado', 'cansado', 'descansado', 'faminto', 'satisfeito', 'sede', 'satisfeito', 'com fome', 'com sede', 'com sono', 'acordado', 'dormindo', 'vivo', 'morto', 'nascendo', 'morrendo', 'crescendo', 'diminuindo', 'aumentando', 'diminuindo', 'subindo', 'descendo', 'entrando', 'saindo', 'chegando', 'partindo', 'indo', 'vindo', 'ficando', 'saindo', 'voltando', 'retornando', 'continuando', 'parando', 'começando', 'terminando', 'iniciando', 'finalizando', 'abrindo', 'fechando', 'ligando', 'desligando', 'acendendo', 'apagando', 'acendendo', 'apagando', 'aquecendo', 'esfriando', 'secando', 'molhando', 'limpando', 'sujando', 'enchendo', 'esvaziando'], EN: ['a', 'an', 'the', 'of', 'in', 'on', 'at', 'to', 'for', 'with', 'from', 'by', 'and', 'or', 'but', 'that', 'me', 'you', 'it', 'he', 'she', 'we', 'they', 'us', 'them', 'his', 'her', 'its', 'our', 'their', 'myself', 'yourself', 'himself', 'herself', 'itself', 'ourselves', 'yourselves', 'themselves', 'this', 'that', 'these', 'those', 'some', 'any', 'many', 'much', 'few', 'little', 'more', 'most', 'other', 'another', 'such', 'same', 'different', 'own', 'main', 'only', 'very', 'quite', 'rather', 'fairly', 'pretty', 'really', 'extremely', 'incredibly', 'absolutely', 'totally', 'completely', 'entirely', 'fully', 'wholly', 'altogether', 'exceedingly', 'intensely', 'highly', 'greatly', 'seriously', 'badly', 'terribly', 'awfully', 'horribly', 'dreadfully', 'frightfully', 'terribly', 'awfully', 'horribly', 'dreadfully', 'frightfully', 'badly', 'seriously', 'greatly', 'highly', 'intensely', 'exceedingly', 'altogether', 'wholly', 'fully', 'entirely', 'completely', 'totally', 'absolutely', 'incredibly', 'extremely', 'really', 'pretty', 'fairly', 'rather', 'quite', 'very', 'only', 'main', 'own', 'different', 'same', 'such', 'another', 'other', 'most', 'more', 'little', 'few', 'much', 'many', 'any', 'some', 'those', 'these', 'that', 'this', 'themselves', 'yourselves', 'ourselves', 'itself', 'herself', 'himself', 'yourself', 'myself', 'their', 'our', 'its', 'her', 'his', 'them', 'us', 'they', 'we', 'she', 'he', 'it', 'you', 'me', 'but', 'or', 'and', 'by', 'from', 'with', 'for', 'to', 'at', 'on', 'in', 'of', 'the', 'an', 'a'], ES: ['de', 'en', 'a', 'por', 'para', 'con', 'y', 'que', 'el', 'la', 'los', 'las', 'un', 'una', 'unos', 'unas', 'me', 'te', 'se', 'nos', 'os', 'le', 'les', 'lo', 'la', 'los', 'las', 'mi', 'tu', 'su', 'nuestro', 'vuestra', 'este', 'ese', 'aquel', 'esta', 'esa', 'aquella', 'estos', 'esos', 'aquellos', 'estas', 'esas', 'aquellas', 'esto', 'eso', 'aquello', 'cada', 'todo', 'toda', 'todos', 'todas', 'otro', 'otra', 'otros', 'otras', 'mismo', 'misma', 'mismos', 'mismas', 'próximo', 'próxima', 'próximos', 'próximas', 'distante', 'distantes', 'junto', 'juntos', 'junta', 'juntas', 'separado', 'separados', 'separada', 'separadas', 'cierto', 'cierta', 'ciertos', 'ciertas', 'duro', 'duros', 'dura', 'duras', 'fácil', 'fáciles', 'difícil', 'difíciles', 'posible', 'posibles', 'imposible', 'imposibles', 'necesario', 'necesarios', 'necesaria', 'necesarias', 'importante', 'importantes', 'grande', 'grandes', 'pequeño', 'pequeña', 'pequeños', 'pequeñas', 'alto', 'altos', 'alta', 'altas', 'bajo', 'bajos', 'baja', 'bajas', 'comprido', 'compridos', 'comprida', 'compridas', 'largo', 'largos', 'larga', 'largas', 'corto', 'cortos', 'corta', 'cortas', 'estrecho', 'estrechos', 'estrecha', 'estrechas', 'grosso', 'grueso', 'grosa', 'gruesa', 'grosos', 'gruesos', 'finos', 'fino', 'fina', 'finas', 'cheio', 'lleno', 'cheios', 'llenos', 'cheia', 'llena', 'cheias', 'llenas', 'vazio', 'vacío', 'vazios', 'vacíos', 'vazia', 'vacía', 'vazias', 'vacías', 'claro', 'clara', 'claros', 'claras', 'escuro', 'oscuro', 'escura', 'oscura', 'escuros', 'oscuros', 'escuras', 'oscuras', 'quente', 'caliente', 'quentes', 'calientes', 'frio', 'frío', 'fria', 'fría', 'frios', 'fríos', 'frias', 'frías', 'seco', 'secos', 'seca', 'secas', 'molhado', 'mojado', 'molhados', 'mojados', 'molhada', 'mojada', 'molhadas', 'mojadas', 'limpo', 'limpios', 'limpia', 'limpias', 'sujo', 'sucio', 'sujos', 'sucios', 'suja', 'sucia', 'sujas', 'sucias', 'novo', 'nuevo', 'nuevos', 'nueva', 'nuevas', 'velho', 'viejo', 'viejos', 'vieja', 'viejas', 'antigo', 'antiguo', 'antiguos', 'antigua', 'antiguas', 'moderno', 'modernos', 'moderna', 'modernas', 'bonito', 'bonitos', 'bonita', 'bonitas', 'feio', 'feo', 'feos', 'fea', 'feas', 'bom', 'bueno', 'buenos', 'buena', 'buenas', 'mau', 'malo', 'malos', 'mala', 'malas', 'melhor', 'mejor', 'melhores', 'mejores', 'pior', 'peor', 'piores', 'peores', 'caro', 'caros', 'cara', 'caras', 'barato', 'baratos', 'barata', 'baratas', 'rápido', 'rápidos', 'rápida', 'rápidas', 'lento', 'lentos', 'lenta', 'lentas', 'certo', 'cierto', 'ciertos', 'cierta', 'ciertas', 'errado', 'equivocado', 'equivocados', 'equivocada', 'equivocadas', 'verdadeiro', 'verdadero', 'verdaderos', 'verdadera', 'verdaderas', 'falso', 'falsos', 'falsa', 'falsas', 'real', 'reales', 'fictício', 'ficticio', 'ficticios', 'ficticia', 'ficticias', 'verdade', 'verdad', 'mentira', 'mentiras', 'seguro', 'seguros', 'segura', 'seguras', 'perigoso', 'peligroso', 'peligrosos', 'peligrosa', 'peligrosas', 'tranquilo', 'tranquilos', 'tranquila', 'tranquilas', 'agitado', 'agitados', 'agitada', 'agitadas', 'calmo', 'calmos', 'calma', 'calmas', 'nervoso', 'nerviosos', 'nerviosa', 'nerviosas', 'alegre', 'alegres', 'triste', 'tristes', 'feliz', 'felices', 'infeliz', 'infelices', 'contente', 'contentos', 'contenta', 'contentas', 'descontente', 'descontentos', 'descontenta', 'descontentas', 'satisfeito', 'satisfecho', 'satisfechos', 'satisfecha', 'satisfechas', 'insatisfeito', 'insatisfecho', 'insatisfechos', 'insatisfecha', 'insatisfechas', 'orgulhoso', 'orgulloso', 'orgullosos', 'orgullosa', 'orgullosas', 'envergonhado', 'avergonzado', 'avergonzados', 'avergonzada', 'avergonzadas', 'surpreso', 'sorprendido', 'sorprendidos', 'sorprendida', 'sorprendidas', 'indiferente', 'indiferentes', 'interessado', 'interesado', 'interesados', 'interesada', 'interesadas', 'entediado', 'aburrido', 'aburridos', 'aburrida', 'aburridas', 'cansado', 'cansados', 'cansada', 'cansadas', 'descansado', 'descansados', 'descansada', 'descansadas', 'faminto', 'hambriento', 'hambrientos', 'hambrienta', 'hambrientas', 'satisfeito', 'satisfecho', 'satisfechos', 'satisfecha', 'satisfechas', 'sede', 'sed', 'satisfeito', 'satisfecho', 'satisfechos', 'satisfecha', 'satisfechas', 'com fome', 'con hambre', 'com sede', 'con sed', 'com sono', 'con sueño', 'acordado', 'despierto', 'despiertos', 'despierta', 'despiertas', 'dormindo', 'durmiendo', 'vivo', 'vivos', 'viva', 'vivas', 'morto', 'muerto', 'muertos', 'muerta', 'muertas', 'nascendo', 'naciendo', 'morrendo', 'muriendo', 'crescendo', 'creciendo', 'diminuindo', 'disminuyendo', 'aumentando', 'aumentando', 'subindo', 'subiendo', 'descendo', 'bajando', 'entrando', 'entrando', 'saindo', 'saliendo', 'chegando', 'llegando', 'partindo', 'partiendo', 'indo', 'yendo', 'vindo', 'viniendo', 'ficando', 'quedando', 'saindo', 'saliendo', 'voltando', 'volviendo', 'retornando', 'retornando', 'continuando', 'continuando', 'parando', 'parando', 'começando', 'comenzando', 'terminando', 'terminando', 'iniciando', 'iniciando', 'finalizando', 'finalizando', 'abrindo', 'abriendo', 'fechando', 'cerrando', 'ligando', 'encendiendo', 'desligando', 'apagando', 'acendendo', 'encendiendo', 'apagando', 'apagando', 'aquecendo', 'calentando', 'esfriando', 'enfriando', 'secando', 'secando', 'molhando', 'mojando', 'limpando', 'limpiando', 'sujando', 'ensuciando', 'enchendo', 'llenando', 'esvaziando', 'vaciando'] }; // Connectives - MUST always start new blocks, never end them const connectives = { PT: ['E', 'É', 'QUE'], EN: ['AND', 'IS', 'THAT'], ES: ['Y', 'ES', 'QUE'] }; // Negation words - must be isolated in short blocks const negationWords = { PT: ['NÃO', 'NUNCA'], EN: ['NOT', 'NEVER'], ES: ['NO', 'NUNCA'] }; // Format text function function formatText() { const text = inputText.value.trim(); if (!text) { outputContainer.innerHTML = '

Please enter some text to format

'; return; } const language = languageSelect.value; const uppercaseMode = uppercaseCheck.checked; try { const formatted = processText(text, language, uppercaseMode); outputContainer.innerHTML = `
${formatted}
`; } catch (error) { outputContainer.innerHTML = `

Error formatting text: ${error.message}

`; } } // Text processing function function processText(text, language, uppercaseMode) { // PRE-PROCESSING & NORMALIZATION // Clean whitespace: Remove duplicate spaces and normalize line breaks let normalized = text.replace(/\s+/g, ' ').trim(); // Convert commas to "!" (attached to preceding word without space) normalized = normalized.replace(/,/g, '!'); // Normalize exclamation marks: Convert all "!!" or "!!!" sequences to single "!" normalized = normalized.replace(/!+/g, '!'); // Preserve all original punctuation exactly as positioned in source text // (already handled by above steps) // DETERMINISTIC SPLITTING (STRONG RULES - HIGHEST PRIORITY) // Split immediately after every "!", "?", or "." (punctuation stays with preceding word) const sentences = normalized.split(/([.!?]+)/).filter(s => s.trim() !== ''); let blocks = []; for (let i = 0; i < sentences.length; i += 2) { const sentence = sentences[i]; const punctuation = sentences[i + 1] || ''; // Process each sentence const sentenceBlocks = processSentence(sentence + punctuation, language); blocks = blocks.concat(sentenceBlocks); } // Apply heuristic optimizations blocks = applyHeuristics(blocks, language); // Apply uppercase if needed if (uppercaseMode) { blocks = blocks.map(block => block.toUpperCase()); } return blocks.join('\n'); } // Process individual sentence with zero-error tolerance function processSentence(sentence, language) { const words = sentence.trim().split(/\s+/); const blocks = []; let currentBlock = ''; let i = 0; while (i < words.length) { const word = words[i]; const cleanWord = word.replace(/[.!?!,]/g, ''); // STRONG RULE: Connectives must start new blocks, never end them if (isConnective(cleanWord, language)) { if (currentBlock) { blocks.push(resolveBlockEnding(currentBlock.trim(), language)); } currentBlock = word; i++; continue; } // Handle negation - isolate in short blocks if (isNegation(cleanWord, language)) { if (currentBlock) { blocks.push(resolveBlockEnding(currentBlock.trim(), language)); } blocks.push(word); currentBlock = ''; i++; continue; } // Calculate character count (excluding spaces and punctuation) const testBlock = currentBlock ? `${currentBlock} ${word}` : word; const charCount = testBlock.replace(/\s/g, '').replace(/[.!?!,]/g, '').length; // EXCEPTION: Single word blocks can exceed 11 characters if (!currentBlock && charCount > 11) { blocks.push(word); currentBlock = ''; i++; continue; } // Check if adding word would exceed 11 character limit if (currentBlock && charCount > 11) { // Resolve current block with anti-weakening principle const resolvedBlock = resolveBlockEnding(currentBlock.trim(), language); if (resolvedBlock) { blocks.push(resolvedBlock); } currentBlock = word; } else { currentBlock = testBlock; } i++; } // Handle remaining block with anti-weakening principle if (currentBlock) { const resolvedBlock = resolveBlockEnding(currentBlock.trim(), language); if (resolvedBlock) { blocks.push(resolvedBlock); } } return blocks; } // Resolve block ending - ZERO ERROR TOLERANCE for tabu endings function resolveBlockEnding(block, language) { if (!block) return ''; const words = block.split(/\s+/); if (words.length === 0) return ''; const lastWord = words[words.length - 1].replace(/[.!?!,]/g, ''); const lastWordClean = lastWord; // STRONG RULE: Never end with tabu words if (isTabu(lastWordClean, language)) { if (words.length === 1) { // Single word is tabu - this shouldn't happen, but return as-is return block; } // Move tabu word to next block const newBlock = words.slice(0, -1).join(' '); return newBlock; } // STRONG RULE: Never end with 2-3 character words if (lastWordClean.length >= 2 && lastWordClean.length <= 3) { if (words.length === 1) { return block; // Single word exception } // Move short word to next block const newBlock = words.slice(0, -1).join(' '); return newBlock; } return block; } // Check if word is a connective function isConnective(word, language) { if (!word) return false; const upperWord = word.toUpperCase(); return connectives[language].includes(upperWord); } // Check if word is tabu function isTabu(word, language) { if (!word) return false; return tabuWords[language].includes(word.toLowerCase()); } // Check if word is a negation function isNegation(word, language) { if (!word) return false; const upperWord = word.toUpperCase(); return negationWords[language].includes(upperWord); } // Apply heuristic optimizations AFTER strong rules are satisfied function applyHeuristics(blocks, language) { const optimizedBlocks = []; for (let i = 0; i < blocks.length; i++) { let block = blocks[i]; const cleanBlock = block.replace(/[.!?!,]/g, '').toUpperCase(); // Price formatting: Isolate "POR/AND/Y " and "E/AND " const priceMatch = block.match(/^(POR|E|AND|Y)\s+(\d+)/i); if (priceMatch) { optimizedBlocks.push(priceMatch[1]); // Preposition optimizedBlocks.push(priceMatch[2]); // Number continue; } // Material lists: Keep "FEITO EM/MADE IN/HECHO EN" intact if (language === 'PT' && /^FEITO\s+EM/i.test(block)) { const parts = block.split(/\s+/); if (parts.length > 2) { optimizedBlocks.push('FEITO EM'); const materials = parts.slice(2).join(' '); // Split materials by E connective const materialParts = materials.split(/\s+E\s+/i); materialParts.forEach((mat, idx) => { if (idx > 0) optimizedBlocks.push('E'); if (mat.trim()) optimizedBlocks.push(mat.trim()); }); continue; } } if (language === 'EN' && /^MADE\s+IN/i.test(block)) { const parts = block.split(/\s+/); if (parts.length > 2) { optimizedBlocks.push('MADE IN'); const materials = parts.slice(2).join(' '); // Split materials by AND connective const materialParts = materials.split(/\s+AND\s+/i); materialParts.forEach((mat, idx) => { if (idx > 0) optimizedBlocks.push('AND'); if (mat.trim()) optimizedBlocks.push(mat.trim()); }); continue; } } if (language === 'ES' && /^HECHO\s+EN/i.test(block)) { const parts = block.split(/\s+/); if (parts.length > 2) { optimizedBlocks.push('HECHO EN'); const materials = parts.slice(2).join(' '); // Split materials by Y connective const materialParts = materials.split(/\s+Y\s+/i); materialParts.forEach((mat, idx) => { if (idx > 0) optimizedBlocks.push('Y'); if (mat.trim()) optimizedBlocks.push(mat.trim()); }); continue; } } // CTA standardization if (language === 'PT' && /CLIQUE\s+EM\s+SAIBA\s+MAIS/i.test(block)) { optimizedBlocks.push('CLIQUE'); optimizedBlocks.push('EM SAIBA'); optimizedBlocks.push('MAIS'); continue; } if (language === 'EN' && /CLICK\s+HERE\s+TO\s+LEARN\s+MORE/i.test(block)) { optimizedBlocks.push('CLICK'); optimizedBlocks.push('HERE TO'); optimizedBlocks.push('LEARN MORE'); continue; } if (language === 'ES' && /HAZ\s+CLIC\s+AQUÍ\s+PARA\s+SABER\s+MÁS/i.test(block)) { optimizedBlocks.push('HAZ CLIC'); optimizedBlocks.push('AQUÍ PARA'); optimizedBlocks.push('SABER MÁS'); continue; } // Subject optimization: Keep short subjects together (A/O/AS/OS + noun) const subjectMatch = block.match(/^(A|O|AS|OS|THE)\s+(\w{4,})/i); if (subjectMatch && block.replace(/\s/g, '').length <= 11) { optimizedBlocks.push(block); continue; } optimizedBlocks.push(block); } // Post-optimization validation to ensure no rule violations return validateAndFixBlocks(optimizedBlocks, language); } // Final validation - ZERO ERROR TOLERANCE function validateAndFixBlocks(blocks, language) { const validatedBlocks = []; for (let i = 0; i < blocks.length; i++) { const block = blocks[i]; const words = block.split(/\s+/); if (words.length === 0) continue; const lastWord = words[words.length - 1].replace(/[.!?!,]/g, ''); // Check for tabu ending if (isTabu(lastWord, language)) { if (words.length > 1) { // Move tabu word to next block or create new block const newBlock = words.slice(0, -1).join(' '); if (newBlock) validatedBlocks.push(newBlock); // Check if we can append to next block if (i + 1 < blocks.length) { const nextBlock = blocks[i + 1]; const combined = `${lastWord} ${nextBlock}`; const charCount = combined.replace(/\s/g, '').length; if (charCount <= 11) { blocks[i + 1] = combined; continue; } } validatedBlocks.push(lastWord); } else { validatedBlocks.push(block); } continue; } // Check for 2-3 character word ending if (lastWord.length >= 2 && lastWord.length <= 3) { if (words.length > 1) { // Move short word to next block or create new block const newBlock = words.slice(0, -1).join(' '); if (newBlock) validatedBlocks.push(newBlock); // Check if we can append to next block if (i + 1 < blocks.length) { const nextBlock = blocks[i + 1]; const combined = `${lastWord} ${nextBlock}`; const charCount = combined.replace(/\s/g, '').length; if (charCount <= 11) { blocks[i + 1] = combined; continue; } } validatedBlocks.push(lastWord); } else { validatedBlocks.push(block); } continue; } // Check character limit const charCount = block.replace(/\s/g, '').replace(/[.!?!,]/g, '').length; if (charCount > 11 && words.length > 1) { // Need to split further const splitResult = splitLongBlock(block, language); validatedBlocks.push(...splitResult); } else { validatedBlocks.push(block); } } return validatedBlocks; } // Split blocks that exceed character limit while respecting rules function splitLongBlock(block, language) { const words = block.split(/\s+/); const result = []; let currentBlock = ''; for (const word of words) { const cleanWord = word.replace(/[.!?!,]/g, ''); // STRONG RULE: Connectives start new blocks if (isConnective(cleanWord, language)) { if (currentBlock) result.push(currentBlock.trim()); currentBlock = word; continue; } const testBlock = currentBlock ? `${currentBlock} ${word}` : word; const charCount = testBlock.replace(/\s/g, '').replace(/[.!?!,]/g, '').length; if (currentBlock && charCount > 11) { const resolved = resolveBlockEnding(currentBlock.trim(), language); if (resolved) result.push(resolved); currentBlock = word; } else { currentBlock = testBlock; } } if (currentBlock) { const resolved = resolveBlockEnding(currentBlock.trim(), language); if (resolved) result.push(resolved); } return result; } // Copy to clipboard function copyToClipboard() { const text = outputContainer.innerText; if (!text || text.includes('Formatted text will appear here') || text.includes('Please enter some text to format')) { return; } navigator.clipboard.writeText(text).then(() => { // Show feedback const originalText = copyBtn.innerHTML; copyBtn.innerHTML = ' Copied!'; feather.replace(); setTimeout(() => { copyBtn.innerHTML = originalText; feather.replace(); }, 2000); }).catch(err => { console.error('Failed to copy:', err); }); } // Download text function downloadText() { const text = outputContainer.innerText; if (!text || text.includes('Formatted text will appear here') || text.includes('Please enter some text to format')) { return; } const blob = new Blob([text], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'formatted-text.txt'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } // Clear input function clearInput() { inputText.value = ''; outputContainer.innerHTML = '

Formatted text will appear here

'; inputText.focus(); } // Event Listeners formatBtn.addEventListener('click', formatText); copyBtn.addEventListener('click', copyToClipboard); downloadBtn.addEventListener('click', downloadText); clearBtn.addEventListener('click', clearInput); // Format on Ctrl+Enter inputText.addEventListener('keydown', (e) => { if (e.ctrlKey && e.key === 'Enter') { formatText(); } }); // Initialize Feather icons feather.replace(); });