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
Chrome, HTML5

Google Developers Days (Tel Aviv & Berlin) And One CloudCon

It was a busy week. Busy but with lots of fun. It’s so great to meet wonderful developers that push the web forward and know (and love) their profession. Last sunday, we had Google Developers Day in Tel Aviv (or Airport city if you want to be accurate) – it was well organized event with more then 1400 developers. In the keynote we had three demos:

  • Android – Ice cream sandwich new capabilities (maps, nfc, HD and other features. To dive deeper, go check Romain Guy’s blog).
  • Chrome/HTML5 amazing new features – I did the demos and I hope to post a list next week.
  • G+ – the new hangout APIs with a robot that eat falafel and drink beers.
Here you can check some coverage:
 The CloudCon was also impressive in terms of the audience (mainly, CIO/CTO dudes) – they liked the story of HTML5 and ChromeOS. I got some good questions on new features: offline, notifications, threads and even on web audio. It could be great to have the new Chromebooks in Israel, it seems that the market really want them. The one argument (or selling point if you want to push here) that conviense IT people is that the TCO (=Total cost of ownership) is 60%-70% cheaper. Yes – these are the numbers… so if your organization can work with web apps (and Citrix for the apps that you don’t want to move to the web) it might be a perfect solution for you.

I hope to get some photos soon (from our dear wonderful photographer) – so I’ll update this post with fresh photos of some great looking people.

Tomorrow we are going to rock Berlin – so keep up with us using G+ with #GDD11 tag  or this blog next week. Btw, for Berlin GDD you might want to search after #GDDDE

Be strong.

This slideshow requires JavaScript.

In case you want to follow the slides from my Talk in CloudCon – The ‘love’ story of HTML5 & ChromeOS.

From Google Developers Day in Berlin here are the talks:

Standard
Chrome, HTML5, webdev

Web Workers (Part 3 Out Of 3) – Shared Wrokers

Shared Worker Example

Web Workers - The bookA web worker is a single JavaScript file loaded and executed on a separate thread (=background and not the UI thread). Dedicated web workers are linked to their owner/creator which is the script that called and loaded them. Shared web workers allow any number of scripts to communicate with a single worker.
Btw, if you missed Part1 and/or Part2 of this series, you might want to read them first.
Shared web workers are identified in two ways: either by the URL of the script used to create it or by explicit name. In the code below we will see how it can be done.

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<title>Shared Web Workers: Show And Tale</title>
</head>
<body>
<h1>Shared Web Workers: Show And Tale</h1>
<article>
To create a shared web worker, you pass a JavaScript file name to a
new instance of the SharedWorker object:
<br/>var worker = new SharedWorker("jsworker.js");
<br/>
<output id="result"></output>
</article>
<script>
var worker = new SharedWorker('sharedWorker1.js');
worker.port.addEventListener("message", function(e) {
document.getElementById('result').textContent += " | " + e.data;
}, false);
worker.port.start();
// post a message to the shared web worker
console.log("Calling the worker from script 1");
worker.port.postMessage("script-1");
</script>
<script>
console.log("Calling the worker from script 2");
worker.port.postMessage("script-2");
</script>
</body>
</html>
// This is the code for: 'sharedWorker1.js' file
// Shared workers that handle the connections and Welcome each new script
//
var connections = 0; // count active connections
self.addEventListener("connect", function (e) {
var port = e.ports[0];
connections++;
port.addEventListener("message", function (e) {
port.postMessage("Welcome to " + e.data +
" (On port #" + connections + ")");
}, false);
port.start();
}, false);

Shared web workers can:

  • load further scripts with importScripts()
  • attach error handlers, and
  • run the port.close() method to prevent further communication on a specific port.

Continue reading

Standard
Business, Chrome, HTML5, webdev

HTML5 Live London – Trip Report

The conference took place on Oct, 19 2011 in the Barbican Conference Centre London. My two goals where to expose enterprise web developer to the new features that HTML5 provide today and to show them how they can leverage these features when they are building enterprises web apps. The other part was to talk with CIO/CTOs about Chrome OS and to ‘bind the dots’ on: how chrome OS harnessing business and developers with more power. Overall, it was a great event in terms of the quality of the audience. Lots of enterprise web developers from financial institution, health care and other big organizations. Some of them are still struggling with IE and the Chrome Frame was a big exciting news for them. Other points I’ve took with me:

  • The Chromebooks were a big hit – everyone love them! I got lots of questions and the major points where around:

    • Security

    • Total cost of ownership (70% reduction)

    • Battery life (8.5h)

  • In the future, I should bring few Chromebook and have a table in the main show so people could ‘play’ with them between sessions.

Here are some of the presentations from the event:

Lastly, I would like to thank the amazing crew of Kaazing – They organized a great event with lots of interesting talks.

I hope to see you there next year…

Be strong.

 

Standard
HTML5, webdev

Web Workers (Part 2 Out Of 3) – Control Your Threads In JavaScript

Quick Intro

If you wish to read the first part of this series please go to Web Workers Part 1.
The Web Workers specification defines an API for spawning background scripts in your web application. Web Workers allow you to do things like fire up long-running scripts to handle computationally intensive tasks, but without blocking the UI or other scripts to handle user interactions. They’re going to help put and end to that nasty ‘unresponsive script’ dialog that we’ve all come to love:

or if you are (still) on windows:

 

Unresponsive script dialog
you will get this common unresponsive script dialog.

 

Workers utilize thread-like message passing to achieve parallelism. They’re perfect for keeping your UI refresh, performant, and responsive for users.

Types of Web Workers And Some Code

It’s worth noting that the specification discusses two kinds of Web Workers:

Restrictions with Local Access

Due to Google Chrome’s security restrictions, workers will not run locally (e.g. from file://) in the latest versions of the browser. Instead, they fail silently! To run your app from the file:// scheme, run Chrome with the --allow-file-access-from-files flag set. It is not recommended to run your primary browser with this flag set. It should only be used for testing purposes and not regular browsing.

(!) Debug: The other good thing to know is that you can debug works in the Chrome Developer Tools by clicking on the Scripts tab, and scrolling down in the column on the right to Workers and clicking on the debug checkbox.

Other browsers do not impose the same restriction.

Same Origin Considerations

Worker scripts must be external files with the same scheme as their calling page. Thus, you cannot load a script from a data: URL or javascript: URL, and an https: page cannot start worker scripts that begin with http: URLs.

The Code

 

<!DOCTYPE HTML>
<html>
  <head>
    <title>Web Worker: The highest prime number</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
    <meta charset=utf-8 />
  </head>
  <style>
    #actions {
      position: fixed;
      top: 10px;
      background: lightBlue;
      padding:8px;
    }
    h1 {
      position: relative;
      bottom: 10px;
      left: 280px;
    }
    #status { 
      position: relative;
      font-size: 120%; 
      background: lightyellow;
      margin:8px; 
    }
    article { 
      position: relative;
      color:red; 
    }
    input {
      width: 80px;
      height: 35px;
      font-size: 120%;
    }
  </style>
  <body>
    <script>
      var myWorker;

      function start() {
        console.log("WebWorker: Starting");
        myWorker = new Worker("highPrime2.js");
        myWorker.addEventListener("message", primeHandler, false);
        var maxNum = $('#upto').val();
        myWorker.postMessage({'cmd': 'start', 'upto': maxNum});
      }

      function stop() {
        if (myWorker != undefined) {
          var msg = "<br/>WebWorker: Terminating " + new Date();
          console.log(msg);
          $('#status').append(msg);
          myWorker.terminate();
          myWorker = null;
        }
      }
      function primeHandler(event) {
        console.log ('got e:'+event.data);
        if (is_numeric(event.data)) {
          $('#result').append(event.data);
        }
        else {
          $('#status').append(JSON.stringify(event.data) );
        }
      }

      function is_numeric(input){
        return typeof(input)=='number';
      }
    </script>

    <h1>Web Worker: The highest prime number</h1>
    <article>The prime numbers: 
      <output id="result"></output>
      <div id="status"></div>
    </article>
    <div id="actions">
      <input type="text" name="upto" id='upto'/>
      <button onclick="start()" title="Start the work">Start</button>
      <button onclick="stop()" title="Stop the work and go have a drink">Stop</button>
    </div>
  </body>
</html>

// And here is the worker code:
<script>
  //
  // A simple way to find prime numbers
  //
  self.addEventListener('message', function(e) {
    var data = e.data;
    var shouldRun = true;

    switch (data.cmd) {
      case 'stop':
        postMessage('Worker stopped the prime calculation (Al Gore is happy now) ' + 
          data.msg );
        shouldRun = false;
        self.close(); // Terminates the worker.
        break;
      case 'start':
        postMessage("Worker start working upto: " + data.upto + " (" + new Date()+ ")<br/>");
        var numbers = isPrime(data.upto);
        postMessage("Got back these numbers: "+ numbers + "<br/>");
        break;
      default:
        postMessage('Dude, unknown cmd: ' + data.msg);
    };
  }, false);

  // simple calculation of primes (not the most efficiate - but works)
  function isPrime(number) {
    var numArray = "";
    var this_number,divisor,not_prime;
    var this_number = 3;
    while(this_number < number) {
      var divisor = parseInt( this_number / 2);
      var not_prime = 0;
      while(divisor > 1) {
        if(this_number % divisor == 0) {
          not_prime = 1;
          divisor = 0;
        }
        else {
          divisor = divisor - 1;
        }
      }
      if(not_prime == 0) {
        numArray += (this_number + " ");
      }
      this_number = this_number + 1;
    }
    return numArray;
  }
</script>

 

The last part of this series is about Shared Web Workers.

You can also find full set of example on github: Web Workers Examples.

Standard
HTML5

HTML5 App Caching – The Easy Way

HTML5 features appcaching, a way to make your web sites and apps work offline, and to increase their performance as well.

I’m sure you know, browsers cache HTML, CSS, JavaScript files, images and other resources of the sites you visit, to speed up the subsequent loading of pages. However, you never know when the browser might discard cached files, and so this is not a reliable way for sites to work offline. But what if we could tell the browser what to cache? Well, with HTML5 application caches (also known as ‘appcache’) we can do just that.

An appcache manifest contain several lines in that order:

  • In the first line we declare “CACHE MANIFEST” (required)
  • Second line: “CACHE:” – which specifies the URLs of resources.
  • We can also optionally specify which resources should not be cached, in a section of the manifest file introduced by the string “NETWORK:”. These resources aren’t just not cached, but further, won’t be used when the user is offline, even if the browser has cached them in its own caches.
  • We can also optionally specify fallback resources to be used when the user is not connected, in a section of the file called “FALLBACK:”
  • You can add comments to the file with, simply by beginning a line with “#” – that’s an important feature to make the version readable for you as a developer. It’s also a nice way to let the browser ‘know’ that something changed in our app and it’s needed to fetch a new version of the app from the network.

Here is a simple example:


CACHE MANIFEST
#version 1.0

CACHE:

#images
/images/logo.png
/images/ido-header.png

#pages
/pages/index.html
/pages/main.html

#CSS
/style/main-style.css

#scripts
/js/main-logic.js

FALLBACK:
/ /offline.html

NETWORK:
sign-new-user.html

Creating a HTML5 cache manifest file the easy way:

Other good reads:
Standard
Chrome, HTML5, webdev

ChromeOS In 5min Video



In this short screencast I’ve touched on:

  • What is ChromeOS?
  • Why it’s great for users, business and developers.
  • How a good web apps look like.
Until next time, please be strong and happy.
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>
  
   var worker = new Worker('highPrime.js');
   worker.onmessage = function (event) {
     document.getElementById('result').textContent = event.data;
   };
  
 </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
Chrome, HTML5

How Easy Is To Use A Chromebook?

Very easy and if you are a (happy) user of gmail your life will get better from the first minute you are logged in. If you have other email service it’s also cool. I’m using both yahoo and gmail on it and it’s working great with both of them.

The ability to run gmail in offline mode is also a doable on a Chromebook. It’s very useful, specially, to people the travel a lot and can’t count that their favorite airline will have wi-fi. I’m writing this post from a Chromebook… but since wordpress don’t have a good webapp with some offline capabilities – I’m using ‘Write Space’. Write Space is a customizable full-screen text-editor that lives in your web-browser. It is designed to minimize the distractions that come between you and your writing and the offline capabilities are really working.

If you are a new to Chromebook – check this one and a half minute demo:

Standard
HTML5, webdev

HTML5 Fields – What Can You Do With It?

HTML5 is the newest specification for HTML, the language that web browsers read to display web pages. HTML5 has many new features intended to make creating websites easier and people’s experience in using those websites better. Among those features are many enhancements to web forms. Here is a list of the major browsers that support these days the new features:

Another place to see which browser support what type of field is http://caniuse.com/#search=input

As a web developer these improvementscan save you lots of time and effort (psss… no java script is needed to validate your fields) in order to see them live I’ve created this ‘playground for HTML5 fields’ so it will be easy to ‘play’ and see what each field is giving you. Here is a basic set to kick things:


<form>
  <fieldset>
    <label>Required</label>
    <input type="text" required />

    <label>Email</label>
    <input type="email" value="some@email.com" />

    <label>Date</label>
    <input type="date" 
     min="2010-08-14" 
     max="2011-08-14" 
     value="2010-08-14"/>

    <label>Range</label>
    <input type="range" min="0" max="50" value="10" />

    <label>Search</label>
    <input type="search" results="10" placeholder="Search..." />

    <label>Tel</label>
    <input type="tel" placeholder="(555) 555-5555" 
     pattern="^\(?\d{3}\)?[-\s]\d{3}[-\s]\d{4}.*?$" />

    <label>Color</label>
    <input type="color" placeholder="e.g. #bbbbbb" />

    <label>Number Range</label>
    <input type="number" step="1" min="-5" max="10" value="0" />
  </fieldset>
<button value="Go">Go</button>
</form>

Good luck and try the JSFiddle link if you wish to fork it and see what are the fields are doing in each case.

Standard