quarta-feira, 25 de julho de 2007

Como realizar requisições AJAX fora do domínio

O Firefox tem um dispositivo de segurança que impede que requisições AJAX sejam realizadas entre domínios. Mas muitas vezes precisamos deste tipo de requisição. Existem métodos para burlar essa segurança, mas vou mostar o método mais simples aqui.

Você deve criar uma página server-side (JSP, ASP ou PHP) que busque a URL para você e então você pode fazer a requisição AJAX para uma página em seu domínio. Um exeplo usando JSP e JSTL (tag library) seria assim:


<%@page contentType="text/xml"
%><%@page pageEncoding="UTF-8"
%><%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"
%><c:import url="${param.url}"></c:import>



É isso mesmo. apenas 4 linhas de código, que facilmente poderiam virar 3. Quem conhce JSP sabe do que eu estou falando.

Para o JSTL funcionar, você precisa ter a biblioteca JSTL no diretorio lib de sua aplicação ou no diretório common/lib do Tomcat (não saberia dizer ao certo o diretório correto para outros containers).

Agora, você deve repara que estou fechando (%>) cada TAG na linha posterior. Isso foi uma dica que aprendi para tirar as linhas em branco que o Tomcat coloca nas páginas quando você insere uma TAG JSP. Isso é muito útil para quando você vai fazer um request de um documento XML. O Firefox só aceita documentos XML que começam na primeira linha do documento.

Como usar?

Supondo que você salvou a sua página JSP como ajax.jsp, no método que faz a requisição você vai usar o URL:


ajax.jsp?url=URL_REAL_QUE_VOCÊ_DESEJA


por exemplo:


ajax.jsp?url=http://www.google.com


Espero que esta postagem seja útil para vocês.

Transparências sem PNG (e caixa de diálogo modal)

Muitas vezes vi artigos e posts afirmando que é impossível se ter transparêcia com compatibilidade para diversos navegadores utilizando somente CSS, mas isto não é verdade. É possivel sim conseguir transparencia sem o uso de imagens.

Supondo que queremos um DIV semi-transparente (50% opaco), podemos criar um código de estilo (css):


.transparente {

filter:progid:DXImageTransform.Microsoft.Alpha(opacity=50) !important;
opacity: .5;
-moz-opacity: .5;
-khtml-opacity: .5;

}


e usá-lo em um DIV com class="transparente".

Entretanto, tudo que você colocar dentro deste DIV ficará com esta transparência. Mesmo que você coloque dentro do DIV transparente um DIV com os filtros setados para 100% da opacidade, os elementos dentro deste DIV opaco continuarão transparentes.

Então como colocar elementos opacos dentro de um DIV com transparência feita por este método? Não coloca dentro. Coloque na frente!

Isso acarreta que os elementos sólidos não estarão dentro do código do DIV transparente, mas em outro DIV posicionado à frente. Você consegue fazer isso setando a propriedade position para absolute e posicionando o DIV opaco na frente do transparente usando as propriedades top e left (Também é sensato neste caso fazer o mesmo com o DIV transparente).

Ok, se a transparência for ocupar somente parte da tela e você precisar de elementos sólidos dentro do DIV transparente, este não é um método ideal. Mas no meu caso, eu queria construir um caixa de diálogo modal (que só permite que se use o resto do sistema após a caixa ter sido fechada).

Para tal, criei um DIV transparente e o coloquei na posição top:0px e left:0px, também com width e height iguais a 100% da página (na verdade IE requer width:103%;height:101.5% e Firefox requer width:100%;height:120% mas não me perguntem porque) e claro com display:none. Lembre-se que para o height fucionar, você deve setar o height do body para 100% também.

Então criei outro DIV (fora do DIV transparente), posicionei no meio da página e quando quero exibir o diálogo modal, seto display do DIV transparente e do sólido para block. É prudente também que você use a propriedade z-index para o DIV sólido sempre aparecer na frente do DIV transparente.

É isso. Caso você não saiba como posicionar um DIV no meio da tela independentemente da resolução do usuário é só perguntar que eu escrevo aqui minha função javascript que faz isso.

O código fica mais ou menos assim:


body {

height:100%;

}
.transparente {

position: absolute;
top:0px;
left:0px;
width:103%;
height:101.5%;
border: none;
background-color:#aaa;
filter:progid:DXImageTransform.Microsoft.Alpha(opacity=50) !important;
opacity: .5;
-moz-opacity: .5;
-khtml-opacity: .5;
overflow:hidden;
display:none;
z-index: 5;

}
html>body .transparente {

width:100%;
height:120%;

}

.solido{

position: absolute;
top:0px;
left:0px;
width:102%;
height:101%;
border: none;
display:none;
z-index: 10;

}
html>body .solido{

width:100%;
height:100%;

}


Aqui vocês vêem o uso de html>body .estilo, que é um CSS hack. IE não interpreta esses códigos, então quando você precisa de estilos diferentes para o Firefox e IE isso ajuda muito.

Qualquer DIV que você colocar dentro do DIV da classe solido terá 100% de opacidade.

Parece que no código, o !important no filtro da microsoft saiu em duas linhas, mas é em uma só. Deve aparecer logo após o filtro.

terça-feira, 24 de julho de 2007

Como se livrar do erro 0x80040111 (NS_ERROR_NOT_AVAILABLE) do Firefox


Construindo uma aplicação complexa para a web, me deparei várias vezes com o erro código: 0x80040111 (NS_ERROR_NOT_AVAILABLE) no Firefox. Gastei bastante tempo procurando no Google alguma página ou post que me explicasse o porque deste erro.

Depois de muita procura, me deparei com o Eric's weblog. Nele, Eric explica que o erro é causado por:


Mozilla calls onload() for all HTTP transactions that succeeded. The only time it calls onerror() is when a network error happened. Inside the onerror handler, accessing the status attribute results in this exception:

Error: [Exception... "Component returned failure code: 0x80040111
(NS_ERROR_NOT_AVAILABLE) [nsIXMLHttpRequest.status]" nsresult: "0x80040111
(NS_ERROR_NOT_AVAILABLE)" location: "JS frame ::
file:///Users/chuck/errtest.html :: anonymous :: line 114" data: no]
Source File: file:///Users/chuck/errtest.html
Line: 114


E que uma solução rápida seria adicionar um try/catch quando estivesse lendo a propriedade status do objeto.

Para aquele problema específico, funcionou perfeitamente e como eu estava fazendo apenas insert no banco de dados para salvar as preferências de usuário, que era executado cada vez que o usuário executasse alguma função, achei que não era absolutamente necessário tratar um possível erro.

O problema reapareceu quando eu precisei tratar um select que era acionado dentro de um iFrame, mas que seu resultado deveria aparecer no parent. Eu simplesmente precisava de todos os dados, inclusive da propriedade status.

Então voltei a procurar no Google algo que me ajudasse. Depois de muito tempo achei uma dica no site matthom que me ajudou a entender melhor problema e me deu uma solução.

Segundo o texto, o problema ocorre quando você tenta realzar uma requisição AJAX e submete e formulário ao mesmo tempo. Então era só trocar o INPUT TYPE='submit' para INPUT TYPE='button' e o problema seria resolvido.

Entretanto, no meu caso eu não estava enviando dados de um formulário. A função era executada em um link no evento onClick. Então essa solução não se aplicava ao meu caso.

Por sorte, ao ler um dos comentários vi que se utilizasse um 'return false;' teria mesmo efeito e resolvi tentar colocar o 'return false;' após a chamada da função no link.

Funcionou perfeitamente. Parei de receber o erro, pois o link, apesar de ter 'href="#"', causa o mesmo impacto no Firefox que tentar submeter um formulário mesmo sem o atributo action.

Isso me ajudou a entender porque eu não recebia essa mensagem de erro ao tentar submeter meus formulários com AJAX. Na minha TAG FORM eu sempre coloco onSubmit='return false;'. Se vocês querem enviar dados de um formulário por AJAX, eu recomendo o uso do evento onSubmit. Fica mais elegante do que mudar o INPUT para button.

Espero que este texto ajude vocês com o mesmo problema.