Corrigir os acentos no chat do Elvenar (macOS) com um userscript
No chat do Elvenar, no macOS, o acento aparece antes da letra (´a em vez de á). Explica a causa, como a confirmar na consola do browser e como instalar um userscript de menos de 50 linhas que a resolve.
No macOS, no browser, o chat do Elvenar mostra ´a em vez de á. Em todos os outros separadores o teclado funciona: só o jogo falha. A tecla do acento escreve o símbolo, a vogal seguinte escreve-se à parte, e nunca se obtém uma letra acentuada. Uma frase como olá como estás? sai ol´a como est´as?.
Este tutorial explica porque acontece, como confirmar o diagnóstico na consola do browser e como instalar um userscript de menos de 50 linhas que corrige o comportamento. O script só atua no campo de texto do chat do jogo.
Requisitos: Chrome no macOS com o layout de teclado Português, e a extensão Tampermonkey.
O sintoma
O comportamento reproduz-se sempre da mesma forma:
- carregar na tecla do acento;
- carregar numa vogal;
- o chat mostra o acento e a vogal separados,
´a, em vez do carácter compostoá.
Não há maneira de acentuar uma letra no chat. O problema não está no teclado, nem na configuração do macOS, nem no browser: o mesmo teclado produz á em qualquer outra página.
Porque acontece
Teclas mortas e composição de texto
No macOS com layout Português, o acento é uma tecla morta: não escreve nada sozinha, espera pela letra seguinte e só então produz o carácter final. O browser trata esta sequência como uma composição de texto, o mesmo mecanismo que se usa para escrever em chinês ou japonês (IME, Input Method Editor). Numa página normal, a composição de ´ com a produz esta sequência de eventos, e o campo de texto fica com á.
O que o Elvenar faz de diferente
O Elvenar é feito em OpenFL e desenha tudo num <canvas>. Um canvas não recebe texto do teclado, por isso o jogo usa um único <input type="text"> escondido, dentro do elemento #openfl-content, e lê dele o que o jogador escreve.
O jogo limpa o valor desse <input> a cada evento input. Para uma tecla normal isto é inofensivo. Para uma composição, é fatal: a composição tem estado interno no browser, e alterar o valor do campo a meio dela cancela-a.
A sequência observada ao carregar no acento e depois no a é:
keydown Dead
compositionstart
compositionupdate ´
input insertCompositionText ´
keydown a
input insertText a
O que falta nesta sequência é revelador: não há compositionend, e o carácter composto á nunca chega a existir. O evento input com o ´ faz o jogo limpar o campo, a composição é cancelada, e o a seguinte entra como uma tecla normal (insertText). O ´ já tinha sido lido pelo jogo, daí o resultado ´a.
As teclas geradas por script, por exemplo por ferramentas de automação, não passam pelo IME do macOS, por isso não reproduzem o erro. Só um teclado real o revela.
Confirmar o diagnóstico
Quem quiser ver a sequência no seu próprio browser pode registá-la na consola (Ferramentas de programador, separador Consola) com a página do jogo aberta:
['keydown', 'compositionstart', 'compositionupdate', 'compositionend', 'beforeinput', 'input']
.forEach((tipo) => window.addEventListener(tipo, (e) => {
console.log(tipo, e.key ?? '', e.data ?? '', e.inputType ?? '', e.isComposing);
}, true));
Com o cursor no chat, carrega no acento e numa vogal. Se o diagnóstico estiver certo, aparece compositionstart e compositionupdate, nunca compositionend, e o input seguinte é um insertText simples com a vogal. Numa página sem o problema, a sequência termina com compositionend e o carácter composto.
A solução
A estratégia é impedir que o jogo veja a composição, para não a poder cancelar, e entregar-lhe só o resultado final. O script faz duas coisas, apenas no <input> do chat:
- Esconde do jogo os eventos de composição e os eventos de teclado e de entrada gerados durante ela. Sem esses eventos, o jogo não limpa o campo a meio, e o browser consegue terminar a composição e produzir o
á. - No
compositionend, entrega ao jogo só o texto final, como se fosse uma inserção normal: escreve o valor no campo e dispara um eventoinputdo tipoinsertText.
Todo o resto da página fica intacto.
O script
O script também está disponível para instalação direta em elvenar-acentos.user.js. O código completo:
// ==UserScript==
// @name Elvenar – acentos (teclas mortas)
// @namespace https://fuzzalab.pt/
// @version 1.0
// @downloadURL https://fuzzalab.pt/downloads/elvenar-acentos.user.js
// @updateURL https://fuzzalab.pt/downloads/elvenar-acentos.user.js
// @description Corrige acentos no chat do Elvenar: o jogo cancelava a composição da tecla morta.
// @match https://*.elvenar.com/game*
// @run-at document-start
// @grant none
// ==/UserScript==
(() => {
// O OpenFL usa um <input> escondido dentro de #openfl-content e limpa-lhe o valor a cada
// evento 'input', o que cancela a composição (´ + a) a meio. Escondemos a composição do
// jogo e, no fim, entregamos-lhe só o texto final (á) como uma inserção normal.
const isChat = (t) => t && t.tagName === 'INPUT' && t.parentElement && t.parentElement.id === 'openfl-content';
let composing = false;
const on = (type, fn) => window.addEventListener(type, fn, true);
on('compositionstart', (e) => {
if (!isChat(e.target)) return;
composing = true;
e.stopImmediatePropagation();
});
on('compositionupdate', (e) => {
if (isChat(e.target)) e.stopImmediatePropagation();
});
on('compositionend', (e) => {
if (!isChat(e.target)) return;
composing = false;
e.stopImmediatePropagation();
const text = e.data;
if (!text) return;
const input = e.target;
input.value = text;
input.dispatchEvent(new InputEvent('input', { data: text, inputType: 'insertText', bubbles: true }));
});
['keydown', 'keypress', 'keyup', 'beforeinput', 'input'].forEach((type) =>
on(type, (e) => {
if (!isChat(e.target)) return;
if (composing || e.isComposing || e.keyCode === 229 ||
e.inputType === 'insertCompositionText' || e.inputType === 'insertFromComposition') {
e.stopImmediatePropagation();
}
})
);
})();
Como o código funciona
O cabeçalho
@match https://*.elvenar.com/game*restringe o script às páginas do jogo, em qualquer subdomínio. Fora delas não corre.@run-at document-startfaz o script correr antes de qualquer código da página. Isto importa porque, quando dois ouvintes estão registados no mesmo alvo e na mesma fase, o primeiro registado corre primeiro. Ao registar os ouvintes antes do jogo, o script garante que corre antes dos do jogo.@grant nonedesativa o isolamento do Tampermonkey. O script corre no contexto da própria página, o que é necessário para os seus ouvintes interferirem com os do jogo.@downloadURLe@updateURLpermitem ao Tampermonkey instalar e atualizar o script a partir do endereço indicado.
A identificação do campo do chat
const isChat = (t) => t && t.tagName === 'INPUT' && t.parentElement && t.parentElement.id === 'openfl-content';
A função devolve verdadeiro só quando o alvo do evento é um <input> filho direto de #openfl-content, o campo escondido do OpenFL. Todos os ouvintes começam por ela, o que garante que nenhum outro campo da página é afetado.
Os ouvintes na fase de captura
const on = (type, fn) => window.addEventListener(type, fn, true);
O terceiro argumento true regista o ouvinte na fase de captura, em que o evento desce da janela até ao alvo. Registado em window, o ouvinte do script é o primeiro a ver cada evento, antes dos ouvintes do jogo no próprio campo. É isso que permite a stopImmediatePropagation() impedir que o jogo chegue a ver o evento.
O estado da composição
A variável composing fica verdadeira entre compositionstart e compositionend. Os eventos compositionstart e compositionupdate são sempre suprimidos para o campo do chat.
A entrega do texto final
No compositionend, o script lê o carácter composto em e.data (por exemplo á), escreve-o em input.value e dispara um InputEvent do tipo insertText. Para o jogo, é indistinguível de alguém ter escrito a letra normalmente.
Os eventos de teclado e de entrada
O último bloco suprime keydown, keypress, keyup, beforeinput e input quando fazem parte da composição. Reconhece-os por quatro sinais: o estado composing, e.isComposing, o keyCode 229 (o valor que os browsers atribuem a teclas processadas por um IME) e os tipos de entrada insertCompositionText e insertFromComposition. Os eventos de teclas normais continuam a chegar ao jogo sem alteração.
Como instalar
- Instala o Tampermonkey no Chrome.
- Em
chrome://extensions, abre os Detalhes do Tampermonkey e liga Permitir scripts de utilizador. Se esse interruptor não existir, liga o Modo de programador no canto superior direito. Sem isto, as versões recentes do Chrome não correm o script, e não aparece nenhum erro. - Abre elvenar-acentos.user.js. O Tampermonkey mostra o ecrã de instalação: carrega em Instalar. Em alternativa, cria um script novo no painel do Tampermonkey e cola o código acima.
- Recarrega o Elvenar.
Verificar que funciona
Com o chat aberto, escreve olá como estás?, e confirma que os acentos saem na posição certa. Vale a pena testar também as combinações menos óbvias: é, ã, ç, e o acento seguido de espaço ou de uma consoante.
Se o resultado ainda for ´a, confirma que o ícone do Tampermonkey mostra o script como ativo na página do jogo e que o interruptor da etapa 2 está ligado.
Compatibilidade
- Testado: Chrome no macOS, com o layout de teclado Português. Confirmado com
á,é,ã,ç, e com o acento seguido de espaço ou de uma consoante. - Não testado: Windows, Linux, outros layouts de teclado, Firefox e Safari.
Resolução de problemas
- O script não faz nada e não há erros. É o caso típico do interruptor da etapa 2 desligado. Sem ele, o Chrome não executa userscripts.
- O script deixou de funcionar depois de uma atualização do jogo. Se o jogo mudar o nome ou a posição do
<input>escondido,isChatdeixa de reconhecer o campo e o script fica inerte, sem estragar nada. A correção é ajustar essa função ao novo seletor. - Erros de CORS na consola. Não vêm deste script. Vêm de outras extensões que também correm na página.
- Outro layout ou outro browser. Um registo de eventos (
keydown,compositionupdate,input) com os acentos a falhar, obtido com o código da secção “Confirmar o diagnóstico”, permite perceber se a sequência é a mesma e ajustar o script.
Atualizar e remover
Como o cabeçalho tem @updateURL, o Tampermonkey verifica periodicamente se existe uma versão mais recente no endereço indicado e atualiza o script. Para o remover, apaga-o no painel do Tampermonkey, ou desativa o interruptor correspondente.
Licença: usa, copia e adapta à vontade.
Mais sobre Elvenar
Se jogas Elvenar, experimenta também o Elvenarchy, o meu site de estatísticas de Elvenar: rankings de jogadores e irmandades, resultados de torneio com histórico desde 2016, Pináculo, Aventuras e ferramentas como o planeador de torneio, para 50 mundos. Conto como e porque o criei em Elvenarchy: as estatísticas de Elvenar que faltavam.
Partilhar
Comentários 0
Ainda sem comentários — sê o primeiro.