Exemplo de uso da API Page Visibility

O Guilherme fez um post alguns meses atrás sobre a API Page Visibility, que permite, entre outras coisas, ao programador front-end definir comportamentos da página quando ela não está visível (por exemplo, quando o usuário muda de tab dentro do browser).

Para um projeto, precisei desse comportamento para verificar se um usuário ficaria ao carregar a página ficar com ela aberta por um certo tempo, afim de criar algumas estatísticas.

Como a API ainda está bem no começo, em draft, as engines dos browsers usam os seus prefixos para tratar os eventos associados a esse comportamento, então para me ajudar usei o plugin Page Visibility for jQuery. O uso é muito simples: associe aos eventos show e hide do $(document) funções. Do exemplo do próprio autor:

$(document).on({
  'show': function() {
    console.log('The page gained visibility; the `show` event was triggered.');
  },
  'hide': function() {
    console.log('The page lost visibility; the `hide` event was triggered.');
  }
});

Então, para usar um contador, você pode fazer assim:

var d = document.getElementById("container_visibility");
var count_timer = null;
var counter = 0;
var f = function() {
count_timer = setTimeout(function() {
    d.innerHTML += counter + "<br />";
    f();
    counter++;
}, 1000)
}
f();
$(document).on({
'show': function() {
    counter = 0;
    d.innerHTML += "contador resetado<br />";
    f();
},
'hide': function() {
    clearTimeout(count_timer);
}
});

O que eu fiz, foi simples: criei uma função f() que chama setTimeout com intervalo de 1 segundo e escreve numa div de id="container" o valor de counter; então, quando a página perde visibilidade, apago o setTimeout com clearTimeout e quando a página ganha visibilidade, reseto o counter e chamo novamente f().

Em http://jsfiddle.net/chrisbenseler/7NH3z/ publiquei um exemplo com essa implementação, ao lado de um counter que não é resetado quando a página perde visibilidade.

Como colocar o Twitter no seu blog (quase sem programar)

Essa semana tive que implementar em um box com os últimos tweets de determinada conta, em um blog para um cliente da MM Café.

A solução mais simples que encontrei foi consumir os dados da API do twitter através do javascript mesmo. Para isso busquei uma solução pronta e a melhor que encontrei foi o Twitterjs. Script extremamente simples de usar, você pode fazer o download da última versão no Google Code ou utilizar o hotlink direto, como no exemplo abaixo:

<script 
 
src="http://twitterjs.googlecode.com/svn/trunk/src/twitter.min.js"
 
type="text/javascript">
</script>

Com o script linkado na sua página tudo o que você precisa é chamar a função que irá requisitar os tweets e passar as configurações desejadas.

Um exemplo simples, que será explicado após o código:

getTwitters("myTweets", { 
id: "andafter",
count: 10,
enableLinks: true,
ignoreReplies: true,
clearContents: true,
template: "<li> %text% <a href="http://twitter.com/%user_screen_name%/statuses/%id%/">%time%</a></li>"
});

No exemplo acima eu vou exibir no meu elemento html com id "myTweets" os últimos 10 tweets do @andafter em forma de lista. O html é simples:

<ul id="myTweets"> <li>Aguarde, carregando tweets...</li> </ul>

Pronto!

Agora vamos a algumas explicações, primeiro das configurações quando você chama a função:

Opções de configuração

Agora vou explicar as configuração quando você chama a função:

  • id: seu user no twitter
     
  • count: quantidade de tweets exibidos
     
  • clearContents: se for true, limpa o elemento html que carregará os tweets
     
  • enableLinks: true/false – deixa os links do tweet clicáveis
     
  • ignoreReplies: true/false – exibir ou não os seus replys para outras pessoas

  • template: o HTML que irá exibir o tweet, neste item que irão as variáveis do tweet , que são explicadas mais a frente neste post
     
  • prefix: mensagem exibida antes de cada tweet (no caso de não usar template)
     
  • timeout: Millisegundos antes de executar a ação do "onTimeout"
     
  • onTimeout: função executada quando o tempo setado acima acaba
     
  • newwindow: true/false – define se os links dos tweets irão ou não abrir em uma nova janela (ou aba)
     
  • callback – Função chamada quando completa a requisição e exibição dos tweets. Esta função não é chamada em caso de timeout.

 

Variáveis do template

Abaixo a explicação das variáveis que você pode usar para exibir seus tweets (no item "template" da configuração):

  • text – a mensagem do tweet
     
  • id – id do tweet
     
  • time – tempo "amigável" da postagem, ex: 2 hours ago
  • created_at – tempo da criação
     
  • user_name – Nome do usuário
     
  • user_screen_name – Username
     
  • user_id – ID do usuário
     
  • user_profile_image_url – Avatar do user
     
  • user_url – Url do user
     
  • user_location – Localização do user
     
  • user_description – Bio do user

 

 

 E o resultado, já apresentei lá em cima, segue um exempo um pouco diferente:

getTwitters("myTweets", { 
id: "andafter",
count: 10,
enableLinks: true,
ignoreReplies: true,
clearContents: true,
template: "<li> %text% <a href="http://twitter.com/%user_screen_name%/statuses/%id%/">%time%</a></li>", timeout: 15, onTimeout: function(){ alert("No momento o twitter não está respondendo."); }
});

Sorteio no Twitter com PHP

Semana passada ganhei cinco seriais do Mind Meister Premium (um aplicativo web para construção de mapas mentais) para distribuir e dois deles resolvi distribuir no Twitter.

Questionado pelo @fefurst sobre os próximos prêmios, falei que não sabia como iria fazer o sorteio e ele, monstrinho programador que é, prontamente respondeu que "ia dar uma olhadinha na API do Twitter".

Dito e feito, na mesma manhã ele me envia a primeira versão de um script em PHP (já viu as novidades do PHP 5.3?)que utiliza a API Serch do Twitter procurando por uma mensagem específica (a da promoção, que você pode setar no script) para sortear entre os participantes…

 

Features

  1. Configuração da frase a ser buscada
  2. Pode sortear vários participantes
  3. Lista todos os tweets que estão participando
  4. Não sorteia o responsável pela promoção
  5. Não sorteia mais de uma vez um usuário
  6. Elimina RT´s duplicados
  7. Permite filtrar a data inicial e final dos tweets que participarão do sorteio

 

O código

Agora está na versão 0.3, tudo na agilidade do Furst para publicar um código bonitinho e corrigir alguns bugs. O código está como deve ser: enxuto e fácil de entender, não ousei nem adicionar mais comentários, pois está bastante legível.

 

 

<?php
/*
 * Sorteio no twitter... 😀 v. 0.3 RC1
 *
 * Auhor: Felipe Furst
 * email: [email protected]
 *
 */
// login de onde origina o sorteio
$login = "gserrano";
// início do perído para avaliação de RT
$data_inicio = strtotime("2009-11-13 08:00");
// fim do perído para avaliação de RT
$data_fim = strtotime("2009-11-18 12:00");
// msg q foi retwitada, atenção as vezes alguém pode retwitar uma msg e ela icar cortada, interessante colocar um código junto da msg ou cortar alguma palavras do final
$rt_string = "@gserrano";
// número de pessoas para sortear
$num_premios = 2;
// ------------------ a partir daqui não se mexe -----------------------
// ------------------ a partir daqui não se mexe -----------------------
// ------------------ a partir daqui não se mexe -----------------------
// ------------------ a partir daqui não se mexe -----------------------
$str = Array("á", "à", "â", "ã", "ä", "é", "è", "ê", "ë", "í", "ì", "î", "ï", "ó", "ò", "ô", "õ", "ö", "ú", "ù", "û", "ü");
$rep = Array("a", "a", "a", "a", "a", "e", "e", "e", "e", "i", "i", "i", "i", "o", "o", "o", "o", "o", "u", "u", "u", "u");
$rt_string = str_replace($str, $rep, strtolower($rt_string));
$search_str = "{$login}";
if(strlen($rt_string) > 0) {
	$temp = explode(" ", $rt_string);
	foreach($temp as $t) {
		$search_str.="+{$t}";
	}
}
$page=1;
$sorteio = Array();
echo "Sorteio de {$num_premios} pr&ecirc;mios.<br><br>";
echo "Twittadas:<br>";
while(true) {
	$twtr_search = "http://search.twitter.com/search.atom?q={$search_str}&rpp=100&page={$page}"; // busca todos os registros possíveis ...
	$str_xml = file_get_contents($twtr_search);
	$xml = new SimpleXMLElement($str_xml);
	$page++;
	if($page > 1500) // a api do twitter só retorna 1500 páginas
		break;
	if(count($xml->{"entry"}) > 0) {
		foreach($xml->{"entry"} as $e) {
			if(strtotime($e->{"published"}) <= $data_fim && strtotime($e->{"published"}) >= $data_inicio) { // tem que estar dentro da data do sorteio
				if(array_search(utf8_decode($e->{"author"}->{"name"}), $sorteio) === false) { // verifica se o cara retwittou mais de uma vez para não aumentar a chance de vitória
					echo date("m.d.y H:i:s", strtotime($e->{"published"})). " - ";
					echo utf8_decode($e->{"author"}->{"name"}).": ".str_replace(Array("<b>", "</b>"), "", utf8_decode($e->{"content"}))."<br>";
					if(strrpos($e->{"author"}->{"name"}, $login) === false) { // não sorteia quem proporciona o sorteio
						$sorteio[] = utf8_decode($e->{"author"}->{"name"});
					}
				}
			}
		}
	}
	else {
		break;
	}
}
if(!empty($sorteio)) {
	echo "<br><br>Sorteado(s):";
	for($i=0; $i<$num_premios; $i++) {
		if(empty($sorteio)) {
			echo "<br><br>sobraram ".($num_premios-$i)." pr&ecirc;mios.";
			break;
		}
		srand(strtotime("now"));
		$randval = rand(0, count($sorteio)-1);
		echo "<br>O vencedor do pr&ecirc;mio n&uacute;mero ".($i+1)." &eacute;: {$sorteio[$randval]}";
		unset($sorteio[$randval]); // não sortei o cara mais de uma vez ...
		$sorteio = array_values($sorteio); // refaz o vetor eliminando a posição nula do maluco removido
	}
}
else {
	echo "<br><br>Não houve twittadas para o sorteio.";
}

 

 

Testei ele aqui e está funcionando perfeitamente. Com essa base dá para implenetar mais coisas, como um reply automático para os vencedores, adicionar periodicamente os concorrentes em um BD para gerar uma "lista" de quem está participando da promoção (caso ela dure vários dias, por exemplo)… criatividade, programadores deste mundo!

 

A base está aí, valeu @fefurst!

Google Analytics libera API para desenvolvedores

Ontem li no Profissionais Web que o Google disponibilizou uma API para o Google Analytics, isso significa que agora será possível desenvolver aplicativos utilizando as estatísticas vindas do Analytics.

Uma verdadeira mão na roda!

 

O que é possível fazer com a API do Analytics?

Com a API significa que você pode ter acesso a TODOS os dados disponíveis no Analytics de um aplicativo terceirizado ou desenvolvido por você, ou em sites de terceiros. Para o meu caso pensei em duas aplicações.

 

1. Exibir relatórios para os clientes em interface própria

 

Atualmente eu trabalho com aplicativos web e com CMS próprio (GS Intra), porém nunca desenvolvi um sistema de estatísticas para ele por dois motivos principais relacionadas ao Analytics:

  • Gratuito
  • Não utiliza recursos do meu servidor
  • Superioridade técnica ao que eu poderia desenvoler

Com o uso da API será possível que eu centralize em um só aplicativo o gerenciamento do conteúdo, dos clientes e estatísticas do site de meus clientes. Não preciso mandar o cliente para um site externo para que ele visualize as estatísticas. Facilita para o cliente, é obviamente é melhor para o desenvolvedor, que agrega mais funcionalidade e valor ao sistema.

 

2. Acompanhar impacto de mudanças no site

Eu tenho sérios problemas para acompanhar o impacto das alterações realizadas no And After. Por exemplo, adiciono um elemento de navegação visando aumentar o tempo médio de visita dentro do site no dia 27 de abril.

Preciso de um log, onde eu adicione nesta data todas as alterações feitas para que posteriormente eu colete informações do "antes e depois". Utilizei por algum tempo o Excel para fazer isso, mas era praticamente no CTRL+C e CTRL+V para mesclar dados do Buscapé, AdSense e Analytics em uma mesma tabela, além das datas das modificações.

Agora é possível eu tem um banco de dados com todas as alterações realizadas em no site (como o exemplo do elemento de navegação) e mesclar com os dados que serão importados da API, facilitando a criação do "antes e depois" das alterações de forma automática. 

Quando (e se) a API do AdSense for liberada então, ficará show. Em um único aplicativo será possível medir o impacto de mudanças nas visitas e rendimentos de um site. 

 

Ta aí a dica para um aplicativo que eu utilizaria. haha 🙂

 

 

Saiba mais sobre a API do Analytics em

Blog oficial do Google Analytics

Google Code Analytics

Javascript

Java

 

 

O que já existe desenvolvido?

Polaris

Até o momento testei um aplicativo desktop, o Polaris. Ele utiliza a plataforma (plataforma?) Adobe Air, e a versão free é extremamente simples de usar.

Apresenta diversas informações em uma pequena janela com uma interface bastante intuitiva. 

Confira algumas telas do Polaris , o  analytics desktop:

Polaris - Google analytics destkop

Polaris - Google analytics destkop

Polaris - Google analytics destkop

Polaris - Google analytics destkop

 

Recomendo! 

 

Mobile

Não testei, mas no blog oficial do Analytics recomendaram uma aplicação Android do Analytics.

 

 

Assim que eu fizer testes com a ferramenta publicarei os resultados e programações aqui no O desenvolvedor, se você já desenvolveu alguma coisa ou quer compartilhas suas idéias pode publicar um artigo ou comentar. 🙂

Construindo uma classe PHP para acessar a API do BlogBlogs

Postei nesse último final-de-semana uma classe em PHP que fiz para acessar a API do BlogBlogs de uma forma fácil.
A API do BlogBlogs retorna através da arquitetura REST arquivos XML que representam informações do perfil de um usuário do BlogBlogs e de blogs cadastrados no sistema.

Download da classe aqui!

Primeiramente, criei duas classes, que abstraem esses dados em forma de objetos. Uma classe é a BlogBlogsBlog, um bean para a entidade blog, e outra a BlogBlogsUser, que por sua vez é um bean para o usuário do sistema. Cada classe recebe no construtor os atributos.

/**
* Dados do usuário do BlogBlogs
*/
class BlogBlogsUser {
    function __construct($username, $firstname, $lastname, $fullname, $thumbnailpicture, $profilepage, $blogs, $favorites, $fans) {
        $this->userName = $username;
        $this->firstName = $firstname;
        $this->lastName = $lastname;
        $this->fullName = $fullname;
        $this->thumbnailPicutre = $thumbnailpicture;
        $this->profilePage = $profilepage;
        $this->blogsCount = $blogs;
        $this->favoritesCount = $favorites;
        $this->fansCount = $fans;
        $this->blogs = array();
    }
   
}

/**
* Dados de um blog do BlogBlogs
*/
class BlogBlogsBlog {
    function __construct($name, $url, $rssurl, $blogpage, $lastupdate, $inboundblogs, $inboundlinks, $rank, $lang) {
        $this->name = $name;
        $this->url = $url;
        $this->rssUrl = $rssurl;
        $this->blogPage = $blogpage;
        $this->lastUpdate = $lastupdate;
        $this->inboundBlogs = $inboundblogs;
        $this->inboundLinks = $inboundlinks;
        $this->rank = $rank;
        $this->lang = $lang;
    }
}

Com isso, podia continuar a implementação. Criei a classe BlogBlogsService. Guardei no atributo apiUrl a url da API, e no construtor da classe recebo o valor da chave da API do BlogBlogs (semelhante ao processo de se usar a API do Google Maps, por exemplo).
Então criei alguns métodos, e os que são úteis para o desenvolvedor:

  • setUserName(): seta o username do usuário pelo qual faremos a busca no sistema do BlogBlogs. O método automaticamente busca todos os blogs e os dados do perfil do usuário
  • getBlogByUrl(): faz a busca por um blog através da sua url

Assim, temos implementadas todas as buscas possíveis pela API.

class BlogBlogsService {
    private $apiUrl = "http://api.blogblogs.com.br/api/rest/";
    private $userKey;
    private $doc;
   
    /**
    * construtor: recebe a chave da API do BlogBlogs
    * para gerar, acesse http://blogblogs.com.br/dev/chave
    */
    function __construct($k) {
            $this->userKey = $k;
            $this->user = null;
    }
   
    /**
    * recebe o userName no blogblogs e faz a consulta
    */
    function setUserName($userName) {
        $path = $this->apiUrl . "userinfo?key=" . $this->userKey . "&username=" . $userName;
        $this->doc = new DOMDocument();
        $this->doc->load($path);
        //verifica erros: caso tenha um nó com namespace error
        if($this->doc->getElementsByTagName("error")->length==1) {
            $this->errorMsg = $this->doc->getElementsByTagName("error")->item(0)->nodeValue;
            throw new Exception($this->errorMsg);
            return false;
        }
       
   
        if($this->doc->getElementsByTagName("result")->length!=1)
            return;
        //dados do usuário
        $userset = $this->doc->getElementsByTagName("result")->item(0);
        $blogBlogsUser = new BlogBlogsUser($userset->getElementsByTagName("username")->item(0)->nodeValue,
                                              $userset->getElementsByTagName("firstname")->item(0)->nodeValue,
                                              $userset->getElementsByTagName("lastname")->item(0)->nodeValue,
                                              $userset->getElementsByTagName("fullname")->item(0)->nodeValue,
                                           $userset->getElementsByTagName("thumbnailpicture")->item(0)->nodeValue,
                                              $userset->getElementsByTagName("profilepage")->item(0)->nodeValue,
                                           $userset->getElementsByTagName("blogs")->item(0)->nodeValue,
                                              $userset->getElementsByTagName("favorites")->item(0)->nodeValue,
                                           $userset->getElementsByTagName("fans")->item(0)->nodeValue);
        //dados de cada blog
        foreach($this->doc->getElementsByTagName("weblog") as $blog) {
            $blog = $this->createBlogWithXMLNode($blog);
            //$blogBlogsUser->addBlog($blog);
            $this->addBlogToUser($blogBlogsUser, $blog);
        }
        $this->user = $blogBlogsUser;
        return true;
    }
   
    /**
    * recupera um blog por uma url
    */
    function getBlogByUrl($url) {
        $path = $this->apiUrl . "bloginfo?key=" . $this->userKey . "&url=" . $url;
        $this->doc = new DOMDocument();
        $this->doc->load($path);
        if($this->doc->getElementsByTagName("weblog")->length!=1)
            return;
        $blog = $this->doc->getElementsByTagName("weblog")->item(0);
        $myBlog = $this->createBlogWithXMLNode($blog);
        return $myBlog;
   
    }
   
    function createBlogWithXMLNode($node) {
        $blog = new BlogBlogsBlog($node->getElementsByTagName("name")->item(0)->nodeValue,
                                  $node->getElementsByTagName("url")->item(0)->nodeValue,
                                  $node->getElementsByTagName("rssurl")->item(0)->nodeValue,
                                  $node->getElementsByTagName("blogpage")->item(0)->nodeValue,
                                  $node->getElementsByTagName("lastupdate")->item(0)->nodeValue,
                                  $node->getElementsByTagName("inboundblogs")->item(0)->nodeValue,
                                  $node->getElementsByTagName("inboundlinks")->item(0)->nodeValue,
                                  $node->getElementsByTagName("rank")->item(0)->nodeValue,
                                  $node->getElementsByTagName("lang")->item(0)->nodeValue);
   
        return $blog;
    }
   
    function addBlogToUser($user, $blog) {
        array_push($user->blogs, $blog);
    }
}

Vamos ver um passo-a-passo de como ver um atributo de um usuário:

$minha_chave = “XXXXXXXXXXXXXXX”;
$bbService = new BlogBlogsService($minha_chave);
$bbService->setUserName("ChrisB");
echo $bbService->user->fullName;

Se quisermos listar os blogs dele, exibindo as urls dos mesmos, acessamos a lista de BlogBlogsBlog que o BlogBlogsUser possue:

foreach($bbService->user->blogs as $blog) {
$blog->url . “<br />”;
}

Ainda podemos fazer a busca diretamente po um blog, pela sua url:

$bbService->getBlogByUrl(”http://www.chrisb.com.br/blog/”)
//para exibir o nome do blog
$bbService->getBlogByUrl(”http://www.chrisb.com.br/blog/”)->name;

Creio que os exemplos, mesmo simples, mostram a facilidade de uso. []s!