^ 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
Android+iPhone+Opera Mobile app:
Stores past emails
Web app:
Stores user preferences
Web app:
Stores purchased MP3s
Firefox extension:
Stores time spent on each website
* Offline the App:
App Cache
* Offline the Data:
Local Storage
Web SQL
Indexed Database
FileSystem
*
Library support
Lawnchair: Generic storage
====
var store = new Lawnchair("checkins");
var checkin = { id: +new Date, mood: "Over the Moon" };
store.save(checkin);
store.get(123);
store.all();
====
Polyfills/Shims
http://j.mp/html5polyfills
images/poly.jpg
^ contentPlace:"1-3"
Beyond persistence: Automatic Syncing
http://s3.mylesbraithwaite.com/presentations/oglf/2009/CouchDB/slide.001.png
^ contentPlace:"1-3"
More like this please!
http://s3.mylesbraithwaite.com/presentations/oglf/2009/CouchDB/slide.001.png
Slides: http://prez.mahemoff.com/wdx-offline
More: http://www.html5rocks.com/tutorials/ #storage #offline
Contact: mahemoff@google.com | @mahemoff
http://prez.mahemoff.com/wd-offline/images/farm3.static.flickr.com__2485__3759228220_4e039da0d4_b.jpg