Essa é uma tradução de “Improve your jQuery-fu, write plugins” de Tomáš Kramár Trnava.
Escolhi traduzir esse texto pois ele foi o tutorial mais simples que encontrei quando precisei escrever um plugin. No final dele você será capaz de entender como o jQuery trabalha com plugins e poderá escrever os seus próprios.
jQuery é um framework JavaScript simples e poderoso que mudou minha forma de programar JavaScript. Um novo JavaScript dizem alguns com certa razão. O código que você cria é pura elegância.
Você começa com 10 linhas de jQuery que poderiam ter sido 20 linhas do tedioso JavaScript manipulando DOM. No final você acaba ficando com duas ou três linhas.
E esse numero pode ficar ainda menor se você refatorar o código para um plugin do jQuery.
Quando o assunto é escrever plugins, pessoas (tipo eu) normalmente pulam fora com medo. Eu ouvi sobre plugins de jQuery a bastante tempo mas nunca havia tentado escrever um. Quando pensava no assunto algumas idéias vinham na minha cabeça:
Deve ser difícil e por isso deixe isso somente para jedis
Isso deve precisar de um conhecimento profundo do jQuery
O que eu vou ganhar?
(Eu vim do mundo Java, imagine).
Nota do tradutor:
(Eu vim do mundo Ruby ;D).
Nada do que eu imaginava é verdade. Escrever um plugin é facil como escrever algo usando o jQuery. Não é necessario nenhum conhecimento especial. Tudo o que é preciso é extender um objeto jQuery. Algo como:
Lembra? Isso é JavaScript, não Java. O código acima é tudo que você precisa para criar um plugin. Dentro da função, $this é a referencia ao objeto jQuery de onde o plugin foi chamado.
No exemplo, $this é um objeto com os elementos
e você pode fazer de tudo com eles. Como o plugin retorna $this, você pode continuar executando metodos do jQuery usando “Method Chaining”.
De volta a minha última questão. O que você possivelmente vai ganhar criando um plugin?
Alto nivel de reusabilidade
Muitos pedaços de JavaScript são geralmente reaproveitáveis, mas criando um plugin você passa ele para outro nível.
Configuração
Isso é ligado ao ponto anterior. O cenário sem plugin é ter um pedaço de código que você copia e cola modificando para seu proposito. Isso pode ficar muito mais fácil com plugins. Você pode escrever o seu para, por exemplo, aceitar algumas opções. Veja como fica agora:
Elegância
O código acima parece melhor que um script inteiro, não parece?
Agora é a hora de mostrar um exemplo real. Vamos fazer um script para tornar clicável cada linha da tabela.
Sempre que eu queria fazer as linhas clicáveis eu copiava esse código e mudava a URL ou o nome do parâmetro.
Com o plugin fica assim:
settings = $.extend(defaults, settings); // escreve em cima das opções default com as opções enviadas
$this = $(this);
Atualmente os captchas usados no Megaupload não são muito “difíceis” de resolver (de fato eles são muito ruins – exemplos abaixo).
Existem porém pontos interessantes aqui:
A API getImageData do HTML 5 é usada para pegar informações sobre os pixels da imagem. No HTML 5 é permitido adicionar uma imagem no elemento canvas (de onde então pose-se extrair informações sobre cada pixel usando JavaScript).
O script implementa uma rede neural, escrita em puro JavaScript.
As informações do pixel, extraídas da imagem usando canvas, são jogadas dentro da rede neural em uma tentativa de adivinhar o caractere usado – algo parecido com Optical Character Recognition (OCR).
Se abrirmos o código fonte podemos perceber que o funcionamento recai em como o captcha é implementado. Como mencionado anteriormente esse não é um captcha muito bom. Ele tem 3 letras, cada uma em uma cor diferente, usando uma possibilidade de 26 caracteres e eles são todos do mesmo tipo gráfico.
O primeiro passo é fácil de entender: O captcha é copiado dentro do canvas e então convertido para escala de cinza.
O canvas é então quebrado em três matrizes de pixels - cada uma contendo um caractere (isso é bem fácil de fazer - uma vez que cada caractere é de uma cor diferente, eles são separados pela diferença de cores usadas).
// Muda todos pixels de tal cor para a cor branca
if (image_data.data[i] == colour) {
image_data.data[i] = 255;
image_data.data[i+1] = 255;
image_data.data[i+2] = 255;
} else { // Todo o resto vira preto
image_data.data[i] = 0;
image_data.data[i+1] = 0;
image_data.data[i+2] = 0;
}
}
}
}
Finalmente qualquer pixel solto é removido da imagem (retornando um caractere limpo). Isso é feito procurando por pixels brancos (que eram da cor da letra) que são cercados de preto (que não bateram com nenhuma cor de letra). Se esse for o caso o pixel é simplesmente removido.
Estamos bem próximos de ter uma forma que pode alimentar uma rede neural, mas não estamos lá ainda. O script agora vai fazer uma forma bem crua de detecção de borda na forma, procurando pelos pixels que cercam o caractere e então o transformando em um retângulo. Nesse trecho a matriz também é reduzida para 20 por 25 pixels.
Então, após todo esse trabalho, o que nós temos? Uma matriz de 20 por 25 pixels contendo um único retângulo preenchido com preto e branco. Terrivelmente excitante.
Agora alguns pontos estratégicos são extraído da matriz para serem usado como "receptores" (eles que alimentarão a rede neural). Como exemplo, o receptor deve olhar para o pixel da posição 9x6 e verificar se ele está "ligado" ou não. Somente 64 estados (muito menos do que a matriz de 20 por 25 inteira) são então jogados para dentro da rede neural.
A questão que você deve estar se perguntando agora é: Por que não fazer somente uma simples comparação de pixels? Por que "isso de" redes neurais? Bom, o problema é que com toda essa redução da informação um monte de ambiguidade existe. Se você rodar a demonstração do script vai perceber que a comparação simples de pixels vai falhar mais que a nossa rede.
A próxima etapa é tentar adivinhar a letra. A rede é então alimentada com 64 valores boleanos (coletados das letras) juntamente com uma serie de valores pre-computados. Um dos conceitos por trás do funcionamento de redes neurais é que você reutiliza valores de execuções anteriores. É como se o autor do script rodasse ele várias vezes e coletasse uma serie de resultados para conseguir um valor ótimo. O resultado em si não tem nenhuma funcionalidade por si só mas ele ajuda a derivar o valor final.
A rede neural utiliza os 64 valores extraídos dos caracteres do captcha para comparar com valores pré-computados de cada letra do alfabeto. O resultado retornado dessas comparações é parecido com ‘98% de chances para A’, ‘36% de chances para B’, etc..
Repetindo com todas letras do captcha o objetivo é atingido. Ele não é 100% perfeito (imagino que resultados melhores seriam alcançados se a letra não fosse transformada em um simples retângulo antes da execução do algoritmo) mas é bom pelo que é – e impressionante considerando que tudo acontece 100% no navegador usando padrões emergentes.
Como nota – o que acontece aqui é bem especifico para esse captcha. Essa técnica deve funcionar em mais alguns captchas mal implementados, mas além disso a complexidade da maioria dos captchas é muito avançada (principalmente para qualquer analise no lado do cliente).
Eu estou esperando trabalhos interessantes derivados desse projeto – isso carrega bastante potencial.
Sinatra é um framework para linguagem Ruby extremamente leve. Ele roda tendo como base o servidor Mongrel, servindo com muita rapidez as requisições.
Por não ter a extensa biblioteca de “helpers” que o Rails tem e também por não seguir a linha MVC de Rails e Merb, o seu uso não é indicado em grandes aplicações.
Com foco em Web Services e pequenos aplicativos, Sinatra é uma ótima solução para rodar pequenas aplicações desenvolvidas em Ruby com muita eficiência.
O propósito do artigo é ser um guia de inicio para quem quer aprender mais sobre o framework, para isso vamos desenvolver um pequeno “own-microblog”…
Trabalhar com novas ferramentas, aceitando qualquer desafio não importando a tecnologia utilizada (leia-se linguagem de programação, framework ou sistema operacional) é motivador. A busca pelo “sempre novo” nos ensina paradigmas diferentes, soluções novas e ainda mantém distante aquele tédio que acaba surgindo quando você já tem certa experiência em alguma tecnologia (afinal, quase tudo por aqui se resume a “cadastros e mais cadastros”).
Que o caminho do aprendizado é duro (haha) todos nós sabemos, porém as vezes ele te cobra muito mais do que devia. Isso depende principalmente da quantidade de informação disponível sobre o assunto e sobre os problemas específicos que você está enfrentando.
Para evitar suicídios ou dores de cabeça que possam ser causados pela “falta de experiência” é que vou relatar aqui os problemas que tive essa semana para fazer uma simples tradução em uma interface em PHP. Alias, quem sabe essa de relatar os problemas no aprendizado vira habito e começo fazer artigos “For Dummies” (ok, dummie não – inexperiente) né?
Tudo para mim sobre esse asunto começou quando chegou a tarefa de traduzir algumas partes de uma interface de webmail. Não saio por ai falando inglês, mas me viro bem na leitura e razoável na escrita. Como eram frases curtas e comuns em interfaces esse não seria o problema. Como a tarefa de localizar algo dentro de um projeto também não é complicada, não importava saber exatamente estavam essas frases. E é ai que comecei a me enganar.
Logo que encontrei as frases a serem traduzidas me dei de cara com o principal mas não único problema: a interface (que na verdade é o software de webmail Squirrelmail guarda as traduções em arquivos separados.
Após uma pequena lembrança sobre isso (por ter utilizado tanto nesse blog, quando em um wiki) resolvi pesquisar como alterar esses arquivos.
Rapidamente eu já sabia que na verdade se tratavam de arquivos do projeto de internacionalização do GNU – gettext – e então procurei algum editor para Windows (obviamente eu não estava na minha maquina e lá só existem maquinas rodando Windows) e encontrei o poEdit. Abri o programa tentei editar e nada feito, nele não há como adicionar uma nova tradução “na mão”.
Para entender melhor a inexistência da opção para adicionar nova tradução, vamos entender como o gettext funciona:
- O programador deve substituir no código as entradas diretas de strings, como “Representação de string” por _(”Representação de string”). Isso identificara que ali deverá ser traduzido.
- Então o programador deve executar um programa que irá gerar um arquivo para tradução de todo o código de um projeto.
- Após a tradução deve ser executado um comando que irá compilar a tradução de forma que ela possa ser executada com maior velocidade.
Como eu não podia, não queria e nem sabia como gerar o arquivo inteiro de tradução novamente (por já ter a maior parte traduzida) eu precisava de uma forma de adicionar algumas frases e palavras facilmente. Abri então o arquivo (.po) no Notepad++ e inclui manualmente as frases. Até então sem saber da parte de compilar esse arquivo, coloquei no ar e fui testar – logicamente sem sucesso.
Em mais uma busca encontrei que o poEdit gerava esse arquivo compilado (.mo), assim como a ferramenta original gettext que é padrão em ambiente GNU. Abri então o .po no programa e ao salvar realmente ele compilou a tradução, ótimo! Ao colocar no ar o problema não se resolveu, desgraça!
*Mexe no arquivo, sobe, baixa outra versão, sobe, muda codificação, altera direto no código e esquece o arquivo externo, pensa melhor e faz a coisa certa: continua quebrando a cabeça mas faz a coisa certa*
Após um longo tempo revendo código, mudando e voltando atrás na idéia de traduzir tudo ali no código mesmo a coisa começou dar certo: algumas partes eram traduzidas assim como eu queria, mas outras ainda não.
O primeiro passo para a solução foi baixar o GNU para Windows e usar a ferramenta original para compilar o arquivo .mo, isso pelo menos evitava alguns erros que o poEdit acusava no arquivo e as modificações que ele fazia sem meu consentimento (principalmente na questão de pluralização).
O segundo foi perceber que para que as novas traduções sejam exibidas na interface é necessário que o servidor seja reiniciado, no caso era o Apache. Como provavelmente por se tratar de um webmail de uma grande companhia da cidade – e com muitos acessos – o servidor carrega várias instâncias do Apache e a cada requisição uma das instâncias retorna a página, as vezes vinha a página traduzida e as vezes não. Isso no começo foi um problema, depois era só questão de dar alguns reloads.
A terceira e a que demorou mais, foi perceber o motivo de que em alguns lugares as frases eram traduzidas e em outros não: plugins.
Dentro dos programas que utilização localização por arquivos é possível definir escopos de atuação de certo arquivo, assim por exemplo um plugins vem com seu próprio arquivo de localização e no código do mesmo é definido a mudança de escopo.
O problema é que os plugins em questão não vinham com tradução nenhuma, e havia uma dificuldade minha para entender que eu poderia (e deveria) ter feito a tradução dentro do escopo.
Porém eu encontrei uma solução que na hora me caiu bem: fiz a tradução dos plugins no arquivo de localização global mesmo, e encontrei como retirava a mudança de escopo que era o grande problema e que provocava confusões, que no caso do SquirrelMail é feito pelo código (PHP): sq_change_text_domain('squirrelmail');
ou em casos genéricos: bindtextdomain("myPHPApp", "./locale");
textdomain("myPHPApp");
Onde “./locale” que é o escopo (ou pasta onde estão os arquivos de localização)
Enfim, se livrando da dor de cabeça:
Prefira sempre manter as traduções em arquivo.
Fuja do poEdit, o pacote GnuWin32 permite rodar a ferramenta gettext mesmo em Windows. Para gerar o arquivo .mo a partir de um .po utilize o seguinte comando dentro do promot (cmd) sem esquecer do caminho do arquivo completo: msgfmt minha_traducao.po -o minha_traducao.mo
Reinicie o Apache (ou seu servidor), caso isso não seja possível manualmente, tente sobrecarregar o servidor com vários refreshes seguidos – segure o dedo no F5 – isso pode fazer com que ele crie novas instâncias.
Preste atenção se algum plugin ou qualquer pedaço de código está mudando o escopo da localização. Isso provavelmente está ocorrendo se na tradução está um termo traduzido que a aplicação não traduz no meio de outros que ela traduz.
Caso isso não resolva seu problema, levante e tome um ar, beba uma agua e pense em algum assunto totalmente diferente do seu problema. Normalmente a solução vem sozinha em sua cabeça, mesmo após você ter procurado ele por horas. Não esquente a cabeça.
E caso isso aqui te ajudou, me dê um link, um comentário ou uma twittada