quarta-feira, 19 de março de 2008

GreaseMonkey para feed do InfoBlogs no GoogleReader

Quem assina o feed do InfoBlogs deve ter o mesmo problema que eu. O link deste feed não é para o post em si, ele é para uma cópia do post na página do próprio InfoBlogs. Particularmente acho isso irritante, pois só costumo entrar no blog do autor quando o post é interessante e desejo comentá-lo. Nesse caso, preciso aguardar o carregamento (que é bem lento) da página do InfoBlogs para só depois poder clicar e ver o post original, onde eu posso comentar.

Para resolver esse problema implementei um script no GreaseMonkey para remontar a página do GoogleReader com os links corretos. Para quem não sabe o GreaseMonkey é uma extensão do Firefox, o melhor navegador existente na atualidade, que permite ao usuário configurar scripts para serem rodados ao término do carregamento de uma determinada página. Eu falei brevemente dele no post em que falo sobre as extensões do Firefox essenciais para um desenvolvedor web, e agora com este exemplo ficará mais fácil entender como ele funciona.

O código do script encontra-se abaixo. O que ele faz é verificar a cada 3 segundos se houve alguma alteração de estado no GoogleReader. Para então mudar os links caso haja necessidade. Só há uma ressalva, ele não funciona para o modo de visualização de lista, só a visão expandida. O motivo é que não gosto da visualização em lista e portanto não me interessei em testar nela :P
// ==UserScript==
// @name GoogleReaderGM
// @namespace http://www.google.com/reader
// @include http://www.google.com/reader/*
// ==/UserScript==

function GoogleReaderGM() {}
GoogleReaderGM.prototype = {
isChanged: function() {
var e = document.getElementById("entries");
var ds = e.getElementsByTagName("div");
if( this._oldUrl != window.location.href ) {
this._oldUrl = window.location.href;
this._oldQtds = ds.length;
return true;
}
if( this._oldQtds != ds.length ) {
this._oldQtds = ds.length;
return true;
}
return false;
},
mudaLinksInfoBlogs: function() {
if( this.isChanged() ) {
var e = document.getElementById("entries");
var as = e.getElementsByTagName("a");
for( i=0; i<as.length; i++ ) {
var a = as[i];
if( a.className=="entry-title-link" && a.href.indexOf("www.infoblogs.com.br/view.action")!=-1 ) {
a.href = a.href.replace("view","redir/go");
}
}
}
}
}

var gr = new GoogleReaderGM();

setInterval(function() { gr.mudaLinksInfoBlogs(); }, 3000);
Como pode ser visto eu defino como um comentário JavaScript em quais páginas esse script será executado. No caso, todas aquelas abaixo de http://www.google.com/reader.

O interessante é que se você quiser pode utilizar esse código como template para executar outras funcionalidades no GoogleReader. Basta adicionar mais alguma função, e utilizando isChanged você fica sabendo se alguma entrada nova apareceu na área que exibe as entradas dos feeds. Isso é claro se você utilizar a visão expandida (Expanded view), pois o método isChanged só foi feito pra ela.

quinta-feira, 13 de março de 2008

Escapando textos em funções de javascript executadas em links

Para determinar a execução de uma função javascript no clique de um link temos duas possibilidades. Ou utilizamos o próprio atributo href da tag a com o protocolo javascript: ou adicionamos o atributo onclick fazendo a chamada à função.

Contudo o comportamento de ambos não é totalmente igual. Principalmente quando chamamos funções que passam como argumento algum texto escapado com a função escape. Normalmente usa-se esse escape para evitar error de javascript devido ao uso de aspas simples e duplas nos textos.

Veja abaixo dois exemplos de cógigo com o erro por não usar escape. O primeiro com aspas simples, que faz com que o javascript ache que o texto terminou antes do que deveria e o segundo com aspas duplas fazendo o navegador acreditar que o atributo terminou:
var textoComAspaSimples = "Quero um copo d'água";
var s = '<a href="javascript:alert(\'' + textoComAspaSimples+ '\')">Clique aqui</a>';
document.writeln(s);

var textoComAspaDupla = 'Você está com "sede"?';
var s = '<a href="javascript:void(0)" onclick="alert(\'' + textoComAspaDupla+ '\')">Clique aqui</a>';
document.writeln(s);
Para evitar esses erros uma boa idéia é utilizar a função escape. No entanto isso não funciona direito por href. Neste atributo o navegador interpreta o código escapado causando o mesmo erro. Isso pode ser observado no exemplo a seguir, no primeiro link gerado o erro acontece, no segundo não:
var textoComAspaSimples = "Quero um copo d'água";
var s = '<a href="javascript:alert(\'' + escape(textoComAspaSimples) + '\')">Clique aqui href</a><br/>';
s += '<a href="javascript:void(0)" onclick="alert(\'' + escape(textoComAspaSimples) + '\')">Clique aqui onclick</a>';
document.writeln(s);
O problema é que com essa solução a função que recebe este parâmetro precisa executar uma transformação reversa utilizando a função unescape. Caso contrário veremos algo parecido com o que o segundo link do exemplo acima mostra: Quero%20um%20copo%20d%27%C3%A1gua.

Implementando a transformação reversa na função que recebe teríamos algo como o mostrado abaixo. Uma nova função que recebe o parâmetro escapado e o transforma para utilizá-lo:
function escapedAlert(s) {
alert( unescape(s) );
}
var textoTodoErrado = "Sentes \"sede\"?\nQuero um copo d'água";
var s = '<a href="javascript:void(0)" onclick="escapedAlert(\'' + escape(textoTodoErrado) + '\')">Clique aqui onclick</a>';
document.writeln(s);
Mas isso ainda não é o ideal pois continuaríamos sem poder utilizar a função dentro do atributo href, e além disso precisaríamos sempre preparar as funções para receber parâmetros escapados. Uma outra alternativa é criar nossa própria função de escape. Como é mostrado no exemplo abaixo:
function escapeForLink(s) {
if(s) {
var r = s.split("\n").join("\\n");
r = r.split("\r").join("\\r");
r = r.split("'").join("\\u0027");
r = r.split('"').join("\\u0022");
return r;
}
return s;
}
var textoTodoErrado = "Sentes \"sede\"?\nQuero um copo d'água";
var s = '<a href="javascript:alert(\'' + escapeForLink(textoTodoErrado) + '\')">Clique aqui href</a><br/>';
s += '<a href="javascript:void(0)" onclick="alert(\'' + escapeForLink(textoTodoErrado) + '\')">Clique aqui onclick</a>';
document.writeln(s);
Com essa função os problemas de escape para passagem de parâmetro de texto em funções executadas apartir de links fica resolvido.