AngularJS/ HTML5 / CSS3/ Javascript/ JQuery

Criando um scroll Infinito com o AngularJS e JQuery

scrollinfinito-jquery

Esse post é o início de uma série de posts relacionados ao AngularJS, se você não conhece, é bom conhecer, muito de seu comportamento é semelhante ao que tinhamos no Flex, por exemplo Databind e a idéia de componentizar. E o mais legal de tudo é que ele é compatível com qualquer biblioteca que manipule o DOM, por exemplo Zepto e JQuery, ou até mesmo o Twitter Bootstrap.

O AngularJS facilita e muito certas tarefas e mantem o DRY bem vivo. Por exemplo, esse post eu quero falar sobre o scroll infinito, igual o comportamento que o Twitter possui ou o LinkedIn possui quando você vai rolando a página e dados vão pipocando em sua frente. Pois bem, como eu faria isso em puro Javascript e usando o seletor do DOM jQuery?

O scroll é um mal necessário, é o que a Microsoft tenta adotar na nova interface do Windows 8 e o que o Flex tentou quando lançou a versão 4.0. Porém é necessário trazer informações à medida que elas vão acontecendo ou carregar só o necessário e à medida que o usuário for investigando elas forem brotando.

scrollinfinito-jquery

Scroll Infinito com Javascript e JQuery.

O HTML é bem simples:

	<div id="conteudo">
    <ul id="lista"></ul>
</div
var contador = 1;
 
$(document).ready(function () {
    carregar();
    $(this).on('scroll', function () {
        if ($(window).scrollTop() + $(window).height() >= $(this).height()) {
            carregar();
        }
    });
});
 
function carregar() {
    for (var i = 0; i < 10; i++) {
        $("#lista").append('<li><img src="http://placehold.it/100x100"/><div><h4>' + contador + ' Item</h4><p>Descrição do item '+contador+'</p></div></li>');
        contador++;
    }
}

A medida que a soma da posição da barra mais a altura da janela superar o tamanho da altura do documento, ele executa a função carregar, jogando mais itens na lista.

O truque também está na maneira como você declara o CSS, margin faz um estrago danado quando não aplicada de forma correta, nesse exemplo eu coloquei uma aparência de uma lista de smartphone, igual no Android.

body, ul,li,img,h4,p{
    margin:auto;
    font-family:'Droid Sans';
}
ul {
	margin-left: -40px;
	list-style:none;
}
li {
    color:#111;
    height: 100px;
    border-bottom:1px solid #fff;
    padding:10px;
}
li h4 {
    font-weight:400;
    font-size:20px;
    padding:0;
}
li img {
    padding-right:10px;
    float:left;
    display:block;
    vertical-align:text-top;
}
li div {
    display:inline;
}
li p {
    font-weight:300;
    font-size:12px;
}
#conteudo {
    width:100%;
    background:rgba(0,0,0,0.1);
    overflow:auto;
}

E aqui está o exemplo rodando, eu usei o jsFiddle para demonstrar melhor.

Você pode ver sendo executado diretamente no jsFiddle

Criando agora uma lista infinita com o AngularJS

O AngularJS possui vários truques na manga para deixar sua vida mais fácil, módulos, tags personalizados, data-bind, repetidores, diretivas, controllers, esses são alguns de seus recursos. A melhor parte dele como mencionei acima é que é compatível com outras bibliotecas.

Para fazer esse exemplo você vai fazer uma pequena alteração no html e substituir o Javascript, os estilos que é o visual continuam o mesmo.

Logo de cara para identificar como um App, você precisa alterar o html e fazer da seguinte forma:

<!doctype html>
<html ng-app="Rolagem">
<head>

Isso define que o módulo Rolagem será seu App nesse arquivo de html, então na parte de repetir os itens você faz da seguinte forma.

<body ng-controller="Main">
	<div id="conteudo" fazer-rolagem="carregar()">
    <ul id="lista">
         <li ng-repeat="i in items">
            <img src="http://placehold.it/100x100"/>
            <div><h4>{{i.id}} Item</h4>
            </div>
        </li>
    </ul>
</div
</body>

Como o AngularJS abstrai bastante o escopo do javascript para com o HTML, é notável algumas propriedades como o ng-controller, fazer-rolagem que são primariamente customizáveis, a presença do ng-repeat indica que o arquivo vai ser repetido N vezes, conforme vamos adicionando novos itens à lista.

E a parte fundamental desse processo é o Javascript:

function Main($scope) {
    $scope.items = [];
 
    var counter = 1;
    $scope.carregar = function() {
        for (var i = 0; i < 10; i++) {
            $scope.items.push({id: counter});
            counter += 1;
        }
    };
 
    $scope.carregar();
}
 
angular.module('Rolagem', []).directive('fazerRolagem', function() {
         return function(scope, elm, attr) {
        elm = $(document);
 
        elm.bind('scroll', function() {
             if ($(window).scrollTop() + $(window).height() >= $(this).height()) {
                 scope.$apply(attr.fazerRolagem);
             }
        });
    };
});

Agora você já sabe como criar uma diretiva, fazer um módulo e carregar os itens conforme forem feitos os scrolls, no JQuery quando você quer escutar um evento você geralmente usa o $on(), no AngularJS é necessário usar o bind, ele que vai escutar pelo evento.

Maneiras diferentes de aplicar a mesma técnica, para um resultado igual, Eu gostei mais do AngularJS, escrevi menos código e atingir o mesmo resultado.

Ah! lembrando que esses códigos estão disponíveis no Github.

BB10/ HTML5 / CSS3/ Mobile

Criando seu primeiro App com BBUI.js e Webworks para BB10

bb10

Uffa! Quem acompanha no twitter, viu que passei uma maratona para criar um App do zero para a plataforma BB10 gastando aproximadamente 29 horas, por simples 2 motivos.

1 – Conhecer o workflow de desenvolvimento da plataforma

2 – Ganhar um device específico para desenvolvedor.

Ok, minha aventura começa na quinta-feira as 10 da manhã, primeiro passo foi conhecer bem o webworks um SDK específico para a plataforma Blackberry desde a versão 5.0 em diante. Por que não o Phonegap? Veja só; Basicamente o phonegap funciona com muita coisa do webworks, então ao invés de tercerizar a API eu partir direto para a plataforma específica, não é assim que tem que ser a cadeia de distribuição? Alô administradores!

Antes que você leia todo o post, esse video abaixo mostra o que vamos fazer até o final do post.

Documentação

Primeira coisa que notei e me senti em casa, documentação, como estava acostumado com as documentações da Adobe, elas não fogem, vasta e muito bem detalhada, inclusive tem suporte a tudo quanto é tipo de desenvolvimento não só para HTML. A documentação mais completa acima de todas, sem dúvida é a do Cascades, que se fosse comparar à outras plataformas, seria a plataforma oficial.

Porém, a blackberry deixa bem claro na página de desenvolvedores, ela ama todas as tecnologias de desenvolvimento e ela deixa bem claro em sua página para os desenvolvedores.

A documentação do webworks é basicamente o empacotador, já que 80% da API do webworks é puramente HTML5. Isso mesmo! Não tem formula mágica, adorei essa parte do desenvolvimento por que frisa justamente a singularidade de uma só API. Claro que é importante notar que há exceções como toda regra, por exemplo APIs que são identicas ao HTML5.

O que é igual a api do HTML5:

  • Geo Localização
  • Captura de Midia( camera apenas)
  • Camera
  • File ( estrutura de arquivos)
  • Persistência de arquivos
  • Cache
  • Aceleração (acelerometro)
  • SMS, Chamar telefone, E-mail
  • Alerta ( javascript:alert(‘mensagem’);
  • Esquemas de URI

O que é específico da plataforma e requer o webworks de fato:

  • BBM
  • Calendário
  • Sensores( Giroscópio, Gravidade, Magnetometro, aproxiamção)
  • Toast
  • Microfone
  • Identity (para identificação do dispositivo)
  • Agenda de contatos
  • Pagamento ( in-App purchase)
  • Systema como nível de bateria e etc.

 

Comunidade e colaboratividade

Ambas andam juntos é claro, e a decisão dela de usar o Github para expor suas APIs, SDK de forma aberta ao público, facilitou ainda mais o trajeto do desenvolvedor aos seus SDK. Tanto é que existem repositórios completos só para listar contribuições dos desenvolvedores espalhados pelo mundo.

O mais importante não é nem os SDKs da Blackberry e sim o Github, você facilmente faz amizade com quem está por trás do projeto e vira e mexe você até contribui para melhorias. Essa idéia de social + código, foi um casamento da raposa, muito bem bolada!

bbuijs

BBUI.js Toolkit para aparência das Apps

Você já ouviu falar do Twitter Bootstrap, JQuery Mobile, Sencha Touch, Enyo, certo? Então adicione a lista o BBUI.JS, ele funciona só para os dispositivos da Blackberry, já que todo seu sistema de eventos é voltado para eventos do próprio navegador, se você tiver tempo, pode mudar a aparência, mais isso ai é meio que matar o Chinês achando que é barata. Sacô?!

É basicamente Javascript e extensívo uso do atributo data-bb-* . É um ponto de partida para você começar a criar algo que respeite o guia de interface da plataforma, só que não fique na regra, você pode mudar e usar o que você quiser.

[pullquote align=”left”]Você pode continuar usando seu framework preferido, lembre-se só da experiência mobile![/pullquote]

A parte de transições de telas built-in é bem legal, com vários efeitos pré fabricados e o mais interessante notar é a parte de navegação entre as telas, lembra muito o navigator do Flex Mobile; Só que usa de fato um Array simples para fazer o cast das telas pelo atributo data-bb-type=”screen”.

Na página do projeto, você encontra mais detalhes da documentação, como inicializar as coisas. Eu quero focar mais na parte Hello World! Quando você terminar de ler o post já sair com um App na mão.

Criando um App de demonstração

[note color=”#FFCC00″]No próprio site do Webworks, você encontra um guia passo-a-passo em inglês mais completo que este, se você quiser saber mais sobre como iniciar com o pé direito, vá direto ao site. http://developer.blackberry.com/html5/[/note]

Primeiro passo

Registre-se como desenvolvedor, é 0800, grátis, oba-oba, FREE, Paga não, Passe-livre.

Segundo passo

Requisite o acesso as chaves para assinar sua App, sem elas a festa não acontece. Elas funcionam para qualquer SDK que você vier usar.

[pullquote align=”left”]Dica importante: Lembre-se do PIN que você fornecer, esse PIN é uma espécie de RG.[/pullquote]

As chaves são obrigatória, elas tem um propósito: Assinar suas obras de arte “Seus Apps” para distribuição e teste.

Terceiro passo

Baixe o WebWorks, SDK Aqui. http://developer.blackberry.com/html5/download/ Dica( use a versão Gold, que é o botãozinho cor de ouro na página).
Para testes e desenvolvimento, vamos usar o Ripple Emulator, que é uma ferramenta para testes e emulação do seu HTML5 para dispositivos móveis, ele foi criado pela Blackberry (a.k.a RIM), e é hoje bastante utilizado.

[pullquote align=”left”]Siga as instruções de instalar o Ripple Emulator no Google Chrome.[/pullquote] Supondo que deu tudo certo nas suas instalações, siga as seguintes instruções:
Baixe as seguintes bibliotecas, elas são necessárias para iniciar o toolkit e acessar tags do HTML de forma mais fácil, ZeptoJS sem dúvida é a melhor para isso no ambito mobile, já que ele é leve, bem leve.

E quanto a ferramenta de desenvolvimento? Bom, use a que você achar melhor para editar páginas em HTML5, CSS3, Javascript. Eu gosto do Sublime Text. Mas não se apegue a esse detalhe, vai de sua escolha e ele não influencia o work-flow para esse exemplo.
Estrutura de uma App

É bem simples, basta ter um arquivo config.xml que é a espinha do projeto e um arquivo html para você indexar.

Vamos fazer o seguinte, veja um exemplo pronto de um arquivo de configuração de um projeto para criá-lo do zero.

<?xml version="1.0" encoding="utf-8"?>
<widget xmlns="http://www.w3.org/ns/widgets"
        xmlns:rim="http://www.blackberry.com/ns/widgets"
        version="1.0.0"
        id="com.apps.suaApp">
 
  <name>To-do-List</name>
  <content src="index.html" />
  <author rim:copyright="2013"
            href="http://www.igorcosta.com/"
            email = "igorcosta@gmail.com">Igor Costa</author>
 
 
  <description>Simples App com lista de tarefas, crie listas e tarefas especificas</description>
 
  <license href="http://www.apache.org/licenses/LICENSE-2.0">
        Licença caso sua App se aplique
  </license>
 
  <access uri="*" subdomains="true" />
  <access subdomains="true" uri="file:///store/home" />
  <access subdomains="true" uri="file:///SDCard" />
  <access subdomains="true" uri="local:///" />
 
  <feature id="blackberry.app">
        <param name="orientation" value="portrait" />
  </feature>
  <feature id="blackberry.app.event"/>
  <feature id="blackberry.system.event" />
  <feature id="blackberry.connection" />
  <feature id="blackberry.ui.toast" />
  <feature id="blackberry.ui.dialog" />
  <feature id="blackberry.io" />
  <feature id="blackberry.io.file" required="true" version="1.0.0.0" />
  <feature id="blackberry.utils"   required="true" version="1.0.0.0" />
  <feature id="blackberry.io.dir"  required="true" version="1.0.0.0" />
 
    <rim:splash src="splash-portrait-768x1280.png" />
    <rim:splash src="splash-landscape-1280x768.png" />
 
 
    <!-- para mais permissões visite 
    http://developer.blackberry.com/html5/documentation/rim_permit_element.html
	-->
    <rim:permissions>
        <rim:permit>access_location_services</rim:permit>
        <rim:permit>use_camera</rim:permit>
        <rim:permit>access_shared</rim:permit>
    </rim:permissions>
 
 
 </widget>

Salve esse arquivo com o nome config.xml em um diretório seu local, por exemplo localhost/bbui_app_exemplo
No mesmo diretório crie um arquivo de html, index.html como eu configurei ali acima para chamar logo que iniciar o arquivo index.html na tag content.

O arquivo config é bem simples e quase auto explicatório o que cada tag faz. A única coisa que você precisa ter em mente é que se você quer acessar a internet em domínios diferentes, a tag access te ajuda nisso, lembre-se de incluir sub-dominios caso exista.

Quarto passo

Criar a app propriamente. Quando você instala o Ripple Emulator no Chrome você pode usar a biblioteca do webworks direto na sua App, sem precisar colocar no diretório, já que você vai utiliza-lo para empacotar a App, basta fazer uma referência ao script dessa forma aqui.

<script type="text/javascript" src="local:///chrome/webworks.js"></script>

A estrutura do App ficou dessa forma.
[pullquote align=”left”]bbuijs-estrutura[/pullquote]

Para economizar tempo, use esse template que eu fiz do arquivo index.html, assim fica mais fácil de você replicar em sua própria máquina.

<!doctype html>
<html>
	<head>
	  <meta charset="utf-8">
	  <meta name="format-detection" content="telephone=no" />
	  <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />  
	<title>Sua App</title>
 
	<!-- Estilos -->
	<link rel="stylesheet" type="text/css" href="css/style.css" />
	<link rel="stylesheet" type="text/css" href="css/bbui.css" />
 
	<!-- Javascript -->
	<script type="text/javascript" src="js/lib/zepto.js"></script>
	<script type="text/javascript" src="js/lib/bbui.js"></script>
    <script type="text/javascript" src="local:///chrome/webworks.js"></script>
 
	</head>
 
	<body>
		<div>
 
		</div>
	</body>
</html>

Ok, estamos prontos para começar a criar o App, mais que App? Para o própsito desse post vamos fazer um App simples, uma TO-DO-LIST, carregamos de um banco de dados local usando Web Storage, editamos e adicionamos itens.

Antes de criar qualquer coisa, deixa eu explicar mais ou menos como funciona o BBUIjs.
bbuijs-estrutura2

Ok, o BBUI.js além do nome ser sugestivo ele já possui um esquema de navegação no próprio toolkit, como você pode ver ai na imagem acima, basicamente o que você precisar em termos de interface básica o próprio toolkit oferece, porém se você quer algo mais complexo, sugiro utilizar outro, ele atende a uma única e exclusiva vantagem de ter a aparência nativa de um App. Quando você termina ela fica tão bem polida que parece muito com um App nativo.

O navegador entre telas funciona muito bem, e já possui efeitos de transição entre elas como Fade,Slide Left/Right,Slide Up/Down. Se você quiser um efeito de carta (flip) por exemplo, ai você vai ter que implementar. Como ele não atende exclusivamente apenas ao BB10, é bom notar algumas particularidades. Esses detalhes você pode conferir no Wiki do projeto.

Para inicializar o toolkit, você precisa escutar o evento webworksready, quando estiver pronto você pode iniciar com esses parametros.

            var webworksreadyFired = false;
            document.addEventListener('webworksready', function(e) {
 
                if (webworksreadyFired) return;
                webworksreadyFired = true;
                app.initialize(); //phonegap
 
                bb.init({actionBarDark: true,
                        controlsDark: true, // controles são pretos ou brancos
                        listsDark: false, // usar uma lista escura
                        bb10ForPlayBook: false, // é para playbook ou não
                        highlightColor: '#00A8DF', // cor de alguns elementos
                        onscreenready : function(element,id,params){
                            // use essa função para criar algum argumento
                        },
                        ondomready : function(element,id,params){
                          // depois que o DOM da tela estiver pronto você pode disparar algum evento antes 
                         // da tela ficar pronta.
                        }
 
                        });
 
                 // Depois de iniciado, chame a tela principal do App.
                 bb.pushScreen('Main.html', 'MainScreen');
            }, false);

Quinto passo

bbuijs-app-estrutura

Vou adotar essa estrutura de navegação do App. Simples e direta.

Como é que se cria as telas? Basicamente por esquema de atributo, o toolkit basicamente usa o atributo data-bb-* para definir as coisas.

Crie os .html conforme mostra a imagem e defina o HTML de cada um deles da seguinte forma.

<div data-bb-type="screen" data-bb-indicator="true" data-bb-back-caption="Voltar">
 
</div>

Para navegar entre as telas basta eu usar o método pushScreen(‘pagina.html’,’idPagina’); Por dentro do toolkit ele faz um popScreen() da tela atual e retorna com a tela anterior.

Então aqui vai o código atual das páginas que tenho.

index.html

<!doctype html>
<html>
	<head>
	  <meta charset="utf-8">
	  <meta name="format-detection" content="telephone=no" />
	  <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />  
	<title>Sua App</title>
 
	<!-- Estilos -->
	<link rel="stylesheet" type="text/css" href="css/style.css" />
	<link rel="stylesheet" type="text/css" href="css/bbui.css" />
 
	<!-- Javascript -->
	<script type="text/javascript" src="js/libs/zepto.js"></script>
	<script type="text/javascript" src="js/libs/bbui.js"></script>
    <script type="text/javascript" src="local:///chrome/webworks.js"></script>
    <script type="text/javascript">
            var webworksreadyFired = false;
            document.addEventListener('webworksready', function(e) {
 
                if (webworksreadyFired) return;
                webworksreadyFired = true;
                bb.init({actionBarDark: true,
                        controlsDark: true,
                        listsDark: false,
                        bb10ForPlayBook: false,
                        highlightColor: '#C82C29',
                        onscreenready : function(element,id,params){
                        },
                        ondomready : function(element,id,params){
                        }
 
                        });
                 bb.pushScreen('Main.html', 'MainScreen');
            }, false);
 
        </script>          
	</head>
 
	<body>
		<div>
 
		</div>
	</body>
</html>

Tela Main.html

<div data-bb-type="screen" data-bb-indicator="true">
	<div data-bb-type="title" data-bb-caption="Principal"></div>
 
	 <ul>
	     <li><a href="#" onClick="bb.pushScreen('Listas.html','ListaScreen')"  title=""> ir para Lista de Tarefas</a></li>
	     <li> <a href="#" onClick="bb.pushScreen('Sobre.html','SobreScreen')"  title=""> ir para Sobre Tela</a> </li>
	     <li><a href="#" onClick="bb.pushScreen('Tarefas.html','ListaScreen')"  title="">ir para Tarefa</a></li>
	 </ul> 
</div>

Telas Sobre.html

<div data-bb-type="screen" data-bb-indicator="true">
	<div data-bb-type="title" data-bb-caption="Tarefas" data-bb-back-caption="Voltar"></div>
</div>

Telas Listas e Tarefas possuem o mesmo código fonte

<div data-bb-type="screen" data-bb-indicator="true">
	<div data-bb-type="title" data-bb-caption="Tarefas" data-bb-back-caption="Voltar"></div>
 
	<div data-bb-type="image-list" data-bb-images="none" data-bb-style="arrowlist">
		 <div data-bb-type="header">Lista de tarefas para fazer</div>
		 <div data-bb-type="item"  data-bb-accent-text="Strength" data-bb-title="Item 01">Descrição</div>
	</div>
</div>

Ok, interface montada, veja o que você tem até agora.

Sexto passo

Já que você tem montado as telas, hora de começar a programa-las, para persistência de banco de dados eu fiz uma classe em Javascript de forma literal, igual como você faz na declaração de um objeto JSON.

/* Copyright 2013 Igor Costa
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this db except in compliance with the License.
 You may obtain a copy of the License at
 
   http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 
 
Simple class for WebStorage persistence, not big deal. Just for to-do-list demo
*/
var Db = {
	currentList:'',
	db : window.openDatabase('geba','0.1','Banco de dados simples para um App de to-do-list',2*1024*1024),
	listaSQL : 'CREATE TABLE IF NOT EXISTS listas (id  INTEGER,lista_nome TEXT NOT NULL, cor_lista TEXT NOT NULL DEFAULT "#f2f2f2",data_criada TEXT NOT NULL, PRIMARY KEY (id))',
	tarefasSQL: 'CREATE TABLE IF NOT EXISTS tarefas(id INTEGER,tarefa_nome  TEXT,completa  REAL,data_criada  TEXT,lista_id  INTEGER NOT NULL,PRIMARY KEY (id),FOREIGN KEY (lista_id) REFERENCES listas (id))',
	open : function () {
			try{
				if (window.openDatabase) {
					if(this.db != null){
						this.db.transaction(
				        function (transaction) {
				        	var demoData = new Date();
				        	// preload de 1 lista e 1 tarefa
				        	transaction.executeSql(Db.listaSQL, [], this.onSucess, this.errorHandler);
				        	transaction.executeSql(Db.tarefasSQL, [], this.onSucess, this.errorHandler);
				        	//To insert new records in the first demo, uncomment this than comment again.
				        	//transaction.executeSql('INSERT INTO listas (id,lista_nome,cor_lista,data_criada) VALUES (1,"Lista Demo","#aa0000","'+demoData+'")',[],Db.onSucess, Db.errorHandler);
				        	//transaction.executeSql('INSERT INTO tarefas (tarefa_nome,completa,data_criada,lista_id) VALUES ("Primeira tarefa demo","true","'+demoData+'",1)',[],Db.onSucess, Db.errorHandler);
				        	//transaction.executeSql('INSERT INTO tarefas (tarefa_nome,completa,data_criada,lista_id) VALUES ("Segunda tarefa demo","true","'+demoData+'",1)',[],Db.onSucess, Db.errorHandler);
				        	//transaction.executeSql('INSERT INTO tarefas (tarefa_nome,completa,data_criada,lista_id) VALUES ("Terceira tarefa demo","true","'+demoData+'",1)',[],Db.onSucess, Db.errorHandler);
				        });
				    }	
				}else{
					var msgSupport = "Doesn't support WebStorage";
					alert(msgSupport);
					console.log(msgSupport);
				}
 
			}catch(e){
					printMsg();
			}
	},
	close : function (){
 
	},
	executeSQL : function (sql){
 
	},
	editRecord : function (object){
 
	},
	deleteTarefa : function (id){
		try{
 
			this.db.transaction(
				        function (transaction) {
 
				        	transaction.executeSql('DELETE FROM tarefas WHERE id=?',[id], Db.onDeleteTarefaSucess, Db.errorHandler);
				        });	
 
 
		}catch(e){
			printMsg();
		}
	},
	onDeleteTarefaSucess : function (e,result){
			document.getElementById('myTarefasList').refresh();
			Db.getTarefas(Db.currentList);
			Db.toast('Tarefa Apagada');
 
	},
	addNewLista : function (name){
		try{
 
			this.db.transaction(
				        function (transaction) {
				        var demoData = new Date();	
				        	transaction.executeSql('INSERT INTO listas (lista_nome,cor_lista,data_criada) VALUES (?,"#aa0000","'+demoData+'")',[name],Db.onSucess, Db.errorHandler);
				        });	
 
			bb.pushScreen('Listas.html','ListaScreen');
		}catch(e){
			printMsg();
		}			
	},
	getListas : function (){
		try{
			this.db.transaction(
				        function (transaction) {
				        	transaction.executeSql('SELECT * from listas',[],Db.onListaSucess, Db.errorHandler);
				        });	
 
		}catch(e){
			printMsg();
		}
	},
	onListaSucess : function (e,result){
				var response = [];
				var len = result.rows.length, i;
				var render;
				  for (i = 0; i < len; i++) {
				   var item = result.rows.item(i);
 
				         render =  document.createElement('div');
					     render.setAttribute('data-bb-type','item');
					     render.setAttribute('data-bb-title',item.lista_nome);
					     render.setAttribute('data-itemid',item.id);
					     render.innerHTML = "Total de tarefas " +item.total;
					     render.setAttribute('data-bb-img','imgs/tag.png');
					     render.addEventListener('click',function(){
					     	bb.pushScreen('Tarefas.html','TarefasScreen',{listaid:$(this).data('itemid')});
					     });
					     response.push(render);
 
				  }document.getElementById('myLists').refresh(response);// voodoo!
	}
	,
	addNewTarefa : function (name){
		try{
 
			this.db.transaction(
				        function (transaction) {
				        var demoData = new Date();	
				        	transaction.executeSql('INSERT INTO tarefas (tarefa_nome,data_criada,lista_id) VALUES (?,"'+demoData+'",'+Db.currentList+')',[name],Db.onSucess, Db.errorHandler);
				        });	
 
			bb.pushScreen('Tarefas.html','TarefasScreen',{listaid:Db.currentList});
		}catch(e){
			printMsg();
		}			
	},
	getTarefas : function (id){
		try{
			Db.currentList = id;
			this.db.transaction(
				        function (transaction) {	
				        	transaction.executeSql('SELECT * FROM tarefas WHERE lista_id = ?',[id],Db.onTarefaSucess, Db.errorHandler);
				        });	
 
 
		}catch(e){
			printMsg();
		}
	},
	onTarefaSucess : function (e,result){
		var response = [];
				var len = result.rows.length, i;
				var render;
				  for (i = 0; i < len; i++) {
				   var item = result.rows.item(i);
				         render =  document.createElement('div');
					     render.setAttribute('data-bb-type','item');
					     render.setAttribute('data-bb-title',item.tarefa_nome);
					     render.setAttribute('data-itemId',item.id);
					     render.innerHTML = "Total de tarefas " +item.data_criada;
					     render.setAttribute('data-bb-img','imgs/bookmark.png');
					     $(render).swipeRight(function (){Db.deleteTarefa(item.id);});
					     response.push(render);
 
				  }document.getElementById('myTarefasList').refresh(response);// voodoo!
	},
	errorHandler: function (transaction, error){
		console.log(error);
		 	if (error.code==1){
		 	} else {
			    console.log('Oops.  Error was  '+error.message+' (Code '+error.code+')');
		 	}
		    return false;
	},
	onSucess: function(e,result){
	 return result;
	},
	printMsg : function (){
			var msg = "Can't open Database" + ' Error: ' + e.error;
			alert(msg);
			console.log(msg);
	},
	toast : function (msg, timeout){
 
		  if(typeof timeout == 'undefined') {
		    timeout = 1000;
		  }
 
		  // toast options
		  options = {
		    timeout: timeout
		  };
 
		  // display the toast message
		  toastId = blackberry.ui.toast.show(msg, options);
	}
}

Como cada função é bem específica para o exemplo,à medida que eu for implementando os métodos eu vou colocando na classe.

O banco de dados é hiper simples, veja a estrutura.

db-todolist

E aqui está o script para você executar.

CREATE TABLE IF NOT EXISTS "listas" (
"id"  INTEGER,
"lista_nome"  TEXT NOT NULL,
"data_criada"  TEXT NOT NULL,
PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "tarefas" (
"id"  INTEGER,
"tarefa_nome"  TEXT,
"cor_tarefa" TEXT,
"completa"  REAL,
"data_criada"  TEXT,
"lista_id"  INTEGER,
PRIMARY KEY ("id"),
CONSTRAINT "lista_id_fk" FOREIGN KEY ("lista_id") REFERENCES "listas" ("id") ON DELETE RESTRICT ON UPDATE RESTRICT
);

Em fim, até ai já fizemos a persistência, falta agora só navegar, e para simplificar, eu criei uma classezinha em literal para fazer o trabalho.

var navigatorController =  {
	openMain : function (){
		 var webworksreadyFired = false;
            document.addEventListener('webworksready', function(e) { 
               if (webworksreadyFired) return;
                webworksreadyFired = true;
                bb.init({actionBarDark: true,
                        controlsDark: false,
                        listsDark: false,
                        bb10ForPlayBook: false,
                        highlightColor: '#C82C29',
                        onscreenready : function(element,id,params){
                        	if(id == "ListaScreen")
                        	{
                        		Db.getListas();
                        	}
                        	if(id == "TarefasScreen")
                        	{
                        		 Db.getTarefas(params.listaid);
                        	}
                        	if(id == 'NewTarefaScreen')
                        	{
 
                        	}
                        },
                        ondomready : function(element,id,params){
 
                        }
 
                        });
                bb.pushScreen('Main.html', 'MainScreen');
				Db.open();
            }, false);
	},
	openSobre : function (){
		bb.pushScreen('Sobre.html','SobreScreen');
	},
	openConfig : function (){
		bb.pushScreen('Config.html','ConfigScreen');
	},
	openListas : function (){
		bb.pushScreen('Listas.html','ListaScreen');
	},
	newLista : function (){
 
			bb.pushScreen('NovaLista.html','NewListScreen');
	},
	saveNewLista : function (){
			Db.addNewLista($('#listaInputName').val());
 
	},
	newTarefa : function (){
			bb.pushScreen('NovaTarefa.html','NewTarefaScreen',{mode:true});
	},
	saveNewTarefa : function (){
			Db.addNewTarefa($('#tarefaInputName').val());
	}
}

Pontos negativos

Coloque em prática seus conhecimentos em Javascript e CSS3, por que muita coisa você vai precisar criar do zero, alguns componentes como o datePicker por exemplo ou um Numeric Stepper, algo como um Slider horizontal de imagens, Item renders para itens da lista, nada que não seja possível, vai dar só mais um pouco de trabalho e não fica com aquela coisa de out-of-the-box (tudo pronto).

O próprio toolkit tem muito código da própria comunidade como por exemplo o iScroll que foi utilizado para navegar entre as telas e nas listas. O legal é você poder hacker o código.

A performance não é 100%, eu testei a geração de efeitos com o context do Canvas e realmente leva-se de 2 a 3 segundos para aplicar um efeito a uma imagem, coisa que em um desktop no Chrome por exemplo é instantaneo, provavelmente seja a versão do webkit que esteja sendo utilziada, acredito que isso só o tempo pode dizer se eles vão melhorar esse item específico ou não.

Condeirações finais

É um pouco trabalhoso usar o BBUIJS, mais o acabamento final do projeto, é fantástico, por que você tem a exata idéia de estar criando um App com aparência nativa do sistema OS e isso meu amigo leitor, é fantástico. Porém nada lhe impede de usar outros toolkits de inicalização.

Código fonte disponível no Github

O código fonte dessa App você pode fazer o Fork no Github, a licença do projeto é via licença Apache.

https://github.com/igorcosta/bbuijs-todolist-demo

Dicas/ HTML5 / CSS3/ ios/ iPad/ Mobile/ Pessoal

Produtividade no desenvolvimento de Apps

mobile_device_image3

Exatos 20 dias que estou trabalhando incansavelmente em jogos para celulares, e acredito que daqui alguns dias terei um primeiro release. O importante é notar a produtividade de desenvolvimento de um App para dispositivos móveis, já que existe farta oferta de players, dificil é ficar fora de todas as opções.
Produtividade no desenvolvimento

Tem IDE que faz isso fácil, por exemplo quem usa o Flash, Flex, Actionscript pode notar que você faz tudo em uma rápida interface gráfica, adotando algumas caracteristicas que seu app irá pedir ao aparelho e até mesmo assinar para distribuir no dispositivo.

Eu não gosto muito dessa produtividade assim tão dada, já que é fácil perceber alguns outputs que o console dá quando está gerando algo para você, eu gosto mesmo é de ver a telinha do console do DOS rodando o comando que digitei, por que eu tenho controle daquilo que eu fiz.

Porém, cada qual com seu gosto, dos males os piores, eu fiz um teste rápido como é o processo de empacotar e testar o App no aparelho usando o PhoneGap(Apache Cordova).

As plataformas que eu testei foram essas: iOS (iphone/ipad retina), Android, Blackberry Dev Alpha, Windows Phone.

Para iPhone/iPad eu gasto cada vez que eu vou testar no dispositivo uma média de 3 até 4 minutos para teste em cada sprint que eu faço.

Para Android eu gasto entre 30 segundos e 1 min e meio para testar no dispositivo, sempre a cada sprint que eu faço.

Para Blackberry eu gasto exatos e travados 8 e 10 segundos para isso, novamente a cada sprint separado que eu faço para plataforma.

Para o Windows Phone eu gasto entre 12 e 15 segundos para fazer o empacotamento e teste no dispositivo.

Ok, eu vi que eu gastava um bom tempo em cada sprint que eu fazia que são de 1 em 1 hora, ou seja Eu perco uma média de ~40 minutos empacotando e testando no dispositivo. Precisava rápido mudar isso e abandonar alguns hábitos e automatizar tudo.

 

Um só código

Ao contrário do que você pensa, o Phonegap não garante um só código para N plataformas, o que ele garante é uma só API para N plataformas, é ai que eu vejo quão redondo eu estava nessa hora, veja bem, cada plataforma possui suas regras, desde acesso a dominios estritos, acesso a recursos e arquivos cada um trabalha de uma maneira diferente para um propósito similar.

[pullquote align=”left”]Como assim de maneira diferente para um propósito similar?[/pullquote]

É que quando você está trabalhando com arquivos locais por exemplo, o iOS tem sua própria caixinha de areia, então requer que o Phonegap gere um arquivo cordova-ios.js e  o Blackberry possui também sua própria caixinha de areia e também gera um arquivo cordova-bb.js específico para a plataforma. Viu só? É por esse motivo que o Phonegap garante uma só API, só que se você é organizado, você não quer que o arquivo cordova-bb.js caia no pacote .app para o iPhone ou vice-versa.

Embora existam técnicas em javascript para testar qual plataforma está rodando e incluir no código html a biblioteca específica, você ainda está travado com o problema e precisa resolver isso. Como? ANT Scripts.
ANT Scripts

Criei um script em ANT para melhorar esse empacotamento e gerar um deploy para todas as 3 plataformas separando os arquivos correspondentes do cordova para cada um. Eu garanto que publico ele aqui em um futuro próximo.
IDE que estou utilizando para desenvolver

SublimeText. Como são jogos em HTML e usam bastante CSS3, Javascript, eu inseri uma série de snippets para melhorar a produtividade ainda mais e é dificil largálo, já que é simples de usar.

Versionamento

É de prache e necessário, eu uso o Git junto com minha conta no Dropbox, eu até já expliquei aqui no site como usar ambos. E para cada release que eu faço para testar nos dispositivos eu também guardo uma cópia separada do arquivo.

Para mim essa foi a melhor configuração que achei para minhas necessidades e eu ainda não reduzir muito o tempo perdido para testar, ainda gasto uma média de 10 minutos para esperar o ANT gerar todos eles, embora eu aproveite esse tempo para tomar um café, que por sinal está me esperando ali. Deixa eu ir!

 

Conte-me como está sendo sua experiência.

HTML 5/ HTML5 / CSS3/ ios/ iPad/ Mobile/ Tablets

5 segredos para optimizar seu CSS para dispositivos móveis

css3-markup

Eu estou começando a ter uma relação amigável com o CSS à medida que eu vou me convencendo que sem ele é impossível ter algo plausível de uso em dispositivos móveis.

A grosso modo você olha para o CSS e pensa, há é molezinha, mel na chupeta, pegar um framework pronto e dizer, Olha o que eu fiz! É absolutamente relutante quando se diz à respeito DIY.

Eu começo pelo fato que tem tanta opção de frameworks em javascript, frameworks responsivos que você friamente pode não re-inventar a roda, mais no fundo você dificilmente vai entender por completo se você não estudar o que eles fazem.

Pois bem, juntei abaixo 10 segredos que eu achei no CSS que são bem específicos para dispositivos móveis, seja para adaptar ao phonegap ou ao firefox OS, você pode usar essas técnicas e não passar pelos problemas que eu passei.

Claro que nem todos os segredos pode ser assim um SEGREDOOO para alguns leitores, o importante é manter a descrição.

 

1- Limpando a bancada (Reset)

Antes de começar qualquer coisa em HTML, você precisa se normalizar, é igual a mãe mandando você arrumar o quarto, ou o mecânico chefe pedindo para você limpar a bagunça no final do expediente.
Trabalhar com CSS para N plataformas requer esse sacrifício, existe o normalizecss que é um pouco mais amplo, envolvendo desktop.

Eu prefiro a seguinte receita, limpe tudo antes de começar e me permita fazer as coisas do meu jeito. E aqui é a minha solução.

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, 
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
	margin: 0;
	padding: 0;
	border: 0;
	font-size: 100%;
	font: inherit;
	vertical-align: baseline;
        overflow: hidden;
}

Quando você olha para cima, vê um tanto de tag sendo atribuído margens, espaçamentos, tamanho de fontes, alinhamentos, tudo isso faz parte, o mais importante de todos ali é a respeito do font-size, deixando a fonte sempre em um tamanho plausível para ajustes no que se diz respeito a telas de baixa, media e alta densidades.

2 – Media Queries

A galera menciona isso como design responsivo, tem o flexbox que está vindo. Eu chamo media queries particularmente de divisor de águas; É o santo graal para criar designs que respondem ao tamanho da tela e assim deixar qualquer usuário feliz.
Veja um exemplo típico abaixo:

Só que media queries possue mais e mais opções do que um simples layout fluido, o principal papel não é só detectar tamanhos mais densidades em telas, como em terra de mobilidade isso é um assunto amplamente diferente, fica difícil adaptar só ao tamanho da tela do usuário, existe muito terreno para ser explorado, o exemplo acima, o código fica assim:

<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="user-scalable=no, initial-scale=0.75, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height"/>  
<title>Ola Mundo</title>
<style type="text/css">
 
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, 
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
	margin: 0;
	padding: 0;
	border: 0;
	font-size: 100%;
	font: inherit;
	vertical-align: baseline;
	overflow: hidden;
}
h2, h3, h4, p {
	color: #fff;
	font-family: sans-serif;
}
#container {
	width: 100%;
	display: inline-block;
}
.caixa {
	width:16.5%;
	height: 100px;
	text-align: center;
	float:left;
	display: block;
	overflow: hidden;
	background-color: #0F9ED8;
	padding-left:20px;
	padding-right:20px;
	margin:5px;
}
/* para dispositivos de baixa densidade */
@media screen and (max-width : 320px) and (max-height: 480px){
body {
	transform:none;
	transform:translate3d(0,0,0);
}
.caixa {
	width:100%;
	background-color: #07508A;
	margin:0 auto;
	margin-bottom: 5px;
}
h2, h3, h4, p { 
	font-size: 22px;
}
}
/* para dispositivos de média densidade */
@media screen and (min-width:480px ) and (min-height: 800px){
body {
	/* brincando um pouco de 3D */
		transform:translate3d(0,0,0);
		transform-style: preserve-3d;
		transform-origin:30% 100%;
		transform-position: top right;
		transform: perspective(700px) rotateX(30deg) rotateY(30deg) rotateZ(-5deg);
}
h2,h3,h4,p {
	color:#000;
	font-size: 36px;
}
.caixa {
	width: 100%;
	margin:0 auto;
	margin-bottom: 5px;
}
}
</style>
</head>
 
<body>
		<div id="container">
				<div class="caixa">
					<h2>Caixa 1</h2>
				</div>
				<div class="caixa">
					<h2>Caixa 2</h2>
				</div>
				<div class="caixa">
					<h2>Caixa 3</h2>
				</div>
				<div class="caixa">
					<h2>Caixa 4</h2>
				</div>
		</div>
</body>
</html>

3 – Tenha cuidado ao usar animações em puro CSS3

É bacana e tal, vale a pena, mas sempre tente colocar a animação para ser executada na GPU do aparelho, caso contrário você vai arranjar uma dúzia de usuários insatisfeitos com sua App por que consome muita bateria e processamento.

Embora a relação da animação seja singular com o HTML, ela requerer um tanto de atenção e para isso eu sugiro usar o [highlight bg=”#f91018″ color=”#000000″]translate3d:(0,0,0)[/highlight]. e forçar sua App a mostrar a animação na GPU, seja de um simples scroll de lista até efeitos de transição.

Por exemplo, digamos que você quer mostrar um alerta animado em sua App, da seguinte forma.

O Código para esse exemplo é esse aqui:

<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="user-scalable=no, initial-scale=0.75, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height"/>  
<title>CSS3 Animação</title>
<style type="text/css">
 
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, 
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
	margin: 0;
	padding: 0;
	border: 0;
	font-size: 100%;
	font: inherit;
	vertical-align: baseline;
	overflow: hidden;
 
}
/* para dispositivos de média densidade */
@media screen and (min-width:480px ) and (max-width: 800px){
 
.alerta{
	position: absolute;
	z-index: 999;
	min-width: 100%;
	min-height: 100%;
	background:rgba(0,0,0,0.8);
}
@keyframes animar-alerta {
    from {
        top:-100px;
    }
    to {
       top:30%;
    }
}
#painel {
	display: block;
	position: absolute;
	margin:auto;
	top:30%;
	left:20%;
	width:60%;
	height: 200px;
	background: rgba(255,255,255,1);
	color: #100;
	animation: animar-alerta 0.8s ease-out;
	transform:translate3d(0,0,0);
	font-family:fantasy;
}
#painel span {
	display: block;
	width:100%;
	padding:5px;
	background-image: -moz-linear-gradient(top, rgba(122,188,255,1) 0%, rgba(96,171,248,1) 44%, rgba(64,150,238,1) 100%);
	color:#000;
}
#painel p {
	text-align: center;
	width: 100%;
	padding-top: 30px;
	margin:auto;
}
.button {
	padding:15px;
	background-image: -moz-linear-gradient(top, rgba(206,220,231,1) 0%, rgba(89,106,114,1) 100%);
	width: 200px;
	height: 44px;
	display: block;
	margin:auto;
	position: absolute;
	top:50%;
	left:20%;
}
 
}
</style>
</head>
 
<body>
	<div class="alerta">
		<div id="painel">
			<span>Confirma?</span>
 
			<p>Tem certeza que deseja excluir item?</p>
			<p><button id="btn_OK">OK</button></p>
		</div>
	</div>
	<a href="#" class="button">Chamar alerta?</a>
</body>
</html>

4 – Criando objetos para telas Retina

Se você cria Apps, você tem que criar primeiro para tela de Retina, sabe aqueles jogos que você ver “HD”, significa que ali os gráficos possuem 264PPI ou seja serrilhados só nos xing-lings.

Puramente em CSS você não tem problema algum, contanto que você tome alguns cuidados quando for colocar imagens, já que esse é o gargalo. Novamente, use o media-queries para especificar uma imagem bem nítida para esses dispositivos.

#image { background: url(image.png); }
 
@media only screen and (-webkit-min-device-pixel-ratio: 2),
       only screen and (-moz-min-device-pixel-ratio: 2),
       only screen and (-o-min-device-pixel-ratio: 2/1),
       only screen and (min-device-pixel-ratio: 2) {
           #image { background: url(image@2x.png); background-size: 50%; }
}

5 – Inputs com estilo

Segundo o guia de interfaces da Apple, qualquer input para o usuário requer que tenha no mínimo 44px. Eu costumo dizer que a Apple pensou em mim também quando escreveu esse guia, já que eu tenho dedo de urso e não quero perder tempo tentando advinhar o que escrevi em um campo ou brincando de gato e rato, tentando colocar o foco no campo.

É engraçado que você pode usar seletores específicos para cada campo do formulário e assim aplicar determinadas ações em campos diferentes, igual como o Twitter bootstrap faz para determinados campos em um formulário.

Os seletores são esses:

input[type=text],
input[type=url],
input[type=tel],
input[type=number],
input[type=color],
input[type=email], {
   /* aplicar estilos aqui... */
}

Veja abaixo um rápido exemplo que eu fiz usando o emulador do Android para simular um formulário com o estilo e sem estilo.

O código com estilo que é o segundo exemplo mostrado, pode ser visto aqui abaixo.

 
<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="user-scalable=no,maximum-scale=1, minimum-scale=1"/>  
<title>CSS3 Animação</title>
<style type="text/css">
 
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, 
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
	margin: 0;
	padding: 0;
	border: 0;
	font-size: 100%;
	font: inherit;
	vertical-align: baseline;
	overflow: hidden;
 
}
input[type="text"]{
	min-height: 44px;
	border:none;
}
input[type=checkbox],
 input[type=radio] {
   display: inline-block;
   font-size: 15px;
   line-height: 1em;
   margin: 0 0.25em 0 0;
   padding: 0;
   width: 1.25em;
   height: 1.25em;
   -webkit-border-radius: 0.25em;
   vertical-align: text-top;
 }
select{
	width: 100%;
}
 input[type=radio] {
    -webkit-border-radius: 2em; /* Make radios round */
  }
 form {
 	font-family: fantasy;
 	display: block;
 	font-size: 22px;
 	margin:10px;
 }
 div, label {
 	width: 100%;
 	display: block;
 	padding-top: 10px;
 }
 textarea {
 	width: 98%;
 }
</style>
</head>
 
<body>
	<form action="#" method="post">
		<div>
			<label for="name">Digite seu nome:</label>
			<input type="text" name="name" id="name" value="" tabindex="1" />
		</div>
 
		<div>
			<h4>Qual seu sexo?</h4>
			<div>
			<label for="radio-choice-1">Masculino</label>
			<input type="radio" name="radio-choice-1" id="radio-choice-1" tabindex="2" value="choice-1" />
 
			<label for="radio-choice-2">Feminino</label>
			<input type="radio" name="radio-choice-2" id="radio-choice-2" tabindex="3" value="choice-2" />
			</div>
		</div>
 
		<div>
			<label for="select-choice">Cidade onde mora:</label>
			<select name="select-choice" id="select-choice">
				<option value="Choice 1">Petrolina</option>
				<option value="Choice 2">Recife</option>
				<option value="Choice 3">São Paulo</option>
			</select>
		</div>
 
		<div>
			<label for="textarea">Comentarios:</label>
			<textarea cols="13" rows="8" name="textarea" id="textarea"></textarea>
		</div>
 
		<div>
			<label for="checkbox">Concorda com os termos</label>
			<input type="checkbox" name="checkbox" id="checkbox" />
		</div>
 
		<div>
			<input type="submit" value="Submit" />
		</div>
	</form>
</body>
</html>

5 – Minimizar o arquivo

Seja HTML ou CSS, o navegador sempre vai interpretar seus arquivos, diferente do compilado, ele quando interpretado demora alguns mili segundos ou até segundos para fazer efeito e ser visto, para evitar maior tempo para rendenizar, sempre minimize o arquivo, que é o processo de remover todos os espaços em branco necessário para diminuir o arquivo em tamanho.

O site Minify CSS, tem um motor para isso, se você quiser pode usar o Yahoo Compressor.

HTML 5/ HTML5 / CSS3

HTML5 e SSE

sse

Se você nunca fez um loop ou um timer para sempre consultar uma determinada URL e atualizar informações, então você nunca passou por um processo de criação de software.

Atualmente temos vários protocolos de transferência de dados entre o lado cliente e servidor. Hoje como é mais conhecido como “Nas nuvens”, o sujeito faz requisições a um determinado serviço e a resposta é enviada “polling”, você requisita e espera que o callback da sua requisição traga a mensagem de volta.

Tanto no Flash Player quanto no AIR, nós tinhamos diversas opções de protocolos de transferência de dados: RTMP,RTMPF, Sockets, XMLSockets, HTTPRequest.

Assim, poderimos fazer long-polling, real time data transfer (RTDF) e deixariamos muitos usuários contentes.

O problema é que se você quer fazer algo totalmente ao vivo, a coisa complica, esbarramos em altos preços de softwares, diversas modificações nos canais de comunicação e em fim conseguir a tal adaptação do model. Ok até ai você faz isso de letra, já que não é tão trabalhoso quanto parece.

É ai aonde entra o SSE ( Server Sent Events ) “eventos enviados pelo servidor”, trata-se de uma nova especificação publicada no W3C por um Ian Hickson engenheiro do Google onde torna as coisas mais simples.

Long polling, short polling, todos querendo ou não sempre vão ficar dependentes das requisições do cliente em um canel multiplo, o SSE já atua um pouco diferente dessa forma. A idéia é trazer para o cliente a resposta mesmo sem sua requisição ser feita.
Pera ai?! Eu estou no ano certo? Em fim, isso já existia à um certo tempo, a galera do Java que o diga, mais agora é legal saber que você com puro javascript pode fazer isso sem ter que re-inventar a roda.

[browser_suporte ie=”no”] [pullquote align=”left”]Na plataforma Mobile Funciona no Firefox OS, iOS e Blackberry 7/10[/pullquote]

O SSE apesar de novo já dá suporte a várias plataformas, afinal quem não quer algo simples como esse. Todos querem dados via stream para seus clientes de maneira fácil.

 

Exemplo prático.

sse

Ok, já que eu não fujo a regra, eu escrevi um exemplo prático com gráficos, os dados vem em forma de json, e no meu lado cliente html5 eu faço o parse deles e gero um gráfico bonitão, igual esse abaixo.

 

Código do PHP stream:

<?php
//header('Content-Type: text/event-stream; charset=utf-8'); // importante ter
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache'); // só pra prevenir cache
 
 
$dados = array(
		array('empresa' => 'GFSA3','valor' => mt_rand (1.7*10, 40*10) / 10),
		array('empresa' => 'PETR4','valor' => mt_rand (4.3*10, 30*10) / 10)
	);
echo "data: " . json_encode($dados) . "\n\n";
flush();
?>

 

Código do lado cliente:

<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<meta charset="utf-8">
<title>Exemplo de HTML 5 com Server Sent Events</title>
<style type="text/css">
 
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, 
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
	margin: 0;
	padding: 0;
	border: 0;
	font-size: 100%;
	font: inherit;
	vertical-align: baseline;
}
#graph {
	text-align: center;
	margin:0 auto;
}
</style>
<script type="text/javascript">
 
var chart;
 
 
 
 	$(document).ready(function() {
 
 	chart = new Highcharts.Chart({
      chart: {
        renderTo: 'graph',
        type: 'areaspline',
        marginRight: 10,
        marginBottom: 50,
        zoomType:'x'
      },
      title: {
        text: 'Portifolio de Ações'
      },
      xAxis: {
                type: 'datetime',
                tickPixelInterval: 150,
        },
 
      yAxis: {
        labels: { 
          enabled: true 
        },
        gridLineWidth: 1,
        title: {
          text: 'Valor',
        },
        plotLines: [{
                    value: 0,
                    width: 1,
                    color: '#808080'
                }]
      },
      series: [
      {
        name: 'Gafisa ON',
        data: [4.3,4.0,19.3]
      }, 
      {
        name: 'Petrobras ON',
        data: [4,18,3]
      }],
      plotOptions: {
                area: {
                    fillColor: {
                        linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1},
                        stops: [
                            [0, Highcharts.getOptions().colors[0]],
                            [1, 'rgba(2,0,0,0)']
                        ]
                    },
                    lineWidth: 1,
                    marker: {
                        enabled: false,
                        states: {
                            hover: {
                                enabled: true,
                                radius: 5
                            }
                        }
                    },
                    shadow: false,
                    states: {
                        hover: {
                            lineWidth: 1
                        }
                    },
                    threshold: null
                }
            }	
    });
 
 
	  if(typeof(EventSource)!=="undefined")
	  {
		var source=new EventSource("cotacoes_stream.php");
		source.onmessage = function(e){
      		var result = $.parseJSON(e.data);
      			chart.series[0].addPoint([(new Date()).getTime(),Number(result[0].valor)], true, true);
      			chart.series[1].addPoint([(new Date()).getTime(),Number(result[1].valor)], true, true);
		}
	 	}else{
	 		alert('Server Sent Events não é suportado pelo seu navegador');
	 	}
 	});
 
</script>
</head>
 
<body>
	<div id="graph"></div>
 
</body>
</html>

Existem várias outras opções mais completas, como o próprio webSocket, só que devido a sua falta de suporte em determinadas plataformas, fica inviável usa-lo.

Porém a grande vantagem do SSE é praticamente coisas simples que vão te tirar do sufoco quando você precisar fazer algo para prototipação com dados reais.

Esse é um exemplo de um material de curso que estou preparando. Aconselho também você dá uma estudada na especificação e verificar as outras configurações que o SSE possui.

HTML 5/ HTML5 / CSS3/ JQuery

Criando apresentações de slides fantásticas com HTML5/CSS3/Javascript

igor

Terminando de aprontar os slides das minhas palestras no Campus Party 6 de São Paulo e eu queria usar uma forma diferente de apresentar, eu coleciono vários ppts de outras palestras, publiquei algumas no slideShare. Mais minha natureza inquieta pede que eu siga em frente e mude para outras alternativas mais abertas.

Pesquisando na internet logo vi que o HTML, CSS3 e Javascript tem muito à oferecer para quem é curioso, e descobri dentre várias opções essas 3 bibliotecas que vai lhe surpreender com o resultado final.

O grande inspirador foi o http://slides.html5rocks.com, e ai nasceram essas ótimas alternativas

RevealJS

Esssa foi minha escolha para as apresentações de slide no Campus Party 6, o RevealJS é feito apenas com HTML, CSS e Javascript, a grande diferença é que se utiliza novos artificios do CSS na versão 3 para animar em 3D e um pouco da ajuda do Javascript, o HTML é nato.

Um ponto positivo do RevealJS para quem é desenvolvedor, é que quando você está em uma apresentação e quer mostrar trechos de códigos, um ppt é um saco nesse quesito, o RevealJS tem uma dependência chamada highlight.js que ajuda nesse quesito.

E não se limite apenas à isso, quando você imprime a apresentação ela vem em um elegante formato PDF(apenas no Google Chrome) com os slides no mesmo formato que você ver, alinhados verticalmente.

ImpressJS

Quem lembra do Prezi? A idéia é justamente essa, só que não feito em Flash/as3, mas em puro CSS3 com efeitos de 3D e com a ajuda do Javascript.

DeckJS

É um pouco mais tímido em relação a efeitos hollywoodianos, mas atende o propósito de rodar em vários navegadores, inclusive no IE, coisa que o ImpressJS e o RevealJS não fazem, já que depende de efeitos 3D e no IE9 para baixo isso é complicado. Embora eles tenham um fallback, perde toda a graça sem os efeitos.

 

Dê adeus ao velho formato ppt em suas apresentações, de hoje em diante comece a usar html. E quem tiver notícias de outras bibliotecas como essas, só deixar o link nos comentários.

frameworks/ HTML 5/ HTML5 / CSS3/ JQuery/ Mobile/ Phonegap/ Web 2.0

Os mitos do desenvolvimento Front-end com HTML, CSS e Javascript

openweb

Eu já havia comentado em outro post aqui no blog sobre os 10 mitos de que tecnologias web não eram tão boas para fazer sistemas. Hoje me deparei com os slides de uma apresentação do Zeno Rocha que traduz tudo aquilo que eu escrevi em texto em imagens e razões para você acreditar que a semântica web está forte e cheia de recursos, navegue pelos slides e veja a variedade de possibilidades.

Sem mencionar que nos slides faltou só as soluções da empresa Sencha e o SDK da Blackberry para dispositivos móveis o Webworks.

Os sdks e bibliotecas para desenvolvimento Web amadureceram tão rápido em pouco mais de 2 anos que eu nem imagino o que estará por vir daqui a 2 anos à frente.

HTML 5/ ios/ JQuery/ JQuery Mobile/ Phonegap

Phonegap só resolve 50% do problema, o resto é contigo

cordova_logo_problem

Ok, eu já falei das vantagens e desvantagens do phonegap em comparação ao Adobe AIR, e eu acho que faltou um post mais completo sobre a maior dificuldade que se tem em criar uma solução mobile completa fora da caixa de areia do SDK nativo.

Phonegap resolve parte do problema em suportar Apps escritos em HTML5/JS/CSS, depois que você cria seu primeiro Hello World, você se pergunta, “Ok, e agora o que eu faço?”.

É ai, onde seus problemas começam, definir uma interface fluída, que funcione em diversos dispositivos é tarefa ultra complicada, já que temos dispositivos de diferentes densidades.

Resolver esses problemas sem o SDK nativo é um baita desafio, em especial quando se tem em mente que grande parte de Apps móveis, são customizáveis, já que você vai enfrentar isso, é bom se preparar, eis algumas ferramentas e frameworks para JS que ajudam a encurtar esse caminho.
Se você é do tipo que gosta de frameworks, com tudo prontinho, essa é a lista que eu sugiro para você.

Sencha Touch – O melhor entre todos

JQuery Mobile – Funciona para apps nativas também, só que não é tão bom para diferentes telas.

LungoJS – Biblioteca bem interessante e completa que imita um pouco do jQuery Mobile, mas trabalha com a idéia de componentização.

KendoUI – Igual ao JQueryMobile, poucos componentes.

Agora se você está em um nível que prefere fazer tudo do zero já que sua App demanda isso, devido seu nível de complicação e elementos gráficos, então aqui vai algumas dicas bem interessantes.

Zepto.js – Biblioteca igual ao jQuery, só que mais leve e compatível com o Phonegap, muito rápida por sinal na hora do loop para montagem de listas.

Foundation da ZURB – Eu uso os media-queries do framework Foundation em meu CSS próprio, já que eu não quero gastar tempo calculando o tamanho exato da fluidez das coisas, eles são os melhores e coloca o Twitter bootstrap no bolso nessa questão.

Abuse e reuse bastante de Media queries, aqui vai uma maneira de como eu resolvi meus problemas nessa questão.

 

Separando cada CSS com suas particularidades e aplicando o estilo quando for necessário em cada tela. Dica Javascript Desabilite o Touch Move no body do documento, ou você vai ter problemas de redimensionamento com o conteúdo sambando para um lado e para outro, caso o usuário toque e arraste na tela.

 document.addEventListener('touchmove', function(e) { e.preventDefault(); }, false);

Precisa controlar melhor os gestos que são usados em sua aplicação? Você pode usar os padrões ‘touchmove’,’touchTap’,’swipeLeft’,’swipeRight’, mas você está acostumado com a sintaxe do JQuery ou ZeptoJS, por que não continuar com isso?

O HammerJS é uma biblioteca para isso, muito útil nas dificuldades de implementar gestos em containers que possuem gestos internos já existentes.

O que o phonegap se propoe é lhe ajudar a acessar recursos nativos do aparelho, e isso ele faz com louvor. O que ele não faz é te dar de mão beijada a interface ou componentes que serão usados para criar uma app móvel. É nessa parte que você se pergunta o por que.

Então, da próxima vez que você for criar Apps móveis com tecnologia HTML, leia esse post ou indique à um amigo. Vai salvar alguns dias em seu calendário apertado de entregas.

 

Android/ Blackberry/ Flex/ Flex Mobile Framework/ HTML 5/ HTML5 / CSS3/ JQuery/ JQuery Mobile

Criando uma lista de scroll infinito para seu próximo projeto Mobile

scroll_inifinito_1

Coisa boa na vida é ter desafios, puxa daqui e puxa dali, e acabamos com uma idéia nova em um novo projeto não é verdade? Outra coisa melhor é você ter alunos onde você dá suporte o tempo todo, é o que acabou motivando a criação desse artigo.

Independente de qual é seu próximo projeto, você vai acabar querendo implementar isso uma vez na vida no seu próximo projeto mobile.

Sorte sua se seu próximo projeto for escrito em Flex Mobile ou JQuery Mobile.

Como funciona o Scroll Inifinito?

Independente do tamanho da tela de seu dispositivo, você pode calcular a altura da tela disponível no aparelho vs. altura que o conteúdo produziu para ser disponibilizado, sendo que você pode pegar a posição atual da barra de rolagem “scroller” quando ela atingir a altura  total do conteúdo ou seja, quando ela não tiver mais para onde deslizar até o final.

É ai onde entra o scroll inifinito, você pegando essas variáveis e fazendo o calculo direito, você pode fazer um Append ou incrementar os elementos de uma lista existente, seja Array, Lista, Datalist não importa, você pode implementar isso de maneira fácil usando o seguinte calculo.

 

posicaoAtualDoScroll == ( tamanhoAtualDaLista – tamanhoDisponivelNaTela)

 

Assim, você pode ir incrementando a lista se a barra de rolagem atinge o tamanho máximo disponível na tela e com isso vai paginando os resultados, tanto localmente quanto via chamada de processo remoto RESTful,RTMP,Ajax.

 

Versão Flex Mobile

Para fazer no Flex Mobile, você precisa conhecer bem a arquitetura Spark, uma vez que é desacoplada, a interface do componente, você pode começar investigando o Skin atual do componente <s:List/> até chegar duas propriedades importante do Skin que fazem menção ao componente em sí, dataGroup e scroller , ambos são peça fundamental para começar a implementar tal tarefa.

O dataGroup é onde os dados são guardados na lista e scroller é o componente que faz a barra de rolagem, é com ele que observamos a mudança acontecendo da posicaoAtualDoScroll e assim fazer o uso da formula  de condição e carregar mais itens.
Já que eu observo a mudança de posição do Scroller, eu tenho que chegar até o componente e escutar via EventListener qual é a propriedade que está sendo modificada, para isso o framework do Flex tem o evento PropertyChangeEvent, ele me dá o novo valor que a propriedade sofreu.

As propriedades em questão do scroller que eu quero saber são “verticalScrollPosition” para posição vertical e “horizontalScrollPosition” para posição horizontal.

Então sabendo qual é as propriedades que eu tenho que olhar eu procuro alinhar a formula usando o novo valor menos o tamanho atual da lista sobre o tamanho da tela.

Acabei criando o seguinte código:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009" 
		xmlns:s="library://ns.adobe.com/flex/spark" title="HomeView">
	<fx:Declarations>
		<!-- Place non-visual elements (e.g., services, value objects) here -->
	</fx:Declarations>
	<fx:Script>
		<![CDATA[
			import mx.collections.ArrayList;
			import mx.events.FlexEvent;
			import mx.events.PropertyChangeEvent;
 
 
			[Bindable]private var dados:ArrayList = new ArrayList();
			private var proxima_pagina:int = 20; // numero de itens para carregar
 
			protected function loadItens(event:FlexEvent):void
			{
				for (var i:int = 0; i <19; i++) 
				{
					var item:Object = new Object();
					item.label = "Item "+i;
					dados.addItem(item);
				}
				lista.scroller.viewport.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE,getPosicao);
 
			}
 
			private var posicao_anterior:int;
 
			protected function getPosicao(event:PropertyChangeEvent	):void
			{	
 
				if(event.property == "verticalScrollPosition"){
 
					if(event.newValue == (event.currentTarget.measuredHeight - event.currentTarget.height)){
							carregarMaisItens();
					}
				}
			}
 
			private function carregarMaisItens():void
			{
				var indice:int = dados.length;
 
				for (var i:int=0;i<=proxima_pagina;i++) 
				{
					var item:Object = new Object();
					item.label = "Item Carregado "+(i+indice);
					dados.addItem(item);
					trace(item['label']);
				}
 
 
			}
 
		]]>
	</fx:Script>
	<s:List id="lista" dataProvider="{dados}" creationComplete="loadItens(event)" width="100%" height="100%">
 
	</s:List>
</s:View>

 

Veja que além do scroller me dá as propriedades de posição do verticalScrollPosition e horizontalScrollPosition, você precisa usar a propriedade reservada dele para pegar essas mudanças acontecendo, é como se fosse uma caixa onde a barra de rolagem vai indo porém é pre-definido um espaço onde isso pode acontecer, que é o caso da variável viewport.

 

Versão JQuery Mobile

Para fazer em JQuery mobile, é também a mesma técnica, porém uma ressalva, o componente de lista do JQuery Mobile requer que você dê um refresh no listView, é uma espécie de renderização dos itens para aplicar os estilos padrões de CSS.

A diferença entre Flex Mobile e JQuery Mobile é que no Flex eu adiciono direto ao componente, já que o pai View não é passível de ter o Scroller, já no caso do JQuery Mobile como tudo é HTML e o DOM é manipulado diretamente no documento, eu escuto as mudanças do $(window) que é a janela ou tamanho da tela disponível, assim comparo através da mesma fórmula o scrollTop que basicamente é equivalente a verticalScrollPosition e diminuo do tamanho da lista menos o tamanho disponível da tela.

O código acabou ficando assim:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<html>
<head>
	<title></title>
	<link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.1/jquery.mobile-1.1.1.min.css" />
	<script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
	<script src="http://code.jquery.com/mobile/1.1.1/jquery.mobile-1.1.1.min.js"></script>
</head>
<body>
<script type="text/javascript">
$(document).ready(function($) {
 
 
 
 
 
	carregarItens(20); // 20 itens 
 
 
 
 
	$(window).scroll(function()
	{
	    if($(window).scrollTop() == $(document).height() - $(window).height())
	    {
	    	  carregarItens(20); // carrega sempre 20 itens
	    }
	        
	    	
	});
 
 
 
 
});
 
 
 
 
function carregarItens(pagina){
	var tamanhoLista = $("#lista li").length; // tamanho atual da lista
	for (var i = 1; i < pagina; i++) {
		var indice = i + tamanhoLista;
		$("#lista").append('<li data-theme="c"><a href="#page1" data-transition="slide"> Item '+indice+'</a></li>');
		$("#lista").listview('refresh');
	};
}
</script>
<!-- Home -->
<div data-role="page" id="page1">
    <div data-theme="d" data-role="header" data-position="fixed">
        <h3>
            Exemplo Lista
        </h3>
    </div>
    <div data-role="content" style="padding: 15px">
        <ul id="lista" data-role="listview" data-divider-theme="d" data-inset="false">
            <li data-theme="c">
                <a href="#page1" data-transition="slide">
                    Item
                </a>
            </li>
        </ul>
    </div>
</div>
</body>
</html>

Da próxima vez que você ver no Twitter ou no Dzone uma lista de scroll infinito, você já saberá como implementa-la.

 

Dicas/ Dicas e truques/ Google/ HTML 5/ HTML5 / CSS3/ Web 2.0

Coloque seu site ou aplicativo web no modo turbo

rocket launch

Se você é fanático por performance e tem acesso shell/root ao seu servidor, vai aqui uma dica muito importante para optimizar todas as páginas que você serve para seus leitores/usuários.

Se seus serviços forem sob o teto do servidor Apache HTTP, então você precisa conhecer urgente o mod_pagespeed que acelera o processo de carregamento do aplicativo/sites a níveis de grandes sites como Google.com e facebook.com

Só para você ter uma idéia, meu site com todas as técnicas que abordei em meu arquivo .htaccess ele beirava os 6.5s para carregar por completo para você leitor. Depois que eu ativei o mod_pagespeed o tempo passou para 1.65s e o melhor de tudo isso, banda e processos foram economizados.

O que o mod_pagespeed faz na verdade?

Ele basicamente optimiza tudo que você deixou para depois e acabou esquecendo de fazer ou simplesmente não deu tempo devido a pressão sofrida no trabalho para liberar uma release.

Então através de filtros pré-definidos via String na sua configuração o mod_pagespeed cria diretivas de rápido acesso permitindo que você faça performance à baixo nível no servidor.

O mod_pagespeed é um santo remédio, porém você deve usa-lo com cautela, já que boa parte de seus filtros ainda são experimentos e nunca se sabe o que poderá causar de ruim para sua aplicação.

 

Pagespeed e derivados

Existem outros módulos no apache que é importante frizar bem para que você não continue achando que o pagespeed é o único salvador da pátria, outros módulos foram usados em meu problema, porém o pagespeed de fato teve um ganho muito mais considerado deixando ele agir sobre os arquivos estáticos.

Módulos que também usei e que são necessários para o pagespeed rodar.

Mod – rewrite

Mod – headers

Mod – deflate

 

Esses módulos podem ser instalados perfeitamente em seu servidor Apache usando os seguintes comandos.

 

Instalando o módulo rewrite.

a2enmod Nome_do_Modulo

 

O comando a2enmod, basicamente diz, Apache2 habilite modulo X, se você quiser ver todos os módulos habilitados em seu servidor Apache, basta ir em cd /etc/apache2/mods-enabled/

e dar um ls

 

Como instalar em um servidor Ubuntu/CentOS/Debian/Fedora

Baixe o módulo pagespeed diretamente aqui, links estão do lado direito.

Uma vez que você escolheu o pacote correto para sua distribuição linux, use o comando wget para baixar o pacote.

root@li437-29:~# wget https://dl-ssl.google.com/dl/linux/direct/mod-pagespeed-beta_current_amd64.deb

 

Como eu tenho um sistema operacional Ubuntu 64 bits usei o pacote acima.

Uma vez que o download completou, basta executar os seguintes comandos se você estiver no Ubuntu.

dpkg -i mod-pagespeed-*.deb
apt-get -f install

Se estiver no CentOS ou Fedora, execute

yum install at  (if you do not already have 'at' installed)
rpm -U mod-pagespeed-*.rpm

 

Depois disso, só habilitar o pagespeed no Apache, usando o comando a2enmod

a2enmod pagespeed

Pronto, seu módulo pagespeed está habilitado e pronto para executar, agora basta configurar os filtros que vem nele para melhorar o desempenho.

Por padrão o módulo é desabilitado justamente por que você precisa configura-lo antes de sair usando, afinal o Google não quer que seu site saia fora do ar antes mesmo de você coloca-lo em modo turbo.

Para isso, abra o arquivo de configuração do pagespeed que fica na pasta /etc/apache2/mods-enabled e use um editor que você esteja confortável em usar, eu uso o Vim, tem gente que gosta de outros e por ai vai. Para abrir é simples, vi pagespeed.conf

O arquivo vem basicamente assim.

# Turn on mod_pagespeed. To completely disable mod_pagespeed, you
# can set this to "off".
ModPagespeed on
 
# Direct Apache to send all HTML output to the mod_pagespeed
# output handler.
AddOutputFilterByType MOD_PAGESPEED_OUTPUT_FILTER text/html
 
# If you want mod_pagespeed process XHTML as well, please uncomment this
# line.
# AddOutputFilterByType MOD_PAGESPEED_OUTPUT_FILTER application/xhtml+xml
 
# The ModPagespeedFileCachePath and
# ModPagespeedGeneratedFilePrefix directories must exist and be
# writable by the apache user (as specified by the User
# directive).
ModPagespeedFileCachePath "/var/mod_pagespeed/cache/"
ModPagespeedGeneratedFilePrefix "/var/mod_pagespeed/files/"
 
# Override the mod_pagespeed 'rewrite level'. The default level
# "CoreFilters" uses a set of rewrite filters that are generally
# safe for most web pages. Most sites should not need to change
# this value and can instead fine-tune the configuration using the
# ModPagespeedDisableFilters and ModPagespeedEnableFilters
# directives, below. Valid values for ModPagespeedRewriteLevel are
# PassThrough, CoreFilters and TestingCoreFilters.
#
# ModPagespeedRewriteLevel PassThrough
 
# Explicitly disables specific filters. This is useful in
# conjuction with ModPagespeedRewriteLevel. For instance, if one
# of the filters in the CoreFilters needs to be disabled for a
# site, that filter can be added to
# ModPagespeedDisableFilters. This directive contains a
# comma-separated list of filter names, and can be repeated.
#
# ModPagespeedDisableFilters rewrite_images
 
# Explicitly enables specific filters. This is useful in
# conjuction with ModPagespeedRewriteLevel. For instance, filters
# not included in the CoreFilters may be enabled using this
# directive. This directive contains a comma-separated list of
# filter names, and can be repeated.
#
ModPagespeedEnableFilters rewrite_javascript,rewrite_css
ModPagespeedEnableFilters collapse_whitespace,elide_attributes
 
# ModPagespeedDomain
# authorizes rewriting of JS, CSS, and Image files found in this
# domain. By default only resources with the same origin as the
# HTML file are rewritten. For example:
 
# ModPagespeedDomain cdn.myhost.com
#
# This will allow resources found on http://cdn.myhost.com to be
# rewritten in addition to those in the same domain as the HTML.
#
# Wildcards (* and ?) are allowed in the domain specification. Be
# careful when using them as if you rewrite domains that do not
# send you traffic, then the site receiving the traffic will not
# know how to serve the rewritten content.
 
# Other defaults (cache sizes and thresholds):
#
# ModPagespeedFileCacheSizeKb 102400
# ModPagespeedFileCacheCleanIntervalMs 3600000
# ModPagespeedLRUCacheKbPerProcess 1024
# ModPagespeedLRUCacheByteLimit 16384
# ModPagespeedCssInlineMaxBytes 2048
# ModPagespeedImageInlineMaxBytes 2048
# ModPagespeedCssImageInlineMaxBytes 2048
# ModPagespeedJsInlineMaxBytes 2048
# ModPagespeedCssOutlineMinBytes 3000
# ModPagespeedJsOutlineMinBytes 3000
 
# Bound the number of images that can be rewritten at any one time; this
# avoids overloading the CPU. Set this to 0 to remove the bound.
#
# ModPagespeedImageMaxRewritesAtOnce 8
 
# You can also customize the number of threads per Apache process
# mod_pagespeed will use to do resource optimization. Plain
# "rewrite threads" are used to do short, latency-sensitive work,
# while "expensive rewrite threads" are used for actual optimization
# work that's more computationally expensive. If you live these unset,
# or use values 
SetHandler mod_pagespeed_beacon
 
# Uncomment the following line if you want to disable statistics entirely.
#
# ModPagespeedStatistics off
 
# This page lets you view statistics about the mod_pagespeed module.
 
Order allow,deny
# You may insert other "Allow from" lines to add hosts you want to
# allow to look at generated statistics. Another possibility is
# to comment out the "Order" and "Allow" options from the config
# file, to allow any client that can reach your server to examine
# statistics. This might be appropriate in an experimental setup or
# if the Apache server is protected by a reverse proxy that will
# filter URLs in some fashion.
Allow from localhost
Allow from 127.0.0.1
SetHandler mod_pagespeed_statistics
 
# Page /mod_pagespeed_message lets you view the latest messages from
# mod_pagespeed, regardless of log-level in your httpd.conf
# ModPagespeedMessageBufferSize is the maximum number of bytes you would
# like to dump to your /mod_pagespeed_message page at one time,
# its default value is 100k bytes.
# Set it to 0 if you want to disable this feature.
ModPagespeedMessageBufferSize 100000
 
Allow from localhost
Allow from 127.0.0.1
SetHandler mod_pagespeed_message
 
Allow from localhost
Allow from 127.0.0.1
SetHandler mod_pagespeed_referer_statistics

O que você precisa habilitar primeiro é o módulo.

 ModPagespeed on

Depois configurar onde ele vai guardar os caches

  ModPagespeedFileCachePath            "/var/mod_pagespeed/cache/"
  ModPagespeedGeneratedFilePrefix      "/var/mod_pagespeed/files/"

Coloque em um diretório de sua preferência.

Com apenas isso, você já vai notar uma significante mudança no carregamento de seu site ou App em 50%. Tem muita configuração para você fazer através de filtros.

Os filtros que utilzei foram esses.

 ModPagespeedEnableFilters combine_css

O filtro combine_css já diz, tudo ele combina tudo que você tem de CSS em um único arquivo.

Outro filtro que usei foi o minificador de Javascript.

ModPagespeedEnableFilters rewrite_javascript

Em caso de você esquecer de minificar seu código javascript, ele faz automático para você.

Outro bastante importante é a configuração das imagens, elas tem um peso absurdo quando o negócio é carregamento.

Eu usei os seguintes filtros.

 ModPagespeedEnableFilters inline_images,recompress_images,resize_images

Peço para carregar todas as imagens em inline, tudo junto, comprima elas e faça um resize caso necessário.

 

Claro que os filtros que eu usei são específicos para meu caso, embora ele vá servir também para vocês na maioria dos casos, recomendo ler os pro e contras de qualquer filtro que você habilite na documentação do Google pageSpeed, que por sinal é bem completa.

E depois disso tudo? Como faço para testar?

Existem diversas ferramentas on-line que analisam o conteúdo do site para você e diz onde está errando, inclusive no próprio site do Page speed, o Google disponibiliza o Page speed Insights, detalhando tudo que você precisa saber para corrigir os erros e melhorar a performance do site.

O mais legal do Pagespeed Insights é a possibilidade de você testar o carregamento em Desktop e Mobile.

Outra ferramenta que fiz bastante uso em meu problema, foi o Full Page Test da Pingdom, para ver se realmente o Pagespeed tinha afetado a performance do site.
No Google Page Speed Insights meu site carregou em 680ms quase 1 segundo. No Pingdom os resultados foram mais reais digamos assim. Carregados em 1.65s.

Sem tudo isso do Pagespeed meu site estaria sendo carregado em meros 5 ou 6 segundos, isso por que boa parte de seu conteúdo é texto.

É isso, escrevei outro post de como eu consegui reduzir o consumo de memória do servidor servindos 50 visitas /segundo nesse blog depois que mudei para servidores novos.
Foi uma grande aventura configurar o Apache e o MySQL para reduzir o consumo de memória.