<?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>Baires IT</title>
	<atom:link href="http://www.bairesit.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.bairesit.com</link>
	<description>Information Technology Engineering</description>
	<lastBuildDate>Fri, 30 Sep 2011 20:22:05 +0000</lastBuildDate>
	<language>es</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Generar Componentes visuales personalizados compatibles con Sencha Touch</title>
		<link>http://www.bairesit.com/2011/09/generar-componentes-visuales-personalizados-compatibles-con-sencha-touch/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=generar-componentes-visuales-personalizados-compatibles-con-sencha-touch</link>
		<comments>http://www.bairesit.com/2011/09/generar-componentes-visuales-personalizados-compatibles-con-sencha-touch/#comments</comments>
		<pubDate>Fri, 30 Sep 2011 16:00:46 +0000</pubDate>
		<dc:creator>sfernandez</dc:creator>
				<category><![CDATA[Laboratorio]]></category>

		<guid isPermaLink="false">http://www.bairesit.com/?p=1293</guid>
		<description><![CDATA[Muchas veces necesitamos mostrar información de tal manera que podamos comprenderla mejor que si la vemos en una tabla, para estos casos un componente visual es lo que puede integrar el contexto con la información y darnos ese plus. Desarrolar con frameworks javascript nos ayuda, ya que estos traen incorporados una gran variedad de gráficos, [...]]]></description>
			<content:encoded><![CDATA[<p>Muchas veces necesitamos mostrar información de tal manera que podamos comprenderla mejor que si la vemos en una tabla, para estos casos <strong>un componente visual</strong> es lo que puede integrar el contexto con la información y darnos ese plus. Desarrolar con frameworks javascript nos ayuda, ya que estos traen incorporados una gran variedad de gráficos, y también nos brindan la posibilidad de generar nuestros própios componentes que se ajusten a nuestras necesidades.<span id="more-1293"></span></p>
<p><strong>Introducción</strong></p>
<p>El lanzamiento de <strong>Sencha ExtJS 4</strong> estuvo marcado por la integración nativa de gráficos, tal es así que inmediatamente la comunidad pidió esta característica para <strong>Touch</strong>. Algunos decidieron exportar los gráficos de ExtJS4 a Touch de manera independiente (sin apoyo oficial), como es el caso de <a href="http://code.google.com/p/oppo-touching/">Oppo-Touching</a>.</p>
<p>En esta entrada vamos a generar 2 componentes visuales, un <strong><em>Semaforo</em></strong> y un <strong><em>Medidor de aguja</em></strong>, ambos totalmente compatibles con Sencha Touch ayudandonos de Oppo-Touching 2.0.</p>
<p><strong><em>Aclaración 1: de aquí en adelante nos vamos a referir tanto a Oppo-touching como a ExtJS4-Charts como lo mismo, y en preferencia, a este último ya que contamos con la API oficial.</em></strong></p>
<p><strong>Instalación</strong></p>
<p>Descargamos el framework desde el repositorio de Google Codes y como cualquier otro proyecto javascript, solo basta con importar el archivo .js en el <code>header</code> del index.html de nuestra aplicación para poder usarlo.</p>
<p>La Arquitectura básica de los componentes es una Superficie (Surface), en el siguiente nivel los motores que renderizan el gráfico (que pueden ser VML, SVG o Canvas, lo que nos brinda una excelente covertura en los navegadores), y en el último nivel los Sprites que vienen a ser los elementos individuales que componen el gráfico.</p>
<p style="text-align: center;"><a href="http://www.bairesit.com/wp-content/uploads/2011/09/02_01_arquitectura_400.png"><img class="size-medium wp-image-1294 aligncenter" title="02_01_arquitectura_400" src="http://www.bairesit.com/wp-content/uploads/2011/09/02_01_arquitectura_400-300x213.png" alt="" width="300" height="213" /></a></p>
<p style="text-align: center;">
<p>Los <em>motores</em> no son mas que las diferentes opciones para dibujar que nos provee HTML5: Canvas, SVG y, el propietario de IE, VML.</p>
<p>Los <em>Sprites</em> son los objetos que componen la superfície del dibujo, existen diferentes tipos y opciones. Estos tienen varias propiedades como el tipo, ancho, alto, coordenada X e Y, relleno, etc.</p>
<p>Para construir nuestro componente personalizado vamos a trabajar agregando y dándoles distintas propiedades a nuestros sprites con el objeto de cumplir con todos los requerimientos que tengamos del componente.</p>
<p>Vamos a tener 2 tipos de Sprites, <em>Estáticos</em> o <em>Dinámicos</em> por así decir, los primeros se dibujarán al momento de renderizar el gráfico y no los modificaremos en toda la vída de este, y los dinámicos van a variar según el comportamiento que le demos a estos, como es la lúz que se enciende en el caso de semáforo, o la aguja en el medidor.</p>
<p>Como vamos a extender la clase <strong>Ext.draw.Component</strong>, los sprites estáticos los agregaremos al momento de crearse mediante un arreglo de <strong><em>items</em></strong> que nos provee esta clase. Es realmente importante crearlos y aplicar estos dentro del <strong>constructor</strong> mediante la función <strong><code>Ext.apply()</code></strong> ya que sino no podremos crear mas de 1 instancia del gráfico.<br />
En cambio, los dinámicos los agregaremos al momento de renderizar el dibujo y los mantendremos referenciados mediantes variables para poder luego actualizarle sus propiedades.</p>
<p><strong>Semáforo</strong></p>
<p>Comenzaremos con una versión simplificada de nuestro componente, y a medida que avancemos le iremos agregando el comportamiento deseado y las características própias.</p>
<pre class="brush:javascript">// file: Semaforo.js

var	rojoApagado 	  = '#0f0000',
	verdeApagado	  = '#000f00',
	amarilloApagado   = '#0f0f00',
	rojoIluminado	  = '#ff0000',
	verdeIluminado    = '#00ff00',
	amarilloIluminado = '#ffff00';

Ext.bairesit.draw.Semaforo = Ext.extend(Ext.draw.Component, {

	/**
	*	Extendemos el método 'constructor' para darle nuestras
	*	propiedades personalizadas, como ser el valorActual,
	*	los límites con los que encenderá cada lúz, los sprites, etc.
	*/
	constructor:function(config) {
		var valorActual			= null;
		var limiteObjetivo		= null;
		var limiteAlerta		= null;
		var esAutoRedibujar		= false;
		var spriteLuzEncendida		= new Array();
		var gradients			= [{
							id    : 'gradientIdSemaforo',
							angle : 45,
							stops : {
								0   : {color: '#e0fea7'},
								100 : {color: '#ffdeb3'}
							}
						}];
		var circuloLuzEncendida		= [{
							type  : 'circle',
							fill  : verdeIluminado,
							radius: 15,
							x     : 30,
							y     : 0,
							stroke: "333"
						}];
		var items 			= new Array({
							type  : 'circle',
							radius: 0,
							x     : 1,
							y     : 1
						},{
							type  : 'circle',
							radius: 1,
							x     : 180,
							y     : 120
						},{
							type  : 'rect',
							width : 40,
							height: 110,
							fill  : 'url(#gradientIdSemaforo)',
							x     : 10,
							y     : 10,
							stroke: "000"
						},{
							type  : 'circle',
							radius: 15,
							fill  : rojoApagado,
							x     : 30,
							y     : 30,
							stroke: "333"
						},{
							type  : 'circle',
							radius: 15,
							fill  : amarilloApagado,
							x     : 30,
							y     : 65,
							stroke: "333"
						},{
							type  : 'circle',
							radius: 15,
							fill  : verdeApagado,
							x     : 30,
							y     : 100,
							stroke: "333"
						});
		config = Ext.apply({
			items              : items,
			gradients          : gradients,
			valorActual        : valorActual,
			limiteAlerta       : limiteAlerta,
			limiteObjetivo     : limiteObjetivo,
			esAutoRedibujar    : esAutoRedibujar,
			spriteLuzEncendida : spriteLuzEncendida,
			circuloLuzEncendida: circuloLuzEncendida
		}, config);

		Ext.bairesit.draw.Semaforo.superclass.constructor.apply(this, arguments);
	},

	onRender: function(){
		Ext.bairesit.draw.Semaforo.superclass.onRender.apply(this, arguments);

		this.spriteLuzEncendida[0] = this.surface.add(this.circuloLuzEncendida[0]);

		if (this.valorActual != null){
			this.redraw();
		}

	}
}</pre>
<p>Los Sprites dinámicos que queremos mantener en variables para poder modificar los agregamos al extender la función <strong>onRender</strong>, para así luego por ejemplo, si actualizamos el valorActual y queremos iluminar el foco correspondiente, le actualizamos sus atributos de la siguiente manera:</p>
<pre class="brush:javascript">iluminarSemaforo: function () {
	var ejeY= 0;
	var urlFill='';

	if (this.valorActual &gt;= this.limiteAlerta){
		ejeY    = 30;
		urlFill = rojoIluminado;
	} else {
		if (this.valorActual &lt;= this.limiteObjetivo){
			ejeY    = 100;
			urlFill = verdeIluminado;
		} else {
			ejeY    = 65
			urlFill = amarilloIluminado;
		}
	}

	this.spriteLuzEncendida[0].setAttributes({
		y	: ejeY,
		fill	: urlFill
	});
	this.spriteLuzEncendida[0].redraw();
}</pre>
<p><strong>Gradientes</strong></p>
<p>Una forma de mejorarle el aspecto es utilizando gradientes de colores para realzar la calidad gráfica. Si bien con Ext.draw.Component podemos generar y utilizar gradientes, éstos pueden ser solamente &#8216;lineales&#8217;. Para las luces iluminadas del semaforo es mucho mas elegante un grandiente radial y un hack para utilizarlos es definiendolos directamente en el index.html de manera explícita dentro de las etiquetas <code>body</code>:</p>
<pre class="brush:html">			...</pre>
<p>Una vez definido, para poder utilizarlo solo deberemos referenciarlos con el id, por ejemplo: <code>fill: 'url(#radialGradientRojo)'</code>.</p>
<p>Basicamente este componente ya funciona y podemos seguir extendiendolo y agregandole mas sprites y características como ser un título, una leyenda que muestre el valorActual, un store que almacene el historial, la posibilidad de dibujar la tendencia que viene teniendo lo que estemos midiendo, etc.</p>
<p>En la <a href="#imagen02">imagen 02</a> se puede apreciar el resultado final del componente y una serie de estados de ejemplo.</p>
<p style="text-align: center;">
<p style="text-align: left;"><!-- Checkpoint 01 --><a href="http://www.bairesit.com/wp-content/uploads/2011/09/02_02_semaforos_2.png"><img class="size-medium wp-image-1295 aligncenter" title="02_02_semaforos_2" src="http://www.bairesit.com/wp-content/uploads/2011/09/02_02_semaforos_2-300x161.png" alt="" width="300" height="161" /></a><br />
<strong>Medidor de Aguja</strong></p>
<p>Crear un medidor de agujas supone un mayor trabajo ya que implican cálculos adicionales y una gran cantidad de sprites dinámicos.</p>
<p>Los requerimientos que tenemos son:</p>
<ul>
<li>Setear los limites inferiores y superiores.</li>
<li>Limites de objetivo y de alerta.</li>
<li>Apreciar graficamente el estado del valor actual.</li>
<li>Posibilidad de visualizar el limite del objetivo.</li>
<li>Posibilidad de apreciar la tendencia.</li>
<li>Posibilidad de apreciar la evolución del valor actual.</li>
</ul>
<p>Dividimos en SubComponentes y los clasificamos:</p>
<table border="1">
<tbody>
<tr>
<td style="padding: 2px; padding-right: 8px; border-style: none;">Fondo del Medidor</td>
<td style="padding: 2px; padding-right: 8px; border-style: none;">→</td>
<td style="padding: 2px; padding-right: 8px; border-style: none;" align="center">Estático</td>
</tr>
<tr>
<td style="padding: 2px; padding-right: 8px; border-style: none;">Líneas de Ejes</td>
<td style="padding: 2px; padding-right: 8px; border-style: none;">→</td>
<td style="padding: 2px; padding-right: 8px; border-style: none;" align="center">Estático</td>
</tr>
<tr>
<td style="padding: 2px; padding-right: 8px; border-style: none;">Valores de Ejes</td>
<td style="padding: 2px; padding-right: 8px; border-style: none;">→</td>
<td style="padding: 2px; padding-right: 8px; border-style: none;" align="center">Dinámico</td>
</tr>
<tr>
<td style="padding: 2px; padding-right: 8px; border-style: none;">Aguja</td>
<td style="padding: 2px; padding-right: 8px; border-style: none;">→</td>
<td style="padding: 2px; padding-right: 8px; border-style: none;" align="center">Dinámico</td>
</tr>
<tr>
<td style="padding: 2px; padding-right: 8px; border-style: none;">Recuadro de leyenda y valor actual</td>
<td style="padding: 2px; padding-right: 8px; border-style: none;">→</td>
<td style="padding: 2px; padding-right: 8px; border-style: none;" align="center">Dinámico</td>
</tr>
<tr>
<td style="padding: 2px; padding-right: 8px; border-style: none;">Tendencia</td>
<td style="padding: 2px; padding-right: 8px; border-style: none;">→</td>
<td style="padding: 2px; padding-right: 8px; border-style: none;" align="center">Dinámico</td>
</tr>
<tr>
<td style="padding: 2px; padding-right: 8px; border-style: none;">Aguja objetivo</td>
<td style="padding: 2px; padding-right: 8px; border-style: none;">→</td>
<td style="padding: 2px; padding-right: 8px; border-style: none;" align="center">Dinámico</td>
</tr>
</tbody>
</table>
<p>Bien, una vez que desglosamos los componentes requeridos, comenzamos con la construcción de los componentes estáticos:</p>
<pre class="brush:javascript">// file: Medidor.js
Ext.bairesit.draw.Semaforo = Ext.extend(Ext.draw.Component, {

	constructor:function(config) {
        ...
        var items     = [{ // Punto para asegurar los límites del gráfico
                              type  : 'circle',
                              radius: 0,
                              x     : 1,
                              y     : 1
                        },{ // Punto para asegurar los límites del gráfico
                              type  : 'circle',
                              radius: 1,
                              x     : 415,
                              y     : 140
                        },{   // medio círculo
                              type  : 'path',
                              fill  : 'url(#gradientIdMedidor)',
                              path  : "M30,130 C36,-2 224,-2 230,130 L 200,130 C 194,38 66,38 60,130 z","stroke-width":"1",
                              stroke: "#000"
                        },{   // línea de cierre
                              type  : 'path',
                              fill  : '#000',
                              path  : "M25 130L 235 130","stroke-width":"1",
                              stroke: "#000"
                        },{   // línea a los 18°
                              type  : 'path',
                              fill  : '#fff',
                              path  : "M37.272 99.871L 32.517 98.326","stroke-width":"1",
                              stroke: "#000"
                        },{   // línea a los 36°
                              type  : 'path',
                              fill  : '#fff',
                              path  : "M51.121 72.691L 46.076 69.752","stroke-width":"1",
                              stroke: "#000"
                        },{   // línea a los 54°
                              type  : 'path',
                              fill  : '#fff',
                              path  : "M72.691 51.121L 69.752 47.076","stroke-width":"1",
                              stroke: "#000"
                        },{   // línea a los 72°
                              type  : 'path',
                              fill  : '#fff',
                              path  : "M99.871 37.272L 98.8326 32.517","stroke-width":"1",
                              stroke: "#000"
                        },{   // línea a los 90°
                              type  : 'path',
                              fill  : '#fff',
                              path  : "M130 25L 130 35","stroke-width":"1",
                              stroke: "#000"
                        },{   // línea a los 108°
                              type  : 'path',
                              fill  : '#fff',
                              path  : "M160.129 37.272L 161.674 32.517","stroke-width":"1",
                              stroke: "#000"
                        },{   // línea a los 126°
                              type  : 'path',
                              fill  : '#fff',
                              path  : "M187.309 51.121L 190.248 47.076","stroke-width":"1",
                              stroke: "#000"
                        },{   // línea a los 144°
                              type  : 'path',
                              fill  : '#fff',
                              path  : "M208.879 72.691L 212.924 69.752","stroke-width":"1",
                              stroke: "#000"
                        },{   // línea a los 162°
                              type  : 'path',
                              fill  : '#fff',
                              path  : "M222.728 99.871L 227.483 98.326","stroke-width":"1",
                              stroke: "#000"
                        },{   // circulo aguja
                              type  : 'circle',
                              fill  : 'url(#radialGradientCentroAguja)',
                              radius: 8,
                              x     : 130,
                              y     : 130
                        }];

        config = Ext.apply({
            items : items,
            ...
        }, config);

        Ext.bairesit.draw.Medidor.superclass.constructor.apply(this, arguments);
    }
	...
}</pre>
<p>Ahora agregamos los sprites dinámicos, como ser los <strong>Valores del Eje</strong>:</p>
<pre class="brush:javascript">constructor:function(config) {
	...
	var items ...
	var spriteAxis      = new Array();
	var axis      = [{
				  type  : 'text',
				  text  : 0,
				  fill  : '#000',
				  font  : '10px Arial',
				  x     : 130,
				  y     : 10
			},{
				  type  : 'text',
				  text  : 1,
				  fill  : '#000',
				  font  : '10px Arial',
				  x     : 130 + 1,
				  y     : 10
			},{
				  type  : 'text',
				  text  : 2,
				  fill  : '#000',
				  font  : '10px Arial',
				  x     : 130 + 2,
				  y     : 10
			},{
				  type  : 'text',
				  text  : 3,
				  fill  : '#000',
				  font  : '10px Arial',
				  x     : 130 + 3,
				  y     : 10
			},{
				  type  : 'text',
				  text  : 4,
				  fill  : '#000',
				  font  : '10px Arial',
				  x     : 130 + 4,
				  y     : 10
			},{
				  type  : 'text',
				  text  : 5,
				  fill  : '#000',
				  font  : '10px Arial',
				  x     : 130 + 5,
				  y     : 10
			},{
				  type  : 'text',
				  text  : 6,
				  fill  : '#000',
				  font  : '10px Arial',
				  x     : 130 + 6,
				  y     : 10
			},{
				  type  : 'text',
				  text  : 7,
				  fill  : '#000',
				  font  : '10px Arial',
				  x     : 130 + 7,
				  y     : 10
			},{
				  type  : 'text',
				  text  : 8,
				  fill  : '#000',
				  font  : '10px Arial',
				  x     : 130 + 8,
				  y     : 10
			},{
				  type  : 'text',
				  text  : 9,
				  fill  : '#000',
				  font  : '10px Arial',
				  x     : 130 + 9,
				  y     : 10
			},{
				  type  : 'text',
				  text  : 10,
				  fill  : '#000',
				  font  : '10px Arial',
				  x     : 130 + 10,
				  y     : 10
			}];

	config = Ext.apply({
		items              : items,
		gradients          : gradients,
		valorActual        : valorActual,
		valorMaximo        : valorMaximo,
		valorMinimo        : valorMinimo,
		limiteObjetivo     : limiteObjetivo,
		limiteAlerta       : limiteAlerta,
		esMinimoMejor      : esMinimoMejor,
		esAutoRedibujar    : esAutoRedibujar,
		axis               : axis,
		spriteAxis         : spriteAxis,
		...
	}, config);
	Ext.bairesit.draw.Medidor.superclass.constructor.apply(this, arguments);
},

anguloARadian: function (angulo){
	var radianes = 0;
	radianes = (angulo * Math.PI) / 180;
	return radianes;
},

calcularCoordenadaX: function (anguloGrados, radio, offset){
	var radianes = this.anguloARadian(anguloGrados);
	var x = (offset + (Math.cos(radianes) * radio));
	return x;
},

calcularCoordenadaY : function (anguloGrados, radio, offset){
	var radianes = this.anguloARadian(anguloGrados);
	var y = (offset - (Math.sin(radianes) * radio));
	return y;
},

dibujarAxis: function(){
	var i = 0, texto = '', arreglo;
	var paso = (this.valorMaximo - this.valorMinimo) / 10;
	for ( i = 0 ; i &lt;= 10 ; i++ ){
		this.axis[i].text = this.valorMinimo + (paso * i);
		this.axis[i].x = 130 + (i * paso);

		/* Dibujo los numeros de la escala */
		arreglo = (String(this.axis[i].text)).split(".");
		texto = arreglo[0] + '.' + ( arreglo[1] ? arreglo[1].substring(0, 1) : '0');

		this.spriteAxis[i].setAttributes({
			text : texto,
			x    : this.calcularCoordenadaX (180-(180/10*i), 115, 120),
			y    : this.calcularCoordenadaY (180-(180/10*i), 110, 125)
		});

		this.spriteAxis[i].redraw();
	}
},

onRender: function(){
	Ext.bairesit.draw.Medidor.superclass.onRender.apply(this, arguments);
	...
	for ( i = 0 ; i &lt;= 10 ; i++ ){
		this.spriteAxis[i] = this.surface.add(this.axis[i]);
	},
	...
},</pre>
<p id="agregamos_la_aguja">Agregamos la <strong>Aguja</strong>:</p>
<pre class="brush:javascript">constructor:function(config) {
	...
	var spriteAguja     = new Array();
	var circulo   = [{
		type  : 'circle',
		fill  : 'url(#radialGradientCentroAguja)',
		radius: 8,
		x     : 130,
		y     : 130
	}];
	var aguja     = [{
		type  : 'path',
		fill  : 'url(#gradientId)',
		path  : "M 93,95 "+10+","+0+" 107,95 z","stroke-width":"1",
		stroke: "#333"
	}];

	config = Ext.apply({
		spriteAguja        : spriteAguja,
		aguja              : aguja,
		circulo            : circulo,
		...

	}, config);
	Ext.bairesit.draw.Medidor.superclass.constructor.apply(this, arguments);
},

dibujarAguja: function () {
	var opacity = 0;
	var radio, angulo, alfa, puntaX0, puntaY0, beta, puntaX1, puntaY1, gama, puntaX2, puntaY2;
	if ((this.valorMinimo &lt;= this.valorActual) &amp;&amp; (this.valorActual &lt;= this.valorMaximo)){
		radio = 90;
		angulo = (((this.valorActual - this.valorMinimo) /(this.valorMaximo - this.valorMinimo)) * 180) ;

		alfa    = 180 - angulo + 90;
		puntaX0 = this.calcularCoordenadaX (alfa, 6, 130);
		puntaY0 = this.calcularCoordenadaY (alfa, 6, 130);

		beta    = 180 - angulo;
		puntaX1 = this.calcularCoordenadaX (beta, radio, 130);
		puntaY1 = this.calcularCoordenadaY (beta, radio, 130);

		gama    = 180 - angulo - 90;
		puntaX2 = this.calcularCoordenadaX (gama, 6, 130);
		puntaY2 = this.calcularCoordenadaY (gama, 6, 130);

		opacity = 1;
		/* Dibujo la Aguja */
		this.spriteAguja[0].setAttributes({
			path:   "M "+puntaX0+","+puntaY0+" "+
			puntaX1+","+puntaY1+" "+
			puntaX2+","+puntaY2+" z",
			"stroke-width":"1"
		});
	}
	this.spriteAguja[0].setAttributes({opacity:opacity});
	this.spriteCirculo[0].setAttributes({opacity:opacity});
	this.spriteAguja[0].redraw();
	this.spriteCirculo[0].redraw();
},

onRender: function(){
	Ext.bairesit.draw.Medidor.superclass.onRender.apply(this, arguments);
	...
	for (var r = 0; r &lt; this.aguja.length ; r = r+1){
		this.spriteAguja[r]   = this.surface.add(this.aguja[r]);
	};

	for (var b = 0; b &lt; this.circulo.length; b = b+1){
		this.spriteCirculo[b] = this.surface.add(this.circulo[b]);
	};
	...
},
...</pre>
<p><!-- Checkpoint 02 --></p>
<h3 id="grafico_de_lineas">Gráfico de Líneas</h3>
<p>Para poder mostrar la evolución del valor actual decidimos utilizar un gráfico de línea provisto por Oppo-touching, y la manera de activarlo es mediante un doble click (o doble tap) en la flecha de tendencia. Para ello, luego de renderizar el componente, le anexamos el comportamiento al sprite de la tendencia.</p>
<pre class="brush:javascript">afterRender:function(){
	Ext.bairesit.draw.Medidor.superclass.afterRender.call(this);
	for (var i = 0 ; i &lt; this.spriteTendencia.length ; i++ ){
		this.spriteTendencia[i].el.on(
			'doubletap',
			this.mostrarDetalle,
			this
		)
	}
},
mostrarDetalle: function (){
	var desplegableHistorial = new Ext.bairesit.draw.LineChart({
		tituloEjeY   : this.tituloEjeY,
		tituloEjeX   : this.tituloEjeX,
		maxY         : this.valorMaximo,
		titulo       : this.titulo,
		store        : this.store,
		esMinimoMejor: this.esMinimoMejor
	});
	desplegableHistorial.setCentered(true);
	desplegableHistorial.show();
}</pre>
<div>El gráfico de líneas es basicamente igual a los provistos en los ejemplos de ExjJS4-Charts con la particularidad de los colores de fondo, donde quisimos destacar las franjas donde está situado el valor. De estas franjas, las 2 inferiores las mostramos como 2 series de dátos adicionales para aprovechar la posibilidad de que esta evolucione en el tiempo. Y la franja superior la seteamos con el valorMaximo.</div>
<pre class="brush:javascript">// file: LineChart.js
Ext.namespace('Ext.bairesit.draw');

	var naranja              = "#eea800";
	var esmeralda            = "#00eeca";
	var amarillo             = "#ffffda";
	var verde                = "#dfffdf";
	var azul                 = "#0080ff";
	var rojo                 = "#FFCCCC";
	var tituloAlertaPeligro  = "Límite Alerta-Peligro";
	var tituloObjetivoAlerta = "Límite Objetivo-Alerta";
	var tituloPeligroAlerta  = "Límite Peligro-Alerta";
	var tituloAlertaObjetivo = "Límite Alerta-Objetivo"
	var colorSeparadorSup    = naranja;
	var colorSeparadorInf    = esmeralda;
	var coloresAscendentes   = [amarillo,verde,azul];
	var coloresDescendentes  = [amarillo,rojo,azul];
	var fondo                = rojo;
	var colores              = coloresAscendentes;
	var tituloSuperior       = tituloAlertaPeligro;
	var tituloInferior       = tituloObjetivoAlerta;

Ext.bairesit.draw.LineChart = Ext.extend(Ext.Panel, {

    ui          : 'dark',
    fullscreen  : true,
    floating    : true,
    modal       : true,
    centered    : true,
    layout      : 'fit',

    constructor:function(config) {
        var maxY          = maxY;
        var titulo        = titulo;
        var tituloEjeY    = tituloEjeY;
        var tituloEjeX    = tituloEjeX;
        var esMinimoMejor = esMinimoMejor;
        var dockedItems   = [{
                                dock  : 'top',
                                xtype : 'toolbar',
                                title : titulo
                            }];

        config = Ext.apply({
            titulo      : titulo,
            tituloEjeY  : tituloEjeY,
            tituloEjeX  : tituloEjeX,
            dockedItems : dockedItems
        }, config);

        Ext.bairesit.draw.LineChart.superclass.constructor.apply(this, arguments);
    },

    initComponent: function(config){
        Ext.bairesit.draw.LineChart.superclass.initComponent.apply(this, arguments);
        this.setTitulo(this.titulo);
    },

    /**
     * Lazy creation
     * When the parent is added into UI display list at the first time, we add the charting
     */
    doComponentLayout : function(width, height, isSetSize) {
        width  = width  * 0.8;
        height = height * 0.7;
        this.display(width, height);
        Ext.bairesit.draw.LineChart.superclass.doComponentLayout.apply(this, arguments);
    },

    display:function(width, height) {
        if(this.chart != null &amp;&amp; this.chart.width == width &amp;&amp; this.chart.height == height) {
            return;
        }

        if (!this.esMinimoMejor){
            colores             = coloresDescendentes;
            fondo               = verde;
            tituloInferior      = tituloPeligroAlerta;
            tituloSuperior      = tituloAlertaObjetivo;
            colorSeparadorSup   = esmeralda;
            colorSeparadorInf   = naranja;
        }

        Ext.chart.theme.Objetivo = Ext.extend(Ext.chart.theme.Category5, {
            constructor: function(config) {
                Ext.chart.theme.Category5.prototype.constructor.call(this, Ext.apply({
                    seriesThemes: [{
                                      fill          : colorSeparadorSup,
                                      stroke        : colorSeparadorSup,
                                      'stroke-width': 1.5
                                  },{
                                      fill          : colorSeparadorInf,
                                      stroke        : colorSeparadorInf,
                                      'stroke-width': 1.5
                                  }, {
                                      fill: azul
                                  }],
                    colors      : colores
                }, config));
            }
        });

        this.chart = new Ext.chart.Chart({
            insetPadding: 30,
            shadow      : true,
            theme       : 'Objetivo',
            legend      : {
                            position: 'right',
                            boxFill : '#eee'
                          },
            axes        : [{
                            type    : 'Numeric',
                            minimum : 0,
                            maximum : this.maxY,
                            position: 'left',
                            fields  : ['valor','objetivo','alerta'],
                            title   : this.tituloEjeY,
                            grid: {
                                odd: {
                                    opacity         : 1,
                                    fill            : fondo,
                                    stroke          : fondo,
                                    'stroke-width'  : 0.5
                                },
                                even: {
                                    opacity         : 1,
                                    fill            : fondo,
                                    stroke          : fondo,
                                    'stroke-width'  : 0.5
                                }
                            }
                        },{
                            type    : 'Category',
                            position: 'bottom',
                            fields  : ['mes'],
                            title   : this.tituloEjeX,
                            label   : { rotate: { degrees : 315 } }
                        }],
            series      : [{
                            type        : 'line',
                            title       : tituloSuperior,
                            axis        : 'left',
                            fill        : true,
                            xField      : 'mes',
                            yField      : 'alerta',
                            style       : { opacity: 1 },
                            showMarkers : false
                        },{
                            type        : 'line',
                            title       : tituloInferior,
                            axis        : 'left',
                            fill        : true,
                            xField      : 'mes',
                            yField      : 'objetivo',
                            style       : { opacity: 1 },
                            showMarkers : false
                        },{
                            type        : 'line',
                            title       : 'Valor',
                            axis        : 'left',
                            xField      : 'mes',
                            yField      : 'valor',
                            highlight   : {
                                            size: 7,
                                            radius: 7
                                        },
                            markerCfg   : {
                                            type          : 'circle',
                                            size          : 4,
                                            radius        : 4,
                                            'stroke-width': 0
                                        }
                        }]
        });
        this.chart.setSize(width, height);
        this.add(this.chart);
        this.chart.bindStore(this.store,true);
    },

    setTitulo: function(titulo){
        this.dockedItems.items[0].title = titulo;
    },

    refresh:function(data){
        this.dockedItems.items[0].title = this.titulo;
        this.chart.bindStore(data, true);
        this.chart.redraw();
    }
});</pre>
<h3 id="resultado_final">Resultado Final</h3>
<p>Para finalizar, revisamos nuevamente todo el código, perfeccionamos todos los detalles, y testeamos.</p>
<p>La siguiente imagen es una captura de los componentes una vez finalizado el trabajo:</p>
<p style="text-align: center;">
<p style="text-align: center;"><a href="http://www.bairesit.com/wp-content/uploads/2011/09/02_03_ResultadoFinal.png"><img class="size-medium wp-image-1296 aligncenter" title="02_03_ResultadoFinal" src="http://www.bairesit.com/wp-content/uploads/2011/09/02_03_ResultadoFinal-300x278.png" alt="" width="300" height="278" /></a></p>
<h3>Aclaraciones finales</h3>
<p>A esta altura muchos se preguntarán porque no decidimos utilizar <a href="http://www.sencha.com/products/touch/charts/">Sencha Touch Charts</a> para la confección de nuestros componentes, para ello tenemos en nuestro descargo que al momento de comenzar el trabajo aun no estaban disponibles ningunas de las versiones de <strong><em>Touch Charts</em></strong> y que estas fueron liberadas practicamente al finalizar con la confección de los gráficos. Sin embargo, igualmente intentamos migrar el código para que funciones con el paquete oficial de Sencha pero no tuvimos éxito debido a que <a href="http://www.sencha.com/blog/introducing-sencha-touch-charts/">Sencha decidió utilizar solamente el motor Canvas</a> para la capa media por una cuestión de compatibilidad con la mayor parte de dispositivos móviles (Android anterior a 3.1 no soporta SVG). Siendo el mayor problema para nosotros que con Canvas todo el gráfico se comporta como una unidad y no nos permite definirle comportamiento a los sprites individualmente como ser el reconocimiento de eventos. Entre nuestros requerimientos se encontraba que el componente sea capaz de mostrar la evolución de los valores y la manera elegida fue que el sprite de la flecha de tendencia reconozca el doble click. Canvas no nos permite esto, por lo tanto, decidimos continuar con el desarrollo utilizando Oppo-touching.</p>
<h3 id="tamaño"><span>Conclusiones</span></h3>
<p>Podemos concluir que en los casos en que no dispongamos de gráficos que cumplan con nuestros requerimientos, utilizando Oppo-touching (y en algunos casos Touch-Charts) podemos generar nuestros própios componentes, ya que esta librería nos provee todas las herramientas para ello.</p>
<p>Utilizar el addons <em>Touch-Charts</em> con soporte oficial de Sencha nos permitiría ciertas ventajas que no disponemos con Oppo-touching ya que este esta basado en ExtJS4-Charts y no fue pensado para interactuar con dispositivos móviles, con pantallas de tamaños reducidos, con el cambio de orientación horizontal o vertical, con capacidades de procesamiento limitadas, reconocimiento de eventos táctiles, etc.</p>
<p>Como desafio hacia el futuro, esperamos poder migrar a Touch-Charts y de alguna manera cumplir con todos los requerimientos mediante una activación alterna de la vista previa de la evolución histórica del estado.</p>
<h3 id="referencias">Referencias</h3>
<ul>
<li><a href="http://code.google.com/p/oppo-touching/">Oppo-Touching</a></li>
<li><a href="http://www.sencha.com/products/extjs/">ExtJS 4</a></li>
<li><a href="http://www.sencha.com/products/touch/charts/">Sencha Touch Charts</a></li>
<li><a href="http://github.com/">Repositorio Github</a></li>
</ul>
<p style="text-align: right;">Por el equipo del Laboratorio de Investigación Tecnológica (<strong><em>LIT</em></strong>) de <em>Baires IT</em></p>
]]></content:encoded>
			<wfw:commentRss>http://www.bairesit.com/2011/09/generar-componentes-visuales-personalizados-compatibles-con-sencha-touch/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Tableros de Control</title>
		<link>http://www.bairesit.com/2011/09/tableros-de-control/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=tableros-de-control</link>
		<comments>http://www.bairesit.com/2011/09/tableros-de-control/#comments</comments>
		<pubDate>Thu, 29 Sep 2011 20:11:39 +0000</pubDate>
		<dc:creator>sfernandez</dc:creator>
				<category><![CDATA[Consultoría]]></category>

		<guid isPermaLink="false">http://www.bairesit.com/?p=1243</guid>
		<description><![CDATA[Baires IT cuenta con una amplia experiencia en desarrollo e implementación de Tableros de Control que lo ayudarán en la toma de decisiones efectivas.]]></description>
			<content:encoded><![CDATA[<p>Baires IT cuenta con una amplia experiencia en desarrollo e implementación de Tableros de Control que lo ayudarán en la toma de decisiones efectiva basada en el análisis de datos sólidos, a través de la definición e implementación gráfica de indicadores clave de negocio que reflejen una vista integral y actualizada de la información.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bairesit.com/2011/09/tableros-de-control/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Gobierno de Datos</title>
		<link>http://www.bairesit.com/2011/09/gobierno-de-datos/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=gobierno-de-datos</link>
		<comments>http://www.bairesit.com/2011/09/gobierno-de-datos/#comments</comments>
		<pubDate>Thu, 29 Sep 2011 20:10:20 +0000</pubDate>
		<dc:creator>sfernandez</dc:creator>
				<category><![CDATA[Consultoría]]></category>

		<guid isPermaLink="false">http://www.bairesit.com/?p=1238</guid>
		<description><![CDATA[El gobierno de datos se refiere a las políticas y procesos por los cuales una compañía administra la calidad, consistencia, usabilidad, seguridad y disponibilidad de sus datos e información corporativa.]]></description>
			<content:encoded><![CDATA[<p>El gobierno de datos se refiere a las políticas y procesos por los cuales una compañía administra la calidad, consistencia, usabilidad, seguridad y disponibilidad de sus datos e información corporativa. Cuando hablamos de información corporativa, no nos limitamos sólo a los datos relacionados a los clientes, productos y servicios, sino que involucra a todos los datos corporativos.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bairesit.com/2011/09/gobierno-de-datos/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Limpieza y Enriquecimiento de Datos</title>
		<link>http://www.bairesit.com/2011/09/limpieza-y-enriquecimiento-de-datos/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=limpieza-y-enriquecimiento-de-datos</link>
		<comments>http://www.bairesit.com/2011/09/limpieza-y-enriquecimiento-de-datos/#comments</comments>
		<pubDate>Thu, 29 Sep 2011 20:08:56 +0000</pubDate>
		<dc:creator>sfernandez</dc:creator>
				<category><![CDATA[Consultoría]]></category>

		<guid isPermaLink="false">http://www.bairesit.com/?p=1235</guid>
		<description><![CDATA[Además de la limpieza y/o enriquecimiento del dato, el servicio se enfoca en asegurar la consistencia de los conjuntos de datos que se hayan formado como la recolección de datos de múltiples bases.]]></description>
			<content:encoded><![CDATA[<p>Nuestro servicio de Limpieza y Enriquecimiento de Datos (Data Cleaning) le permitirá definir, diseñar en detalle e implementar los planes de corrección y mejora de los datos surgidos a través de un diagnóstico. Además de la limpieza y/o enriquecimiento del dato, el servicio se enfoca en asegurar la consistencia de los conjuntos de datos que se hayan formado como la recolección de datos de múltiples bases. Las actividades realizadas involucran tareas de corrección y eliminación de los datos &#8220;sucios&#8221; de una o múltiples bases de datos (p.e.: datos incorrectos, inválidos, desactualizados, redundantes, incompletos, no-íntegros, etc.)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bairesit.com/2011/09/limpieza-y-enriquecimiento-de-datos/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Conversión y Migración de Datos</title>
		<link>http://www.bairesit.com/2011/09/conversion-y-migracion-de-datos/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=conversion-y-migracion-de-datos</link>
		<comments>http://www.bairesit.com/2011/09/conversion-y-migracion-de-datos/#comments</comments>
		<pubDate>Thu, 29 Sep 2011 20:07:12 +0000</pubDate>
		<dc:creator>sfernandez</dc:creator>
				<category><![CDATA[Consultoría]]></category>

		<guid isPermaLink="false">http://www.bairesit.com/?p=1232</guid>
		<description><![CDATA[Para lograr asegurar una correcta migración y conversión de datos, hemos diseñado un servicio de Control de la Conversión de datos]]></description>
			<content:encoded><![CDATA[<p>Para lograr asegurar una correcta migración y conversión de datos, hemos diseñado un servicio de Control de la Conversión de datos, enfocado a asistir a la Gerencia del proyecto, al líder del grupo de migración, y a los usuarios involucrados, aportando metodología y herramientas, que permitan disponer en el nuevo sistema de los datos necesarios y con un nivel de calidad aceptable, que soporten de manera adecuada las funcionalidades del nuevo sistema.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bairesit.com/2011/09/conversion-y-migracion-de-datos/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Calidad de Datos</title>
		<link>http://www.bairesit.com/2011/09/diagnostico-de-calidad-de-datos/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=diagnostico-de-calidad-de-datos</link>
		<comments>http://www.bairesit.com/2011/09/diagnostico-de-calidad-de-datos/#comments</comments>
		<pubDate>Thu, 29 Sep 2011 20:00:29 +0000</pubDate>
		<dc:creator>sfernandez</dc:creator>
				<category><![CDATA[Consultoría]]></category>

		<guid isPermaLink="false">http://www.bairesit.com/?p=1226</guid>
		<description><![CDATA[Nuestro servicio de Diagnóstico de Calidad de Datos le permitirá identificar los problemas de datos que afecten las entidades críticas del negocio.]]></description>
			<content:encoded><![CDATA[<p>Nuestro servicio de Diagnóstico de Calidad de Datos le permitirá evaluar el nivel de calidad que poseen los datos de negocio con el objetivo de determinar la situación actual de los datos de cara a una situación o iniciativa actual. Realizando actividades de descubrimiento de datos, el profiling, y el posterior análisis de los resultados, identificamos los problemas de datos que afecten las entidades críticas del negocio.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bairesit.com/2011/09/diagnostico-de-calidad-de-datos/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Consultoría</title>
		<link>http://www.bairesit.com/2011/09/consultoria-3/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=consultoria-3</link>
		<comments>http://www.bairesit.com/2011/09/consultoria-3/#comments</comments>
		<pubDate>Thu, 29 Sep 2011 19:53:16 +0000</pubDate>
		<dc:creator>sfernandez</dc:creator>
				<category><![CDATA[Servicios]]></category>

		<guid isPermaLink="false">http://www.bairesit.com/?p=1223</guid>
		<description><![CDATA[Consultoría en Calidad de Datos, Gobierno de Datos, Implementación de Tableros de Control.]]></description>
			<content:encoded><![CDATA[<p>Consultoría en Calidad de Datos, Gobierno de Datos, Implementación de Tableros de Control.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bairesit.com/2011/09/consultoria-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Consultoría</title>
		<link>http://www.bairesit.com/2011/09/consultoria-2/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=consultoria-2</link>
		<comments>http://www.bairesit.com/2011/09/consultoria-2/#comments</comments>
		<pubDate>Thu, 29 Sep 2011 19:42:40 +0000</pubDate>
		<dc:creator>sfernandez</dc:creator>
				<category><![CDATA[Sin categoría]]></category>

		<guid isPermaLink="false">http://www.bairesit.com/?p=1219</guid>
		<description><![CDATA[Consultoría en Calidad de Datos, Gobierno de Datos, Implementación de Tableros de Control.]]></description>
			<content:encoded><![CDATA[<p>Consultoría en Calidad de Datos, Gobierno de Datos, Implementación de Tableros de Control.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bairesit.com/2011/09/consultoria-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Introduccion a Aplicaciones RIA para dispositivos móviles</title>
		<link>http://www.bairesit.com/2011/09/introduccion-a-aplicaciones-ria-para-dispositivos-moviles/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=introduccion-a-aplicaciones-ria-para-dispositivos-moviles</link>
		<comments>http://www.bairesit.com/2011/09/introduccion-a-aplicaciones-ria-para-dispositivos-moviles/#comments</comments>
		<pubDate>Mon, 19 Sep 2011 12:00:00 +0000</pubDate>
		<dc:creator>sfernandez</dc:creator>
				<category><![CDATA[Laboratorio]]></category>

		<guid isPermaLink="false">http://www.bairesit.com/?p=1270</guid>
		<description><![CDATA[Sencha Touch es un framework JavaScript Web RIA basado en Sencha ExtJS orientado a los dispositivos móviles y las prestaciones de estos. Entre sus características mas destacadas en encuentran el manejo de eventos táctiles, geolocalización, integración de datos (JSON, XML, YQL, etc), rápida construcción, multiplataforma, alta escalabilidad, etc.]]></description>
			<content:encoded><![CDATA[<p> </p>
<p>Con la aceptación del estandar <strong>HTML5</strong> por parte de los browsers mas importantes y el rápido crecimiento de las ventas de los <strong>smartphones</strong> y <strong>tablets</strong> todas las miradas se posaron en desarrollar <strong>aplicaciones RIA</strong>.</p>
<p>En <strong>Baires IT</strong> no sólo desarrollamos aplicaciones RIA con <strong>Sencha Ext</strong> que nos ofrece una excelente base, sino también soluciones específicas para dispositivos móviles. En este artículo mostraremos el empleo de <strong>Sencha Touch</strong> para este fin.<span id="more-1270"></span></p>
<h3>Introducción</h3>
<p><em>Sencha Touch</em> es un framework JavaScript Web RIA basado en <em>Sencha ExtJS</em> orientado a los dispositivos móviles y las prestaciones de estos. Entre sus características mas destacadas en encuentran el manejo de <em>eventos táctiles</em>, <em>geolocalización</em>, <em>integración de datos (JSON, XML, YQL, etc)</em>, <em>rápida construcción</em>, <em>multiplataforma</em>, <em>alta escalabilidad</em>, etc.</p>
<p>Para introducirnos vamos a construir una aplicación que nos geolocalice en un mapa, y podamos consultar datos remotos sobre el estado actual del clima.</p>
<p>Además de Sencha Touch vamos a necesitar un servidor web y PHP, en nuestro caso utilizaremos Apache 2.2.17 y PHP 5.3.5.</p>
<h3>Instalación</h3>
<p>Para instalar el framework es tan simple como descargarlo desde la página oficial de Sencha, lo descomprimimos y lo colocamos dentro una carpeta de nuestro proyecto web. Para utilizarlo alcanza con refereciarlo en la sección de scripts de nuestro <em>index.html</em>.</p>
<p><!-- --></p>
<h3>Creación de una Aplicación</h3>
<p>En nuestro proyecto web generaremos una estructura como la siguiente:</p>
<p style="padding-left: 30px;"> </p>
<p style="text-align: center;"><a href="http://www.bairesit.com/wp-content/uploads/2011/09/00_Estructura_directorios.png"><img class="size-full wp-image-1280 aligncenter" title="00_Estructura_directorios" src="http://www.bairesit.com/wp-content/uploads/2011/09/00_Estructura_directorios.png" alt="" width="280" height="162" /></a></p>
<p>Debemos generar un archivo app.js en el que incluiremos el siguiente código para definir la aplicación:</p>
<pre class="brush:javascript">GeoWeather = new Ext.Application({
    name: "GeoWeather",
    launch: function() {
        this.views.viewport = new this.views.Viewport();
    }
});</pre>
<p>Para instanciar el Viewport y sus características deberemos crear el archivo Viewport.js:</p>
<pre class="brush:javascript">GeoWeather.views.Viewport = Ext.extend(Ext.TabPanel, {
    fullscreen:true,
    tabBar: {
        dock: 'bottom',
        layout: {
            pack: 'center'
        }
    },
    defaults: {
        layout: {
           pack: 'fit'
        }
    },
    items: [
        GeoWeather.views.TabAbout
    ]
});</pre>
<p>y finalmente el contenido de tabAbout.js:</p>
<pre class="brush:javascript">GeoWeather.views.TabAbout = new Ext.Panel({
    title: "About",
    iconCls: "info",
    fullscreen: true,
    styleHtmlContent: true,
    html: "
<div>Desarrollado por el Laboratorio de Investigación Tecnológica (LIT) de Baires IT</div>

",
    dockedItems:[{
            xtype: 'toolbar',
            dock: 'top',
            id: 'barraTabAbout',
            title: 'About...'
    }]
});</pre>
<p>Y finalmente referenciamos los nuevos archivos js en el index.html para que los cargue el browser:</p>
<pre class="brush:html&gt; ... &lt;mce:script type=">&lt;script src="app/views/tabAbout.js" type="text/javascript"&gt;<!--mce:0-->&lt;/script&gt;
&lt;script src="app/views/Viewport.js" type="text/javascript"&gt;<!--mce:1-->&lt;/script&gt;
 ...</pre>
<p>El resultado visto con Chrome y con un emulador de Android 2.3.1 es el siguiente:</p>
<p style="text-align: center;"><a href="http://www.bairesit.com/wp-content/uploads/2011/09/01_img_TabAbout.png"><img class="size-medium wp-image-1281 aligncenter" title="01_img_TabAbout" src="http://www.bairesit.com/wp-content/uploads/2011/09/01_img_TabAbout-300x184.png" alt="" width="300" height="184" /></a></p>
<p style="text-align: center;"> </p>
<p><!-- Checkpoint 01 --></p>
<h3>Geolocalizando</h3>
<p>HTML5 definió en su estandar que los browsers permitan geolocalizar al visitante y así aprovechar esta información con diferentes fines. Por supuesto Sencha adoptó esta característica y la extendió al asociarle métodos y eventos para así enriquecer las Aplicaciones Web RIA que se pueden lograr con este framework.</p>
<p>Agregaremos un subdirectorio &#8216;util&#8217; al directorio &#8216;app&#8217; de nuestro proyecto, y crearemos un archivo <em>tools.js</em>.</p>
<p>En este definiremos la función <em>obtenerCoordenadasSencha()</em> y la llamaremos justo antes de que se cree el Viewport:</p>
<pre class="brush:javascript">function obtenerCoodenadasSencha(){
    var geo = new Ext.util.GeoLocation({
        listeners: {
            locationupdate: function (geo) {
                localStorage.setItem('HomeLatitud',geo.latitude);
                localStorage.setItem('HomeLongitud',geo.longitude);
                localStorage.setItem('latitud',geo.latitude);
                localStorage.setItem('longitud',geo.longitude);
                alert('Usted se encuentra en ' +
                        'Latitud:'+geo.latitude+', ' +
                        'Longitud:'+geo.longitude);
            },
            locationerror: function (   geo,
                                        bTimeout,
                                        bPermissionDenied,
                                        bLocationUnavailable,
                                        message) {
                if(bTimeout){
                    alert('Timeout occurred.');
                }
                else{
                    alert('Error occurred.');
                }
            }
        }
    });
}</pre>
<p>Ya podemos observar que utilizamos ademas localStorage que es un almacenamiento definido por HTML5.</p>
<p>Hasta aquí el resultado visto con Chrome:</p>
<p style="text-align: center;"><a href="http://www.bairesit.com/wp-content/uploads/2011/09/02_img_AlertGeolocation.png"><img class="size-medium wp-image-1282 aligncenter" title="02_img_AlertGeolocation" src="http://www.bairesit.com/wp-content/uploads/2011/09/02_img_AlertGeolocation-300x184.png" alt="" width="300" height="184" /></a></p>
<p style="text-align: center;"> </p>
<p><!-- Checkpoint 02 --></p>
<h3>Consultar datos a otros Data Web Services</h3>
<p>En los comienzos de la informática, las mayoria de las aplicaciones eras procesos batch que tenían una entrada de datos, un proceso y una salida. Hace unos años esto fue cambiando y las aplicaciones se convirtieron en productoras y consumidoras de datos de manera dinámica. Para las Aplicaciones Web RIA es fundamental este dinamismo<!-- porque es el... (cri cri cri)-->.</p>
<p>Anteriormente destacamos como una de las características de Sencha Touch era el tener un potente paquete de manejo de Datos. Esta característica nos brinda muchas posibilidades a la hora de seleccionar la fuente, ya sea AJAX, JSON, YQL, etc y utilizarlos en nuestros Templates o Stores.</p>
<p>En esta sección vamos a enfocarnos en algunas de las posibilidades con las que podemos trabajar:</p>
<ul>
<li>Hacer una peticion JSON remota y aplicarla directamente en un XTemplate:</li>
</ul>
<pre class="brush:javascript">function hacerPeticionJSON(lat, lon) {
    Ext.util.JSONP.request({
        url: 'http://free.worldweatheronline.com/feed/weather.ashx',
        callbackKey: 'callback',
        params: {
            key: 'fccb3b08c6141118112306',
            q: lat +','+ lon,
            format: 'json',
            num_of_days: 5
        },
        callback: function(result) {
            var weather = result.data.weather;
            if (weather) {
                var panel = GeoWeather.views.TabPronostico,
                            weatherTpl = panel.tplPronostico;
                panel.update('');
                panel.update(weatherTpl.applyTemplate(weather));
            } else {
                alert('No hay datos de Pronóstico para esta ubicación\n'
                    + ' lat:' + lat
                    + ' long:'+ lon);
            }
        }
    });
}</pre>
<ul>
<li>Definir y rellenar de manera estática un Store en la aplicación</li>
</ul>
<pre class="brush:javascript">// Definimos y registramos un Modelo
Ext.regModel('CodigoDescripcion', {
    fields: ['codigo', 'descripcion']
});

// Creamos el Store y le rellenamos los datos de manera estática
GeoWeather.views.descripcionStore = new Ext.data.JsonStore({
    model  : 'CodigoDescripcion',
    sorters: 'codigo',
    data: [
        { codigo: '113', descripcion: 'Despejado'},
        { codigo: '116', descripcion: 'Parcialmente Nublado'},
        { codigo: '119', descripcion: 'Nublado'},
        { codigo: '122', descripcion: 'Encapotado'},
        { codigo: '143', descripcion: 'Bruma'},
		...
    ]
});</pre>
<ul>
<li>Hacer un requerimiento de datos externos, guardarlos en JsonStore y usarlos cuando se desee</li>
</ul>
<pre class="brush:javascript">// Definimos y registramos un Modelo
Ext.regModel('CiudadesApi', {
    fields: ['areaName', 'country' ,'region' ,'latitude', 'longitude']
});

// Creamos el Store
GeoWeather.views.cityStore = new Ext.data.JsonStore({
    model  : 'CiudadesApi',
    sorters: 'areaName'
});

// definimos una funcion que hace un requerimiento de datos
// y el callback rellena el Store con los datos obtenidos.
function consultaCiudades(ciudad) {
    var error = false;
    Ext.util.JSONP.request({
        url: 'app/util/CityJson.php',
        callbackKey: 'callback',
        params: {
            key: 'fccb3b08c6141118112306',
            query: ciudad,
            format: 'json',
            num_of_results: 10
        },
        callback: function(result) {
            var error = false;
            if (result){
                ciudades = result.result;
                if (ciudades) {
                    GeoWeather.views.cityStore.add(ciudades);
                    showDesplegableCiudades();
                } else {
                    error=true;
                }
            } else {
                error = true;
            }
            Ext.getBody().unmask();
            if (error){
                alert('No se han encontrado resultados para su consulta');
            }
        }
    });
}

// Definimos una lista que hace uso del Store definido y llenado anteriormente
GeoWeather.views.PanelCiudadesList = new Ext.List({
    store:GeoWeather.views.cityStore,
    fullscreen: true,
    itemTpl : new Ext.XTemplate(
        '',
                '{[this.retornaValor(values.value)]}',
         '',
        '',
            ' - ({[this.retornaValor(values.value)]})',
        '',
        ' - ',
        '',
            '{[this.retornaValor(values.value)]}',
        '',
        {
        compiled: true,
        retornaValor: function(valor){
            return (valor);
        }}),
        onItemDisclosure: function(record){
            var latLon = record.data.latitude+','+record.data.longitude;
            localStorage.setItem('latitud',record.data.latitude);
            localStorage.setItem('longitud',record.data.longitude);
            moverMapa(record.data.latitude,record.data.longitude);
            cerrarDesplegableCiudades();
        }
});</pre>
<ul>
<li>También podemos almacenarla dentro de un localStorage complementando y extendiendo HTML5.</li>
</ul>
<pre class="brush:javascript">// Definimos y registramos un Modelo
Ext.regModel('ModeloVientos', {
    fields: [ 'id', 'siglaIngles', 'siglaEspaniol', 'direccion'],
    proxy: {
        type: 'localstorage',
        id  : 'vientos'
    }
});

// Creamos el Store
GeoWeather.views.vientoStore = new Ext.data.Store({
    model  : 'ModeloVientos',
    sorters: 'siglaIngles'
});

// Agregamos los datos que deseamos almacenar en la localStorage.
// Estos datos al estar guardados en la localStorage pueden ser accedidos de cualquier otro módulo del sitio
// no necesariamente programados en Sencha Touch/ExtJS ya que se guardaron en la cache del navegador.
GeoWeather.views.vientoStore.load();
GeoWeather.views.vientoStore.add({siglaIngles: 'N',     siglaEspaniol:'N',   direccion:'Norte'});
GeoWeather.views.vientoStore.add({siglaIngles: 'NNE',   siglaEspaniol:'NNE', direccion:'Nornoreste'});
GeoWeather.views.vientoStore.add({siglaIngles: 'NE',    siglaEspaniol:'NE',  direccion:'Noreste'});
GeoWeather.views.vientoStore.add({siglaIngles: 'ENE',   siglaEspaniol:'ENE', direccion:'Estenoreste'});
GeoWeather.views.vientoStore.add({siglaIngles: 'E',     siglaEspaniol:'E',   direccion:'Este'});
GeoWeather.views.vientoStore.add({siglaIngles: 'ESE',   siglaEspaniol:'ESE', direccion:'Estesureste'});
...
GeoWeather.views.vientoStore.sync();</pre>
<p>Como vemos, las posibilidades son amplias, lo cual nos brinda una libertad de seleccionar la mas apropiada a nuestro requerimiento.</p>
<h3>Agregando otras pestañas</h3>
<p>El siguiente paso es agregar las demás pestañas, definirle los templates, rellenarlos, etc.</p>
<p>El mapa es un elemento provisto por Google, para lo cual debemos estudiarnos las API de ellos.</p>
<h3>Resultado Final</h3>
<p>Una vez pulidos los detalles menores vemos la aplicación en todo su esplendor.</p>
<p style="text-align: center;"><a href="http://www.bairesit.com/wp-content/uploads/2011/09/04_img_ResultadoFinal.png"><img class="size-medium wp-image-1283 aligncenter" title="04_img_ResultadoFinal" src="http://www.bairesit.com/wp-content/uploads/2011/09/04_img_ResultadoFinal-300x209.png" alt="" width="300" height="209" /></a></p>
<p style="text-align: center;"> </p>
<h3>Conclusiones:</h3>
<p>Si bien podemos decir que con la llegada de HTML5 las Aplicaciones Web RIA se manifestaron mas que interesantes a la hora de una solución rápida y multiplataforma, no podemos dejar de decir que Sencha nos dió el impulso final para comenzar a desarrollar aplicaciones para dispositivos móviles permitiendonos desarrollar aplicaciones web móviles que se ven y se sienten nativas en iPhone, Android, y BlackBerry.</p>
<h3>Referencias:</h3>
<ul>
<li><a href="http://www.sencha.com/products/touch/">Sencha Touch</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.bairesit.com/2011/09/introduccion-a-aplicaciones-ria-para-dispositivos-moviles/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Generar formularios dinámicos HTML con inputEx, CouchDB y Spring 3</title>
		<link>http://www.bairesit.com/2011/07/generar-formularios-dinamicos-html-con-inputex-couchdb-y-spring-3/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=generar-formularios-dinamicos-html-con-inputex-couchdb-y-spring-3</link>
		<comments>http://www.bairesit.com/2011/07/generar-formularios-dinamicos-html-con-inputex-couchdb-y-spring-3/#comments</comments>
		<pubDate>Fri, 15 Jul 2011 12:00:00 +0000</pubDate>
		<dc:creator>Administrador de Contenidos</dc:creator>
				<category><![CDATA[Laboratorio]]></category>

		<guid isPermaLink="false">http://www.bairesit.com/?p=927</guid>
		<description><![CDATA[Cuando es necesario presentar formularios web cuyos campos deben generarse de manera dinámica, muchas veces se decide almacenar la definición de los mismos en bases de datos relacionales o en XML, lo cual requiere un esfuerzo importante para la recuperación de la información, procesamiento, etc. Las bases de datos documentales ofrecen una alternativa interesante para [...]]]></description>
			<content:encoded><![CDATA[<p>Cuando es necesario presentar <strong>formularios</strong> web cuyos <strong>campos</strong> deben generarse de manera <strong>dinámica</strong>, muchas veces se decide almacenar la definición de los mismos en bases de datos relacionales o en XML, lo cual requiere un esfuerzo importante para la recuperación de la información, procesamiento, etc.<span id="more-927"></span></p>
<p>Las <strong>bases de datos documentales</strong> ofrecen una alternativa interesante para almacenar la definición de un formulario, sin perjuicio de la utilización de una base de datos relacional para la persistencia de los datos ingresados por el usuario. En este artículo se muestra cómo es posible generar una interfaz dinámica en HTML con muy poco código, haciendo uso de <strong>inputEx</strong>. Además se propone almacenar el documento con la definición de dicha interfaz en <strong>CouchDB</strong>, empleando JSON y accediendo a la misma mediante <strong>REST</strong>.</p>
<h3>Introducción</h3>
<p><strong>CouchDB</strong> es una base de datos orientada a documentos, sin      esquema, desarrollada en Erlang. Tiene una interfaz REST, por lo que no es      necesario ningún driver propietario para la conexión, y los datos son intercambiados      utilizando la popular notación JSON. Posee características que favorecen      la alta escalabilidad, como la tecnología en la cual está desarrollada, la      capacidad de replicación automática, etc.</p>
<p><strong>inputEx </strong>es un framework Javascript Web      RIA basado en Yahoo! UI (YUI), que permite generar formularios HTML      dinámicamente en el cliente, a partir de un objeto Javascript,      habitualmente representado utilizando notación JSON.</p>
<p><strong>RESTTemplate</strong> es una clase de utilidad      provista por Spring 3 que nos posibilita interactuar con servicios REST de      manera sencilla. En nuestro ejemplo la utilizaremos para acceder a      CouchDB.</p>
<p>Para este artículo se utilizó <strong>Ubuntu Linux 10.04</strong>.</p>
<h3>Instalación de CouchDB</h3>
<pre class="brush:shell">
    $ sudo apt-get install couchdb
</pre>
<p>Una vez instalada, es posible verificar su correcto funcionamiento utilizando el comando curl:</p>
<pre class="brush:shell">
    $ curl http://localhost:5984/
    {"couchdb":"Welcome","version":"1.0.0"}
</pre>
<h3>Creación de un formulario HTML inicial</h3>
<p>Para crear un formulario HTML de manera rápida, podemos ayudarnos con el &#8220;Form Builder&#8221; de inputEx:</p>
<p><a title="http://neyric.github.com/inputex/tools/builder/index.html" href="http://neyric.github.com/inputex/tools/builder/index.html">http://neyric.github.com/inputex/tools/builder/index.html</a></p>
<p>Como resultado de la creación del formulario, obtenemos un fragmento de JSON que insertaremos en CouchDB.</p>
<h3>Carga del formulario en CouchDB</h3>
<p>Ingresando a <a title="http://localhost:5984/_utils/" href="http://localhost:5984/_utils/">http://localhost:5984/_utils/</a> se accede a Futon, la interfaz de administración de CouchDB.</p>
<p>Desde allí es posible crear una base de datos llamada &#8220;forms&#8221; presionando el botón &#8220;Create database&#8221;.</p>
<p>Dentro de la base de datos recién creada, presionar el botón &#8220;New   Document&#8221;. Puede observarse un documento con un único field llamado   &#8220;_id&#8221; y un valor generado automáticamente.</p>
<p>Seleccionar la pestaña &#8220;Source&#8221; y hacer doble clic en el código JSON, pegando el código generado en el Form Builder de inputEx.</p>
<p>Importante: No eliminar el atributo &#8220;_id&#8221; generado automáticamente   por CouchDB, ya que es necesario para la identificación del documento.</p>
<p>Esta es la configuración del formulario recientemente creado:</p>
<p style="text-align: center;"><strong><a href="http://www.bairesit.com/wp-content/uploads/2011/01/imagen1.png"><img class="size-full wp-image-941  aligncenter" title="imagen1" src="http://www.bairesit.com/wp-content/uploads/2011/01/imagen1.png" alt=""></a></strong></p>
<p>Desde nuestra aplicación Spring 3, agregamos un Controller que   interactúe con la base de datos y nos retorne el JSON necesario para   renderizar el formulario.</p>
<pre class="brush:java">
    @Controller
    public class FormController {
        ...
        @RequestMapping("/form/${id}")
        public String showForm(ModelMap model, @PathVariable String id) {
            // Recuperación del JSON del formulario desde CouchDB
            String form = restTemplate.getForObject(url, String.class, id);
            // Se coloca en el Model de Spring MVC
            model.put("formDefinition", form);
            // Se indica al view resolver que muestre la vista llamada dynamicForm
            // (que será resuelta a dynamicForm.jsp)
            return "dynamicForm";
        }
    }
</pre>
<p>Finalmente, escribimos la página JSP (dynamicForm.jsp) responsable de   generar dinámicamente el formulario, haciendo uso de inputEx:</p>
<pre class="brush:js">
    ...
    &lt;script type="text/javascript"&gt;
        var formDef = ${formDefinition};
        formDef.parentEl = 'formContainer';
        YAHOO.util.Event.onDOMReady(function(){ new inputEx.Form(formDef); });
    &lt;/script&gt;
    ...
</pre>
<p>Lo único que hace la página JSP es volcar el JSON retornado por CouchDB en un objeto inputEx.Form.</p>
<p>Este es el resultado terminado:</p>
<p style="text-align: center;"><strong><a href="http://www.bairesit.com/wp-content/uploads/2011/01/imagen2.png"><img class="size-full wp-image-942  aligncenter" title="imagen2" src="http://www.bairesit.com/wp-content/uploads/2011/01/imagen2.png" alt=""></a></strong></p>
<p>La configuración del formulario dinámico se encuentra en la base de   datos, por lo que es posible regresar a Futon y agregar un fragmento de   JSON dentro de la colección &#8220;fields&#8221;, con los datos de un nuevo campo   &#8220;birthday&#8221;:</p>
<pre class="brush:js">
    fields: [
    ...
        {
            "name": "birthday",
            "type": "datepicker",
            "label": "Fecha de Nac."
        },
    ...
    ]
</pre>
<p>Refrescando la página, sin necesidad de reiniciar la aplicación, se puede apreciar el resultado de la modificación:</p>
<p style="text-align: center;"><strong><a href="http://www.bairesit.com/wp-content/uploads/2011/01/imagen3.png"><img class="size-full wp-image-943  aligncenter" title="imagen3" src="http://www.bairesit.com/wp-content/uploads/2011/01/imagen3.png" alt=""></a></strong></p>
<p>Para más detalles, el código fuente completo del ejemplo se encuentra en: <a title="http://github.com/bairesit/dynamicforms/archives/master" href="http://github.com/bairesit/dynamicforms/archives/master">http://github.com/bairesit/dynamicforms/archives/master</a></p>
<h3>Conclusión</h3>
<p>Es posible combinar una base de datos documental, que intercambie   datos en formato JSON, con la interfaz web para la generación rápida y   ágil de contenido dinámico. Si bien podríamos haber almacenado el JSON   en un archivo de texto, o .js, nos pareció interesante externalizarlo a   una DB, para lograr el mayor grado de dinamismo y facilidad de   administración posibles.</p>
<p style="font-size: 12px; text-align: right;"><strong><em>Por el Laboratorio de Investigación Tecnológica (LIT) de Baires IT</em></strong></p>
]]></content:encoded>
			<wfw:commentRss>http://www.bairesit.com/2011/07/generar-formularios-dinamicos-html-con-inputex-couchdb-y-spring-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

