^ contentPlace:"3-3" HTML5 Offline for Fun and Performance Michael Mahemoff http://creativebits.org/files/html5-logo-1.jpg Recent (and Not-So-Recent) Projects #000 Offline Wiki: TiddlyWiki http://www.borism.net/wp-killer/images-big/tiddlywiki.png Offline and On: Mobile HTML5 images/zero.jpg Offline and On: Chrome Web Store, ChromeOS http://www.geeky-gadgets.com/wp-content/uploads/2010/12/google-chrome-web-store.jpg ^ contentPlace:"1-1" "The Web, Offline" http://28.media.tumblr.com/tumblr_lken99RFmO1qb02vjo1_500.jpg Divs and Spans, Sitting on a Plane http://prez.mahemoff.com/wd-offline/images/urlsonaplane.jpg images/plane.jpg http://www.designshak.com/wp-content/uploads/2009/08/bruce_on_rails.jpg images/zonda-dhh.png Do divs on a plane really matter? http://prez.mahemoff.com/wd-offline/images/urlsonaplane.jpg
The Year is 2011
Fully Offline http://3.bp.blogspot.com/-4GRdPFTuLe0/TWeVXQPbgWI/AAAAAAAABNM/3KS_6-O1_QE/s1600/sinuous-game.jpg "Always" Online (no such thing!) images/screenshot-20110217.png Online-Offline ← critical in 2011 http://kayo8.files.wordpress.com/2007/02/google-docs.gif Google IO two weeks ago ... http://www.jonkragh.com/wp-content/uploads/google-map-marker-google-io-20091.jpg App Cache Web SQL images/springpad.jpg App Cache Web SQL (planned) FileSystem (planned) http://www.designbeckons.com/wp-content/uploads/2010/08/CM-Capture-11.jpg App Cache IndexedDB FileSystem http://cashrevelations.com/magazine/wp-content/media/cloud9-ide.jpg images/angrybirds.png
Right, let's do this.
* Offline the App:
App Cache * Offline the Data:
Local Storage
Web SQL
Indexed Database
FileSystem * Library support * Offline the App:
App Cache
* Offline the Data:
Local Storage
Web SQL
Indexed Database
FileSystem * Library support Demo - html5photos.appspot.com images/html5photos.jpg Offline the App: App Cache ==== <html manifest="cache.manifest"> CACHE MANIFEST index.html index.css index.js logo.png ... ==== Serve it with the right MIME type ==== AddType text/cache-manifest manifest ====
Browser checks manifesto on every visit
... So version numbers are good ==== CACHE MANIFEST # 3.141 index.html ... ==== ... Even better, generate the manifest ==== CACHE MANIFEST <?= checksum($files) ?> <? foreach ($files as $file) { ?> <?= $file ?> <? } ?> ====
Make sure manifest is not cached!
Graceful Degradation: The Offline Experience ==== <html manifest="cache.manifest"> CACHE MANIFEST CACHE: index.html ... FALLBACK: /checkins/* no-go.html NETWORK: /weather/* ==== The "URLs on a plane" problem http://prez.mahemoff.com/wd-offline/images/urlsonaplane.jpg http://prez.mahemoff.com/wd-offline/images/farm5.static.flickr.com__4103__5004321820_8b09d3c58d_o.jpg http://prez.mahemoff.com/wd-offline/images/farm5.static.flickr.com__4081__5017980645_7d997740a1_b.jpg http://www.geeky-gadgets.com/wp-content/uploads/2010/12/google-chrome-web-store.jpg Declarative → Procedural ==== alert(window.applicationCache.status); window.applicationCache.addEventListener("cached", onAppCached, false); window.applicationCache.update(); window.applicationCache.swapCache(); ==== * Offline the App:
App Cache * Offline the Data:Local Storage
Web SQL
Indexed Database
FileSystem * Library Support Content images/news.jpg User data images/springitems.jpg Stats — offline analytics? http://venturebeat.com/wp-content/uploads/2009/06/google_analytics.jpg Background apps — Pre-emptive downloading images/background.png Demo — j.mp/geomood images/geomood.jpg
cookies
window.name
userData
file-writing
plugins (Flash, Applets, Gears)
* Offline the App:
App Cache * Offline the Data:
Local Storage
Web SQL
Indexed Database
FileSystem
* Library support 1. LocalStorage aka "Web Storage" #000 LocalStorage ==== localStorage.foo = "bar"; localStorage["foo"] = "bar"; localStorage.setItem("foo", "bar"); ====
LocalStorage: It's all about the string.

(despite the spec)

(De)serialization ==== localStorage.checkins = JSON.stringify([]); // reset localStorage.checkins = JSON.stringify(checkins); var allCheckins = JSON.parse(localStorage.checkins); ====

Local Storage

  • So easy!
  • All modern browsers
  • Can use SessionStorage too
  • Fires semantic events
  • Local Storage

  • Synchronous
  • Only strings
  • No indexing
  • Unstructured
  • All storage mechanisms are subject to quotas
    Get around quotas with
    installed web apps or native APIs
    2. Web SQL Database (Deprecated but widespread) #000 images/websql.png Web SQL Database: open+create DB ==== var db = openDatabase('geomood', '1.0', 'Geo-Mood Checkins', 8192); db.transaction(function(tx) { tx.executeSql("create table if not exists " + "checkins(id integer primary key asc," + "time integer," + "latitude float," + "longitude float," + "mood string)", [], onCreated, this.onError ); }); ==== Web SQL Database: queries ==== store.db.transaction(function(tx) { tx.executeSql( "select * from checkins where mood=?", [moodQuery], function(tx, results) { for (i = 0; i < results.rows.length; i++) { handler(clone(results.rows.item(i))); }, store.onError ); }); ====

    Web SQL

  • Mobile-friendly: Android + iOS + others
  • Asynchronous (or synchronous)
  • Indexed - fast searching
  • Structured - DRY
  • Web SQL

  • Deprecated!
  • Not on FF or IE
  • Complex
  • 3. Indexed Database aka "Teh New H0tness" images/indexed.png
    In IndexedDB, "Requests" are first-class citizens
    Indexed Database — async idiom ==== var request = doSomething(); request.onsuccess = function() { doSomething(request.result); }; request.onerror = function() { ... }; ==== Indexed Database — open database ==== var openRequest = indexedDB.open("geomood", "Geo-Mood Checkins"); openRequest.onsuccess = function(ev) { db = ev.target.result; db.onerror = function(ev) { console.log("db error", ev.target.webkitErrorMessage); }; if (db.version!="1.5") resetDB(); // MUST set version to change DB } } ==== Indexed Database — create/update database ==== ... if (db.version!="1.5") { var versionRequest = db.setVersion("1.5"); versionRequest.onsuccess = function(ev) { if (db.objectStoreNames.contains("checkins")) db.deleteObjectStore("checkins"); // synchronous var checkinsStore = db.createObjectStore ("checkins", { keyPath: "time" }); // synchronous }; } ==== Indexed Database — create object store ==== db.createObjectStore("checkins"); // provide key when add()ing db.createObjectStore("checkins", { autoIncrement: true }); // key auto-generated db.createObjectStore("checkins", { keyPath: "time" }); // key inferred ==== Indexed Database — add and retrieve ==== var tx = db.transaction([], IDBTransaction.READ_WRITE, 0); // or READ_ONLY var store = tx.objectStore("checkins"); var addRequest = store.add(checkin); // "put" if unique addRequest.onsuccess = function() { ... } var getRequest = store.get(id); // "put" if unique getRequest.onsuccess = function() { show(request.result); } ====
    Are we missing something?
    Indexed Database — yes we have index! ==== var store = db.createObjectStore("checkins"); store.createIndex("moodIndex", "mood", { unique: false }); var moodIndex = store.index("moodIndex") var getRequest = moodIndex.get("happy"); getRequest.onSuccess = function() { show(request.result()); }; ==== Indexed Database — iterating through results ==== var cursorRequest = moodIndex.openCursor(new IDBKeyRange.only(moodQuery)); cursorRequest.onsuccess = function(ev) { var cursor = cursorRequest.result; if (cursor) { show(cursor.value); cursor.continue(); } }; ==== Indexed Database — Neutralize across browsers ==== var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB; var IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.mozIDBTransaction; var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.mozIDBKeyRange; ====

    IndexedDB

  • Asynchronous (or synchronous)
  • Fast
  • NOSQL - Simple data structure
  • IndexedDB

  • Complex API (needs library support)
  • Immature compatibility
    (Chrome+FF; no mobile)
  • NOSQL - Not DRY
  • 4. FileSystem aka "actual content on your hard drive!" #000 No "multiple DBs"...just work from the root ==== / log.tt photos/ archive/ fun.jpg morefun.jpg docs/ work.pdf accounts.doc music/ gaga.mp3 ==== FileSystem — Create directory ==== requestFileSystem( window.PERSISTENT, // or TEMPORARY 1024*1024, function(fs) { fs.root.getDirectory( "checkins", { create: true }, function(dir) { checkinsDir = dir; }, function() { console.log("error"); } ); }, function(e) { console.log("error " + e.code); } ); ====
    File API gives us Readers and Writers,
    just like C, C++ et al
    File API — Reader/Writer idiom ==== reader = new FileReader(); reader.onload = function() { doSomething(this.result); }; reader.readAsText(file); ==== FileSystem — Create file ==== fs.root.getFile( "checkins/" + checkin.time, {create: true, exclusive: true}, function(file) { file.createWriter(function(writer) { var bb = new WebKitBlobBuilder; bb.append(JSON.stringify(checkin)); writer.write(bb.getBlob("text/plain")); }); } ); ==== FileSystem — Read directory ==== checkinsDir.createReader().readEntries(function(fileEntries) { ... }); ====
    For now, need Chromium's
    --unlimited-quota-for-files flag
    WebFS: A commonJS Adaptor http://goo.gl/TU0Iv

    FileSystem

  • Store big files
  • Store binary files
  • Accessible outside browser
  • FileSystem

  • Immature compatibility (Chromium)
  • Unstructured, unversioned
  •  The Ultimate HTML5
    Offline Trivia Quiz
    http://atari.fandal.cz/files/images/games/t/trivia_quiz.png