Juice Lib

Acabo de disponibilizar no Google Code a JUICE Lib, uma biblioteca javascript para desenvolvimento de interfaces web baseadas em AJAX. Esta biblioteca é composta por vários pedaços de código que eu escrevi ao longo dos últimos 3 anos para meus projetos web, e que eu resolvi compilar e organizar.

A Juice oferece as seguintes funcionalidades:

  • funções para manipulação de texto, como StringBuffers e funções strip em Strings.
  • funções map, reduce e filter para manipulação de dados em arrays.
  • funções para manipulação de dados em dicionários, ou arrays associativos.
  • sisteminha simples de log.
  • funções de tratamento de eventos onload e de teclado.
  • e finalmente, funções para execução de chamadas RPC com XMLHttpRequest de forma simples e elegante.

E porque utilizar esta lib, se já existem milhares de outras ? Bem, eu diria que a resposta mais direta seria que a Juice Lib é leve, simples de se usar e fornece funções básicas para implementação de funcionalidades úteis em interfaces web. Aos poucos irei adicionar outras funcionalidades que implementei e que considero úteis.

O código pode ser visualizado e carregado em http://code.google.com/p/juicelib/. A documentação atualmente é inexistente, mas eu forneço junto com a lib um arquivo HTML com chamadas a todas as funções do sistema que funciona como um HowTo.

Dicas, sugestões e críticas serão bem vindas.

ECMAScript

A ECMA International é uma associação de industrias fundada em 1961 cujo propósito principal é criar padrões de tecnologia de informação e comunicação e de eletrônicos para consumidores.É esta associação que mantém a linguagem ECMAScript, conhecida também como Javascript.

A linguagem Javascript foi desenvolvida pela Netscape e pela Sun Microsystems em 1995, e incorporada ao Netscape Navigator em 1996. Neste mesmo ano, a Netscape submeteu a linguagem de script para a ECMA. Em 1996, começaram os trabalhos para padronização da linguagem ECMA-262, cuja primeira versão foi oficialmente aceita em 1997.

A versão atual é a 3.0, lançada em 1999. Uma versão 4.0 está em andamento.

A especificação completa da versão 3.0 pode ser encontrada aqui, em formato PDF. Eu diria que este documento é uma fonte essencial para aspirantes a Jedi Hackers Masters em Javascript.

Otimização de código javascript

O artigo Efficient Javascript apresenta várias dicas para otimizar códigos em javascript. Importantíssimo para quem desenvolve aplicações web que usam a scripts de forma intensiva.

Objetos em Javascript

Javascript fornece mecanismos básicos para criação de objetos.

Segue abaixo um código simples, de um objeto que simula o comportamento de um StringBuffer em Javascript (cujas vantagens foram discutidas neste post):

function StringBuffer() {
    this.buffer = [];
};

StringBuffer.prototype.append = function append(string) {
   this.buffer.push(string);
   return this;
};

StringBuffer.prototype.toString =function toString() {
   return this.buffer.join("");
};

O código acima cria um objeto do tipo StringBuffer, com um construtor vazio e com um atributo buffer. Depois cria 2 métodos: append e toString. Dois pontos são interessantes no código acima:

  1. Um objeto em Javascript é no fundo uma função, e o construtor é a lista de parâmetros desta função.
  2. O objeto prototype, interno a qualquer objeto Javascript, permite que se adicione métodos e atributos em runtime em qualquer objeto Javascript, incluindo objetos da biblioteca padão.

Uma outra forma de escrever o objeto acima seria com a seguinte sintaxe:

function StringBuffer() {
   this.buffer = [];

   append = function append(string) {
      this.buffer.push(string);
      return this;
   };

   toString = function toString() {
    return this.buffer.join("");
   };
}

Qualquer instrução que estiver fora de alguma função interna será executada automaticamente quando se criar um novo objeto do tipo StringBuffer, sendo portanto o equivalente à um construtor.

Minha opinião: o uso de objetos em JS é altamente recomendado para suprir a falta de estruturas de dados mais sofisticadas e para organizar certas partes do código

Captura de teclas em Javascript - Parte 2

Demorei pra escrever a segunda parte desse pequeno tutorial, mas antes tarde do que nunca. No post anterior eu expliquei brevemente o funcionamento da captura de teclas em Javascript. Neste vou colocar uma receitinha de bolo.

Segue o código, compatível com IE e Firefox. Qualquer dúvida com os comentarios, entre em contato:

document.onkeyup=handleKeyboardAction;

function handleKeyboardAction(e){

   var code;

  // Obtém o evento. No caso do Firefox, este
  // evento é passado como argumento, e no caso do IE,
  // deve ser obtido através do objeto window.
   if (!e) var e = window.event; 

   // Detecta o target da tecla
   var targ;
   if (e.target) targ = e.target;
   else if (e.srcElement) targ = e.srcElement;

   // Este código previne um erro do navegador Safari:
  // Se o usuari clica num DIV com texto, os outros browsers
  // retornam o DIV como sendo o target. Safari retorna  o nó contendo
  // o texto (nodeType 3). Nesse caso, o target que nos interessa é o pai.
   if (targ.nodeType == 3) // defeat Safari bug
      targ = targ.parentNode;

  // Obtém o nome da TAG HTML do target do evento
   tag = targ.tagName.toUpperCase();

  // Verifica se o evento não esta sendo acionado em nenhum
  // campo como campo de texto e combobox.
  // Esta verificação é importante, pois o handler pode bloquear
  // o funcionamento adqueado desses campos (por exemplo, em vez de escrever
  // a letra no campo, executa uma função).
   if (tag == "INPUT")
      return;

   if (tag == "SELECT")
		return;

   // Detecta o codigo da tecla
   if (e.keyCode) code = e.keyCode;
   else if (e.which) code = e.which;

   var character = String.fromCharCode(code);

  // Executa o procedimento associado à uma letra.
   if(character == "R"){
   } 

       //Seta para cima
	if(code == 38) {
           ...
          return;
	} 

	//Seta para direita
	if(code == 39) {
            ...
            return;
	} 

	//Seta para esquerda
	if(code == 37) {
		return;
	}
}

Captura de teclas em Javascript - Parte 1

A sigla Web 2.0 tem sido muito utilizada recentemente. Talvez seja o nome mais marketeiro depois de AJAX. Aliás Web 2.0 depende muito de AJAX. O fato é que softwares Web-based com comportamentos muito semelhantes à aplicações desktop tem pipocado pela internet, e com certeza os exemplos mais impressionantes e eficientes são os produtos lançados pela Google, como o Google Maps, Google Calendar, Google Reader, entre outros. E de fato, o grande barato da Web 2.0 é tentar mudar o paradigma de uso da internet, tornando a experiência do usuário mais rica e interativa (detesto essa expressão “experiência rica”, mas no momento não me veio nada mais apropriado), fazendo com que a web deixe de ser um conjunto de hipertextos, e passe a ser um conjunto de serviços.

Neste esforço de deixar as interfaces web mais interativas, dinâmicas e eficientes, a captura e tratamento de eventos de teclado pode ser uma característica desejável e desejada. A boa notícia é que fazer isso é relativamente simples, usando Javascript. Existem basicamente 3 tipos de eventos de tecla que se pode capturar: keyDown, keyPress e keyUp. Os eventos keyDown e keyUp são disparados respectivamente no momento de descida e subida da tecla, e o evento keyPress é disparado quando a tecla está pressionada. Portanto quando uma tecla é pressionada, os eventos são disparados na ordem keyDown, keyPress e keyUp.

É interessante notar que a forma como os eventos é disparada varia ligeiramente dependendo do S.O: no Linux, os eventos keyDown e keyUp são disparados apenas uma vez por evento de tecla, e keyPress é disparado várias vezes, enquanto a tecla estiver pressionada (não sei bem qual a taxa de disparo, mas é bastante rápida). Já no Windows (tanto no IE quanto no Firefox), apenas o evento onKeyUp é disparado apenas uma vez por evento. Os eventos keyDown e keyPress são disparados sequencialmente enquanto a tecla estiver pressionada (ou seja, caso você mantenha a tecla apertada, o sistema irá lançar a seqüência keyDown-keyPress-keyDown-keyPress….). Não tive a oportunidade de testar no Mac OS, o que é uma folha imperdoável já que eu só uso Mac em casa, mas testarei assim que possível. Esta pequena diferença faz com que a estratégia de captura seja diferente dependo do evento desejado. As seguinte situações me vêem à cabeça:

  1. O sistema deseja capturar apenas um evento por tecla pressionada. No Linux pode se usar onKeyDown ou onKeyUp. No Windows, apenas onKeyUp. Portanto, para que o script seja funcional em todos os sistemas, a melhor escolha é onKeyUp.
  2. O sistema deseja capturar repetidamente uma tecla pressionada, para simular por exemplo o funcionamento de uma tecla pressionada numa caixa de texto (imprime várias vezes o caractere pressionado). Neste caso, a melhor escolha é onKeyPress, que funciona nos dois SOs. Mas é preciso tomar cuidado no caso do Windows: caso o handler de onKeyDown estiver habilitado também, o sistema irá capturar duas vezes o evento, o que pode ser problemático.
  3. O sistema deseja capturar uma vez a descida da tecla, e uma vez a subida. Isso pode ser útil para medir o tempo de pressão da tecla. No caso do Linux, é muito simples: um handler para keyDown e um handler para keyUp, e ambos eventos serão disparados apenas uma vez. No caso do Windows, a estratégia é mais complicada, uma vez que onKeyDown será lançado repetidas vezes. Eu não tenho uma solução em mãos, mas deve ser necessário fazer um controle de qual tecla foi apertada, e não executar caso o evento ja tenha sido processado.
  4. O sistema deseja capturar a descida da tecla uma vez, processar a tecla pressionada várias vezes, e capturar a subida da tecla uma vez. Tanto no caso do Windows quanto do Linux, me parece que a melhor solução é criar um handler para cada evento, mesclando as situações 2 e 3. Em ambos os casos, a ordem de execução será keyDown, keyPress e keyUp. No caso do windows, será necessário utilizar a mesma solução da situação 3 para evitar que keyDown seja processado várias vezes.

Como eu não sei qual o tamanho máximo de um artigo neste blog, vou deixar para o próximo post a descrição de como escrever o código para capturar estes eventos.

Divs e combos no IE

Aposto que muitos desenvolvedores Web já tiveram o seguinte problema com Internet Explorer: criam um DIV (por exemplo, em um menu contextual, ou um tooltip), que aparece bonitinho na tela, mas que para seu desespero fica embaixo de uma caixa de seleção (o famoso combo box). O pior é quando se descobre isso um mês depois, quando um cliente resolve redimensionar a janela, e o combo box em questão vai parar no meio da área visível do DIV.

Quem já teve este problema, sabe que mudar o z-index não adianta nada, porque no IE o combobox fica acima de tudo, não levando em conta o parâmetro de profundidade.

Graças a algumas pesquisas do meu colega Christian, encontramos uma solução para isso: basta criar um IFRAME e colocar exatamente embaixo do DIV, com as mesmas coordenadas e tamanho e com zindex do IFRAME inferior ao do DIV. Por algum motivo que me escapa, o IFRAME encobre o COMBOBOX, e mesmo assim aceita o parâmetro de profundidade, exibindo o DIV de forma correta.

Concatenação eficiente de Strings em Javascript

Quem programa em Java sabe que operações de concatenações de String são muito mais eficientes utilizando StringBuffer e função append do que usando o comando String A = String B + String C.

Isso porque a cada operação de soma dessas, uma nova String é alocada para que seja feita a cópia dos dados, e alocação de memória em Java é uma operação cara. Em Javascript, esse problema também ocorre. Pesquisando na Internet, descobri um código que simula o comportamento de um StringBuffer em nessa linguagem.

Fizemos alguns testes no IE e no firefox, com um loop que concatena Strings. No Firefox, a diferença entre usar o operador + e a função append é pequena. A grande vantagen é que a segunda é mais estável em termos de performance que a primeira.

Em compensação, no IE o uso de append é absurdamente mais eficiente do que no firefox. Testes mostram que para concatenar 1000 Strings de tamanho 100, o append era 60 vezes mais rápido. Portanto, seu uso é altamente recomendável em scripts que façam muitas concatenações e que precisam ser otimizados.

Segue o código:

function StringBuffer() {
this.buffer = [];
};
StringBuffer.prototype.append = function append(string) {
this.buffer.push(string);
return this;
};
StringBuffer.prototype.toString = function toString() {
return this.buffer.join("");
};

Para usar, basta criar uma variável do tipo StringBuffer:

var buffer = new StringBuffer();buffer.append("Hello");
buffer.append(" ");
buffer.append("world"):
System.out.println(buffer.join()):

Esta técnica é muito útil na hora de atualizar o conteúdo de uma DIV em uma página web usando o comando innerHTML.Via de regra, nunca use document.getElementById("minhadiv").innerHTML += "nova string" repetidamente. Sempre use um string buffer para armazenar a string intermediária, e modifique o innerHTML somente no final da operação.

Envio de grandes conjuntos de dados com AJAX

Hoje, totalmente por acaso, descobri alguns macetes para envio e recepção de grandes quantidades de dados usando requisições HTTP assíncronas, como por exemplo textos. Estou trabalhando em um sistema que permite que o usuário cadastre pareceres sobre informações do sistema, e estes pareceres não tem limitação de tamanho.O sistema foi implementado com AJAX, para permitir que os pareceres sejam carregados dinâmicamente, e gravados assincronamente.

O primeiro problema que tive foi que os textos enviados eram sempre truncados em 4096 bytes. A primeira reação foi verificar se estava enviando os dados via POST ou GET: o correto é enviar por POST, uma vez que GET tem tamanho limitado. Os dados realmente estavam sendo enviados por POST, que a priori não tem limitação alguma. Pesquisando um pouco, descobri que precisava definir um header Content-length com o tamanho do conteúdo do texto, antes de enviar a requisição. O comando correto é

requestObject.open('POST', url, true);
requestObject.setRequestHeader('Content-Type',
                             'application/x-www-form-urlencoded');
if (parameters != null)
    requestObject.setRequestHeader("Content-length",
                                   parameters.length);
requestObject.send(parameters);

Bom, eu não sou um expert do protocolo HTTP, mas a minha explicação seria que de fato o método POST aceita uma quantidade ilimitada de dados, mas por default ele define o tamanho máximo como 4096, para evitar erros. Caso o usuário deseje enviar mais, deve informar o tamanho total.

Resolvido este problema, tive um problema semelhante ao ler os dados para exibir na tela. Meu sistema abre uma requisição AJAX, que envia um XML contendo o texto do parecer inserido pelo usuário. Para recuperar o texto, executava o seguinte comando:

var value = decodeURIComponent(
                 root.getElementsByTagName('value')[0].
                                       firstChild.data);

Este código retornava o texto corretamente no IE, mas cortava em 4096 bytes (novamente !) no Firefox. O mais estranho é que tanto no envio quanto na recepção, o XML gerado estava correto. Portanto o problema estava na leitura do CDATA do XML. Fuçando um pouco (desta vez não encontrei nada na internet), descobri que em caso de CDATA com mais de 4096 bytes, o DOM do Firefox considera que a tag value possui mais de um nó filho, cada um com no máximo 4K. Assim, o código correto para obter o texto em ambos os navegadores é

for(var i=0; i i++)
     value += decodeURIComponent(
                     root.getElementsByTagName('value')[0]
                           .childNodes[i].data);

FireBug - Ferramenta profiça para debug em Firefox

O slogan da página diz tudo: Its not your grandma’s Javascript Console. A extensão FireBug fornece ferramentas bem interessantes para análise de páginas, javascripts e afins. Usada em conjunto com as funções de análise de CSS do WebDevelopper, temos uma IDE completa.Vale ressaltar a função que permite bisbilhotar as conexões assíncronas via HTTPXMLRequest, o coração do AJAX. Esta extensão registra quando uma chamada assíncrona foi feita, quem executou a chamada, e o melhor de tudo: imprime o XML de retorno !!!!! Eliminamos a necessidade de ficar imprimindo informações no servidor ou ter que gerar javascript extra para exibir o conteúdo de retorno.

Pequena biblioteca AJAX

Nas últimas semanas tenho desenvolvido muitas coisas usando a técnica AJAX…digo técnica porque usar a palavra tecnologia neste caso não me parece apropriado. Mas isso é assunto pra outro tópico.Os resultados finais são bem interessantes e satisfatórios: as interfaces geradas com Javascript+XML+requisição assíncrona são bem mais próximas de desktop, leves e dinâmicas, e a comunicaçao entre navegador e servidor é feita de forma muito eficiente e rápida.

Minha única ressalva é que, pra quem usa algum framework como struts como eu estou usando atualmente, o uso de AJAX força a ter que reescrever em javascript muitas das funcionalidades já existentes.

Meu sonho de consumo atualmente seria um bom conjunto de taglibs que gerasse código de requisisição e validação de formulários usando AJAX, deixando o código mais limpo e evitando algumas gambiarras necessárias. Existem algumas libs por aí, mas sinceramente nenhuma delas conseguiu chamar a atenção. Quem sabe um dia não me animo a escrever uma.

Mas enquanto não acho a solução ideal, estou usando uma solução caseira desenvolvida inicialmente por um colega e com algumas extensões minhas: o arquivo ajax.js possui um conjunto de funções que facilitam o uso de AJAX, tanto no acesso aos dados do arquivo XML quanto no acesso e modificação do DOM HTML. Está bem simples e sem nenhuma pretensão de ser uma biblioteca definitiva. Mas pode ser uma mão na roda para quem está começando. Comentários, sugestões e críticas são bem vindos.

« Previous Page