<?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>ZedBlog &#187; Desarrollo Web</title>
	<atom:link href="http://blog.zedplan.com/category/desarrollo-web/feed" rel="self" type="application/rss+xml" />
	<link>http://blog.zedplan.com</link>
	<description></description>
	<lastBuildDate>Mon, 31 May 2010 14:57:21 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Internacionalización en Javascript usando Poedit</title>
		<link>http://blog.zedplan.com/internacionalizacion-en-javascript-usando-poedit/143</link>
		<comments>http://blog.zedplan.com/internacionalizacion-en-javascript-usando-poedit/143#comments</comments>
		<pubDate>Mon, 31 May 2010 14:57:21 +0000</pubDate>
		<dc:creator>Demián Rodríguez</dc:creator>
				<category><![CDATA[Desarrollo Web]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[gettext]]></category>
		<category><![CDATA[internacionalización]]></category>
		<category><![CDATA[localización]]></category>
		<category><![CDATA[poedit]]></category>

		<guid isPermaLink="false">http://blog.zedplan.com/?p=143</guid>
		<description><![CDATA[Gettext es uno de los sistemas mas utilizados para internacionalizar aplicaciones. Básicamente, un programa (xgettext) analiza el codigo fuente de los archivos de la aplicación, extrae las cadenas  y genera un catálogo que permite al usuario realizar la traducción usando una aplicación, como por ejemplo Poedit. Luego el catálogo (archivo .po) se compila y se crea [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://es.wikipedia.org/wiki/Gettext" target="_blank">Gettext</a> es uno de los sistemas mas utilizados para internacionalizar aplicaciones. Básicamente, un programa (xgettext) analiza el codigo fuente de los archivos de la aplicación, extrae las cadenas  y genera un catálogo que permite al usuario realizar la traducción usando una aplicación, como por ejemplo <a href="http://www.poedit.net/" target="_blank">Poedit</a>. Luego el catálogo (archivo .po) se compila y se crea un archivo binario con extensión .mo, el cual posee todas las traducciones en un formato optimizado.<br />
Lamentablemente, el comando xgettext carece de soporte para Javascript. El intérprete de Python es lo mas cercano posible, pero a mi no me ha funcionado correctamente porque no analiza bien las expresiones regulares y algunas cadenas.</p>
<p>Hasta ahora la forma mas común de internacionalizar una aplicación en Javascript era identificar cada una de las frases con una clave o variable, pero el problema de esta técnica es que hay que agregar las frases manualmente.</p>
<p>He decidido desarrollar un pequeño script en PHP que se utiliza en conjunto con Poedit y facilita esta tarea. Éste funciona como intérprete Javascript y analiza el codigo fuente para extraer las cadenas y generar un catálogo,  luego se utiliza otro script para convertirlo a formato JSON, el cual se incluye en nuestra página como un archivo externo. De esta manera tenemos disponible las frases traducidas en una variable global.</p>
<p>El intérprete es lo suficientemente inteligente para detectar cadenas con o sin comillas escapadas y excluir las expresiones regulares y comentarios.</p>
<p>Para utilizarlo, primero debemos instalar Poedit. Abrimos la aplicación, vamos a Archivo -&gt; Nuevo catálogo y completamos los datos:</p>
<p style="text-align: center;"><a href="http://blog.zedplan.com/wp-content/uploads/Settings_001.png"><img class="size-medium wp-image-147 aligncenter" title="Settings_001" src="http://blog.zedplan.com/wp-content/uploads/Settings_001-300x255.png" alt="" width="300" height="255" /></a></p>
<p>Cambiamos a la solapa Rutas y agregamos la ruta de la carpeta que contiene nuestros archivos Javascript:</p>
<p style="text-align: center;"><a href="http://blog.zedplan.com/wp-content/uploads/Settings_002.png"><img class="size-medium wp-image-148 aligncenter" title="Settings_002" src="http://blog.zedplan.com/wp-content/uploads/Settings_002-300x255.png" alt="" width="300" height="255" /></a></p>
<p>Cambiamos a la última solapa y agregamos los nombres de las funciones que usaremos para invocar una traducción. En este caso usamos el guión bajo y como alias &#8220;jsgettext&#8221;:</p>
<p style="text-align: center;"><a href="http://blog.zedplan.com/wp-content/uploads/Settings_003.png"><img class="size-medium wp-image-149 aligncenter" title="Settings_003" src="http://blog.zedplan.com/wp-content/uploads/Settings_003-300x255.png" alt="" width="300" height="255" /></a></p>
<p>La magia ocurre cuando agregamos el nuevo intérprete. Vamos a Editar -&gt; Preferencias, seleccionamos la solapa de intérpretes y hacemos click en &#8220;Agregar&#8221;:</p>
<p style="text-align: center;"><a href="http://blog.zedplan.com/wp-content/uploads/Parser-setup_005.png"><img class="size-medium wp-image-153  aligncenter" title="Parser setup_005" src="http://blog.zedplan.com/wp-content/uploads/Parser-setup_005-215x300.png" alt="" width="215" height="300" /></a></p>
<p>Luego simplemente debemos actualizar el catálogo haciendo click en el botón &#8220;Actualizar catálogo&#8221; y nos aparecerán las frases extraídas de los archivos JS. Nos tomamos el trabajo de traducirlas y guardamos el catálogo.<br />
<span style="font-size: 13.3333px;">Descartamos el archivo .mo que nos genera el programa ya que a continuación vamos a transformar el archivo .po en un .js usando la utilidad po2json.php:</span></p>
<p><code>php /ruta/a/jsgettext/po2json.php -i catalog.po -o l10n.js -k l10n</code></p>
<p>Eso nos genera un archivo con la variable global &#8220;l10n&#8221;, cuyo valor es un objeto que contiene las frases traducidas.<br />
Podemos acceder a una traducción usando la función &#8220;_&#8221;:</p>
<p><code>function _(s) {<br />
return typeof l10n[s] != 'undefined' ? l10n[s] : s;<br />
}<br />
alert(_("Hello world"))<br />
</code></p>
<p>El código esta alojado en Google Code: <a href="http://code.google.com/p/jsgettext/">http://code.google.com/p/jsgettext/<br />
</a><span style="font-size: 13.3333px;">Pueden reportar bugs o enviar alguna sugerencia o mejora <img src='http://blog.zedplan.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </span></p>
<a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zedplan.com%2Finternacionalizacion-en-javascript-usando-poedit%2F143&amp;linkname=Internacionalizaci%C3%B3n%20en%20Javascript%20usando%20Poedit"><img src="http://blog.zedplan.com/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share/Bookmark"/></a>]]></content:encoded>
			<wfw:commentRss>http://blog.zedplan.com/internacionalizacion-en-javascript-usando-poedit/143/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Upload de archivos con barra de progreso usando XMLHttpRequest y la nueva API de HTML5</title>
		<link>http://blog.zedplan.com/upload-de-archivos-con-barra-de-progreso-usando-xmlhttprequest-y-la-nueva-api-de-html5/117</link>
		<comments>http://blog.zedplan.com/upload-de-archivos-con-barra-de-progreso-usando-xmlhttprequest-y-la-nueva-api-de-html5/117#comments</comments>
		<pubDate>Mon, 15 Feb 2010 18:12:14 +0000</pubDate>
		<dc:creator>Demián Rodríguez</dc:creator>
				<category><![CDATA[AJAX]]></category>
		<category><![CDATA[Desarrollo Web]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[FileReader]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[XMLHttpRequest]]></category>

		<guid isPermaLink="false">http://blog.zedplan.com/?p=117</guid>
		<description><![CDATA[Creo que para muchos ha sido un desafío implementar un upload de archivos con barra de progreso ya que se debía enviar el formulario a un iframe oculto y mientras tanto revisar el progreso haciendo peticiones AJAX en pequeños intervalos. A esto había que sumarle todo el proceso del lado del servidor para calcular el [...]]]></description>
			<content:encoded><![CDATA[<p>Creo que para muchos ha sido un desafío implementar un upload de archivos con barra de progreso ya que se debía enviar el formulario a un iframe oculto y mientras tanto revisar el progreso haciendo peticiones AJAX en pequeños intervalos. A esto había que sumarle todo el proceso del lado del servidor para calcular el porcentaje, y no todos los lenguajes nos proveen esa información.</p>
<p>Si bien la posibilidad de leer archivos ya existía en Firefox 3, combinado con las nuevas características de la última versión de este navegador, se pueden implementar funcionalidades mas avanzadas.</p>
<p>Con la nueva API podemos leer archivos del lado del cliente y de este modo enviar el contenido binario mediante una petición usando el nuevo método <code>sendAsBinary()</code> del objeto  <code>XMLHttpRequest</code>.</p>
<p>Para acceder a los archivos, tenemos la propiedad <code>files</code> disponible en los elementos <code>input</code> de tipo <code>file</code> y ademas en el objeto <a href="https://developer.mozilla.org/En/DragDrop/DataTransfer" target="_blank"><code>DataTransfer</code></a> (accesible en las operaciones de drag &amp; drop). Esta propiedad es un objeto de tipo <code><a href="https://developer.mozilla.org/en/DOM/FileList" target="_blank">FileList</a></code> que representa una colección de objetos de tipo <a href="https://developer.mozilla.org/en/DOM/File" target="_blank"><code>File</code></a>. Un objeto <code>File</code> dispone de propiedades que proveen información básica del archivo, como el nombre, el tipo y el tamaño.</p>
<p>Gecko 1.9.2 imlementa el objeto <a href="https://developer.mozilla.org/en/DOM/FileReader" target="_blank"><code>FileReader</code></a>, el cual nos permite leer archivos en forma asincrónica y asi evitar que el navegador no responda durante la operación de lectura.</p>
<p>Si bien tenemos la posibilidad de enviar el contenido de un archivo como binario, para enviar múltiples archivos debemos generar el POST tal cual lo haría un formulario con el atributo <code>enctype="multipart/form-data"</code>.</p>
<p>Pueden ver en acción <a href="http://www.zedplan.com/es/demos/ajax-upload" target="_blank">un ejemplo muy sencillo</a> &#8211; basado en jQuery &#8211; que permite seleccionar multiples archivos y enviarlos en una sola petición. Se puede ver el progreso mediante una barra. Del lado del server, con PHP hago un <code>print_r()</code> con el contenido de la variable <code>$_FILES</code>. Lo pueden ver con el Firebug.</p>
<p>Veamos paso a paso el código Javascript del ejemplo.</p>
<pre>upload = [];

j(function() {
	j('#upload-progressbar').progressbar();

	j('#files').bind('change', function(e) {
		var files = e.target.files;
		var fileCount = 0;
		j.each(files, function(k, v) {
			fileCount++;
			reader = new FileReader();
			reader.onloadend = function(e) {
				upload.push({
					name : v.name,
					type : v.type || 'text/plain',
					bin : e.target.result // el contenido del archivo
				});
				if (upload.length == fileCount) uploadFiles();
			};
			reader.readAsBinaryString(v);
		});
	});
});
</pre>
<p>Al cargar la página, agregamos el evento <code>change</code> para el <code>input</code>. Cuando el usuario selecciona los archivos, iteramos por cada uno de ellos y leemos su contenido usando el metodo <code>readAsBinaryString()</code> del objeto <code>FileReader</code>. Al ser asincrónico, debemos utilizar el evento <code>onloadend</code> para detectar cuando terminó la operación. Dentro de esta función creamos un objeto con algunas propiedades del archivo y guardamos una referencia en un array global. Cuando detectamos que se han leído todos los archivos, procedemos a enviar el formulario:</p>
<pre>function uploadFiles() {
	var xhr = new XMLHttpRequest();

	j('#upload-progressbar').show();

	xhr.upload.addEventListener('progress', function(e) {
		if (e.lengthComputable) {
			var perc = Math.round((e.loaded * 100) / e.total);
			j('#upload-progressbar').progressbar('value', perc);
		}
	}, false);
	xhr.upload.addEventListener('load', function(e) {
		j('#upload-progressbar').progressbar('value', 100);
	}, false);

	xhr.open("POST", '/es/demos/ajax-upload');

	var BOUNDARY = '---------------------------1966284435497298061834782736';
	var rn = "\r\n";
	var req = '';
	j.each(upload, function(k, v) {
		req += "--" + BOUNDARY + rn + "Content-Disposition: form-data; name=\"files[]\"";
		req += '; filename="' + v.name + '"' + rn + 'Content-type: ' + v.type;
		req += rn + rn + v.bin + rn;
	});
	req += "--" + BOUNDARY + '--';

	xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
	xhr.sendAsBinary(req);
}
</pre>
<p>Creamos un objeto <code>XMLHttpRequest</code>. Aquí vemos que <code>xhr.upload</code> hace referencia a un objeto que contiene información sobre el proceso de upload y nos ofrece algunos eventos útiles. Abrimos una conección para enviar el formulario por POST y luego generamos el contenido binario que recibiremos en el servidor. Debemos establecer que el <code>Content-Type</code> es de tipo <code>multipart/form-data</code>, y como indica el nombre, quiere decir que la petición esta formada por múltiples partes. Para separarlas se utiliza una cadena que no aparezca dentro del contenido de los archivos (si estan aburridos pueden leer mas detalles <a href="http://www.faqs.org/rfcs/rfc1867.html" target="_blank">aquí</a>). En este caso es la variable <code>BOUNDARY</code>. Finalmente, la magia ocurre en el método <code>sendAsBinary()</code>. Pueden ver mas detalladamente el contenido del POST con el Firebug.</p>
<p>Esta técnica nos permite, por ejemplo, hacer una vista previa de las imágenes en el cliente o redimensionarlas antes de subirlas.</p>
<p>Quizas piensen en la seguridad como yo e intenten hacer esto:</p>
<pre>j('#files').val('/etc/passwd');
</pre>
<p>Pero <span style="text-decoration: line-through;">desgraciadamente</span> afortunadamente nos encontramos con un error de seguridad <img src='http://blog.zedplan.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>No he investigado mucho en cuanto a compatibilidad en distintos navegadores. Si alguien tiene información al respecto es bienvenida.</p>
<p>UPDATE: El comportamiento en Firefox 3.6.2 sobre Ubuntu es algo inestable. A veces envía la petición pero se queda esperando una respuesta indefinidamente, y al seleccionar varios archivos se cierra el navegador de forma inesperada.</p>
<a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zedplan.com%2Fupload-de-archivos-con-barra-de-progreso-usando-xmlhttprequest-y-la-nueva-api-de-html5%2F117&amp;linkname=Upload%20de%20archivos%20con%20barra%20de%20progreso%20usando%20XMLHttpRequest%20y%20la%20nueva%20API%20de%20HTML5"><img src="http://blog.zedplan.com/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share/Bookmark"/></a>]]></content:encoded>
			<wfw:commentRss>http://blog.zedplan.com/upload-de-archivos-con-barra-de-progreso-usando-xmlhttprequest-y-la-nueva-api-de-html5/117/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Como acceder a múltiples servicios web en paralelo con PHP</title>
		<link>http://blog.zedplan.com/como-acceder-a-multiples-servicios-web-en-paralelo-con-php/109</link>
		<comments>http://blog.zedplan.com/como-acceder-a-multiples-servicios-web-en-paralelo-con-php/109#comments</comments>
		<pubDate>Sun, 11 Oct 2009 03:03:45 +0000</pubDate>
		<dc:creator>Demián Rodríguez</dc:creator>
				<category><![CDATA[AJAX]]></category>
		<category><![CDATA[Desarrollo Web]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[concurrente]]></category>
		<category><![CDATA[conección]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[paralelo]]></category>
		<category><![CDATA[rest]]></category>
		<category><![CDATA[servicio web]]></category>
		<category><![CDATA[soap]]></category>

		<guid isPermaLink="false">http://blog.zedplan.com/?p=109</guid>
		<description><![CDATA[Veamos el caso de una aplicación Web donde se realizan búsquedas en base a cierta información ingresada por el usuario, y para generar los resultados, el sistema debe consultar varios servicios web, los cuales tardan varios segundos en responder. Habría que buscar una forma de minimizar el tiempo de búsqueda para que al cliente le [...]]]></description>
			<content:encoded><![CDATA[<p>Veamos el caso de una aplicación Web donde se realizan búsquedas en base a cierta información ingresada por el usuario, y para generar los resultados, el sistema debe consultar varios servicios web, los cuales tardan varios segundos en responder. Habría que buscar una forma de minimizar el tiempo de búsqueda para que al cliente le lleguen los resultados antes que la muerte.<span style="color: #000000;"><br />
</span>La solución que propongo fue aplicada en un sistema de búsqueda y reserva de vuelos y hoteles.<br style="color: #ff0000;" /><br />
Se pueden utilizar varias técnicas, yo tuve en cuenta las siguientes:</p>
<ul>
<li><strong>Acceder a los web services en forma concurrente utilizando CURL:</strong> Se podría buscar y generar la página de resultados directamente, o hacerlo mediante AJAX, lo cual nos permitiría hacer un <a id="cbnj" title="polling" href="http://es.wikipedia.org/wiki/Polling" target="_blank">polling</a> e ir obteniendo resultados parciales.<br />
Sería la solución mas elegante, pero hay que tener en cuenta que <span style="text-decoration: line-through;">desgraciadamente</span> no todos los web services son de tipo <a id="c.jc" title="REST" href="http://es.wikipedia.org/wiki/REST" target="_blank">REST</a> y algunos usan SOAP, por lo tanto se hace dificil implementarlo usando esta librería porque habría que generar el XML e interpretar la respuesta manualmente. Mucho trabajo a mi parecer.</li>
<li><strong>Implementar threads:</strong> Quizás piensen en el suicidio antes de tener en cuenta esta opción.</li>
<li><strong>Buscar la solución en el lado del cliente y enviar una petición AJAX por cada web service:</strong> Aún así tenemos el problema de los límites de conección por host que imponen algunos navegadores como IE 6 y 7.</li>
</ul>
<p>En principio opté por la última opción, pero me vi forzado a descartar el uso de AJAX debido a que algunos browsers imponen un límite de 2 conecciones concurrentes por host y yo necesito consultar 6 servicios web. Para evitar este límite tenemos que engañar al navegador creando al menos 3 subdominios, los cuales nos permitiría realizar 6 peticiones en paralelo, 2 por cada uno.<br />
Quizás la solución no es muy elegante pero es muy usada para agilizar la descarga de los distintos componentes de la página. Tal es el caso de Google Maps, que utiliza subdominios mt0.google.com, mt1.google.com, mt2.google.com, etc.<br />
Si bien no podemos enviar una petición a otro subdominio usando AJAX (hasta el momento), podemos inyectar un script en el documento y cumpliría la misma función, siempre y cuando la respuesta sea en formato JSON. Aca va un ejemplo usando jQuery:</p>
<pre class="js">$(function() {
    var webServices = [1,2,3,4,5,6]; // id's de los webservices
    var count = 1, host;
    for (var i = 0; i &lt; webServices.length; i++) {
        // generamos un host
        host = 'http://ws' + count + '.' + location.host + '/search?ws=' + i;
        if ((i+1)%2 == 0) count++;
        // enviamos el request, jQuery inyecta el script en el documento al detectar otro dominio
        $.getJSON(host, function(json) {
            // actualizamos los resultados...
            $('#results').html(json.results);
        });
    }
});</pre>
<p>Aunque en el título menciono a PHP, mas bien nos limita en vez de darnos una solución práctica.</p>
<a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zedplan.com%2Fcomo-acceder-a-multiples-servicios-web-en-paralelo-con-php%2F109&amp;linkname=Como%20acceder%20a%20m%C3%BAltiples%20servicios%20web%20en%20paralelo%20con%20PHP"><img src="http://blog.zedplan.com/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share/Bookmark"/></a>]]></content:encoded>
			<wfw:commentRss>http://blog.zedplan.com/como-acceder-a-multiples-servicios-web-en-paralelo-con-php/109/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Geolocalizando al usuario</title>
		<link>http://blog.zedplan.com/geolocalizando-al-usuario/90</link>
		<comments>http://blog.zedplan.com/geolocalizando-al-usuario/90#comments</comments>
		<pubDate>Tue, 14 Jul 2009 19:12:52 +0000</pubDate>
		<dc:creator>Demián Rodríguez</dc:creator>
				<category><![CDATA[Desarrollo Web]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[geolocation]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[HTML5]]></category>

		<guid isPermaLink="false">http://blog.zedplan.com/?p=90</guid>
		<description><![CDATA[Una de las nuevas características de los navegadores actuales que introdujeron soporte para HTML5 es la API de Geolocalización, la cual nos permite obtener las coordenadas de la posición actual del usuario. Actualmente solo Firefox 3.5  y Opera 10 beta lo implementan en forma nativa, pero el servicio también está disponible instalando Gears.
La API hace [...]]]></description>
			<content:encoded><![CDATA[<p>Una de las nuevas características de los navegadores actuales que introdujeron soporte para HTML5 es la API de Geolocalización, la cual nos permite obtener las coordenadas de la posición actual del usuario. Actualmente solo Firefox 3.5  y Opera 10 beta lo implementan en forma nativa, pero el servicio también está disponible instalando <a href="http://code.google.com/intl/es-ES/apis/gears/" target="_blank">Gears</a>.</p>
<p>La API hace transparente al programador la forma en que se obtiene la posición del usuario. Hay varias formas de determinarla y éstas dependen de la plataforma y el dispositivo del usuario. Si se accede desde un dispositivo con GPS, se podrá obtener las coordenadas en forma casi exacta. Si se accede desde una PC o móvil sin GPS, el navegador envía una solicitud al servicio de geolocalización de Google con información sobre los puntos de acceso WiFi cercanos, aunque en este último, la exactitud de la posición obtenida deja mucho que desear en algunos casos.</p>
<p>A continuación les dejo un código de prueba que usé para determinar la localización con Firefox 3.5.<br />
Irónicamente, a pesar de estar ubicado en el barrio de Recoleta, las coordenadas resultantes me ubican en Puerto Madero <strong><span style="text-decoration: line-through;">a metros de las oficinas de Google</span></strong>.</p>
<pre class="js">if (navigator.geolocation) {
    // nos aseguramos de que el browser soporte la API
    navigator.geolocation.getCurrentPosition(function(position) {
    	var latitude = position.coords.latitude;
    	var longitude = position.coords.longitude;
        // aca podemos ubicar el punto en un mapa
        // o hacer una geocodificación inversa para obtener la dirección
    }, function(error) {
	// error es un objeto con las siguientes propiedades:
    	// - code: código de error
    	// - message: mensaje de error
    });
}</pre>
<p>Al método <code>getCurrentPosition()</code> se le envía como primer parámetro un callback que recibirá un objeto con las propiedades de la posición. El segundo parámetro es opcional y es un callback que se llamará si ocurre algún error en el proceso.</p>
<p>Si usamos la <a href="http://code.google.com/intl/es/apis/gears/api_geolocation.html" target="_blank">API de Gears</a>, el proceso es el mismo, salvo que tenemos algunas opciones adicionales, incluso el objeto <code>position</code> ya nos devuelve la dirección luego de hacer una geocodificación inversa.</p>
<p>Enlaces útiles:</p>
<ul>
<li><a href="http://www.w3.org/TR/geolocation-API" target="_blank">W3C Geolocation API</a></li>
<li><a href="https://developer.mozilla.org/Es/Using_geolocation" target="_blank">Mozilla Developer Center: Using Geolocation<br />
</a></li>
</ul>
<a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zedplan.com%2Fgeolocalizando-al-usuario%2F90&amp;linkname=Geolocalizando%20al%20usuario"><img src="http://blog.zedplan.com/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share/Bookmark"/></a>]]></content:encoded>
			<wfw:commentRss>http://blog.zedplan.com/geolocalizando-al-usuario/90/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>¿Ya podemos usar HTML 5?</title>
		<link>http://blog.zedplan.com/ya-podemos-usar-html-5/61</link>
		<comments>http://blog.zedplan.com/ya-podemos-usar-html-5/61#comments</comments>
		<pubDate>Mon, 15 Jun 2009 16:00:33 +0000</pubDate>
		<dc:creator>Demián Rodríguez</dc:creator>
				<category><![CDATA[Desarrollo Web]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[compatibilidad]]></category>
		<category><![CDATA[doctype]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[Web Forms 2.0]]></category>
		<category><![CDATA[XHTML]]></category>
		<category><![CDATA[XML]]></category>

		<guid isPermaLink="false">http://blog.zedplan.com/?p=61</guid>
		<description><![CDATA[Vamos a comentar algunas de las nuevas características de este nuevo lenguaje que pretende reemplazar al ya decrépito HTML 4.01. Esta nueva revisión del lenguaje nació hace unos años bajo el nombre Web Applications 1.0 y continúa siendo desarrollado activamente por el grupo WHATWG como HTML5, y además su otra variante XHTML5. Se dice que [...]]]></description>
			<content:encoded><![CDATA[<p>Vamos a comentar algunas de las nuevas características de este nuevo lenguaje que pretende reemplazar al ya decrépito HTML 4.01. Esta nueva revisión del lenguaje nació hace unos años bajo el nombre <em>Web Applications 1.0</em> y continúa siendo desarrollado activamente por el grupo <a href="http://es.wikipedia.org/wiki/Web_Hypertext_Application_Technology_Working_Group">WHATWG</a> como <strong>HTML5</strong>, y además su otra variante <strong>XHTML5</strong>. Se dice que estará completo para el 2012, pero la mayoría de los navegadores actuales ya implementan gran parte de las nuevas características del lenguaje.</p>
<p>Algunas de las mejoras que nos provee HTML5 son las siguientes:</p>
<ul>
<li>Nuevos tags como &lt;header&gt;, &lt;footer&gt;, &lt;section&gt;, etc.  En su mayoría son reemplazos semánticos para los bloques genéricos como &lt;div&gt; o &lt;span&gt;.</li>
<li>La incorporación de <em>Web Forms 2.0</em>, una actualización para el manejo de formularios que agrega nuevos tipos de datos y facilidad para la validación de campos.</li>
<li>Se eliminaron elementos como &lt;font&gt; y &lt;center&gt;, cuya funcionalidad puede ser reemplazada por CSS.</li>
<li>Grandes cambios en la API del DOM, incluyendo soporte para dibujar en 2D usando el nuevo elemento &lt;canvas&gt;, soporte para aplicaciones offline, Drag &amp; Drop, controles de reproducción multimedia para usar con los elementos &lt;audio&gt; y &lt;video&gt;, manejo avanzado del historial y mucho mas.</li>
</ul>
<p>HTML5 nos dá la opción de crear el documento usando la vieja sintaxis compatible con HTML 4 u optar por un documento XML.</p>
<p>Ejemplo para la primera opción:</p>
<pre class="html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;title&gt;Documento de ejemplo&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;p&gt;&lt;img src="/images/logo.png" alt="" /&gt; Hola mundo&lt;/p&gt;
  &lt;/body&gt;
&lt;/html&gt;</pre>
<p>Datos importantes:</p>
<ul>
<li>El documento se sirve como <code>Content-Type: text/html</code></li>
<li>El DOCTYPE  es requerido y no necesita referirse a ningún DTD.</li>
<li>Nueva forma de especificar el juego de caracteres mediante el atributo charset del &lt;meta&gt;.</li>
<li>Por cuestiones de compatibilidad, se permite cerrar los tags vacíos como &lt;img&gt;, &lt;input&gt; o &lt;br&gt; con una barra, usando la misma sintaxis que XML.</li>
<li>Ventaja: compatible con navegadores que no soportan HTML5</li>
</ul>
<p>Ejemplo conforme a la sintaxis XML:</p>
<pre class="html">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml"&gt;
  &lt;head&gt;
    &lt;title&gt;Documento de ejemplo&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;p&gt;&lt;img src="/images/logo.png" alt="" /&gt; Hola mundo&lt;/p&gt;
  &lt;/body&gt;
&lt;/html&gt;</pre>
<p>Datos importantes:</p>
<ul>
<li>El documento se sirve como <code>Content-Type: </code><code>application/xhtml+xml</code></li>
<li>El DOCTYPE  es opcional.</li>
<li>Se requiere definir el namespace como <code>http://www.w3.org/1999/xhtml</code></li>
<li>Alcanza con especificar el charset en la definición del XML.</li>
<li>Ventaja: obliga a escribir código XML válido.</li>
</ul>
<p>Uno de los grandes cambios que debemos tener en cuenta es la forma en que se sirve el documento. Hasta ahora estábamos acostumbrados a enviarle al navegador un documento XHTML como <code>text/html</code> por problemas con <span style="text-decoration: line-through;">navegadores defectuosos como Internet Explorer</span> ciertos navegadores. Pero esto hará que el navegador interprete erróneamente el documento XML como HTML.</p>
<p>Lo que no queda del todo claro es como va a ser la compatibilidad hacia atrás. No solo tendremos que lidiar con navegadores &#8220;prehistóricos&#8221; por un largo tiempo, sino que además se han introducido cambios en el significado de varios elementos como &lt;strong&gt;, &lt;small&gt;, &lt;b&gt;, &lt;i&gt;, que implicaría tener que reescribir parte de la estructura del documento.</p>
<p>Adicionalmente, ¿qué sucede si servimos un documento en HTML5 en un browser que no lo soporta? ¿Cómo se interpretan los nuevos elementos?<br />
Uno de los problemas encontrados en Internet Explorer para todas sus versiones es la incapacidad de renderizar y darle estilo a los elementos desconocidos. Para esto se logró <a href="http://ejohn.org/blog/html5-shiv/" target="_blank">hallar un hack</a>, el cual consiste en crear el elemento dińamicamente usando el método del DOM <code>document.createElement()</code> en el head del documento.</p>
<p>Les dejo algunos enlaces relevantes (en inglés):</p>
<ul>
<li><a href="http://wiki.whatwg.org/wiki/HTML_vs._XHTML" target="_blank">Diferencias entre HTML5 y XHTML5</a></li>
<li><a href="http://dev.w3.org/html5/html4-differences/" target="_blank">Diferencias entre HTML5 y HTML4</a></li>
<li><a href="http://dev.w3.org/html5/html-author/" target="_blank">Guía del desarrollador para comenzar con HTML5</a></li>
<li><a href="http://a.deveria.com/caniuse/#agents=All&amp;eras=now,near,far&amp;cats=HTML5,Summary&amp;statuses=rec,cr,wd,ietf" target="_blank">Tablas de compatibilidad entre navegadores</a></li>
</ul>
<a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zedplan.com%2Fya-podemos-usar-html-5%2F61&amp;linkname=%C2%BFYa%20podemos%20usar%20HTML%205%3F"><img src="http://blog.zedplan.com/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share/Bookmark"/></a>]]></content:encoded>
			<wfw:commentRss>http://blog.zedplan.com/ya-podemos-usar-html-5/61/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Uso del historial en aplicaciones AJAX cambiando el título del documento dinámicamente</title>
		<link>http://blog.zedplan.com/uso-del-historial-en-aplicaciones-ajax-cambiando-el-titulo-del-documento-dinamicamente/18</link>
		<comments>http://blog.zedplan.com/uso-del-historial-en-aplicaciones-ajax-cambiando-el-titulo-del-documento-dinamicamente/18#comments</comments>
		<pubDate>Sat, 23 May 2009 23:13:34 +0000</pubDate>
		<dc:creator>Demián Rodríguez</dc:creator>
				<category><![CDATA[AJAX]]></category>
		<category><![CDATA[bookmark]]></category>
		<category><![CDATA[history]]></category>
		<category><![CDATA[URL]]></category>

		<guid isPermaLink="false">http://blog.zedplan.com/?p=18</guid>
		<description><![CDATA[Actualmente podemos encontrar gran cantidad de librerías que faciliten la utilización del historial del navegador para registrar cambios en el estado de la página, pero ninguna de ellas (o al menos las que yo encontré) logran resolver esta situación.
Si estás perdido y no sabes de que estoy hablando, aquí hay un muy buen artículo (en [...]]]></description>
			<content:encoded><![CDATA[<p>Actualmente podemos encontrar gran cantidad de librerías que faciliten la utilización del historial del navegador para registrar cambios en el estado de la página, pero ninguna de ellas (o al menos las que yo encontré) logran resolver esta situación.</p>
<p>Si estás perdido y no sabes de que estoy hablando, <a href="http://ajaxpatterns.org/Unique_URLs" target="_blank">aquí</a> hay un muy buen artículo (en inglés) que explica en detalle como aplicar la técnica del uso del botón atrás/adelante en aplicaciones AJAX.</p>
<p>Detecté el problema mientras desarrollaba el <a href="http://www.zedplan.com/es/portfolio">portfolio de ZedPlan</a>. Al seleccionar un proyecto se actualiza la URL, lo cual hace que mediante una solicitud AJAX se obtengan los datos del mismo y se actualice el documento. También me pareció lógico actualizar el título, ya que sino quedará guardado en el historial del navegador el título del documento oiriginal, y sería poco intuitivo para el usuario.</p>
<p>En ese momento noté que en Firefox el historial no estaba sincronizado con el título del documento actual. ¿Por qué?, parece que si cambiamos el título del documento una vez que se guardó la entrada en el historial, el navegador no lo actualiza y debemos aplicar un pequeño hack, el cual consiste en actualizar el hash de la URL nuevamente de la siguiente forma:</p>
<pre class="js">window.location.hash = window.location.hash;</pre>
<p>Como era de esperarse,  en Internet Exporer 6 y 7 tenemos otro problema. Para ver reflejado en el historial el título del documento, debemos cambiarlo en el iframe que se usa como hack, ya que se extrae de la etiqueta &lt;title&gt;. Para esto, al src del iframe le enviamos como parámetro el título del documento junto con el hash. El truco acá es cambiar el título antes de actualizar el iframe, ya que si lo cambiamos con javascript no se va a ver reflejado el cambio en el historial.</p>
<p>Veamos un ejemplo usando la <a href="http://www.zedplan.com/scripts/HistoryManager.js">clase HistoryManager</a>, la cual desarrollé hace unos meses y la modifiqué para agregar soporte a los títulos dinámicos. Requiere al framework <a href="http://www.prototypejs.org" target="_blank">Prototype</a>, aunque con pocas modificaciones se podría adaptar a otros frameworks como jQuery.</p>
<p>Supongamos que tenemos una página donde tenemos contenido agrupado en tabs, según el tab seleccionado mostramos u ocultamos lo que corresponda, pero guardando en el historial cada cambio de tab para permitir <a href="http://es.wikipedia.org/wiki/Marcador_de_Internet" target="_blank">bookmarking</a>.<br />
Al cargar la página ejecutamos la función init():</p>
<pre class="js">function init() {
	hm = new HistoryManager(
			update, // callback
			200, 	// intervalo en milisegundos para revisar cambios en el hash
			'/history.php', // URL del documento que se usa como hack para el IE (abajo está el codigo fuente)
			true // habilitar títulos dinámicos, asi debemos llamar manualmente al metodo setTitle() en la función update()
		);
}
/**
 * Esta función se ejecuta cuando hubo cambios en el hash.
 * @param string nuevo hash
 * @param string hash anterior
 */
function update(hash, prev) {
	 // si no hay ni hubo hash no hacemos nada en este caso.
	if (!hash &amp;&amp; !prev) return;

	// en este caso el contenido que generamos depende de un id, el cual lo obtenemos del hash
	// por ejemplo, si queremos mostrar un contenido u otro dependiendo de un tab seleccionado
	// el formato del hash podría ser "#tab-1" o "#tab-2"
	var id = hash.split('-').last();

	// establecemos el titulo del documento segun el nuevo contenido
	// acá lo sabemos de antemano, pero sería mas común obtenerlo mediante AJAX segun el id
	if (hash) this.setTitle("Tab " + id); // debería ser mas descriptivo el título!
	else this.setTitle("Home"); // si no hay hash, reestablecemos el titulo original
}</pre>
<p>Y el código fuente de history.php</p>
<pre class="html">&lt;?
	$hash = json_encode(isset($_GET['hash']) ? $_GET['hash'] : '');
	$title = isset($_GET['title']) ? $_GET['title'] : '';
?&gt;
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr"&gt;
&lt;head&gt;
	&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;
	&lt;title&gt;&lt;?=htmlspecialchars($title)?&gt;&lt;/title&gt;
	&lt;meta name="robots" content="noindex, nofollow" /&gt;
	&lt;script type="text/javascript"&gt;/*&lt;![CDATA[*/
		hash = &lt;?=$hash?&gt;;
		function update() {
			if (!parent) return;
			parent.location.hash = hash;
		}
	/*]]&gt;*/&lt;/script&gt;
&lt;/head&gt;
&lt;body onload="update()"&gt;&lt;/body&gt;
&lt;/html&gt;</pre>
<p>Esta clase fué probada en Firefox 3, IE 6 &amp; 7, Chrome, Safari 4 para Windows y Opera 9.64.</p>
<a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zedplan.com%2Fuso-del-historial-en-aplicaciones-ajax-cambiando-el-titulo-del-documento-dinamicamente%2F18&amp;linkname=Uso%20del%20historial%20en%20aplicaciones%20AJAX%20cambiando%20el%20t%C3%ADtulo%20del%20documento%20din%C3%A1micamente"><img src="http://blog.zedplan.com/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share/Bookmark"/></a>]]></content:encoded>
			<wfw:commentRss>http://blog.zedplan.com/uso-del-historial-en-aplicaciones-ajax-cambiando-el-titulo-del-documento-dinamicamente/18/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
