<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Werneck Paiva</title>
	<atom:link href="http://blog.werneckpaiva.com.br/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.werneckpaiva.com.br</link>
	<description>Eis aqui mais um site de um desenvolvedor :-)</description>
	<lastBuildDate>Thu, 16 Feb 2012 03:42:33 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Queries com a &#8220;nova&#8221; JPA 2 criteria API</title>
		<link>http://blog.werneckpaiva.com.br/2012/01/queries-com-a-nova-jpa2-criteria-api/</link>
		<comments>http://blog.werneckpaiva.com.br/2012/01/queries-com-a-nova-jpa2-criteria-api/#comments</comments>
		<pubDate>Sat, 21 Jan 2012 02:06:52 +0000</pubDate>
		<dc:creator>Ricardo Paiva</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[consulta]]></category>
		<category><![CDATA[criteria]]></category>
		<category><![CDATA[hibernate]]></category>
		<category><![CDATA[hql]]></category>
		<category><![CDATA[jpa]]></category>
		<category><![CDATA[jpa 2]]></category>
		<category><![CDATA[jpa2]]></category>
		<category><![CDATA[jpql]]></category>
		<category><![CDATA[persistence]]></category>
		<category><![CDATA[persistência]]></category>
		<category><![CDATA[query]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://blog.werneckpaiva.com.br/?p=308</guid>
		<description><![CDATA[A JPA 2 (Java Persistence API) oferece um novo mecanismos para construir consultas denominado JPA criteria API. Similar ao Hibernate Criteria API, é uma alternativa à linguagem JPQL e HQL. Você constrói consultas SQL programaticamente, fazendo uso da orientação objeto e verificação de tipos.]]></description>
			<content:encoded><![CDATA[<div name="googleone_share_1" style="position:relative;z-index:5;float: left; margin-right: 10px;"><g:plusone size="standard" count="" href="http://blog.werneckpaiva.com.br/2012/01/queries-com-a-nova-jpa2-criteria-api/">{lang: 'pt-BR'}</g:plusone></div><p>Eu sempre achei a linguagem SQL muito interessante, por ter quê de linguagem natural. Aliás, a idéia inicial era realmente criar uma linguagem de consultas, bem próxima à maneira como nós, humanos, nos comunicamos. O tempo passou, a linguagem cresceu, e as consultas SQL se tornaram verdadeiros monstros. É difícil encontrar uma query mais complexa que contenha fluidez similar a uma sentença em inglês. Além disso, eu sempre achei chato misturar SQL no meio da minha aplicação (mas isso é uma birra minha que não tem nada a ver com a história).</p>
<p>Há cerca de 3 anos fui apresentado a uma API de consultas do Hibernate (Hibernate Criteria API). A idéia é que programaticamente poderíamos construir consultas SQL, ao invés de usar a linguagem de consultas do Hibernate (HQL). Achei a idéia muito boa e fiz uso desta API por alguns anos.</p>
<p>No mundo JPA (Java Persistence API), somente com a JPA 2, passamos a ter acesso a uma API de criteria. Existem inúmeras vantagens e desvantagens em usar uma API de criteria, mas as que eu gosto de destacar são:</p>
<div>Vantagens:</div>
<ol>
<li>Verificação de erros &#8211; Muitos erros podem ser detectados em tempo de compilação;</li>
<li>Segurança &#8211; como as consultas são construídas pelo motor da API, você fica praticamente imune a SQL injections;</li>
<li>Queries dinâmicas podem ser construídas mais facilmente, ao invés montar strings complexas.</li>
<li>Tipagem forte &#8211; a JPA criteria API leva vantagem em relação à Hibernate Criteria API em relação a verficação de tipos.</li>
</ol>
<div>Desvantagens:</div>
<ol>
<li>Complexidade &#8211; uma vez que a maioria dos desenvolvedores está acostumada com o SQL/HQL/JPQL, migrar para uma API de criteria não é simples.</li>
</ol>
<p>Executando uma query JPQL:</p>
<pre class="brush: java">
Query query = entityManager.createQuery("SELECT p FROM Product p");
List results = query.getResultList();
</pre>
<p>A mesma query JPQL, mas de forma tipada, seria:</p>
<pre class="brush: java">
TypedQuery&lt;Product> typedQuery = entityManager.createQuery("SELECT p FROM Product p", Product.class);
List&lt;Product> results = typedQuery.getResultList();
</pre>
<p>Executando esta consulta usando a JPA Criteria API:</p>
<pre class="brush: java">
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery&lt;Product> query = builder.createQuery(Product.class);
Root&lt;Product> from = query.from(Product.class);
CriteriaQuery&lt;Product> select = query.select(from);

TypedQuery&lt;Product> typedQuery = entityManager.createQuery(select);
List&lt;Product> results = typedQuery.getResultList();
</pre>
<p>Perceba que os dois últimos comandos são bem parecidos com a maneira como a JPQL funciona. A primeira vista, esta estratégia parece ser bem mais complexa e trabalhosa, mas se encurtarmos um pouco o código, temos uma estrutura bem parecida com uma query SQL:</p>
<pre class="brush: java">
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery&lt;Product> query = builder.createQuery(Product.class);

TypedQuery&lt;Product> typedQuery = entityManager.createQuery(
    query.select(
       query.from(Product.class)
    )
);
List&lt;Product> results = typedQuery.getResultList();
</pre>
<p>Veja um exemplo de como fazermos uma consulta com a cláusula where.<br />
Usando a JPQL:</p>
<pre class="brush: java">
TypedQuery&lt;Product> typedQuery = entityManager.createQuery(
    "SELECT p "+
    "FROM Product p "+
    "WHERE p.price > :price",
Product.class);
typedQuery.setParameter("price", price);
List&lt;Product> results = typedQuery.getResultList();
</pre>
<p>Usando a JPA Criteria API:</p>
<pre class="brush: java">
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery&lt;Product> query = builder.createQuery(Product.class);
Root&lt;Product> from = query.from(Product.class);
TypedQuery&lt;Product> typedQuery = entityManager.createQuery(
    query.select(from )
    .where(
       builder.gt(from.get("price"), price)
    )
);
List&lt;Product> results = typedQuery.getResultList();
</pre>
<p>Sim, tudo bem, eu admito, está tudo muito mais complicado do que as queries JPQL. Mas eu ainda acho que queries com muitos componentes dinâmicos são mais fáceis de fazer, quando usamos a JPA Criteria API.</p>
<p>Vamos dar uma olhada numa query que envolva múltiplas entidades (joins).</p>
<p>Usando a JPQL:</p>
<pre class="brush: java">
TypedQuery&lt;Product> typedQuery = entityManager.createQuery(
      "SELECT p "+
      "FROM Product p "+
      "WHERE p.supplier.name=:supplier",
Product.class);
typedQuery.setParameter("supplier", supplierName);
List&lt;Product> results = typedQuery.getResultList();
</pre>
<p>Usando a JPA Criteria API:</p>
<pre class="brush: java">
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery&lt;Product> query = builder.createQuery(Product.class);
Root&lt;Product> from = query.from(Product.class);
TypedQuery&lt;Product> typedQuery = entityManager.createQuery(
    query.select(from )
    .where(
       builder.equal(from.join("supplier").get("name"), supplierName)
    )
);
List&lt;Product> results = typedQuery.getResultList();
</pre>
<p>Para fazer uma ordenação, basta usar o método CriteriaQuery.orderBy():</p>
<pre class="brush: java">
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery&lt;Product> query = builder.createQuery(Product.class);
Root&lt;Product> from = query.from(Product.class);
TypedQuery&lt;Product> typedQuery = entityManager.createQuery(
    query.select(from )
    .where(
       builder.gt(from.get("price"), price)
    )
    .orderBy(builder.asc(from.get("name")))
);
List&lt;Product> results = typedQuery.getResultList();
</pre>
<p>Vejamos uma consulta resultado de uma tela de busca onde o usuário pode filtra por diversos critérios: maior preço, menor preço, nome e categoria. A estratégia será adicionar mais ou menos cláusulas ao predicado usado pelo where.</p>
<pre class="brush: java">
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery&lt;Product> query = builder.createQuery(Product.class);
Root&lt;Product> from = query.from(Product.class);

Predicate predicate = builder.and();

// product.price > minPrice
if (minPrice != null &#038;&#038; minPrice > 0){
     predicate = builder.and(predicate, builder.ge(from.get("price"), minPrice));
}
// product.price &lt; maxPrice
if (maxPrice != null &#038;&#038; maxPrice > 0){
    predicate = builder.and(predicate,
        builder.le(from.get("price"), maxPrice));
}

// product.name like %productName%
if (productName != null &#038;&#038; productName.length > 2){
    predicate = builder.and(predicate,
        builder.like(from.&lt;String>get("name"), "%"+productName+"%"));
}

// product.category.name like %categoryName%
if (categoryName != null &#038;&#038; categoryName.length() > 2){
    predicate = builder.and(predicate,
        builder.like(
            from.join("category").&lt;String>get("name"),
            "%"+categoryName+"%"));
}

TypedQuery&lt;Product> typedQuery = entityManager.createQuery(
    query.select(from )
    .where( predicate )
    .orderBy(builder.asc(from.get("name")))
);
List&lt;Product> results = typedQuery.getResultList();
</pre>
<p>Recentemente precisei fazer uma consulta a partir de um relacioamento unidirecional que me deu um certo trabalho. Fazendo algumas simplificações, eu queria obter todos professores (Teacher) de turmas (ClassSection) de uma determinada escola (School). Turma aponta para professor, mas professor não aponta para turma. </p>
<p><tt>Teacher <- ClassSection -> School</tt></p>
<p>Aconsulta ficou mais ou menos assim:</p>
<pre class="brush: java">
CriteriaBuilder builder  = entityManager.getCriteriaBuilder();
CriteriaQuery&lt;Teacher> query = builder.createQuery(Teacher.class);
Root&lt;Teacher> fromTeacher = query.from(Teacher.class);
Root&lt;ClassSection> fromClassSection = query.from(ClassSection.class);

Join&lt;ClassSection, Teacher> teacherJoin = fromClassSection.join("teacher");
Join&lt;ClassSection, School> schoolJoin = fromClassSection.join("school");

TypedQuery&lt;Teacher> typedQuery = getEntityManager().createQuery(query
    .select(fromTeacher)
    .where(builder.and(
            builder.equal(fromTeacher, teacherJoin),
            builder.equal(schoolJoin.get("id"), schoolId)
    ))
    .orderBy(builder.asc(fromTeacher.get("firstName")))
    .distinct(true)
);

List&lt;Teacher> teachers = typedQuery.getResultList();
</pre>
<p>Perceba que faço a cahamada <tt>query.from()</tt> duas vezes, uma para Teacher e outra para ClassSection, entreatanto, o método <tt>createQuery()</tt> sempre terá o tipo da classe que será retornada como resultado da consulta.</p>
<p>Para saber mais:<br />
<a href="http://www.objectdb.com/java/jpa/query/criteria" target="_blank">http://www.objectdb.com/java/jpa/query/criteria</a><br />
<a href="http://www.altuure.com/2010/09/23/jpa-criteria-api-by-samples-part-i/" target="_blank">http://www.altuure.com/2010/09/23/jpa-criteria-api-by-samples-part-i/</a><br />
<a href="http://www.altuure.com/2010/09/23/jpa-criteria-api-by-samples-%E2%80%93-part-ii/" target="_blank">http://www.altuure.com/2010/09/23/jpa-criteria-api-by-samples-%E2%80%93-part-ii/</a></p>
<div name="googleone_share_1" style="position:relative;z-index:5;float: left; margin-right: 10px;"><g:plusone size="standard" count="" href="http://blog.werneckpaiva.com.br/2012/01/queries-com-a-nova-jpa2-criteria-api/">{lang: 'pt-BR'}</g:plusone></div>]]></content:encoded>
			<wfw:commentRss>http://blog.werneckpaiva.com.br/2012/01/queries-com-a-nova-jpa2-criteria-api/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Como usar autowire do Spring a partir do método main()</title>
		<link>http://blog.werneckpaiva.com.br/2012/01/como-usar-autowire-do-spring-a-partir-do-metodo-main/</link>
		<comments>http://blog.werneckpaiva.com.br/2012/01/como-usar-autowire-do-spring-a-partir-do-metodo-main/#comments</comments>
		<pubDate>Fri, 20 Jan 2012 16:59:39 +0000</pubDate>
		<dc:creator>Ricardo Paiva</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[autowire]]></category>
		<category><![CDATA[autowired]]></category>
		<category><![CDATA[main]]></category>
		<category><![CDATA[maven]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[standalone]]></category>

		<guid isPermaLink="false">http://blog.werneckpaiva.com.br/?p=301</guid>
		<description><![CDATA[Algumas vezes precisamos rodar o Spring a partir de uma aplicação standalone (aquela que possui o método main()). Podemos usar todos os recursos do Spring, inclusive as anotações @Autowired, mesmo na classe que possui o método main.]]></description>
			<content:encoded><![CDATA[<div name="googleone_share_1" style="position:relative;z-index:5;float: left; margin-right: 10px;"><g:plusone size="standard" count="" href="http://blog.werneckpaiva.com.br/2012/01/como-usar-autowire-do-spring-a-partir-do-metodo-main/">{lang: 'pt-BR'}</g:plusone></div><p>Que o Spring é um framework fantástico, eu não preciso dizer. Seus projetistas criaram, a partir de um paradigma de container IOC, um ecossistema de frameworks que nos auxiliam em inúmeras tarefas, desde testes unitários a controle transacional, passando por segurança, MVC entre outras.</p>
<p>Algumas vezes precisamos rodar o Spring a partir de uma aplicação standalone (aquela que possui o método main()). Isso não é lá muito complicado, mas é interessante perceber que podemos usar todos os recursos do Spring, inclusive as anotações @Autowired, mesmo na classe que possui o método main.</p>
<p>Para inicializarmos o Spring, usalmente fornecemos um arquivo XML de configuração do ApplicationContext. A diferença agora é que iremos solicitar ao próprio Spring que inicialize a classe que contém o método main, e faça uso das anotações de @Autowired que estamos usando.</p>
<p>Veja o exemplo de uma classe que depende de um bean de serviço:</p>
<pre class="brush:java">
@Component
public class MyApplication{

        @Autowired
        private IMyService myService;

	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
		MyApplication app = context.getBean(MyApplication.class);
		app.start()
	}

	private void start(){
	        myService.doSomething();
                // ...
	}
}

@Service
public class MyService implements IMyService {
    public String doSomething() {
        // ...
    }
}
</pre>
<p>O arquivo config.xml deverá estar localizado no classpath da aplicação. Se estiver usando Maven, coloque na pasta src/main/resource</p>
<pre class="brush: xml">
&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

  &lt;context:annotation-config />
  &lt;context:component-scan base-package="mypackage" />

&lt;/beans>
</pre>
<p>Se você quer saber como criar uma aplicação standalone com um único JAR e todas as suas dependências usando o Maven, <a href="http://blog.werneckpaiva.com.br/2012/01/como-criar-um-jar-executavel-com-todas-as-dependencias-usando-maven/">leia o artigo que escrevi sobre o assunto</a>.</p>
<div name="googleone_share_1" style="position:relative;z-index:5;float: left; margin-right: 10px;"><g:plusone size="standard" count="" href="http://blog.werneckpaiva.com.br/2012/01/como-usar-autowire-do-spring-a-partir-do-metodo-main/">{lang: 'pt-BR'}</g:plusone></div>]]></content:encoded>
			<wfw:commentRss>http://blog.werneckpaiva.com.br/2012/01/como-usar-autowire-do-spring-a-partir-do-metodo-main/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Como criar um JAR executável com todas as dependências usando Maven</title>
		<link>http://blog.werneckpaiva.com.br/2012/01/como-criar-um-jar-executavel-com-todas-as-dependencias-usando-maven/</link>
		<comments>http://blog.werneckpaiva.com.br/2012/01/como-criar-um-jar-executavel-com-todas-as-dependencias-usando-maven/#comments</comments>
		<pubDate>Fri, 20 Jan 2012 16:32:20 +0000</pubDate>
		<dc:creator>Ricardo Paiva</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[dependências]]></category>
		<category><![CDATA[dependency]]></category>
		<category><![CDATA[jar]]></category>
		<category><![CDATA[jar executável]]></category>
		<category><![CDATA[maven]]></category>

		<guid isPermaLink="false">http://blog.werneckpaiva.com.br/?p=293</guid>
		<description><![CDATA[Algumas vezes precisamos criar uma aplicação standalone, que possua dependências e que gere apenas um arquivo JAR. É possível usar o Maven para fazer o build, gerenciar as dependências e ainda criar o arquivo MANIFEST.MF com as informações necessárias para que a aplicação rode sem precisarmos informar qual classe possui o método main().]]></description>
			<content:encoded><![CDATA[<div name="googleone_share_1" style="position:relative;z-index:5;float: left; margin-right: 10px;"><g:plusone size="standard" count="" href="http://blog.werneckpaiva.com.br/2012/01/como-criar-um-jar-executavel-com-todas-as-dependencias-usando-maven/">{lang: 'pt-BR'}</g:plusone></div><p>Algumas vezes precisamos criar uma aplicação standalone, que possua dependências e que gere apenas um arquivo JAR. É possível usar o Maven para fazer o build, gerenciar as dependências e ainda criar o arquivo MANIFEST.MF com as informações necessárias para que a aplicação rode sem precisarmos informar qual classe possui o método main().</p>
<p>O plugin que faz a mágica da geração do JAR incluindo todas as dependências é o maven-assembly-plugin. Ele irá incluir todos os arquivos .class junto com as classes de nossa aplicação.</p>
<pre class="brush:xml">
<plugins>
<plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <configuration>
            <descriptorRefs>
                <descriptorRef>jar-with-dependencies</descriptorRef>
            </descriptorRefs>
            <archive>
                <manifest>
                    <addClasspath>true</addClasspath>
                    <mainClass>fully.qualified.MainClass</mainClass>
                </manifest>
            </archive>
        </configuration>
        <executions>
            <execution>
                <id>make-my-jar-with-dependencies</id>
<phase>package</phase>
                <goals>
                    <goal>single</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
</plugins></pre>
<p>Para fazer o build em sua aplicação, simplesmente rode o maven:</p>
<pre class="brush:shell">$ mvn clean install</pre>
<p>Para executar seu programa, faça:</p>
<pre class="brush:shell">$ java -jar target/suaApplicacao-jar.with-dependencies.jar</pre>
<div name="googleone_share_1" style="position:relative;z-index:5;float: left; margin-right: 10px;"><g:plusone size="standard" count="" href="http://blog.werneckpaiva.com.br/2012/01/como-criar-um-jar-executavel-com-todas-as-dependencias-usando-maven/">{lang: 'pt-BR'}</g:plusone></div>]]></content:encoded>
			<wfw:commentRss>http://blog.werneckpaiva.com.br/2012/01/como-criar-um-jar-executavel-com-todas-as-dependencias-usando-maven/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Como funciona o Map Reduce usado pelo Google</title>
		<link>http://blog.werneckpaiva.com.br/2011/08/como-funciona-o-map-reduce-usado-pelo-google/</link>
		<comments>http://blog.werneckpaiva.com.br/2011/08/como-funciona-o-map-reduce-usado-pelo-google/#comments</comments>
		<pubDate>Wed, 24 Aug 2011 01:54:59 +0000</pubDate>
		<dc:creator>Ricardo Paiva</dc:creator>
				<category><![CDATA[Algoritmos]]></category>
		<category><![CDATA[distribuído]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[map]]></category>
		<category><![CDATA[MapReduce]]></category>
		<category><![CDATA[paralelo]]></category>
		<category><![CDATA[programação paralela]]></category>
		<category><![CDATA[reduce]]></category>

		<guid isPermaLink="false">http://blog.werneckpaiva.com.br/?p=265</guid>
		<description><![CDATA[MapReduce é um modelo de programação proposto pelo Google para para facilitar o processamento de grandes volumes de dados. Simplifica a criação de programas paralelos e distribuídos.]]></description>
			<content:encoded><![CDATA[<div name="googleone_share_1" style="position:relative;z-index:5;float: left; margin-right: 10px;"><g:plusone size="standard" count="" href="http://blog.werneckpaiva.com.br/2011/08/como-funciona-o-map-reduce-usado-pelo-google/">{lang: 'pt-BR'}</g:plusone></div><h4 id="internal-source-marker_0.35662519303150475" dir="ltr"><strong>Introdução</strong></h4>
<p>MapReduce é um modelo de programação proposto pelo Google para para facilitar o processamento de grandes volumes de dados. A partir de um paradigma inspirado em primitivas de programação funcional, foi criado um framework que permitisse a manipulação de grande volume de dados de forma paralela e distribuída, além de prover tolerância a falha, escalonamento de I/O e monitoramento. Um grande número de aplicações reais podem ser expressas nesse modelo de programação.</p>
<p>O modelo de programação MapReduce consiste na construção de um programa formado por duas operações basicas: <em>map</em> e r<em>educe</em>. A operação de <em>map</em> recebe um par chave/valor e gera um conjunto intermediário de dados, também no formato chave/valor. A operação de <em>reduce</em> é executada para cada chave intermediária, com todos os conjuntos de valores intermediários associados àquela chave combinados. Em geral a operação de map é usada para encontrar algo, e a operação de reduce é usada para fazer a sumarização do resultado.</p>
<h4>Exemplo</h4>
<p>Considere o problema de contar o número de ocorrências de uma palavra em uma grande coleção de documentos. Veja o pseudo-código para este problema usando MapReduce:</p>
<pre class="brush:ruby">map(String input_key, String input_value):
    // input_key: document name
    // input_value: document contents
    for each word w in input_value:
        EmitIntermediate(w, "1");

reduce(String output_key, Iterator intermediate_values):
    // output_key: a word
    // output_values: a list of counts
    int result = 0;
    for each v in intermediate_values:
        result += ParseInt(v);
    Emit(AsString(result));</pre>
<p>Para cara palavra do documento de entrada, a função <em>map</em> emite o valor &#8217;1&#8242; associado à chave que representa a palavra em questão. A função de <em>reduce</em> soma todas as contagens emitidas para uma mesma chave, ou seja, uma mesma palavra<em>.</em></p>
<h4><span class="Apple-style-span" style="font-weight: bold;">Execução</span></h4>
<p style="text-align: center;"><a href="http://blog.werneckpaiva.com.br/wp-content/uploads/2011/08/mapreduce1.png"><img title="" src="http://blog.werneckpaiva.com.br/wp-content/uploads/2011/08/mapreduce1.png" alt="" width="504" height="393" /></a></p>
<h4>Execução paralela</h4>
<p style="text-align: center;"><a href="http://blog.werneckpaiva.com.br/wp-content/uploads/2011/08/mapreduce2.png"><img class="aligncenter size-full wp-image-342" title="" src="http://blog.werneckpaiva.com.br/wp-content/uploads/2011/08/mapreduce2.png" alt="" width="659" height="473" /></a></p>
<p>A operação de <em>map</em> é distribuída em múltiplas máquinas a partir do particionamento dos dados em pedaços que podem ser processasdos em paralelo por diferente máquinas. A operação de <em>reduce</em> é distribuída através do particionamento das chaves intermediárias. O tamanho e a função de particionamento são parâmetros fornecidos pelo usuário.</p>
<h4>Passo-a-passo de execução</h4>
<ol>
<li>A biblioteca MapReduce, no programa do usuário, primeiro divide os dados de entrada em M pedaços. Em seguida inicia várias cópias do programa em um cluster de computadores.</li>
<li>Uma das cópias do programa é especial &#8211; o <em>master</em>. As outras cópias são denominadas <em>workers</em> e recebem trabalho do <em>master</em>. Existem M tarefas de Map e R tarefas de reduce para serem assinaladas. O<em> master</em> seleciona <em>workers</em> ociosos e assinala a eles uma tarefa de <em>map</em> ou de <em>reduce</em>.</li>
<li>Um <em>worker</em> que possui uma tarefa de <em>map</em> le o conteúdo correspondente ao pedaço da entrada. Ele interpreta os pares chave/valor a partir dos dados de entrada e passa como parâmetro para a função de <em>map</em> do usuário. Os pares chave/valor intermediários produzidos pela função de map são armazenados em memória.</li>
<li>Periodicamente os pares de dados dos buffers são escritos em disco, particionados em R regiões pela função de particionamento. A localização desses pares de dados no disco é informada ao <em>master</em>, que irá repassar essa localização para os <em>workers</em> com tarefas de <em>reduce</em>.</li>
<li>Quando um <em>worker</em> de <em>reduce</em> é notificado da localização dos dados pelo <em>master</em>, este usa uma chamada de procedimento remota para buscar os dados do disco local dos <em>workers</em> de <em>map</em>. Quando os dados já foram lidos, ele ordena os dados pelas chaves intermediárias, para que todas as ocorrências de uma mesma chave seja agrupada junto. A operação de ordenação é necessária, pois muitas chaves diferentes são mapeadas para a mesma tarefa de <em>reduce</em>. Se a quantidade de dados intermediários é muito grande para caber em memória, uma ordenação externa é usada.</li>
<li>O <em>worker</em> de <em>reduce</em> percorrer os dados intermediários já ordenados e para cada chave encontrada, ele passa a chave os valores intermediários para a função de <em>reduce</em> definida pelo usuário. A saída de cada função de <em>reduce</em> é adicionada ao final de um arquivo de saída para aquela partição de <em>reduce.</em></li>
<li>Quando todas as tarefas de <em>map</em> e <em>reduce</em> foram terminadas, o <em>master</em> retorna o programa do usuário.</li>
</ol>
<p>Ao final da execução do programa, o resultado está disponível em R arquivos (um para cada operação de <em>reduce</em>). É comum que os arquivos de resultados de uma operação de MapReduce sejam entradas para outra chamada de MapReduce.</p>
<h4>Tolerância a falhas</h4>
<p>Uma vez que o framework de MapReduce foi criado para ajudar o processamento de uma quantidade enorme de dados em um cluster com inúmeras máquinas, lidar com falhas é algo essencial. O processo <em>master</em> envia um ping periodicamente para cada <em>worker</em>. Se o <em>master</em> não receber uma resposta de um <em>worker</em> em um certo período de tempo, o <em>master</em> assume que aquela máquina falhou. Todas as tarefas de <em>map</em> completadas pelo <em>worker</em> são resetadas para seu estado inicial e são re-escalonadas para outro <em>worker</em>.  Essas tarefas precisam ser re-executadas em caso de falha, pois seus arquivos de saída são armazenado no disco local da máquina que falhou e, portanto, ficam inacessíveis. O mesmo acontece com as tarefas de <em>map</em> ou <em>reduce</em> que estão em progresso. Tarefas de <em>reduce</em> completadas não precisam ser re-executadas, pois seus arquivos de saída são armazenados no sistema de arquivo global.</p>
<p>No caso do processo <em>master</em> falhar, é necessário um controle mais complexo, pois o processo master é o elo entre a execução das tarefas de <em>map</em> e <em>reduce.</em> O processo <em>master</em> deve executar <em>checkpoints</em> periódicos de suas estruturas de dados. Em caso de falha, uma nova instância pode ser levantada, recuperando a partir do último estado que foi salvo. O MapReduce assume que só existirá um único processo <em>master</em> (<em>Single Master</em>), portanto, falhas neste processo são indesejáveis.</p>
<p>Ao final da execução do programa, algumas máquinas, apesar de ainda responderem, podem apresentar um tempo de resposta muito inferior a média das outras máquinas. Por exemplo, falhas nos discos podem reduzir a taxa de leitura de 30MB/s para 1MB/s. Para evitar que estes processo atrasem a execução do programa, quando o programa está perto de terminar, algumas cópias das tarefas restantes são iniciadas (tarefas backup). A tarefa será marcada como completada assim que, ou a tarefa primária ou uma tarefa de backup, responder. A título de exemplo, um programa de ordenação, pode demorar até 44% mais tempo se o mecanismo de tarefas de backup estiver desligado.</p>
<h4>Localidade</h4>
<p>A largura de banda é um recurso relativamente escasso neste ambiente de computação. A quantidade de comunicação é reduzida pelo fato de que os dados de entrada (gerenciados pelo sistema de arquivos global &#8211; GFS) está armazenado no disco local das máquinas que fazem parte do cluster. O GFS implementeado pelo Google divide cada arquivo em blocos de 64MB e armazena algumas cópias destes blocos em máquinas diferentes (tipicamente 3). O processo <em>master</em> do MapReduce leva em conta a localização dos dados na hora de escalonar as tarefas de <em>map</em> em uma máquina que contém uma das répicas dos dados de entrada.</p>
<p>Para saber mais, você pode acessar a página mantida pelo Google com informações sobre o <a title="MapReduce" href="http://labs.google.com/papers/mapreduce.html" target="_blank">MapReduce</a></p>
<p>Acesse também o projeto <a title="Projeto Hadoop" href="http://hadoop.apache.org/" target="_blank">Hadoop</a>, uma implementação <em>open source</em> de MapReduce em Java, mantida pelo grupo Apache.</p>
<div name="googleone_share_1" style="position:relative;z-index:5;float: left; margin-right: 10px;"><g:plusone size="standard" count="" href="http://blog.werneckpaiva.com.br/2011/08/como-funciona-o-map-reduce-usado-pelo-google/">{lang: 'pt-BR'}</g:plusone></div>]]></content:encoded>
			<wfw:commentRss>http://blog.werneckpaiva.com.br/2011/08/como-funciona-o-map-reduce-usado-pelo-google/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Máscara de imagens com Canvas do HTML5</title>
		<link>http://blog.werneckpaiva.com.br/2011/05/mascara-de-imagens-com-canvas-do-html5/</link>
		<comments>http://blog.werneckpaiva.com.br/2011/05/mascara-de-imagens-com-canvas-do-html5/#comments</comments>
		<pubDate>Sat, 14 May 2011 04:29:56 +0000</pubDate>
		<dc:creator>Ricardo Paiva</dc:creator>
				<category><![CDATA[HTML5]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[bit blit]]></category>
		<category><![CDATA[Bit Blitting]]></category>
		<category><![CDATA[canvas]]></category>
		<category><![CDATA[máscara]]></category>

		<guid isPermaLink="false">http://blog.werneckpaiva.com.br/?p=201</guid>
		<description><![CDATA[Aprenda como criar uma máscara em uma imagem usando o canvas HTML5. Você verá que é possível ter máscaras em diversos formatos e ainda é fazer animação come elas, algo impossível antes do HTML5.]]></description>
			<content:encoded><![CDATA[<div name="googleone_share_1" style="position:relative;z-index:5;float: left; margin-right: 10px;"><g:plusone size="standard" count="" href="http://blog.werneckpaiva.com.br/2011/05/mascara-de-imagens-com-canvas-do-html5/">{lang: 'pt-BR'}</g:plusone></div><p>Antes do HTML5, criar uma máscara para uma imagem era algo que exigia, necessariamente, a manipulação do arquivo em uma ferramenta gráfica externa, usando a  transparência do formato PNG. Isso atende muitos casos, mas não permite criar máscaras dinâmicas, ou seja, máscaras criadas via Javascript ou que mudem ao longo do tempo, como em uma animação por exemplo. É possível criar uma máscara usando uma DIV, mas isso só atende a problemas onde a máscara é regular (retangular). Se você quiser fazer uma animação com uma máscara no formato de um círculo ou de um polígono irregular, precisará lançar mão do Canvas do HTML5.</p>
<p>A primeira técnica para criar uma máscara usando canvas é a mais simples e imediata, mas tem alguns problemas de performance. Para fazer animações mais sofisticadas é preciso usar uma outra técnica, a mesma usada em alguns jogos (Bit Blit). Vou apresentar as duas técnicas, no intuito de identificar as diferenças entre elas.</p>
<h3>Primeira técnica: clip()</h3>
<p>Esta técnica compreende o desenho de um polígono qualquer, sucedido pela chamada do método <tt>context.clip()</tt>. Qualquer elemento desenhado em seguida, será recortado pelo polígono desenhado anteriormente. É importante que o polígono seja fechado e que seja preenchido com alguma cor.</p>
<p>
<div><canvas class="image" id="canvasImage1" width="640" height="300"></canvas></div>
<script type="text/javascript">// <![CDATA[
var imageTop = new Image(); var canvas1 = document.getElementById("canvasImage1"); var ctx = canvas1.getContext("2d"); /* Circle */ 		ctx.arc(300, 150, 150, 0, Math.PI*2, false); ctx.fillStyle = "#FFFFFF"; ctx.fill(); ctx.clip(); imageTop.src = "http://blog.werneckpaiva.com.br/exemplos/mask/foto1.jpg"; imageTop.onload = function(){ ctx.drawImage(imageTop, 0, 0); } 
// ]]&gt;</script>
</p>
<pre class="brush:js">&lt;canvas class="image" id="canvas1" width="640" height="300"&gt;&lt;/canvas&gt;

&lt;script type="text/javascript"&gt;
    var imageTop = new Image(); 

    // Obter o objeto de canvas e seu contexto de desenho 2D
    var canvas1 = document.getElementById("canvas1");
    var ctx = canvas1.getContext("2d"); 

    /* Circulo */
    ctx.arc(300, 150, 150, 0, Math.PI*2, false);
    ctx.fillStyle = "#FFFFFF";
    ctx.fill();
    //  Criar uma máscara
    ctx.clip(); 

    imageTop.src = "foto1.jpg";
    // Aguarde a imagem carregar
    imageTop.onload = function(){
        // Desenhar a imagem no canvas
        ctx.drawImage(imageTop, 0, 0);
    }
&lt;/script&gt;</pre>
<p>O exemplo acima começa instanciando um objeto de imagem, que irá guardar o conteúdo da imagem que receberá a máscara. Em seguida acessamos o contexto 2D do canvas criado. Com a API de Canvas podemos fazer inúmeros desenhos. Em nosso exemplo fizemos um círculo completo com um preenchimento em branco. Apesar da cor branca não aparecer, isso influencia, em parte, as bordas da máscara. O método <tt>clip()</tt> define que qualquer coisa que for desenhada no canvas a partir de então, será &#8216;recortado&#8217; pela máscara. Em seguida definimos a URL da imagem e, ao ser carregada, desenhamos a imagem no canvas.</p>
<p>Esta me parece a técnica mais simples de se entender e de implementar, mas sofre com performance quando resolvemos animar a máscara, em especial círculos. Além disso algumas pessoas podem reclamar da falta de resolução no contorno.</p>
<h3>Segunda técnica: Bit Blit</h3>
<p>A técnica de <a href="http://en.wikipedia.org/wiki/Bit_blit" target="_blank">Bit Blit</a> é bem consagrada em jogos, para aumentar a performance de renderização dos componentes gráficos. Eu mesmo já escrevi um artigo de como explorar a técnica usando Flash (<a href="http://blog.werneckpaiva.com.br/2011/01/bit-blitting-com-flex/" target="_blank">Aumento da Performance de Jogos em Flash com Bit blitting</a>). A idéia básica é usar operações de bit com os pixels dos elementos gráficos, de tal forma que possamos gerar a imagem final com operações mais leves.  </p>
<p>Como estamos interessados em criar máscaras, vamos fazer desenhos com uma cor sólida (preto). Usando a operação de &#8216;source-atop&#8217; vamos renderizar nossa imagem por cima do nosso desenho. O canvas entenderá que o elemento gráfico que sucede a operação &#8216;source-atop&#8217; deverá ter uma interseção com o que foi desenhado previamente, ou seja, somente a área da imagem que estiver por cima do elemento gráfico em preto que desenhamos aparecerá no canvas.</p>
<p>
<div><canvas class="image" id="canvasImage2" width="640" height="300" style="border: 1px dotted #666666;" ></canvas></div>
<script type="text/javascript">// <![CDATA[
var image2 = new Image(); 
var canvas2 = document.getElementById("canvasImage2"); 
var ctx2 = canvas2.getContext("2d"); 
image2.src = "http://blog.werneckpaiva.com.br/exemplos/mask/foto2.jpg"; 
image2.onload = function(){ 
	ctx2.fillStyle = "#000000"; 
	ctx2.fillRect(100, 100, 400, 180); 
	ctx2.globalCompositeOperation = 'source-atop'; 
	ctx2.drawImage(image2, 0, -100); 
} 
// ]]&gt;</script>
</p>
<pre class="brush:js">&lt;canvas class="image" id="canvas1" width="640" height="300"&gt;&lt;/canvas&gt;

&lt;script type="text/javascript"&gt;
    var image2 = new Image();
    var canvas2 = document.getElementById("canvasImage2");
    var ctx2 = canvas2.getContext("2d");
    image2.src = "foto2.jpg";
    image2.onload = function(){
	 ctx2.fillStyle = "#000000";
	 ctx2.fillRect(100, 100, 400, 200);
	 ctx2.globalCompositeOperation = 'source-atop';
 	 ctx2.drawImage(image2, 0, 00);
    }
&lt;/script&gt;</pre>
<p>Assim como o exemplo anterior, neste esperamos carregar a imagem e em seguida fazemos o desenho de uma figura geométrica. Usamos o método <tt>context.fillRect(x, y, larg, alt)</tt> para desenhar um retângulo. O atributo <tt>context.globalCompositeOperation = 'source-atop'</tt> indica que qualquer elemento gráfico renderizado posteriormente deverá ter uma interseção com o que já foi desenhado.</p>
<p>Do ponto de vista de performance, as duas técnicas são similares quando trabalhamos com polígonos simples, como retângulos. No entando, ao desenhar círculos com o método <tt>arc()</tt> temos uma queda significativa de performance. É aí que a técnica de Bit blit entra e nos ajuda bastante. Por incrível que pareça, desenhar um quadrado com um degradê circular (radial) é mais rápido do que desenhar um arco. Sabendo disso, podemos desenhar um degradê radial com o mesmo diâmetro do círculo, dentro de um quadrado, usando o método <tt>context.createRadialGradient(startX, startY, startR, endX, endY, endR)</tt>. Como estamos usando um degradê, temos a vantagem de podermos suavizar as bordas, tornando os contornos mais suaves.</p>
<p>
<div><canvas class="image" id="canvasImage3" width="640" height="300" style="border: 1px dotted #666666;" ></canvas></div>
<script type="text/javascript">// <![CDATA[
var image3 = new Image(); 
var canvas3 = document.getElementById("canvasImage3"); 
var ctx3 = canvas3.getContext("2d"); 
image3.src = "http://blog.werneckpaiva.com.br/exemplos/mask/foto2.jpg"; 
image3.onload = function(){
        var radius = 100;
        var diameter = radius * 2;
	var x = 110;
	var y = 80;
	var r = ctx3.createRadialGradient(x + radius, y + radius, radius-5, x + radius, y + radius, radius);
	r.addColorStop(0.5, '#000');	
	r.addColorStop(0.95, 'rgba(0,0,0,0)');
	ctx3.fillStyle = r;
	ctx3.fillRect(x, y, diameter, diameter);
	ctx3.globalCompositeOperation = 'source-atop'; 
	ctx3.drawImage(image3, 0, -100); 
} 
// ]]&gt;</script>
</p>
<pre class="brush:js">&lt;canvas class="image" id="canvas1" width="640" height="300"&gt;&lt;/canvas&gt;

&lt;script type="text/javascript"&gt;
var image3 = new Image();
var canvas3 = document.getElementById("canvasImage3");
var ctx3 = canvas3.getContext("2d");
image3.src = "http://blog.werneckpaiva.com.br/exemplos/mask/foto2.jpg";
image3.onload = function(){
        var radius = 100;
        var diameter = radius * 2;
	var x = 110;
	var y = 80;
	var r = ctx3.createRadialGradient(x + radius, y + radius, radius-5, x + radius, y + radius, radius);
	r.addColorStop(0.5, '#000');
	r.addColorStop(0.95, 'rgba(0,0,0,0)');
	ctx3.fillStyle = r;
	ctx3.fillRect(x, y, diameter, diameter);
	ctx3.globalCompositeOperation = 'source-atop';
	ctx3.drawImage(image3, 0, -100);
}
&lt;/script&gt;</pre>
<p>Apos carregar a imagem, definimos um raio e um ponto x e y, onde o quadrado será posicionado. Em seguida criamos um preenchimento do tipo RadialGradient que é composto por dois círculos (<tt>var r = ctx3.createRadialGradient(x + radius, y + radius, radius-5, x + radius, y + radius, radius)</tt>). Quanto menor o círculo mais interno, mais &#8216;esfumaçado&#8217; será a borda do círculo e, no exemplo, a diferença entre eles é de 5px. Como o gradiente pode ter várias cores, definimos uma cor sólida até 95%. A partir deste ponto, o degradê passa a ser transparente. Aplicamos o objeto de degradê criado ao estilo de preenchimento (<tt>ctx3.fillStyle = r</tt>). Desenhamos, então, o quadrado no ponto x, y e, por fim, usamos o mesmo recurso de operação bitmap usado no exemplo anterior (<tt>ctx3.globalCompositeOperation = 'source-atop'</tt>) para exibir a imagem com a máscara.</p>
<p>Um exemplo interessante do uso de uma máscara animada é este abaixo. <strong>Clique no canvas</strong> para a ver a animação.</p>
<p>
<div id="canvas4Container" style="width:640px; height:480px"><canvas class="image" id="canvasImage4" width="640" height="480" style="border: 1px dotted #666666;" ></canvas></div>
<script type="text/javascript">// <![CDATA[
	var image4 = new Image();
	var radius = 50;
	var x = 0;
	var y=0;
	var velX = 10;
	var velY = 10;

var canvas4 = document.getElementById("canvasImage4");
		var ctx4 = canvas4.getContext("2d");
		var diameter = radius * 2;		
		
		function animate(){
			ctx4.clearRect(0, 0, canvas4.width, canvas4.height);
			x+=velX;
			y+=velY;		
			ctx4.globalCompositeOperation = 'source-over'
			if (x <= 0) { velX *= -1; x=0; }
			if (x + diameter >= canvas4.width){ velX *= -1; x = canvas4.width - diameter }
			if (y <= 0){ velY *= -1; y = 0; }
			if (y + diameter >= canvas4.height){ velY *= -1; y=canvas4.height - diameter; }
			var r = ctx4.createRadialGradient(x + radius, y + radius, radius-5, x + radius, y + radius, radius);
			r.addColorStop(0.5, '#000');	
			r.addColorStop(0.95, 'rgba(0,0,0,0)');
			ctx4.fillStyle = r;
			ctx4.fillRect(x, y, diameter, diameter);
			ctx4.globalCompositeOperation = 'source-atop';
			ctx4.drawImage(imageTop, 0, 0)
		}
		image4.src = "http://blog.werneckpaiva.com.br/exemplos/mask/foto1.jpg"
		var timer = null;
		image4.onload = function(){
			animate();
		}
		document.getElementById("canvas4Container").onclick = function(){
			if (timer != null){
				clearInterval(timer);
				timer = null;
			} else {
				timer = setInterval(animate, 60)
			}
		}


// ]]&gt;</script>
</p>
<p>Você pode executar o exemplo acima a partir <a href="http://blog.werneckpaiva.com.br/exemplos/mask/mask2.html" target="_blank">desta página</a>, ou ver um exemplo mais sofisticado <a href="http://blog.werneckpaiva.com.br/exemplos/mask/mask3.html" target="_blank">nesta página.</a></p>
<div name="googleone_share_1" style="position:relative;z-index:5;float: left; margin-right: 10px;"><g:plusone size="standard" count="" href="http://blog.werneckpaiva.com.br/2011/05/mascara-de-imagens-com-canvas-do-html5/">{lang: 'pt-BR'}</g:plusone></div>]]></content:encoded>
			<wfw:commentRss>http://blog.werneckpaiva.com.br/2011/05/mascara-de-imagens-com-canvas-do-html5/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Utilize efeitos de flip do iPhone e iPad em aplicações web com WebKit</title>
		<link>http://blog.werneckpaiva.com.br/2011/05/utilize-efeitos-de-flip-do-iphone-e-ipad-em-aplicacoes-web-com-webkit/</link>
		<comments>http://blog.werneckpaiva.com.br/2011/05/utilize-efeitos-de-flip-do-iphone-e-ipad-em-aplicacoes-web-com-webkit/#comments</comments>
		<pubDate>Wed, 04 May 2011 04:52:06 +0000</pubDate>
		<dc:creator>Ricardo Paiva</dc:creator>
				<category><![CDATA[Dispositivos Móveis]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[ipad]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[3d]]></category>
		<category><![CDATA[animação]]></category>
		<category><![CDATA[efeito]]></category>
		<category><![CDATA[flip]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[safari]]></category>
		<category><![CDATA[slide]]></category>
		<category><![CDATA[webkit]]></category>

		<guid isPermaLink="false">http://blog.werneckpaiva.com.br/?p=177</guid>
		<description><![CDATA[{lang: 'pt-BR'}Recentemente comecei um trabalho para iPad. Apesar do projeto não ser nem uma aplicação web, nem uma aplicação nativa, a ferramenta me permitia que eu usasse recursos de HTML 5, especialmente os suportados pelo WebKit. O desafio foi usar alguns efeitos que são encontrados facilmente no iPhone, mas que ainda são raridades na Web. [...]]]></description>
			<content:encoded><![CDATA[<div name="googleone_share_1" style="position:relative;z-index:5;float: left; margin-right: 10px;"><g:plusone size="standard" count="" href="http://blog.werneckpaiva.com.br/2011/05/utilize-efeitos-de-flip-do-iphone-e-ipad-em-aplicacoes-web-com-webkit/">{lang: 'pt-BR'}</g:plusone></div><p>Recentemente comecei um trabalho para iPad. Apesar do projeto não ser nem uma aplicação web, nem uma aplicação nativa, a ferramenta me permitia que eu usasse recursos de HTML 5, especialmente os suportados pelo WebKit. O desafio foi usar alguns efeitos que são encontrados facilmente no iPhone, mas que ainda são raridades na Web.</p>
<p>Inicialmente esbarrei com o framework <a href="http://jqtouch.com/" target="_blank">JQTouch </a> &#8211; uma excelente solução para criar aplicações web para dispositivos móveis. Bem maduro e com muitas funcionalidades. A idéia básica do framework é transformar uma página HTML com marcações simples, em uma aplicação no estilo iPhone, incluindo design e efeitos. Para o meu problema seria um <em>overkill</em> e ainda teria um trabalho enorme para customizar a interface.</p>
<p>Me baseei, então, <a href="http://line25.com/articles/super-cool-css-flip-effect-with-webkit-animation" target="_blank">neste excelente artigo</a>, que possui um <a href="http://line25.com/wp-content/uploads/2010/webkit-flip/demo/index.html" target="_blank">exemplo</a> bem bacana. A diferença básica do exemplo que usarei será o uso de javascript para disparar o efeito 3D. A implementação que usarei só funciona no <a href="http://www.apple.com/safari/download/" target="_blank">Safari</a>. Apesar do navegador Chrome também se basear no mesmo engine que o Safari (WebKit), infelizmente o efeito 3D não funciona corretamente.</p>
<p>Enquanto o W3C ainda não bate o martelo para o padrão de efeitos 3D, o projeto WebKit se adiantou e criou alguns excelentes efeitos que são aplicados por meio de CSS. A estrela da festa é o efeito <tt>'-webkit-transform'</tt>. Com ele é possível fazer rotações nos eixos X e Y (<tt>rotate</tt>), mover (<tt>translate</tt>), redimensionar (<tt>scale</tt>) ou mesmo mudar a perspectiva de um objeto (<tt>skew</tt>). Você pode se aprofundar mais nos efeitos 3D disponíveis a partir <a href="http://www.webkit.org/blog/386/3d-transforms/">deste artigo</a> que possui alguns exemplos bem sofisticados.</p>
<p>Para fazer nosso efeito de flip iremos utilizar o recurso de rotação. Para que o efeito 3D seja notado, precisamos definir a perspectiva que iremos trabalhar, o tempo de duração da transição e uma flag que habilita o efeito 3D. O efeito de flip a alguns quadrados apresentados na página. Ao serem clicados, o quadrado irá virar e para revelar o conteúdo da parte posterior.</p>
<p>O HTML será definido assim:</p>
<pre class="brush:xml">&lt;body&gt;
	&lt;div id="content"&gt;
    	&lt;div id="box1" class="box"&gt;
	    	&lt;div class="face front"&gt;&lt;/div&gt;
    	    &lt;div class="face back"&gt;&lt;/div&gt;
        &lt;/div&gt;
        &lt;div id="box2" class="box"&gt;
	    	&lt;div class="face front"&gt;&lt;/div&gt;
    	    &lt;div class="face back"&gt;&lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/body&gt;</pre>
<p>Criamos um container global <tt>"content"</tt>, apenas para organização. Cada quadrado será definido pela div com CSS<tt> "box"</tt> que contém o conteúdo da face frontal (<tt>"face front"</tt>) e da face posterior (<tt>"face back"</tt>). Para que não haja desajuste na hora do efeito, ambas as faces serão definidas com o mesmo tamanho.</p>
<p>O código CSS irá definir tamanhos, posicionamento e toda a configuração dos efeitos de transição:</p>
<pre class="brush:css">&lt;style&gt;
	#content{
		-webkit-perspective: 1000;
		padding:20px;
	}

	.box{
		width:200px; height:200px;
		/*  Efeito 3D habilitado */
		-webkit-transform-style: preserve-3d;
		/* Tempo de duração da transição */
		-webkit-transition: 0.8s;
		position:absolute;
	}

	.face {
		width:200px;
		height:200px;
		-webkit-border-radius:15px;
		-webkit-box-shadow: 5px 5px 5px #888;
		position:absolute;
		-webkit-backface-visibility: hidden;
	}
	.face.front{
		background:#eeeeee;
		border:1px solid #CCC;
	}
	.face.back{
		border:1px solid #999;
		/* Face posterior é inicializada já rotacionada */
		-webkit-transform: rotateY(180deg);
		background:#F60;
	}

	.box.rotate {
		/* Efeito de rotação que será aplicado */
		-webkit-transform: rotateY(180deg);
	}

	#box1{ top:80px;  left:100px; }
	#box2{ top:200px; left:400px; }
&lt;/style&gt;</pre>
<p>O container mais externo <tt>"content"</tt> possui uma perspectiva de visualização, para que o efeito 3D seja percebido. A face posterior (marcada pela classe CSS <tt>"back"</tt>) é inicializada com uma rotação de 180º, para que seja considerada efetivamente como a parte de trás do quadrado. As faces são definidas com posicionamento absoluto, permitindo que uma esteja sobreposta a outra.</p>
<p>A única coisa que falta é criar o javascript que irá aplicar o efeito de rotação quando o usuário clicar sobre um quadrado:</p>
<pre class="brush:js">&lt;script type="text/javascript" src="js/jquery-1.6.min.js"&gt;&lt;/script&gt;
&lt;script&gt;
        // Quando a página carregar
	$(function(){
		$("#content .box").click(function(el){
			$(this).toggleClass("rotate");
		});
	});
&lt;/script&gt;</pre>
<p>Quando a página carregar, todos elementos marcados com a classe CSS <tt>"box"</tt> terão seu evento de click monitorados. Ao serem disparados, a classe CSS <tt>"rotate"</tt> será aplicada, provocando o efeito de rotação do quadrado. Estamos usando o jquery para simplificar a manipulação dos elementos da página.</p>
<p>Experimente este um <a href="http://blog.werneckpaiva.com.br/exemplos/flip/flip.html" target="_blank">demo</a> deste exemplo (lembre-se de usar o Safari).</p>
<p>Espero que a dica a tenha ajudado e vamos torcer para que estes efeitos estejam disponíveis em todos os browsers em breve.</p>
<div name="googleone_share_1" style="position:relative;z-index:5;float: left; margin-right: 10px;"><g:plusone size="standard" count="" href="http://blog.werneckpaiva.com.br/2011/05/utilize-efeitos-de-flip-do-iphone-e-ipad-em-aplicacoes-web-com-webkit/">{lang: 'pt-BR'}</g:plusone></div>]]></content:encoded>
			<wfw:commentRss>http://blog.werneckpaiva.com.br/2011/05/utilize-efeitos-de-flip-do-iphone-e-ipad-em-aplicacoes-web-com-webkit/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ajax com Javascript não intrusivo (unobtrusive Ajax)</title>
		<link>http://blog.werneckpaiva.com.br/2011/03/ajax-com-java-script-nao-intrusivo-unobtrusive-ajax/</link>
		<comments>http://blog.werneckpaiva.com.br/2011/03/ajax-com-java-script-nao-intrusivo-unobtrusive-ajax/#comments</comments>
		<pubDate>Sun, 06 Mar 2011 05:54:15 +0000</pubDate>
		<dc:creator>Ricardo Paiva</dc:creator>
				<category><![CDATA[Ajax]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[java script]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[não intrusivo]]></category>
		<category><![CDATA[unobtrusive]]></category>

		<guid isPermaLink="false">http://blog.werneckpaiva.com.br/?p=74</guid>
		<description><![CDATA[{lang: 'pt-BR'}O que é javascript não intrusivo? Quando criamos nossas páginas HTML, repletas de javascript, estamos sujeitos a espalhar trechos de código por diversos elementos da página, como botões, links, imagens etc. Esta é uma prática bastante comum e muito ruim, pois dificulta muito a manutenção e reuso do código gerado. O javascript não intrusivo [...]]]></description>
			<content:encoded><![CDATA[<div name="googleone_share_1" style="position:relative;z-index:5;float: left; margin-right: 10px;"><g:plusone size="standard" count="" href="http://blog.werneckpaiva.com.br/2011/03/ajax-com-java-script-nao-intrusivo-unobtrusive-ajax/">{lang: 'pt-BR'}</g:plusone></div><p>O que é javascript não intrusivo?</p>
<p>Quando criamos nossas páginas HTML, repletas de javascript, estamos sujeitos a espalhar trechos de código por diversos elementos da página, como botões, links, imagens etc. Esta é uma prática bastante comum e muito ruim, pois dificulta muito a manutenção e reuso do código gerado. O javascript não intrusivo (unobtrusive javascript) sugere que o código HTML fique separado do código javascript, trazendo organização, modularidade, e maior facilidade na criação de plugins. Ao invés de adicionarmos códigos javascript aos eventos diretamente nos elementos HTML, construimos um HTML mais simples, e adicionamos um código javascript que irá observar quando o evento for disparado.</p>
<p>Ao invés de fazermos isso:</p>
<pre class="brush:js">&lt;script&gt;
   function exibeResultado(){
       // faz alguma coisa ...
   }
&lt;/script&gt;

&lt;button onclick="exibeResultado()"&gt;Exibir&lt;/button&gt;</pre>
<p>Fazemos isso:</p>
<pre class="brush:js">&lt;script&gt;
    // Quando a página for carregada
    $(function(){
         $("#btnExibir").click(function(){
               // faz alguma coisa ...
         }
     }
&lt;/script&gt;
&lt;button id="btnExibir"&gt;Exibir&lt;/button&gt;</pre>
<p>Neste exemplo, fazendo uso da biblioteca jQuery para definir o comportamento do clique do botão quando a página for totalmente carregada.</p>
<p>Esta estratégia é especialmente útil quando lidamos com páginas que serão exibidas em diferentes navegadores. Se um determinado navegador não possui uma certa funcionalidade, o nosso Javascript simplesmente falha, mas o usuário ainda será capaz de ver o conteúdo original.</p>
<p>Um bom exemplo desta técnica, é o framework <a href="http://www.huddletogether.com/projects/lightbox2/" target="_blank">Lightbox 2</a> para exibir fotos na forma de um album. Par ausá-lo basta criar um link para a foto original e avisar ao lightbox que aquela foto será exibida através do atributo <tt>rel="lightbox"</tt> no link. Algo como:</p>
<pre class="brush:xml">&lt;a href="images/foto1.jpg" rel="lightbox" title="Viagem para Porto de galinhas"&gt;Foto da viagem&lt;/a&gt;</pre>
<p>Se o Lightbox não funcionar, o usuário ainda será capaz de clicar no link e abrir a foto em outra página.</p>
<p>Para que um link vire uma chamada ajax devemos proceder da mesma maneira, via Javascript trocamos o comportamento padrão por uma chamada ajax. Entretanto, só isso não basta. É preciso dizer ao browser que precisamos remover a ação de mudar de página quando o link for clickado.</p>
<pre class="brush:xml">&lt;script&gt;

    // Extrai o postID da url de um link
    function extractPostID(url){
<div id="_mcePaste">        var token = "postID="</div>
<div id="_mcePaste">        var posToken = url.indexOf(token);</div>
<div id="_mcePaste">        var postID = url.substring(posToken + token.length);</div>
<div id="_mcePaste">        return postID;</div>

}

    // Carrega o post via Ajax
    function loadPost(postID){
        // Faz Carrega o conteúdo da chamada ajax no elemento com id "post"
        $("#post").load("post.php?postID="+postID, function(){
            // Ao terminar a chamada ajax, redefinir o comportamento dos botões
            setNavHandler();
        });,
    } 

    // Cancela o comportamento padrão de um click em um link
    function preventDefault(e){
        if( e.preventDefault ) { e.preventDefault(); }
        e.returnValue = false;

        if (e.stopPropagation) e.stopPropagation();   // DOM Level 2
        else e.cancelBubble = true;  // IE
    } 

    // Substitui o redirecionamento de um link para uma chamada Ajax
    function btnNavClickHandler(e){
        if (!e) e = window.event;  // IE event model

        // Obtém o endereço do link
        var target;
        if (e.target) target = e.target;
        else if (e.srcElement) target = e.srcElement;
        else if (target.nodeType == 3) target = target.parentNode; // defeat Safari bug

        preventDefault(e);
        var postID = extractPostID(target.href);
        loadPost(postID);
     }

     // Define uma ação para os botões de próximo e anterior
     function setNavHandler(){
        $("#btnNext").click(btnNavClickHandler);
        $("#btnPrev").click(btnNavClickHandler); 
     }

     // Quando a página é carregada
     $(function(){
         setNavHandler()
     });
&lt;/script&gt;

&lt;div id="post"&gt;
    &lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed enim velit, pretium at volutpat vitae, volutpat at augue. Nullam tempus, urna id gravida vestibulum, enim urna sodales tortor, in pulvinar nisi eros pretium est.&lt;/p&gt;
    &lt;a id="btnPrev" href="post.php?postID=1"&gt;Post Anterior&lt;/a&gt;
    &lt;a id="btnNext" href="post.php?postID=3"&gt;Próximo Post&lt;/a&gt;
&lt;/div&gt;</pre>
<p>O exemplo acima representa uma página com um post. Ao clicar nos botões de próximo ou anterior, o usuário seria redirecionado para uma outra página, porém, alteramos este comportamento para que uma chamada Ajax seja feita, e o resultado substitua o conteúdo da página.</p>
<p>Repare que cancelamos o comportamento padrão do link criando um método genérico  preventDefault(), que engloba rotinas específicas para cada browser. Este é o &#8220;pulo do gato&#8221; do Ajax não-intrusivo, pois sem isto, o link continuaria funcionando e, mesmo que o Ajax fosse executado, ele não iria impedir que a página fosse redirecionada.</p>
<p>Exemplo com suporte a botão de back e histórico: <a title="Abrir exemplo em uma nova janela" href="/exemplos/unobtrusive-ajax/post.php" target="_blank">unobstrusive-ajax</a><br />
Não abordei o controle de histórico neste artigo para mantê-lo mais simples.</p>
<p>Código fonte do exemplo (incluindo os arquivos PHP):  <a href="http://blog.werneckpaiva.com.br/wp-content/uploads/2011/03/unobtrusive-ajax.zip">unobtrusive-ajax.zip</a>.</p>
<div name="googleone_share_1" style="position:relative;z-index:5;float: left; margin-right: 10px;"><g:plusone size="standard" count="" href="http://blog.werneckpaiva.com.br/2011/03/ajax-com-java-script-nao-intrusivo-unobtrusive-ajax/">{lang: 'pt-BR'}</g:plusone></div>]]></content:encoded>
			<wfw:commentRss>http://blog.werneckpaiva.com.br/2011/03/ajax-com-java-script-nao-intrusivo-unobtrusive-ajax/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Percorrendo arquivos com nomes com espaço no Linux</title>
		<link>http://blog.werneckpaiva.com.br/2011/01/percorrendo-arquivos-com-nomes-com-espaco-no-linux/</link>
		<comments>http://blog.werneckpaiva.com.br/2011/01/percorrendo-arquivos-com-nomes-com-espaco-no-linux/#comments</comments>
		<pubDate>Fri, 14 Jan 2011 04:02:07 +0000</pubDate>
		<dc:creator>Ricardo Paiva</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Shell Script]]></category>
		<category><![CDATA[arquivos]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[iterando]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[pipe]]></category>
		<category><![CDATA[shell]]></category>
		<category><![CDATA[shellscript]]></category>
		<category><![CDATA[while]]></category>

		<guid isPermaLink="false">http://blog.werneckpaiva.com.br/?p=148</guid>
		<description><![CDATA[Quando manipulamos arquivos com nomes compostos, ou seja, que contenham espaço no nome, alguns comandos podem não funcionar. Aprenda como usar o comando while em conjunto com o comando read e evitar esse problema.]]></description>
			<content:encoded><![CDATA[<div name="googleone_share_1" style="position:relative;z-index:5;float: left; margin-right: 10px;"><g:plusone size="standard" count="" href="http://blog.werneckpaiva.com.br/2011/01/percorrendo-arquivos-com-nomes-com-espaco-no-linux/">{lang: 'pt-BR'}</g:plusone></div><p>O pipe é uma das grandes invenções do Unix. Através dele podemos encadear processos e transformar pequenas utilitários em ferramentas capazes de realizar tarefas realmente complexas.</p>
<p>A idéia é muito simples: todo processo tem associado a ele um arquivo de entrada, um arquivo de saída e um arquivo de erro. Como no Linux (ou qualquer outro Unix) um dispositivo é representado por um arquivo A entrada padrão de um processo, portanto, é o teclado, e a saída padrão e de err é a tela (console). A grande engenhosidade do pipe é a capacidade de redirecionar a saída de um processo, que seria para tela, para a entrada de outro processo. Isto permite que comandos sejam encadeados para realizar uma tarefa mais sofisticada.</p>
<p>Além de podermos encadear processos, o shell (aí depende do shell) ainda nos oferece uma linguagem de programação, o ShellScript. O ShellScript permite criar variáveis, condicionais, loopings etc. Usarei o Bash como referência.</p>
<p>Vamos pensar num bastante comum: Você tem um projeto em um diretório cheio de arquivos e subdiretórios. Você deseja mudar a permissão de todos os arquivos com a extensão *.pdf.</p>
<p>Primeiramente, precisamos procurar pelos arquivos usando o comando <em>find</em></p>
<pre class="brush:shell">$ find projeto/ -iname "*.pdf"
projeto/relatorios/relatorio.pdf
projeto/contratos/contrato servico.pdf
projeto/documentacao/auditoria.pdf
projeto/documentacao/especificacao de produtos.pdf</pre>
<p>Em seguida, precisamos passar o resultado do comando <em>find</em> para o comando <em>chmod</em>, para mudarmos as permissões. Infelizmente o comando chmod não recebe dados da entrada padrão, portanto, iremos usar uma outra ferramenta, as aspas invertidas (ou crase). Este mecanismo permite que a saída de um processo seja usado como parâmetro na chamada de outro processo:</p>
<pre class="brush:shell">chmod 640 `find projeto/ -iname "*.pdf"`
chmod: cannot access 'projeto/contratos/contrato': No such file or director
chmod: cannot access 'servico.pdf': No such file or director
chmod: cannot access 'projeto/documentacao/especificacao': No such file or director
chmod: cannot access 'de': No such file or director
chmod: cannot access 'produtos.pdf': No such file or director</pre>
<p>Perceberam a confusão que se deu quando os arquivos tem espaço nos nomes?. Ao invés de iterar para cada arquivo, o <em>chmod</em> iterou para cada parte de nome entre espaços. </p>
<p>Ao invés de usar a crase, podemos usar o <em>while</em> em combinação com o comando <em>read</em>, e esquecer as dores de cabeça. Tudo funciona perfeitamente.</p>
<pre class="brush:shell">find projeto/ -name "*.pdf" | while read f; do chmod 640 "$f"; done</pre>
<p>O comando <em>while </em>irá iterar em cada nova entrada do comando <em>read,</em> e este, por sua vez, irá ler cada &#8216;linha&#8217; da saída do comando <em>find</em> e gravar na variável f. A variável f pode ser usada com qualquer tipo de comando dentro do bloco &#8220;do &#8230; done&#8221;. Tudo se encaixa.</p>
<p>Eu uso essa estratégia para uma infinidade de coisas:</p>
<pre class="brush:shell"># Mudar as permissões apenas de diretórios
find . -type d | while read f; do chmod 775 "$f". done

# Procurar um texto em apenas um tipo de arquivo
find . -iname "*.xml" | while read f; do grep "palavra" "$f"; done

# remover diretórios .svn de um projeto
find . -iname ".svn" -type d | while read d; do rm -rf "$d"; done</pre>
<p>Seria possível usar o parâmetro -delete do comando <em>find </em> para excluir arquivos, mas este parâmetro não funcionaria com diretórios não vazios.</p>
<div name="googleone_share_1" style="position:relative;z-index:5;float: left; margin-right: 10px;"><g:plusone size="standard" count="" href="http://blog.werneckpaiva.com.br/2011/01/percorrendo-arquivos-com-nomes-com-espaco-no-linux/">{lang: 'pt-BR'}</g:plusone></div>]]></content:encoded>
			<wfw:commentRss>http://blog.werneckpaiva.com.br/2011/01/percorrendo-arquivos-com-nomes-com-espaco-no-linux/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Geolocalização com HTML 5</title>
		<link>http://blog.werneckpaiva.com.br/2011/01/geolocalizacao-com-html-5/</link>
		<comments>http://blog.werneckpaiva.com.br/2011/01/geolocalizacao-com-html-5/#comments</comments>
		<pubDate>Thu, 13 Jan 2011 15:09:58 +0000</pubDate>
		<dc:creator>Ricardo Paiva</dc:creator>
				<category><![CDATA[Dispositivos Móveis]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[geolocalização]]></category>
		<category><![CDATA[geolocation]]></category>
		<category><![CDATA[Google Maps]]></category>
		<category><![CDATA[latitude]]></category>
		<category><![CDATA[longitude]]></category>

		<guid isPermaLink="false">http://blog.werneckpaiva.com.br/?p=45</guid>
		<description><![CDATA[Aprenda como usar o recurso de localização do HTML5. É possível obter as coordenadas (latitude e longitude) de um usuário e exibí-las em um mapa usando o google maps.]]></description>
			<content:encoded><![CDATA[<div name="googleone_share_1" style="position:relative;z-index:5;float: left; margin-right: 10px;"><g:plusone size="standard" count="" href="http://blog.werneckpaiva.com.br/2011/01/geolocalizacao-com-html-5/">{lang: 'pt-BR'}</g:plusone></div><p>Obter a localização do usuário é uma tarefa especialmente útil para aplicações web, sobretudo as que irão rodar em dispositivos móveis. Felizmente a especificação do HTML 5 pensou nisso e criou uma API que fornece os dados que a gente precisa, de uma maneira relativamente simples.</p>
<p>Uma característica da especificação, é que ela não restringe qual será a origem da informação. Ou seja, em um dispositivo móvel, como um celular, seria possível obter a localização do usuário usando o GPS, a localização da antena de celular, informações da antena wi-fi, base dados de endereço IP etc. O browser irá tratar a origem destas informações e, provavelmente, irá passar essa responsabilidade para o sistema operacional.</p>
<p>Obter a localização de um usuário depende da autorização dele, portanto esta é uma tarefa assíncrona. Uma vez que nosso script solicita a localização, uma mensagem será exibida pedindo ao usuário a autorização para que aquele site tenha acesso às informações de posicionamento.</p>
<p><img class="aligncenter size-full wp-image-95" title="geolocation" src="http://blog.werneckpaiva.com.br/wp-content/uploads/2011/01/geolocation.jpg" alt="" width="600" height="31" /></p>
<p>A localização do usuário é representada por uma coordenada, formada pela latitude e longitude. O objeto recebido como resultado é do tipo Position e possui um atributo do tipo Coordinates com uma série de informações:</p>
<ul>
<li>latitude: Posicionamento horizontal no mapa</li>
<li>longitude: Posicionamento vertical no mapa</li>
<li>accutacy: precisão em metros das coordenadas de localização. Está sempre disponível.</li>
<li>altitude: altitude em metros em relação ao nível do mar</li>
<li>altitudeAccuracy: precisão em metros da altitude</li>
<li>heading: direção da viagem, definida em graus (0º, 360º), contando no sentido horário em relação ao norte geográfico.</li>
<li>speed: velocidade de terra do usuário em metros/segundo.</li>
</ul>
<p>Se o valor da altitude, heading e speed não estiverem disponíveis, os atributos terão valor null.</p>
<p>Veja o codigo abaixo para obter a localização do usuário:</p>
<pre class="brush:js"> function updateMap(position) {
      // Localização formada por longitude e latitude
      var lat = position.coords.latitude;
      var long = position.coords.longitude;
}

// Solicita a localização
navigator.geolocation.getCurrentPosition(updateMap);</pre>
<p>É possível também observar a posição do usuário a medida que ela se altera:</p>
<pre class="brush:js">&lt;script&gt;
function updateMap(position) {
  // Atualiza um mapa com a nova posição
}

// solicita a localização continuamente
var watchId = navigator.geolocation.watchPosition(scrollMap);

function btnStopMapClickHandler() {
     // cancela a requisição
    navigator.geolocation.clearWatch(watchId);
}
&lt;/script&gt;

&lt;button onclick="btnStopMapClickHandler()"&gt;Parar&lt;/button&gt;</pre>
<p>É possível ainda detectar quando o recurso de localização não está disponível, ou quando o usuário não permitiu que o seu script obtenha sua localização:</p>
<pre class="brush:js">&lt;script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true"&gt;&lt;/script&gt;
&lt;script type="text/javascript"&gt; 

	// Características default do mapa
	var mapOptions = {
		zoom: 8,
		mapTypeId: google.maps.MapTypeId.ROADMAP
	};

	var map;

	function updateMapPosition(position){
		var lat = position.coords.latitude;
		var lng = position.coords.longitude;

		// Centraliza o mapa na localização do usuário
		var point = new google.maps.LatLng(lat, lng);
		map.setCenter(point);

		// Adiciona um círculo referente à precisão da medida
		var circle = new google.maps.Circle({
			center: point,
			strokeColor: "#003399",
			strokeOpacity: 0.8,
			strokeWeight: 1,
			fillColor: "#003399",
			fillOpacity: 0.35,
			radius: position.coords.accuracy
		});
		circle.setMap(map);

		// Adiciona uma marca ao mapa
		var options = { position: point }
		var marker = new google.maps.Marker(options);
		marker.setMap(map);
	}

	function handleError(error){
		alert("Não foi possível sua localização");
	}

	this.onload=function(){
		// Cria um mapa do Google Maps
		map = new google.maps.Map(document.getElementById("geoMap"), mapOptions);
		// O browser é compatível com geolocalização?
		if (navigator.geolocation) {
			 // Solicita a localização do usuário
		  	 navigator.geolocation.getCurrentPosition(updateMapPosition, handleError);
		}
	}

&lt;/script&gt; 

&lt;div id="geoMap"&gt;&lt;/div&gt;</pre>
<p>Abaixo, um exemplo funcional da API de geolocalização do HTML 5 e o mapa do GoogleMaps:</p>
<p>
<style>
#geoMap { width:700px; height:500px; border:1px solid #999999; margin:auto; }
</style>

<button onclick="showLocation()">Mostrar localização</button>
<div id="geoMap"></div>

<script src="http://maps.google.com/maps/api/js?sensor=true" type="text/javascript"></script> 
<script type="text/javascript">// <![CDATA[
var mapOptions = { zoom: 8, mapTypeId: google.maps.MapTypeId.ROADMAP }; 
var map;  
function updateMapPosition(position){ 
  var lat = position.coords.latitude; 
  var lng = position.coords.longitude; 
  var point = new google.maps.LatLng(lat, lng); 
  map.setCenter(point); 
  var circle = new google.maps.Circle({ center: point, strokeColor: "#003399", strokeOpacity: 0.8, strokeWeight: 1, fillColor: "#003399", fillOpacity: 0.35, radius: position.coords.accuracy }); 
  circle.setMap(map); 
  var options = { position: point } ; 
  var marker = new google.maps.Marker(options);  
  marker.setMap(map); 
} 
function handleError(error){ alert("Não foi possível sua localização"); } 
function showLocation(){ 
  if (navigator.geolocation) {  
    navigator.geolocation.getCurrentPosition(updateMapPosition, handleError); 
  }
} 
map = new google.maps.Map(document.getElementById("geoMap"), mapOptions);
// ]]&gt;</script>
</p>
<div name="googleone_share_1" style="position:relative;z-index:5;float: left; margin-right: 10px;"><g:plusone size="standard" count="" href="http://blog.werneckpaiva.com.br/2011/01/geolocalizacao-com-html-5/">{lang: 'pt-BR'}</g:plusone></div>]]></content:encoded>
			<wfw:commentRss>http://blog.werneckpaiva.com.br/2011/01/geolocalizacao-com-html-5/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Drag and drop de múltiplos arquivos para upload via Ajax com HTML 5</title>
		<link>http://blog.werneckpaiva.com.br/2011/01/drag-and-drop-de-multiplosarquivos-para-upload-com-html-5/</link>
		<comments>http://blog.werneckpaiva.com.br/2011/01/drag-and-drop-de-multiplosarquivos-para-upload-com-html-5/#comments</comments>
		<pubDate>Wed, 05 Jan 2011 18:11:03 +0000</pubDate>
		<dc:creator>Ricardo Paiva</dc:creator>
				<category><![CDATA[HTML5]]></category>
		<category><![CDATA[Arquivo]]></category>
		<category><![CDATA[Drag and Drop]]></category>
		<category><![CDATA[Upload]]></category>

		<guid isPermaLink="false">http://blog.werneckpaiva.com.br/?p=67</guid>
		<description><![CDATA[{lang: 'pt-BR'}Uma funcionalidade bem esperada do HTML5 é a capacidade de arrastar e soltar arquivos para dentro do browser. Desta forma, o usuário é capaz de fazer upload ou manipular arquivos, sem ter navegar por toda árvore de diretórios do sistema através da caixa de diálogo padrão do sistema.  É possível também fazer upload de [...]]]></description>
			<content:encoded><![CDATA[<div name="googleone_share_1" style="position:relative;z-index:5;float: left; margin-right: 10px;"><g:plusone size="standard" count="" href="http://blog.werneckpaiva.com.br/2011/01/drag-and-drop-de-multiplosarquivos-para-upload-com-html-5/">{lang: 'pt-BR'}</g:plusone></div><p>Uma funcionalidade bem esperada do HTML5 é a capacidade de arrastar e soltar arquivos para dentro do browser. Desta forma, o usuário é capaz de fazer upload ou manipular arquivos, sem ter navegar por toda árvore de diretórios do sistema através da caixa de diálogo padrão do sistema.  É possível também fazer upload de múltiplos arquivos simultaneamente usando AJAX, e é isso que eu explicarei neste artigo.</p>
<p>Infelizmente, até o momento, a API de manipulação de arquivos ainda não está plenamente definida. O exemplo construído funciona apenas no <strong>Firefox </strong>e no<strong> Chrome</strong>.</p>
<p>O passo principal da especificação HTML 5 para oferecer o mecanismo de Drag and Drop, foi definir um conjunto de eventos para os diversos passos que executamos ao arrastarmos e soltarmos um objeto. E a API de Drag and Drop não está restrita à manipulação de arquivos. É possível manipular qualquer elemento visual da página, usando a nova <a class="wp-oembed" href="http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#dnd" target="_blank">API nativa de Drag and Drop</a>.</p>
<p>Em nosso caso não precisamos nos preocupar com o objeto que será arrastado, pois o sistema operacional já irá tratá-lo. Nossos esforços estarão focados em tratar a zona alvo do arquivo. A idéia principal é adicionar <em>event listeners</em> aos eventos de drag and drop e tratá-los da maneira correta.</p>
<p>Para que elemento html aceite a operação de &#8216;drop&#8217;, é preciso que ele escute pelo menos um dos três eventos:</p>
<ol>
<li><strong>dragenter</strong>: Usado para definir se o elemento aceitará ou não o drop de um objeto. Para aceitar, deve-se cancelar o compartamento padrão do evento.</li>
<li><strong>dragover</strong>: Determina que tipo de feedback será exibido ao usuário (mudança de cor, alpha etc).</li>
<li><strong>drop</strong>: Evento que permite realizar uma operação quando o usuário solta o objeto na zona alvo.</li>
</ol>
<p>Existe ainda mais um evento <strong>dragleave </strong>que é executado quando o usuário deixa a zona alvo sem realizar o &#8216;drop&#8217; do objeto.</p>
<p>Uma parte importante da manipulação de eventos de Drag and Drop é evitar o comportamento padrão do browser, e isso é feito com o método preventDefault() do objeto de evento</p>
<pre class="brush:js">dropArea.addEventListener("dragover", function(event) {
	  event.preventDefault();
}, true);</pre>
<p>O objeto de evento, obtido quando se escuta um determinado evento, transporta um outro objeto de tipo DataTransfer. Este objeto, entre outras coisas, contém a lista de arquivos que foram selecionados e arrastados para a zona alvo do Drag and Drop.</p>
<pre class="brush:js">dropArea.addEventListener("drop", function(event) {
    for (var i = 0; i&lt;allFiles.length; i++){
	var file = event.dataTransfer.files[i];
        // ...
    }
}</pre>
<p>Uma outra novidade da API html5 é interface FileReader, que provê métodos para leitura de arquivos e eventos para manipular o resultado. Podemos juntar a interface FileReader com os arquivos contidos no objeto DataTransfer e fazer uma manipulação para criarmos um elemento do tipo &lt;img&gt; cujo atributo src é uma url fictícia, com o conteúdo do arquivo. Desta forma, criamos um preview do arquivo que será feito upload (assumindo que o arquivo será uma imagem).</p>
<pre class="brush:js">var file = event.dataTransfer.files[0]
var img = document.createElement("img");
var reader = new FileReader();
reader.onloadend = function() {
	img.src = this.result;
}
reader.readAsDataURL(file);
img.classList.add("obj");
var content  = document.getElementById("content");
content.appendChild(img);</pre>
<p>Por fim, precisamos realizar a operação de upload usando Ajax. Queremos fazer upload de todos os arquivos simultaneamente. Ainda não existe um consenso a respeito da maneira como os dados são enviados para o servidor. O objeto File, implementado pelo Firefox, possui o método getAsBinary(), que não é definida pela especificação. Já a versão 4, deste mesmo browser, trás o objeto FormData, que permite enviar os dados de forma mais simples e eficiente. O objeto FormData também foi implementado pelo Chrome e me parece que se firmará como futuro padrão.</p>
<p>A interface FormData é bem simples de ser usada e não exige manipulação direta do protocolo de comunicação. Através dela é possível associar identificadores/valores de um formulário e enviar todo o objeto via Ajax.</p>
<pre class="brush:js">var f = new FormData();
var file = event.dataTransfer.files[0]
f.append("images", file);
var request = new XMLHttpRequest();
request.open("POST", "upload.php", true);
request.send(f);</pre>
<p>O envio é feito de forma eficiente, e o arquivo não precisa ser todo armazenado em memória, como é o caso do segundo método de envio que veremos a seguir.</p>
<p>A segunda forma de envio, compatível com o Firefox, envia os dados via POST usando o header de transmissão de arquivos (multipart/form-data). Os dados do arquivo precisam estar em um formato específico, ditado pela RFC 2388. Criamos, portanto, uma grande string contendo todo o conteúdo dos arquivos:</p>
<pre class="brush:js">var boundary = '------multipartformboundary' + (new   Date).getTime();
var dashdash = '--';
var crlf     = '\r\n';

/* String seguindo a RFC2388. */
var builder = '';

builder += dashdash;
builder += boundary;
builder += crlf;

/* Para cada arquivo que foi arrastado. */
for (var i=0; i&lt;filesToUpload.length; i++){
       var file = event.dataTransfer.files[i];

       /* Gerar os headers. */
       builder += 'Content-Disposition: form-data; name="images[]"';
       if (file.fileName) {
           builder += '; filename="' + file.fileName + '"';
       }
       builder += crlf;

       builder += 'Content-Type: application/octet-stream';
       builder += crlf;
       builder += crlf; 

       /* Anexar os dados binários. */
       builder += file.getAsBinary();
       builder += crlf;

       builder += dashdash;
       builder += boundary;
       builder += crlf;
}

/* Marcar o final da requisição. */
builder += dashdash;
builder += boundary;
builder += dashdash;
builder += crlf;</pre>
<p>Finalmente criamos a rquisição Ajax através de um objeto do tipo XMLHttpRequest e enviamos a string criada que contém o conteúdo dos arquivos já no formato adequado.</p>
<p><span style="font-family: Consolas, Monaco, 'Courier New', Courier, monospace; line-height: 18px; font-size: 12px; white-space: pre;">var request = new XMLHttpRequest();</span></p>
<pre class="brush:js">request.open("POST", "upload.php", true);
request.setRequestHeader('content-type', 'multipart/form-data; boundary='+ boundary);
request.sendAsBinary(builder);        

request.onload = function(event) {
     alert("Arquivo enviado");
};</pre>
<p>Voila! Múltiplos arquivos sendo enviados usando Drag and Drop por meio de requisições Ajax.</p>
<p>Você pode olhar o código fonte de meu exemplo: <a href="http://blog.werneckpaiva.com.br/wp-content/uploads/2011/01/upload.zip">upload.zip</a><br />
Adicionei também um arquivo PHP para manipular os arquivos que chegam no servidor. Não se esqueça que, para executar o exemplo, é preciso dar permissão de escrita na pasta upload/.</p>
<p>Bons estudos e até o próximo artigo.</p>
<div name="googleone_share_1" style="position:relative;z-index:5;float: left; margin-right: 10px;"><g:plusone size="standard" count="" href="http://blog.werneckpaiva.com.br/2011/01/drag-and-drop-de-multiplosarquivos-para-upload-com-html-5/">{lang: 'pt-BR'}</g:plusone></div>]]></content:encoded>
			<wfw:commentRss>http://blog.werneckpaiva.com.br/2011/01/drag-and-drop-de-multiplosarquivos-para-upload-com-html-5/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

