Desarrollando aplicaciones en HTML5 para AppUp. Parte 4: una Lista de Tareas

Desarrollando aplicaciones en HTML5 para AppUp
Parte 4: Una lista de tareas

Introducción

En esta cuarta entrega de nuestra serie sobre desarrollo de aplicaciones en HTML5 para AppUp analizaremos una simple aplicación: una lista de tareas. Nos servirá para ilustrar algunas de las capacidades de HTML5 tales como Web Storage, localización y utilización de librerías de terceros. Y finalmente, también describiremos el proceso de enviar una aplicación a la tienda de aplicaciones de AppUp.

El código y los archivos utilizados en este ejemplo pueden bajarse fácilmente aquí. El artículo original en inglés es de jefftranter y puede leerse aquí. Vayamos entonces al artículo.

Descripción general

Nuestra aplicación será un simple manejador de tareas que le permitirá al usuario ingresar una lista de cosas para hacer, cada una con una descripción y un campo con el estado en el que se encuentra dicha tarea. De hecho, la aplicación es una versión simplificada de la verdadera aplicación que el autor del blog, Jeff Tranter, y su equipo están desarrollando.

Para mantener la aplicación lo más simple posible, sólo soportará la siguientes capacidades:

  • Soporta una única lista de tareas.
  • Cada tarea tendrá un nombre y el status en que se encuentra.
  • Tendrá un checkbox para marcar las tareas como "completadas".
  • Se podrá agregar, eliminar o renombrar tareas.
  • Se podrá, a su vez, eliminar todas las tareas completadas o directamente todos los items.
  • Utilizaremos Local Storage, es decir, no se almacenarán en ningún servidor central.
  • El usuario, si lo desea, podrá ordenar los items alfabéticamente.


La aplicación solicitará al usuario que confirme cualquier operación que resulte en pérdida de información tal como la eliminación de una tarea. Por último agregaremos una pequeña caja "acerca de" para mostrar la versión y el autor de la aplicación.

La imagen 1 muestra la interfaz de usuario de la aplicación. En este caso, con cinco items ingresados. Cada uno de ellos tiene un checkbox para indicar el estado en el que se encuentra y un botón para renombrar o eliminar la tarea.

Pantalla principal
Imagen 1: Pantalla principal de la Lista de Tareas

Arriba a la derecha hay un botón de Cerrar. Recuerda que se trata de una aplicación y no de una página web, así que un usuario debe poder cerrar la aplicación cuando termina de utilizarla. Arriba a la izquierda está el menú que muestra las operaciones soportadas. La imagen 2 muestra el menú desplegado cuando se da clic al botón.

 

Application Menu
Imagen 2: Menú de la aplicación

La imagen 3 muestra la interfaz con la que interactúa el usuario para agregar una tarea nueva. Un cuadro de diálogo se abre para que el usuario ingrese el nombre de la tarea, junto con los botones de aceptar o cancelar la operación. Las operaciones tales como eliminar o renombrar utilizan un diálogo similar solicitando al usuario la confirmación.

 

Adding A New Item
Imagen 3: agregando un nuevo item

Web Storage

Esta aplicación utiliza Web Storage para almacenar la información. Démosle una mirada a este feature clave de HTML5.

Web Storage es una API para almacenamiento persistente del lado del cliente web. Una base de datos del lado del cliente es la que almacena información en pares de valores clave (key values). La información se almacena localmente en el browser. Típicamente soportan hasta 5 megabytes de datos. Los navegadores más actuales soportan esta capacidad.

Hay dos tipos de almacenamiento: "Local Storage" es utilizado para acceder al área de almacenamiento local de una página web. "Session storage" representa las áreas de almacenamiento específicas al contexto actual de navegación de alto nivel. Los sitios pueden agregar datos al almacenamiento por sesión y serán accesibles para cualquier página del mismo sitio que abramos en una misma ventana. Cada contexto de navegación de alto nivel tiene un único set de áreas de almacenamiento por sesión, una por cada origen.

El "Session Storage" (almacenamiento por sesión) está diseñado para escenarios donde el usuario está efectuando una sola transacción pero que podría estar realizando múltiples transacciones en diferentes ventanas al mismo tiempo. El mecanismo de "Local storage" o almacenamiento local está diseñado para conservar los datos a través de múltiples ventanas y que dura más allá de la sesión actual. En particular, aplicaciones web podrían requerir almacenar varios megabytes de información del usuario, tales como archivos de la bandeja de correo, y que se guardarían del lado del cliente por cuestiones de performance.

Veamos unos ejemplos para ilustrar cómo se usa el Local Storage. La transcripción 1 muestra cómo introducir datos al Local Storage, incluyendo un chequeo de errores.

if 

(typeof (localStorage) == 'undefined') 
  {
    alert('Your browser does not support HTML5 localStorage. Try upgrading.');
  }
  else 
  {
    try 
    {
      localStorage.setItem('name', 'Hello World!') ; // Save string "Hello World" with key "name"
    }
    catch (e)  
    {
      if (e == QUOTA_EXCEEDED_ERR)
      {
        alert('Not enough space. Quota exceeded!'); // Data not stored because due to lack of space.
      }
    }
  }
Transcripción 1: Introduciendo datos al Local Storage

 

La transcripción 2 muestra cómo podemos recuperar del Local Storage la cadena que salvamos previamente, así como también eliminarla.

 

 value = localStorage.getItem('name'); // Should return string "Hello World!" localStorage.removeItem('name'); // Removes string "Hello World!" with key "name"
Transcripción 2: Recuperando datos del Local Storage

 

Hay más por saber sobre la Web Storage API, pero es muy simple de utilizar. La referencia completa para Web Storage puede hallarse aquí.

Localización

En el mercado actual la localización de las aplicaciones es muy importante. Los ejemplos anteriores ingoraron este hecho en pos de la simplicidad. Para esta aplicación pensé que podríamos mostrar un ejemplo de cómo localizar una aplicación en HTML5.

En este caso usamos un código de un tercero l10n.js para hacerlo más fácil. l10n.js es una librería de JavaScript que permite la localización pasiva a través de métodos nativos de JavaScript.

Para usarlo hemos bajado e incluído el archivo To use it, we download and include the file l10n.js en nuestra aplicación. En nuestro index.html file agregamos la primera línea mostrada en la Transcripción 3. Pusimos nuestras traducciones en un archivo y lo incluimos. En este caso usamos el archivo localization/localization.js que está incluído en la segunda línea de la Transcripción 3.

 

<script src="l10n/l10n.js" type="text/javascript"></script> ... <script src="localization/localization.js" type="text/javascript"></script>
Transcripción 3: El código de localización en index.html

 

Cada vez que referenciamos una cadena visible en nuestro código la incluimos en la función toLocaleString. El archivo main.js muestra muchos ejemplos de esto. Al ejecutarse, la librería de localización busca la traducción apropiada para la cadena en el lenguaje correspondiente del archivo localization/localization.js. Este archivo implementa la función toLocaleString.

En este ejemplo mostramos unas traducciones de muestra en ruso para las cadenas de la aplicación. La imagen 4 muestra la aplicación corriendo bajo el locale ruso.

Aplicación corriendo en ruso
Imagen 4: Aplicación corriendo en ruso

Si las traducciones no son provistas por la localización utilizada estas recaen en las cadenas que hallamos en el código fuente de JavaScript.

Ésta es sólo una de las posibles formas de localización pero en la práctica es bastante fácil de utilizar.

Código de un tercero

Muchas veces es posible reducir el esfuerzo de escribir código y evitar reinventar la rueda en una aplicación web simplemente con utilizar código y librerías de un tercero. Una amplia gama de librerías útiles de JavaScript están disponibles y muchas de ellas son gratuitas. Para esta aplicación utilizamos tres librerías externas.

Usamos popupmenu.js, un simple menú pop-up en JavaScript escrito por Jiro Nishiguchi. Esta es una pequeña librería (de unas 150 líneas de código) que facilitó la implementación de los menúes en nuestra aplicación.

También utilizamos jQuery Alerts Dialogs. Éste provee una alternativa para las alert, confirm, and prompt funciones que ofrecen una serie de mejoras.

Finalmente usamos jQuery. JQuery es una librería que provee soluciones para una gran cantidad de cosas incluyendo navegar documentos, seleccionar elementos del DOM, crear animaciones, procesar eventos y desarrollar aplicaciones AJAX. Es la aplicación más popular de las que se usan hoy en día.

Al utilizar cualquier código de terceros se debe prestar especial atención a la licencia de uso para asegurarse que se está respetando las condiciones que ésta impone.

Paso a paso del código

Nuestra aplicación provee el típico icon.png que es un archivo de imagen utilizado por el Encapsulator como ícono de la aplicación. También incluímos una hoja de estilo app.csspara establecer algunas pautas de cómo se verá.

La ejecución, como es usual, empieza con el index.html. Incluye también todos los archivos de JavaScript que estaremos utilizando. Muestra título y encabezado y algunos botones como el de "Menu" y "Close". El body del HTML llama a la función init al cargar la página.

El código de la aplicación en sentido estricto está en main.js y son sólo unas 300 líneas de JavaScript. Si bien es muy larga como para mostrar todo el código aquí, daré una breve descripción de qué hace cada una de las funciones y mostrar algunos pedazos de código, especialmente de las funciones más complejas. Recomiendo que examinen la totalidad del código por su cuenta.

La función init es llamada por index.html al comienzo para la inicialización.

La función closeApplication está vinculada al botón Close. Simplemente llama a la función de la API de AppUp intel.adp.encapsulator.closeapplication para cerrar la aplicación.

La función onAddNewItem, mostrada en la transcripción 4, agrega un nuevo item a la lista. Utiliza Web Local Storage.

function onAddNewItem()
{
    jPrompt('Please enter item name:'.toLocaleString(), '', 
            'To Do List'.toLocaleString(), function (itemName) {

        if (itemName == "" || itemName 

== null)
            return;

        var dateNow = new Date;

        if (typeof(localStorage) == 'undefined' )
        {
            jAlert('Your browser does not support HTML5 localStorage. Try upgrading.'.toLocaleString(),
                   'To Do List'.toLocaleString());
        }
        else
        {
            try
            {
                var values = new Array();
                values.push(itemName);
                values.push("false");

                localStorage.setItem("item_"+dateNow.toDateString().replace(new RegExp(" ",'g'),"_") + "_"+
                                     dateNow.toTimeString().substring(0,8).replace(new RegExp(":",'g'),"_"),
                                     values.join(";"));
                getAllItems();
            }
            catch (e)
            {
                if (e == QUOTA_EXCEEDED_ERR)
                {
                    jAlert('Quota exceeded!'.toLocaleString (), 'To Do List'.toLocaleString ());
                }
            }
        }

    });
}
Transcripción 4: Función onAddNewItem del archivo main.js

 

Función onClearCompletedItems (transcripción 5) recorre los ítems en local storage y elimina cualquiera que esté marcado como "completado". Advierte al usuario lo que hará antes de proceder.

 

function onClearCompletedItems()
{
    jConfirm('Are you sure you want to delete all completed items?'.toLocaleString(), 
             'To Do List'.toLocaleString(), function (answer) {

        if (answer)
        {
            var checked=false;
            do
            {
                var logLength = 

localStorage.length-1;

                for (var i = 0; i <= logLength; i+ +)
                {
                    var itemKey = 

localStorage.key(i);
                    var values = 

localStorage.getItem(itemKey);

                    if (values)
                    {
                        values = values.split(";");
                        var value = values[0];
                        checked = values[1];

                        if (checked == "true")
                        {
                            localStorage.removeItem(itemKey);
                        }
                    }
                }
            } while (checked == "true")

            getAllItems(); 

// Refresh the list of items
        }
    });
}
Transcripción 5: Función onClearCompletedItems desde el archivomain.js

 

La función onClearAllItems elimina todos los items simplemente limpiado todo el local storage.

La función onSortByName establece en el local storage que queremos los items ordenados por nombere y luego llama a getAllItems.

La función onUnsortedList elimina la señal en local storage que indicaba si queríamos los items ordenados por nombre y llama a getAllItems.

La función onAbout utiliza la función jAlert para mostrar una caja muy simple mostrando el nombre de la aplicación, la versión y la información de copyright.

La funciónonCheckItem, mostrada en la transcripción 6, es llamada cuando la opción "completada" del checkbox se marca o se desmarca para un item. Reemplaza la entrada en local storage del item con uno que refleja el nuevo "status".

 

function onCheckItem(data)
{
    if (typeof(localStorage) == 'undefined' )
    {
        jAlert('Your browser does not support HTML5 localStorage. Try upgrading.'.toLocaleString(),
               'To Do List'.toLocaleString());
    }
    else
    {
        try
        {
            var itemKey = data.item(0).id;
            var values = localStorage.getItem (itemKey);
            values = values.split(";");
            var value = values[0];
            var checked = values[1];

            localStorage.removeItem(itemKey); // Remove item with old name 

            values = new Array();
            values.push(value) ;
            values.push( (checked == "false") ? "true" : "false");
            localStorage.setItem(itemKey, values.join(";")); // Add new item
            getAllItems();
        }
        catch (e) 
        {
            if (e == QUOTA_EXCEEDED_ERR)
            {
                jAlert('Quota exceeded!'.toLocaleString (), 'To Do List'.toLocaleString ());
            }
        }
    }
}
Transcripción 6: Función onCheckItem del archivo main.js

 

La función onDeleteItem es llamada cuando se invoca la opción "borrar" del menú y elimina un ítem del local storage luego de la confirmación del usuario.

La función onRenameItem, mostrada en la transcripción 7, se encarga del proceso de renombrar un item, obteniendo del usuario el nuevo nombre y reemplazando la entrada correspondiente del item en el local storage.

 

function onRenameItem(data)
{
    if (typeof(localStorage) == 'undefined' )
    {
        jAlert('Your browser does not support HTML5 localStorage. Try upgrading.'.toLocaleString(), 
               'To Do List'.toLocaleString());
    }
    else
    {
        try
        {
            var itemKey = data.item(0).id;
            var values = localStorage.getItem (itemKey);
            values = values.split(";");
            var value = values[0];

            jPrompt('Please enter new item name:'.toLocaleString (), value, 
                    'To Do List'.toLocaleString(), function  (itemName) 
            {

                if (itemName == "" || itemName == null)
                    return;

                var checked = values[1];
                localStorage.removeItem(itemKey); // Remove item with old name
                values = new Array();
                values.push(itemName);
                values.push(checked);
                localStorage.setItem(itemKey, values.join(";")); // Add new item
                getAllItems();
            });
        }
        catch (e) 
        {
            if (e == QUOTA_EXCEEDED_ERR)
            {
                jAlert('Quota exceeded!'.toLocaleString (), 'To Do List'.toLocaleString ());
            }
        }
    }
}
Listing 7: Function onRenameItem from file main.js

 

La función getAllItems (transcripción 8) realiza una iteración a través del local storage y genera elHTML para la lista que se mostrará. Si "ordenar por nombre" está seleccionado, ordenará los registros antes de construir la lista.

 

function getAllItems() { var timeLog = ""; var i = 0; var sortByName = localStorage.getItem("sortByName"); var items = new Array(); var logLength = localStorage.length-1; for (i = 0; i <= logLength; i++) { var itemKey = localStorage.key(i); if (itemKey == "sortByName") continue; var values = localStorage.getItem(itemKey); if (values) { values = values.split(";"); var value = values[0]; var checked = values[1]; if (sortByName == "true") { items.push(value); } else if (checked == "false") { timeLog += '<span class="check" id="'+itemKey+'" onclick="onCheckItem('+itemKey+')"><img src="icons/checkbox_off.png"/></span> <strong>'+value+'</strong> <span class="edit" id="'+itemKey+'" onclick="onRenameItem('+itemKey+')"><img src="icons/rename.png"/></span> <span class="delete" id="'+itemKey+'" onclick="onDeleteItem('+itemKey+')"><img src="icons/delete.png"/></span><br>'; } else { timeLog += '<span class="check" id="'+itemKey+'" onclick="onCheckItem('+itemKey+')"><img src="icons/checkbox_on.png"/></span> <strong><del>'+value+'</del></strong> <span class="edit" id="'+itemKey+'" onclick="onRenameItem('+itemKey+')"><img src="icons/rename.png"/></span> <span class="delete" id="'+itemKey+'" onclick="onDeleteItem('+itemKey+')"><img src="icons/delete.png"/></span><br>'; } } } if (sortByName == "true") { var indexes = new Array(); items.sort(); for (i = 0; i < items.length; i++) { for (var j = 0; j <= logLength; j++) { var itemKey = localStorage.key(j); if (itemKey == "sortByName") continue; var values = localStorage.getItem(itemKey); if (values) { values = values.split(";"); var value = values[0]; var checked = values[1]; if (items[i] == value) { if (-1 != indexes.indexOf(j)) { continue; } else { indexes.push(j); } if (checked == "false") { timeLog += '<span class="check" id="'+itemKey+'" onclick="onCheckItem('+itemKey+')"><img src="icons/checkbox_off.png"/></span> <strong>'+value+'</strong> <span class="edit" id="'+itemKey+'" onclick="onRenameItem('+itemKey+')"><img src="icons/rename.png"/></span> <span class="delete" id="'+itemKey+'" onclick="onDeleteItem('+itemKey+')"><img src="icons/delete.png"/></span><br>'; } else { timeLog += '<span class="check" id="'+itemKey+'" onclick="onCheckItem('+itemKey+')"><img src="icons/checkbox_on.png"/></span> <strong><del>'+value+'</del></strong> <span class="edit" id="'+itemKey+'" onclick="onRenameItem('+itemKey+')"><img src="icons/rename.png"/></span> <span class="delete" id="'+itemKey+'" onclick="onDeleteItem('+itemKey+')"><img src="icons/delete.png"/></span><br>'; } break; } } } } } if (timeLog == "") timeLog = 'No items in your list. Add one.'.toLocaleString(); $("#theLog").html(timeLog); }
Transcripción 8: Función getAllItems del archivo main.js

 

La función showMenu, presentada en la transcripción 9, está relacionada con el botón Menú y muestra las opciones del mismo, enlazándolas con las correspondientes funciones de JavaScript. Nótese que las cadenas correspondientes han sido localizadas.

 

function showMenu()
{
    var popup = new PopupMenu();
    popup.add("Add New Item".toLocaleString() , onAddNewItem);
    popup.addSeparator();
    popup.add("Clear Completed Items".toLocaleString() , onClearCompletedItems);
    popup.add("Clear All".toLocaleString() , onClearAllItems);
    popup.addSeparator();
    popup.add("Sort By Name".toLocaleString() , onSortByName);
    popup.add("Unsorted List".toLocaleString() , onUnsortedList);
    popup.addSeparator();
    popup.add("About To Do List".toLocaleString() , onAbout);
    popup.setSize(180, 0);
    popup.bind();
    popup.show();
}
Listing 9: Function showMenu from file main.js

 

Subiéndola a AppUp

En lecciones anteriores vimos cómo usar el Encapsulator para crear una aplicación en HTML5 para Windows. Una vez que nuestra aplicación está terminada, desearemos subirla a AppUp. Miremos cuáles son los pasos involucrados para poder subir nuestra lista de tareas a la tienda de aplicaciones.

 

Generar y probar la aplicación con Encapsulator

Antes de subir la aplicación a la tienda debes probarla bien, incluyendo tests en todas las plataformas que planeas soportar.

Encapsulator tiene algunos seteos adicionales que podrías querer utilizar en "Additional app settings" (Imagen 5).


Imagen 5: Seteos adicionales de Encapsulator

También necesitarás utilizar el GUID que se obtiene como parte del proceso de hacer el "submit" de tu aplicación. Ingresa ese GUID en el campo correspondiente del Encapsulator. La imagen 6 muestra un ejemplo. Cuando especificas un GUID, Encapsulator construirá tu aplicación en modo "release" y deshabilitará algunas herramientas de debugging tal como el Web Inspector.


Imagen 6: Configuración del Encapsulator

 

Firma digital del paquete

El paquete msi de Windows subido a AppUp debe ser firmado digitalmente para poder verificar quién es el responsable de la aplicación. Las instrucciones para hacer esto pueden hallarse aquí. Si no tienes esta clave para firmar puedes obtenerla gratis como desarrollador AppUp. Sólo debes seguir estas instrucciones para firmar el archivo msi ya sea como organización o como individuo.

Completa la información solicitada siguiendo el formulario web

El proceso para subir una aplicación a AppUp se inicia al ingresar a tu tablero en el sitio del programa para desarrolladores de Intel y seleccionar la opción "Iniciar una nueva aplicación" (Imagen 7).


Imagen 7: Iniciando una nueva aplicación desde el tablero

Primero debes indicar el nombre de la aplicación. Este nombre deberá ser único. Si ya ha sido utilizado se te solicitará que ingreses otro. Como en este caso "To Do List" estaba tomado lo cambiamos a "Simple To Do List" (Imagen 8).


Imagen 8: Ingresando el nombre de la aplicación

Ahora deberás completar el formulario para subir la aplicación. Tiene una serie de solapas que te permitirá completar la información sobre los distintos aspectos de tu aplicación. Hay una ayuda detallada acerca de cómo completar cada uno de los formularios. Las imágenes 9 y 10 algunos de ellos ya completados con motivo de la subida de nuestra nueva aplicación.


Imagen 9: Envío de aplicaciones (Solapa "Información de la aplicación")


Imagen 10: Envío de aplicaciones (Solapa "Precios")

En cualquier momento puedes salvar tu trabajo y volver luego. Cuando el paquete de tu aplicación es testeado y firmado digitalmente puedes subirlo desde el formulario.

Opción de hacer que el paquete sea accesible a Beta Testers

AppUp te permite hacer una versión beta de tu aplicación y que esté disponible a ciertos usuarios antes de ser lanzada. Es muy recomendable hacer eso. La imagen 11 muestra la pantalla principal.


Imagen 11: La pantalla de Beta Testing

Tener la aplicación validada por el Equipo de Validación de AppUp

Cuando consideres que tu aplicación está probada y lista para ser enviada, puedes subirla para validación. El equipo de AppUp realizará una serie de pruebas a tu aplicación para asegurarse que el proceso está completo y que la aplicación corre correctamente. Una descripción detallada del proceso de validación puede ser hallada aquí.

After you submit your application you can check your Dashboard to see the validation status. Figure 12 shows an example.


Imagen 12: El status de la aplicación

Si el envío falla los pormenores serán indicados y recibirás un e-mail. De esta forma podrás corregir los errores y enviarla nuevamente.

Una vez que se haya validado la aplicación se publica en AppUp

Cuando el envío de tu aplicación ha sido validado por el equipo de Validación de AppUp, se mostrará como "publicado" en tu Tablero y recibirás un mail de confirmación. Tu aplicación está ahora en la tienda de AppUp para que los interesados la compren (si no es gratuita) y la bajen. Tu Tablero también muestra información de tus aplicaciones publicadas incluyendo la cantidad de downloads y comentarios.

Resumiendo

En este artículos hemos mirado una aplicación que utilizó Web Storage, código de terceros y que fue localizada. Paseamos por el proceso de enviarla a la tienda de Intel AppUp. Espero que este tutorial haya sido una inspiración para desarrollar nuevas aplicaciones y subirlas a AppUp.Up.

Copyright © 2011, Intel Corporation.
Tags:
Nähere Informationen zur Compiler-Optimierung finden Sie in unserem Optimierungshinweis.