Modern Web Architecture: The HTML5 Web Storage

Abstract

One of the most important features of HTML5 is the Web Storage feature. Using the Web Storage feature, the client state can be saved on the browser level without consuming the network bandwidth or the server memory. The Web Storage increases the scalability of the web applications on the server level and on the network level. In this article, I will show you how to work with the HTML5 Web Storage feature.

Difference between the old cookies and the HTML5 Web Storage

Unlike cookies, the HTML5 Web Storage is more scalable. The HTML5 Web Storage content is not transferred between the client and the server per every request. This means using the HTML5 Web Storage, you can store unlimited amount of information on the browser without affecting the network bandwidth. While the web cookies have a limited size, and are transferred between the server and the client per every request.

Checking if the browser is supporting the HTML5 Web Storage

HTML5 Web Storage is supported in most of the modern browsers (IE8, Firefox, Safari, Chrome, and Opera). To check if the Web Storage is supported, check if the web storage objects are defined as follows.

function isStorageSupported() {
	if (typeof(localStorage) === "undefined" || typeof(sessionStorage) === "undefined") {
		alert("Web Storage is no suppored ...");
		return false;
	}
	
	return true;
}

The HTML5 Web Storage Objects

The Web Storage is represented by two main objects the localStorage object and the sessionStorage object. The main difference between them is that the localStorage object is stored in the browser with no expiration date, however, the sessionStorage object is stored in the browser and is removed once the user session ends (i.e., its life time is the user session). It is important to know that there is another difference which is localStorage can work in local mode while the sessionStorage can work only inside a hosted page on the server.

If you try to run the sessionStorage in local mode, you will face an error. For example, the error message in Firefox is:

Operation is not supported

I created an example that illustrates how to use the HTML5 Web Storage objects. In this example, there is a text area “information” field and four buttons for (saving the “information” field value in localStorage, retrieving the stored “information” field value from localStorage, saving the “information” field value in sessionStorage, retrieving the stored “information” field value from sessionStorage).

To store any data in the localStorage object, just add the information in the (key, value) form as follows:

localStorage.key = value;

In the example to store the “information” field data in the localStorage object:

localStorage.information = document.getElementById("information").value;

To retrieve the data from the localStorage object, you can retrieve it using the key as follows:

alert(localStorage.key);

In the example to get the “information” data from the localStorage object:

alert("Data from local storage is: " + localStorage.information);
document.getElementById("information").value = localStorage.information;

Using the sessionStorage object is pretty the same as localStorage. In the example to store the “information” field data in the sessionStorage object:

sessionStorage.information = document.getElementById("information").value;

To get the “information” data from the sessionStorage object:

alert("Data from session storage is: " + sessionStorage.information);
document.getElementById("information").value = sessionStorage.information;

I attach below, the complete example code:

<!DOCTYPE html>
<HTML>
<HEAD>
	<TITLE>Welcome to the WebStorage test</TITLE>
	
	<SCRIPT type="text/javascript">
	
		function isStorageSupported() {
			if (typeof(localStorage) === "undefined" || typeof(sessionStorage) === "undefined") {
				alert("Web Storage is no suppored ...");
				return false;
			}
			
			return true;
		}
	
		function storeInformationInLocalStorage() {
			if (isStorageSupported()) {
				localStorage.information = document.getElementById("information").value;
			}
		}
		
		function readInformationFromLocalStorage() {
			if (isStorageSupported()) {
				alert("Data from local storage is: " + localStorage.information);
				document.getElementById("information").value = localStorage.information;
			}			
		}		
	
		function storeInformationInSessionStorage() {
			if (isStorageSupported()) {
				sessionStorage.information = document.getElementById("information").value;
			}
		}
		
		function readInformationFromSessionStorage() {
			if (isStorageSupported()) {
				alert("Data from session storage is: " + sessionStorage.information);
				document.getElementById("information").value = sessionStorage.information;
			}				
		}	
	
	</SCRIPT>
</HEAD>
<BODY>
	<label for="information">Enter some information in the textArea: </label><br/>
	<textarea id="information" rows="2" cols="20"></textarea>	
	<br/><br/>

	<INPUT TYPE="button" value="Store in the localStorage" onclick="javascript:storeInformationInLocalStorage()"/>
	<INPUT TYPE="button" value="Read from the localStorage" onclick="javascript:readInformationFromLocalStorage()"/>

	<br/>

	<INPUT TYPE="button" value="Store in the sessionStorage" onclick="javascript:storeInformationInSessionStorage()"/>
	<INPUT TYPE="button" value="Read from the sessionStorage" onclick="javascript:readInformationFromSessionStorage()"/>	
</BODY>
</HTML>

You can delete any of the key/value pairs in the Web Storage using the sessionStorage.removeItem(key) and the localStorage.removeItem(key). If you want to delete all of the key/value pairs, you can use the sessionStorage.clear() and localStorage.clear().

I attach the example for your reference here.

Testing JavaScript code using Jasmine (Kickstart)

Jasmine is one of the JavaScript unit testing frameworks. It has a nice advantage that its syntax is readable and a very human friendly. In this post, I will give you a kickstart to help you start working with Jasmine.

First of all, Let’s create a simple JavaScript object that we need to perform some unit testing on it:

/* Basics.js file */
function Basics() {
}

Basics.prototype.add = function(x, y) {
	return x + y;
};

As shown in the Basics.js file, Basics is a simple JavaScript object that contains one method that adds the x and y parameters and finally returns the result to the caller.

To start working with the Jasmine framework, download the framework from http://pivotal.github.com/jasmine/download.html. Make sure that you have the following folder structure after extracting the downloaded zip file:
jasmine structure

You are having three folders and one html file.
1. lib folder: contains the source files of the Jasmine framework.
2. src folder: contains the source JavaScript files that will be tested.
3. spec folder: contains the testing JavaScript files.
4. SpecRunner.html file: is used for running the JavaScript test suites.

Place the Basics.js in the src folder after making sure that you remove all the old contents of the src and the spec folders.

Now, Let’s write the test suite in the BasicsSpec.js file.

describe("Basics", function() {
  var basics = new Basics();  

  it("should be able to make correct addition", function() {
    expect(basics.add(3, 2)).toEqual(5);
  });

});

As we see here, the testing syntax is very human friendly, it means “describe Basics object which should be able to make correct addition”. The describe keyword represents a test suite that can contain one or more specs. Every spec is represented by the it keyword.

Inside the spec, there is an expectation that expects the basics.add(3, 2) is equal to 5. In Jasmine, the expectation is represented by the expect keyword and the equality checking is represented by a matcher called toEquals.

In Jasmine, there are many other built-in matchers; you can find all of them here: https://github.com/pivotal/jasmine/wiki/Matchers.

Finally, do not forget to remove the old source and spec JS includes from the SpecRunner.html and to add the new includes instead:

  <!-- include source files here... -->
  <script type="text/javascript" src="src/Basics.js"></script>  
  
  <!-- include spec files here... -->
  <script type="text/javascript" src="spec/BasicsSpec.js"></script>

After running the SpecRunner.html file, you will find the following screenshot telling you that the test spec runs successfully.

screenshot

I wish this kickstart can be useful for you. For your reference, download the kickstart example from here.

Detecting the Page Leave event in JavaScript

You may need to detect if the user is leaving your web page for displaying a warning message to the user mentioning that he may not leave the page and the form is containing data filled by him. To be able to detect this event in JavaScript, you need to deal with the "onbeforeunload" event. The following example explains the idea.

In this example, there is a simple form that contains an input text. When the user enters something in the input text, a warning message appears.

<HTML>
<HEAD>
	<TITLE>Welcome to the test Page</TITLE>
	<SCRIPT type="text/javascript">
		window.onbeforeunload = function(event) {
			
			if (hasData()) {
				return "You entered information in the page. Are you sure you want to leave now?"; // Very important
			}
		}
		
		function hasData() {
			var someThing = document.getElementById("someThing");
			
			if (someThing.value != "") {
				return true;
			}		
			
			return false;
		}
	</SCRIPT>
	
</HEAD>
<BODY>
	<H1>Welcome to the test page</H1>
	
	<FORM>
		<LABEL for="someThing">Enter something here:</LABEL>
		<INPUT type="text" id="someThing"/>
	</FORM>
</BODY>
</HTML>

The "onbeforeunload" event is called when the user tries to leave the page. As shown in the code, to display the interruption message "You entered information in the page. Are you sure you want to leave now?", you have to specify the interruption message in the return of the event. The interruption message is displayed as shown in the figure below.
IE Preview

This code works with me on IE, Firefox, and Safari. I wish that this tip can be useful for you.

“Ajax Status Zero” Resolution

“Ajax status is zero” is one of the hardest errors to debug especially when the Ajax readyState is 4. If we looked at the XMLHTTPRequest documentation, we will find that the readyState attribute can have one of the following values:

  • 0 “The object has been constructed.”
  • 1 “The open() method has been successfully invoked. During this state request headers can be set using setRequestHeader() and the request can be made using the send() method.”
  • 2 “All redirects (if any) have been followed and all HTTP headers of the final response have been received. Several response members of the object are now available.”
  • 3 “The response entity body is being received.”
  • 4 “The data transfer has been completed.”

This means that although the data transfer is completed, the status is not OK (not 200).

After some hours of investigation, I found that the XHR (XMLHTTPRequest) zero status means that there is a permission problem of the Ajax request. In other words, the Ajax request is trying to access a domain that the request is not authorized to access.

The actual problem was that my JavaScript JsTestDriver asynchronous test case was running on the JsTestDriver server which is using a different port other than the one that is used by the application server which hosts the Ajax servlet.

The other interesting point to mention is that I only faced this issue on Firefox 10 while in Internet Explorer 8, the Ajax request worked without any problems.

To solve this problem, I authorized the Ajax request that comes from the JsTestDriver server to access the application server by setting the "Access-Control-Allow-Origin" header from my servlet as follows.

response.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:42442");

The "http://127.0.0.1:42442" is the JsTestDriver server address from which the Ajax request is constructed and sent to the application server.

After making this workaround the scenario worked fine on all the browsers, I wish that this tip can be helpful for you.

Generating Test Reports using JsTestDriver

JsTestDriver is one of the most powerful JavaScript unit testing tools. I wrote an article in developerWorks about efficient JavaScript unit testing using JsTestDriver, please read first it before reading this post.

In this post, I will show you how to generate test coverage reports using the JsTestDriver. First of all, Let’s understand what is meant by “Test Coverage”. “Test Coverage” is one of the measures of software testing. It describes how much the source code of the program is tested.

The coverage criteria can be on the function level (Function coverage), on the statement level (Statement coverage), and on the code branch level (Branch coverage).

JsTestDriver has a plugin that can generate code coverage for the JavaScript code. To configure this plugin, you should:

  • Download the “coverage.jar” from http://code.google.com/p/js-test-driver/downloads/list
  • Add the coverage plugin declaration to the configuration file (jsTestDriver.conf file) as follows:

    ...
    plugin: 
    - name: "coverage" 
      jar: "plugins/coverage.jar" 
      module: "com.google.jstestdriver.coverage.CoverageModule"
    
  • Specify the --testOutput [output_folder] flag in the test running command. This will get the test coverage files in the output_folder you specified.

Currently, The JsTestDriver generates the test coverage files in LCOV format. Unfortunately, JsTestDriver does not generate HTML reports directly as shown in the figure below.
Genarated LCOV files

To generate the HTML reports from the LCOV files, you can use the LCOV visualizer, check it here: http://ltp.sourceforge.net/coverage/lcov.php.

The LCOV visualizer tool is working on Linux Redhat environment. To generate the report from the tool, execute this command on the jsTestDriver.conf-coverage.dat file:

genhtml jsTestDriver.conf-coverage.dat

For more information about how to install the LCOV visualizer on Linux Redhat environment, and converting the LCOV files to HTML reports:
http://www.technicaladvices.com/2012/05/02/converting-the-jstestdriver-coverage-files-to-html-reports/.

Finally for your reference, You can download the project from here, and the LCOV test coverage files from here.

I wish that this tip can be useful.

How to Create a Custom Dojo Component

I published an article about how to create custom Dojo components in Wazi. This article shows you how to create a custom Dojo component from scratch. It illustrates very important concepts like the dojo attach point, the widget structure, and the widget template. I wish you will enjoy this article: http://olex.openlogic.com/wazi/2012/how-to-create-a-custom-dojo-component/.

Iterate over a JSON object

One of the most useful advantages of the JSON format is that it is browser friendly. You donot need to write complicated code for navigating or iterating over a JSON structure. In this post, I will show you how to iterate over a JSON object.

Assume that you are having the following JSON object.

<script type="text/javascript">
var personStr = "{\"Name\":\"Hazem Saleh\",\"Occupation\":\"Software Engineer\",\"Nationality\":\"Egyptian\",\"Age\":\"102\"}";

var personObject = eval("(" + personStr + ")");
</script>

You may notice that I put the JavaScript String between parentheses. This is mandatory to avoid the json invalid label browser error.

if you want to simply iterate over the JSON object you should use the standard JavaScript for in loop as follows.

<script type="text/javascript">
for (var key in personObject) {
  if (personObject.hasOwnProperty(key)) {
    alert(key + " -> " + personObject[key]);
  }
}
</script>

I wish that this tip can be helpful to you.

Java encoding similiar to JavaScript encodeURIComponent

You may have a REST service that returns an encoded output to a JavaScript client which decodes this output in the client side. Unfortunately, this scenario will not always work because of the mismatch between both the Java java.net.URLEncoder encode() API and the JavaScript encodeURIComponent().

To be able to make the java.net.URLEncoder encode() behavior equivalent to the JavaScript encodeURIComponent(), all what you should do is the following String replacements.

public static String encodeURIComponent(String component)   {     
	String result = null;      
	
	try {       
		result = URLEncoder.encode(component, "UTF-8")   
			   .replaceAll("\\%28", "(")                          
			   .replaceAll("\\%29", ")")   		
			   .replaceAll("\\+", "%20")                          
			   .replaceAll("\\%27", "'")   			   
			   .replaceAll("\\%21", "!")
			   .replaceAll("\\%7E", "~");     
	catch (UnsupportedEncodingException e) {       
		result = component;     
	}      
	
	return result;   
}   

After that you can safely decode the output in the client side using the JavaScript decodeURIComponent().

References:

http://stackoverflow.com/questions/607176/java-equivalent-to-javascripts-encodeuricomponent-that-produces-identical-outpu

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/encodeURIComponent

http://docs.oracle.com/javase/1.4.2/docs/api/java/net/URLEncoder.html

Refreshing Dijit tree in Dojo (code that works)

Refreshing the Dijit tree in Dojo with a new data store does not have a ready-made API in Dojo (Iam talking about Dojo 1.4). In this post, I will show a technique with example that will illustrate how to refresh the Dijit tree with a new data store.

The solution is about extending the dijit.Tree to support this feature as shown in the refreshModel method.

<script type="text/javascript">// <![CDATA[
	dojo.extend(dijit.Tree, {
		refreshModel: function () {

			// reset the itemNodes Map
			this._itemNodesMap = {};

			// reset the state of the rootNode
			this.rootNode.state = "UNCHECKED";

			// Nullify the tree.model's root-children
			this.model.root.children = null;

			// remove the rootNode
			if (this.rootNode) {
				this.rootNode.destroyRecursive();
			}

			// reload the tree
			this._load();
		}
	});
// ]]></script>

Let’s see now an example of refreshing the tree using this new introduced API.

	

<button id="refreshTreeButton" class="buttonClass" onclick="refreshTreeInformation();" type="button">Refresh Tree</button><script type="text/javascript">// <![CDATA[
	dojo.addOnLoad(function() {	
		var store = new dojo.data.ItemFileWriteStore({
			url: "sampleData.json"
		});				

		var treeModel = new dijit.tree.ForestStoreModel({
			store: store,
			query: {"type": "parent"},
			rootId: "root",
			rootLabel: "Groups",
			childrenAttrs: ["children"]
		});

		var treeObject = new dijit.Tree({
			model: treeModel
		}, "treeDiv");
	});

	function refreshTreeInformation() {		
		var treeObject = dijit.byId("treeDiv");
		var newStore = new dojo.data.ItemFileWriteStore({
			url: "sampleData2.json"
		});						

		treeObject.model.store = newStore;		
		treeObject.model.query = {"type": "parent"};	
		treeObject.rootId = "root";	
		treeObject.rootLabel = "Groups";	
		treeObject.childrenAttrs = ["children"];						

		treeObject.refreshModel();
	}			
// ]]></script>

In the refreshTreeInformation function, the tree object is retrieved, a new store is loaded, and assigned to the tree model object. Finally calling the refreshModel method will make the displayed tree insync with the underlying model. you can download the code from here.

I wish this tip can help.

Getting the Selected Dijit Tree node in Dojo

This post shows you how to get the selected node in a Dijit tree.

Let’s assume we have the following dijit tree data store.

{
"identifier":"id",
"label":"name",
"items":[
{
	"type": "parent",
	"name": "Countries",
	"id": "countries",
	"children":[
		{"type":"Leaf", "name":"Country 1", "id":"country1"},
		{"type":"Leaf", "name":"Country 2", "id":"country2"},
		{"type":"Leaf", "name":"Country 3", "id":"country3"},
		{"type":"Leaf", "name":"Country 4", "id":"country4"},
		{"type":"Leaf", "name":"Country 5", "id":"country5"},
		{"type":"Leaf", "name":"Country 6", "id":"country6"}
	]
}]}

Using the following JavaScript code the tree is loaded with the desired hierarchy.

<script type="text/javascript">// <![CDATA[
dojo.require("dojo.data.ItemFileWriteStore");
dojo.require("dijit.Tree");
dojo.require("dijit.tree.ForestStoreModel");				

var store = new dojo.data.ItemFileWriteStore({
	url: "sampleData.JSON"
});

var treeModel = new dijit.tree.ForestStoreModel({
	store: store,
	query: {"type": "parent"},
	rootId: "root",
	rootLabel: "Groups",
	childrenAttrs: ["children"]
});

var treeObject = new dijit.Tree({
	model: treeModel
}, "treeDiv");

//...
// ]]></script>

In order to get the selected tree node object you should use the “selectedItem” attribute in the Dijit tree object as follows

dijit.byId("[Tree Node ID]").attr("selectedItem");

In our example to show the id of the selected leaf node when clicking the “Get Selected Node” button, the following code will do the job:

if (dijit.byId("treeDiv").attr("selectedItem") != null) {
	if (dijit.byId("treeDiv").attr("selectedItem").type == "Leaf") {
		document.getElementById("selectedNode").innerHTML =
                "Selected Node is " + dijit.byId("treeDiv").attr("selectedItem").id;
	}
}

This code is tested in Dojo 1.4 environment. download the code from here.