HTML5, JavaScript, webdev

Convert WebSQL To IndexedDB Tutorial

Exactly a year ago on November 18, 2010, the W3C announced that Web SQL database is a deprecated specification. Many major browsers including Chrome, Safari, Opera and nearly all Webkit based mobile devices support WebSQL, however, if you are going to start a new project and/or you wish to have your code running with the new version of client side database (that will receive updates and improvements) you should implement indexedDB as your client side database. In this short post we will see what are the main steps to refractor your WebSQL code to IndexedDB.

First lets create the databases


// WebSQL 
database = openDatabase('todos1', '1.0', 'todo list example db', 2*1024*1024);

// InxededDB
var todoDB = {};
var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;
todoDB.indexedDB = {};
todoDB.indexedDB.db = null;


Create Table/ObjectStore

In both cases we need some ‘space’ to save our information


// WebSQL - creating a new table
 database.transaction(function(tx) {
     tx.executeSql("CREATE TABLE IF NOT EXISTS tasks (id REAL UNIQUE, text TEXT)", []);
   });

// IndexedDB - creating a new object store that will hold our data
todoDB.indexedDB.open = function() {
        var request = indexedDB.open("todos");

        request.onsuccess = function(e) {
          var v = "2.0 beta"; // yes! you can put strings in the version not just numbers
          todoDB.indexedDB.db = e.target.result;
          var db = todoDB.indexedDB.db;
          // We can only create Object stores in a setVersion transaction;
          if (v!= db.version) {
            var setVrequest = db.setVersion(v);

            // onsuccess is the only place we can create Object Stores
            setVrequest.onsuccess = function(e) {
              if(db.objectStoreNames.contains("todo")) {
                db.deleteObjectStore("todo");
              }
              var store = db.createObjectStore("todo",
              {keyPath: "timeStamp"});
              todoDB.indexedDB.getAllTodoItems();
            };
          }
          else {
            todoDB.indexedDB.getAllTodoItems();
          }
        };
        request.onfailure = todoDB.indexedDB.onerror;
      }

Add Item

Now it’s time to add some data to our database, no?


// WebSQL
function addTodo() {
        var todo = document.getElementById("todo");
        var task = {
          "id": new Date().getTime(),
          "text": todo.value };
        
        database.transaction(function(tx) {
          tx.executeSql('INSERT INTO tasks (id, text) values (?, ?)', [task.id, task.text]);
        });
        // now let clean it to the next todo
        todo.value = "";
        showAll();
      }
      
// IndexedDB
todoDB.indexedDB.addTodo = function(todoText) {
        var db = todoDB.indexedDB.db;
        var trans = db.transaction(['todo'], IDBTransaction.READ_WRITE);
        var store = trans.objectStore("todo");

        var data = {
          "text": todoText,
          "timeStamp": new Date().getTime()
        };

        var request = store.put(data);

        request.onsuccess = function(e) {
          todoDB.indexedDB.getAllTodoItems();
        };

        request.onerror = function(e) {
          console.log("Error Adding: ", e);
        };
      };

Fetch Items

After you have data it’s only make sense to show it to the world (and your dear friends)


// WebSQL
function showAll() {
        document.getElementById("ourList").innerHTML = "" ; 
        database.transaction(function(tx) {
          tx.executeSql('SELECT * FROM tasks', [], function (tx, results) {
            var len = results.rows.length, i;
            for (i = 0; i  Todo text: " + results.rows.item(i).text);
              
              var a = document.createElement("a");
              a.textContent = " [Delete]";
              a.setAttribute('data-key', results.rows.item(i).id);
              a.setAttribute('data-val', results.rows.item(i).text);
              a.addEventListener("click", function() {
                deleteTodo(this.getAttribute("data-key"),this.getAttribute("data-val") );
              }, false);
              li.appendChild(t);
              li.appendChild(a);
              document.getElementById("ourList").appendChild(li);
            }
          });        
        });
      }

// IndexedDB
function showAll() {
        document.getElementById("ourList").innerHTML = "" ;   
        var request = window.indexedDB.open("todos");
        request.onsuccess = function(event) {
          // Enumerate the entire object store.
          var db = todoDB.indexedDB.db;
          var trans = db.transaction(["todo"], IDBTransaction.READ_ONLY);
          var request = trans.objectStore("todo").openCursor();
  
          request.onsuccess = function(event) {
            var cursor = request.result || event.result;
            // If cursor is null then we've completed the enumeration.
            if (!cursor) {
              return;
            }
            var element = document.createElement("div");
            element.textContent = "key: " + cursor.key + " => Todo text: " + cursor.value.text;
            document.getElementById("ourList").appendChild(element);
            cursor.continue();
          }
        }                    
      }


Delete Item

In rare cases we wish to delete stuff… It’s easy.


// WebSQL
function deleteTodo(id, text) {
        if (confirm("Are you sure you want to Delete "+ text +"?")) {
          database.transaction(function(tx) {
            tx.executeSql('DELETE FROM tasks WHERE id=?', [id]); 
          });
          showAll();
        } 
      }

// IndexedDB
todoDB.indexedDB.deleteTodo = function(id) {
        var db = todoDB.indexedDB.db;
        var trans = db.transaction(["todo"], IDBTransaction.READ_WRITE);
        var store = trans.objectStore("todo");

        var request = store.delete(id);

        request.onsuccess = function(e) {
          todoDB.indexedDB.getAllTodoItems();
        };

        request.onerror = function(e) {
          console.log("Error Adding: ", e);
        };
      };


As Oscar Wilde said: “…Consistency is the last refuge of the unimaginative…” – so in our case, let’s save data locally and have more performance in our web apps (with some consistency).

Live Example

All the code is on github – https://github.com/greenido/WebSQL-to-IndexedDB-example

and you can play with a live example.

Standard
HTML5, webdev

Web Workers (Part 1 Out Of 3)

Short History

In modern web applications there are lots of cases when we need to do some stuff in the background. The only way to do it today in most of the modern browsers is by using Web Workers. Web Workers provide a standard way for browsers to run JavaScript in the background.  It let you spawn multiple “threads” that all run at the same time. For more about multithreading this is a good place to start your reading.

Web Workers can do lots of things:

  • Complex mathematical calculations
  • Make network requests
  • Access local storage

all while the main web page responds to the user actions (e.g. scrolling, typing some text or clicking around your app).

What is a Worker?

A ‘worker’ is a script that will be loaded and executed in the background. Web Workers provide a way to do this seamlessly, for example:
new Worker(“worker.js”);
The above will load the script, located at ‘worker.js’, and execute it in the background.

There are some big (very big) limitations (but please don’t worry, we will see how to solve them in the next post):

  • Workers don’t have access to the DOM: No document, getElementById, etc. However, you can use setTimeout, setInterval, and XMLHttpRequest.
  • Workers don’t have direct access to the ‘parent’ page.

Can We Use Web Workers?

In order to find out if we can use web workers we need to check if there is a Worker property on the global window object. If our browser doesn’t support the Web Worker API, the Worker property will be undefined.

isWorkersAvailable() {
return !!window.Worker;
}

Instead of writing this function yourself, you can use Modernizr to detect support for web workers (Pss… Modernizr is an open-source JavaScript library that helps you build the next generation of HTML5 and CSS3-powered websites by doing lots of work for you and saving you from reinventing the wheel again and again – Thank you @KuraFire  @paul_irish and @SlexAxton )

if (Modernizr.webworkers) {
  // window.Worker is available!
} else {
  // no native support for web workers
}

 

Short Example

 

//
// A simple way to find prime numbers
//
var n = 1;
search: while (true) {
  n += 1;
  for (var i = 2; i <= Math.sqrt(n); i += 1)
    if (n % i == 0)
     continue search;
  // found a prime!
  postMessage(n);
}

<!DOCTYPE HTML>
<html>
 <head>
  <title>Web Worker: The highest prime number</title>
 </head>
 <body>

  <h1>Web Worker: The highest prime number</h1>
  <article>The highest prime number discovered so far is: 
	  <output id="result"></output>
  </article>
  <script>
   var worker = new Worker('highPrime.js');
   worker.onmessage = function (event) {
     document.getElementById('result').textContent = event.data;
   };
  </script>
 </body>
</html>

 

In the next post I’ll dive deeper on more interesting stuff you can do with workers. We will see how to communicate with one dedicated worker and how we can share workers (just for fun).
Here you can continue reading the second part of this series.

More (good) sources

Standard
business, webdev

The All New TheCarConnection (Looks Great On iPad2)

We’ve  launched TheCarConnection.com today (after 3 weeks of beta testing for selective users). It was after few months of thinking, listening, designing and building a completely new set of make-model pages that we believe bring you the best automotive experience you can find on the web.We made sure it will be optimize to tablets (e.g. iPad or if you like the iPad2) and all the leading browsers.

Unlike all the other ‘car sites’ – we are bring you all the information from around the web into one place. This way, it’s not only our editors opinion that you get (and might like or not) – but also a wide range of other leading sources/editors and opinions. It is a great tool to get all the best information on each car in one (very nice) place. Your savvy editors not only driving the cars but doing the extra mile and doing the ‘leg work’ for you by bring you the ‘bottom line’ review with all the thoughts about this specific car.

For example, if you are into the new F-150, you can get our ‘Ford F150 2011 executive summary review‘ or read more deeply on all the different aspects of this new model. Want to check out what Edmund/Kbb/Cars are thinking? no worries – just go to our ‘web buzz’ and get all the reviews (more then 300), Q&A (more then 2000!) and tweets.

Here are some examples of the new car reviews:

If you want to know what is the a good price or even great price for the new Volt. You have it in our very cool graphs.

It’s a great tool to use and come prepare for your visit in the dealership ;)

For all the ones that want to buy a used car – we have more then 4,500,000 used car listings, so you could find something in the right area,color,look and feel.

Last but not least, if you got a question or you want to share your knowledge. Check out the new ‘stack-overflow’ for cars. As always, comments are most welcome! Feel free to use our official TCC twitter.

Standard
business

The Web Is Shifting

Here is another lighting talk I’ve planed to this year Java Posse Roundup. Unfortunately, I didn’t have time to give it. So in order to share it with some girls that might find it interesting – Here you go:

The web is becoming more sociable than searchable, research firm Hitwise said that the two sites accounted for 14 per cent of all US internet visits last week. Facebook’s home page recorded 7.07 per cent of traffic and Google’s 7.03 per cent. You may  read more about the fact that Facebook got more unique users in the USA then Google all last week… so it’s clearly the direction that the web is moving. I only wonder, what are people doing all this time on Facebook.

Facebook is like a Starbucks (You know… friends don’t let they friend drink there) where everyone hangs out for hours but almost never buys anything. The revenue gap between sites like Facebook and Google should narrow over time.  Cost-per-click search ads are extremely good at harvesting intent, but bad at generating intent.

Standard