IoT-Gadgets-Logo-272-90

Developing HTML5 Applications for AppUp - Part 4: A To Do List Application

By
Ash
 - 
Jan 14, 2012

Introduction


In this fourth installment of our series on developing HTML5 applications for AppUp, we'll look at a simple To Do list application. It will illustrate some HTML5 features such as Web Storage, localization, and use of third party libraries. We'll also describe the process of submitting an application to the AppUp store.

All of the source files described in this article can be downloaded from here.
Requirements


Our application will be a simple To Do list manager that will allow a user to enter a list of tasks, each of which will have a description and a completion status. This is a simplified version of a real application that is being developed by the author's team.

To keep the example application simple, we only want to support these basic features:

  • Support only a single list of tasks/todo items.
  • Each task will have a name and a completion status.
  • Provide a checkbox to mark tasks as complete.
  • Be able to add, remove, or rename tasks.
  • Be able to remove all completed items, or all items.
  • Use local storage, no central server.
  • Allow the user to sort items alphabetically, if desired.

We will ask the user to confirm any operation that results in loss of data, such as deleting tasks. We'll also implement a simple about box to show the application author and version.

Figure 1 shows the user interface for the application, in this case with five items entered. Each item in the list has a checkbox to indicate completion status, and image buttons for renaming or removing the item.


Figure 1: To Do List Main Screen

On the upper right is a Close button. Remember, this is an application, not a web site, so a user will expect to close the application when finished with it. On the top left is a menu that provides the supported operations. Figure 2 shows the application menu that pops up when the button is pressed.

Figure 2: Application Menu

Figure 3 shows the user interface for adding a new item. A dialog opens prompting the user for the text for the item name, with buttons to accept or cancel the operation. Operations such as remove and rename use a similar dialog to ask the user to confirm.


Figure 3: Adding A New Item

Web Storage


This application uses Web Storage to store it's data. Let's take a look at this HTML5 feature.

HTML5 Web Storage is an API for persistent storage in Web clients. It is a client side database which stores data as key value pairs. The data is stored locally in the browser. Implementations typically support up to five megabytes of data. Most recent web browsers support Web Storage.

There are two types of storage. Local storage is used to access a web page's local storage area. Session storage represents the set of storage areas specific to the current top-level browsing context. Sites can add data to the session storage, and it will be accessible to any page from the same site opened in that window. Each top-level browsing context has a unique set of session storage areas, one for each origin.

Session storage is designed for scenarios where the user is carrying out a single transaction, but could be carrying out multiple transactions in different windows at the same time. The Local storage mechanism is designed for storage that spans multiple windows, and lasts beyond the current session. In particular, Web applications may wish to store megabytes of user data, such as entire user-authored documents or a user's mailbox, on the client side for performance reasons.

Let's look at some simple examples that show how to use local storage. Listing 1 shows how to write to local storage, including some error checking.

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.
      }
    }
  }

Listing 1: Writing To Local Storage

 

Listing 2 shows how we can retrieve the previously saved string from local storage as well as how to remove or delete it.

   value = localStorage.getItem('name'); // Should return string "Hello World!"

   localStorage.removeItem('name'); // Removes string "Hello World!" with key "name"

Listing 2: Reading From Local Storage

There is a little more to the web Storage API, but it is quite simple to use. The definitive reference for Web Storage can be found here.

Localization


In today's global market, localization of applications is important. Our earlier examples ignored this issue for the sake of simplicity. For this application I thought we would show an example of how an HTML5 application could be localized.

In this case we've used some third party code called l10n.js to make this easier. l10n.js is a JavaScript library that enables passive localization through native JavaScript methods.

To use it, we download and include the file l10n.js in our application. In our index.html file we added the first line shown below in listing 3. We then put our actual translations in a file and included it. In our case we use the file localization/localization.js which is included in the second line in listing 3.

<script src="l10n/l10n.js" type="text/javascript"></script> 
...
<script src="localization/localization.js" type="text/javascript"></script>

Listing 3: Localization Code In index.html

Whenever we reference a user visible string in our code we wrap it in the function toLocaleString. The file main.js has many examples of this. At run time the localization library looks up the appropriate translation for the string in the current locale from the file localization/localization.js. This file implements the function toLocaleString.

In our case we provided sample Russian translations for the application strings. Figure 4 shows the application running under a Russian locale.

Figure 4: Application Running in Russian Language

If translations are not provided for the current locale it falls back to the strings in the JavaScript source code.

This is just one approach for localization but in practice is quite easy to use.

Third Party Code

You can often reduce development effort and avoid reinventing the wheel in web applications by leveraging third party code and libraries. A wide variety of useful JavaScript libraries are available, many of them free. For this application we used three third party libraries.

We used popupmenu.js, a simple JavaScript popup menu library written by Jiro Nishiguchi. This is a small library (about 150 lines of code) that made it easier to implement popup menus in our application.

We also used the jQuery Alerts Dialogs. This provides a replacement for the standard JavaScript alert, confirm, and prompt functions that offers a number of enhancements.

Finally, we used jQuery. JQuery is a JavaScript library that provides facilities for a number of things including navigating documents, selecting DOM elements, creating animations, handling events, and developing AJAX applications. It is the most popular JavaScript library in use today.

You should check the licensing on any third party code you use to make sure that you are complying with any conditions that it imposes.

Code Walk-Through

Our application provides the standard icon.png image file used by Encapsulator for the application's icon. We also include a style sheet app.cssto set some of the styling we want.

Execution as usual begins with the file index.html. It includes all of the JavaScript files we will be using. It displays a title and header, and some menu buttons for "Menu" and "Close". The body of the HTML calls function init on load.

The code for the application proper is in main.js and is only about 300 lines of JavaScript. While it is too big to list the entire file here, I'll give a brief description of what each function does and show code snippets for the more complex functions. I encourage you to examine the code yourself.

Function init is called by index.html on startup to do initialization.

Function closeApplication is linked to the Close button. It simply calls the AppUp API function intel.adp.encapsulator.closeapplication to close the application.

Function onAddNewItem, shown in Listing 4, adds a new item to the list. It uses 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());
                }
            }
        }

    });
}

Listing 4: Function onAddNewItem from file main.js

Function onClearCompletedItems (Listing 5) iterates through the items in local storage and removes any that are marked as completed. It prompts the user before doing so.

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
        }
    });
}

Listing 5: Function onClearCompletedItems from file main.js

Function onClearAllItems removes all items by simply clearing all local storage.

Function onSortByName sets a flag in local storage that we want items sorted by name, and then calls getAllItems.

Function onUnsortedList clears the flag in local storage that indicates if we want items sorted by name, and calls getAllItems.

Function onAbout uses the jAlert function to display a simple about box showing the application name, version, and copyright information.

Function onCheckItem, shown in Listing 6, is called when the completed checkbox is set or cleared for an item. It replaces the entry in local storage for the item with one that reflects the new checked 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());
            }
        }
    }
}

Listing 6: Function onCheckItem from file main.js

Function onDeleteItem is called when the delete menu entry is invoked, and removes an item from local storage after confirming with the user.

Function onRenameItem, shown in Listing 7, handles renaming of an item, getting the new name from the user and replacing the item's entry in 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

Function getAllItems (Listing 8) iterates through local storage and generates HTML for the list to be displayed. If sort by name is enabled, it sorts the items first before building up the list.

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 id="'+itemKey+'" onclick="onCheckItem('+itemKey+')"><img src="icons/checkbox_off.png"/></span> <strong>'+value+'</strong> <span id="'+itemKey+'" onclick="onRenameItem('+itemKey+')"><img src="icons/rename.png"/></span> <span id="'+itemKey+'" onclick="onDeleteItem('+itemKey+')"><img src="icons/delete.png"/></span><br>';
            }
            else
            {
                timeLog += '<span id="'+itemKey+'" onclick="onCheckItem('+itemKey+')"><img src="icons/checkbox_on.png"/></span> <strong><del>'+value+'</del></strong> <span id="'+itemKey+'" onclick="onRenameItem('+itemKey+')"><img src="icons/rename.png"/></span> <span 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 id="'+itemKey+'" onclick="onCheckItem('+itemKey+')"><img src="icons/checkbox_off.png"/></span> <strong>'+value+'</strong> <span id="'+itemKey+'" onclick="onRenameItem('+itemKey+')"><img src="icons/rename.png"/></span> <span id="'+itemKey+'" onclick="onDeleteItem('+itemKey+')"><img src="icons/delete.png"/></span><br>';
                        }
                        else
                        {
                            timeLog += '<span id="'+itemKey+'" onclick="onCheckItem('+itemKey+')"><img src="icons/checkbox_on.png"/></span> <strong><del>'+value+'</del></strong> <span id="'+itemKey+'" onclick="onRenameItem('+itemKey+')"><img src="icons/rename.png"/></span> <span 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);
}

Listing 8: Function getAllItems from file main.js

Function showMenu, shown in Listing 9, is attached to the Menu button and displays the menu options, linking them to the appropriate JavaScript functions. Note that the menu strings are localized.

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

Submitting To AppUp

Earlier we looked at how to use Encapsulator to build an HTML5 application for Windows. Once our application is complete, we would like to submit it to AppUp. Let's take a look at the steps involved in getting our simple To Do List application into the AppUp store.

 

Build And Test The Application With Encapsulator

Before you submit your application you should test it well, including testing on all the platforms that you plan to support.

Encapsulator has some settings found under "Additional app settings" that you may want to set (see figure 5).


Figure 5: Encapsulator Additional Settings

You will also need to enter a GUID that you will obtain as part of the application submission process. Enter that GUID in the field in Encapsulator. Figure 6 shows an example. When you specify a GUID, Encapsulator will build your application in release mode and disable some debug tools such as the Web Inspector.

Figure 6: Encapsulator Appup Center Settings

Sign The Package

Windows msi packages submitted to AppUp need to be digitally signed to verify the submitter. Instructions for doing this can be found here. If you don't have a signing key, you can get one free as an AppUp developer. Follow the documented procedure to sign the msi file either as an individual or organization, whatever is applicable.

Fill In Required Information Using The Web Interface

The AppUp submission process is initiated by going to your Dashboard at the Intel app developer web site and selecting "Start a New Application" (see Figure 7).

Figure 7: Starting A New Application From The Dashboard

You first need to enter the application name. This name must be unique. If it has been used, you will get a notice. In my case the name "To Do List" was taken so I used "Simple To Do List" (see Figure 8).

Figure 8: Entering Application Name

You now need to fill out the Application Submission page. It has a number of tabs, entitled Application Info, Pricing, Upload Info, Intel AppUp Center, Validation, and Overview. You can step through the forms filling in all the required information. There is detailed help on all of the fields. Figures 9 and 10 show some of the screens filled out for the Simple To Do List application.

Figure 9: Submitting Application (Application Info Tab)

Figure 10: Submitting Application (Pricing Tab)

At any time you can save your work and come back to it later. When your application package is tested and digitally signed, you can upload it from the form.

Optionally Make Package Available To Beta Testers

AppUp lets you make a beta version of your application available to selected users before it is released. It is highly recommended that you do this. Figure 11 shows the relevant screen.


Figure 11: Beta Testing Screen

Have Application Validated By AppUp Validation Team

When you feel your application is tested and ready for submission, you can submit it for validation. The AppUp team will perform a number of tests on your application to make sure that the submission is complete and that the application runs correctly. A detailed description of the validation process can be found here.

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

Figure 12: Applications Status

If your submission fails, details will be indicated and you will also receive an e-mail. You can then correct the issue and submit the application again.

When Validated, Application Will Be Published To AppUp

When you application submission has been validated by the AppUp validation team it will show up as published in your Dashboard and you will receive an e-mail. Your application is now available from the AppUp store for users to purchase (if non-free) and download. Your dashboard also shows information about your published applications including the number of downloads and any reviews.

 

Source Intel AppUp(SM) Developer Program

IoT-Gadgets-Logo-272-90

About us

IoT Gadgets is dedicated to bring you all the Internet of Things IoT news that pertains to gadgets. Simple. We love for you to join us on this journey.

Contact us: [email protected]

FOLLOW US

crossmenu linkedin facebook pinterest youtube rss twitter instagram facebook-blank rss-blank linkedin-blank pinterest youtube twitter instagram