Offline
As your world used to be
-
As your world used to be
-
Blog: bit.ly/ido-blog
Slides: bit.ly/offline-11
localStorage for simple key-value storage
if (!localStorage.getItem("checkins")) { localStorage.setItem("checkins", JSON.stringify([])); }
Easy API
setItem() getItem() removeItem() clear()
window.sessionStorage
Solution:
var userstr = JSON.stringify({ user: 'john', id: 10 }); localStorage.setItem('user', userstr); var user = JSON.parse(localStorage.getItem('user'));
http://example.com:80/ \ \ \_ port \ \_ domain \_ scheme
document.querySelector('#ta').addEventListener('keyup', function(e) { localStorage.setItem('value', this.value); localStorage.setItem('timestamp', (new Date()).getTime()); }, false);
Currently only Chrome and Firefox have implemented IndexedDB, however, the major browser vendors have indicated an intention to support it.
Today, it's supported via vendor prefixes. Let's simplify this:
window.indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;
Retrieving by key ( indexes ):
// db.createObjectStore("Friend", "id", true); db.createIndex("FriendNames", "name", false); var index = db.openIndex('FriendNames'); var id = index.get('Eric');
Querying ( cursors ):
// Restrict to names beginning A-E var range = new KeyRange.bound('A', 'E'); var cursor = index.openObjectCursor(range); while (cursor.continue()) { console.log(cursor.value.name); }
var idbRequest = window.indexedDB.open('Database Name'); idbRequest.onsuccess = function(event) { var db = event.srcElement.result; var transaction = db.transaction([], IDBTransaction.READ_ONLY); var curRequest = transaction.objectStore('ObjectStore Name').openCursor(); curRequest.onsuccess = ...; };
<html manifest="example.appcache">... </html>
CACHE MANIFEST # 2010-11-17-v0.0.1 # Explicitly cached entries CACHE: index.html stylesheet.css images/logo.png # static.html will be served if the user is offline FALLBACK: / /static.html # Resources that require the user to be online. NETWORK: *
text/cache-manifest
applicationCache.addEventListener('updateready', function(e){ if (applicationCache.status == applicationCache.UPDATEREADY){ if (confirm('Load new content?')) { ... } } });
window.requestFileSystem( TEMPORARY, // persistent vs. temporary storage 1024 * 1024, // size (bytes) of needed space initFs, // success callback opt_errorHandler // opt. error callback, denial of access );
blob:
)data:
)filesystem:
) Newvar img = document.createElement('img'); // filesystem:http://example.com/temporary/myfile.png img.src = fileEntry.toURL(); document.body.appendChild(img);
Retrieve a file by its filesystem URL:
window.resolveLocalFileSystemURL(img.src, function(fileEntry) { ... });
function initFs(fs) { fs.root.getFile('logFile.txt', {create: true}, function(fileEntry) { // fileEntry.isFile == true // fileEntry.name == 'logFile.txt' // fileEntry.fullPath == '/logFile.txt' // Get a File obj fileEntry.file(function(file) { ... }, errorHandler); // fileEntry.remove(function() {}, errorHandler); // fileEntry.moveTo(...); // fileEntry.copyTo(...); // fileEntry.getParent(function(dirEntry) {}, errorHandler); }, errorHandler); }
document.querySelector('#terminal').ondrop = function(e) { var files = e.dataTransfer.files; window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) { Array.prototype.slice.call(files || [], 0).forEach(function(file, i) { fs.root.getFile(file.name, {create: true, exclusive: true}, function(fileEntry) { fileEntry.createWriter(function(fileWriter) { fileWriter.write(f); // Note: write() can take a File | Blob. }, errorHandler); }, errorHandler); }); }, errorHandler); };
Web Workers supports the classic store-and-forward paradigm. Instead of directly communicating with a server, app communicates with a Worker that buffers changes locally and communicates with the server when online.
A Shared Worker can:
navigator.onLine
- know when you're all aloneif (navigator.onLine) { console.log('ONLINE!'); } else { console.log('Connection flaky'); }
window.addEventListener('online', function(e) { // Re-sync data with server. }, false); window.addEventListener('offline', function(e) { // Queue up events for server. }, false);
"Google and Bing break up their JavaScript and CSS into smaller blocks and save them in localStorage on mobile devices. Simultaneously they set a cookie so that the server knows not to send that payload on subsequent searches, a savings of 150-170 kB before gzipping."
App cache & localStorage survey - Steve Souders
Notion of Temporary storage and Persistent Storage
(*) All above is on Google Chrome
// Request Status webkitStorageInfo.queryUsageAndQuota( webkitStorageInfo.TEMPORARY, // or PERSISTENT usageCallback, errorCallback); // Request Quota webkitStorageInfo.requestQuota( webkitStorageInfo.TEMPORARY, // or PERSISTENT quotaCallback, errorCallback);
FireFox | Safari | Chrome.png | Opera | IE | |
---|---|---|---|---|---|
Web Storage | Y | Y | Y | Y | Y (8+) |
IndexedDB | Y | N | Y | N | N |
WebSQL | N | Y | Y | Y | N |
App Cache | Y | Y | Y | Y | N |
File System API | N | N | Y | N | N |
FireFox | Safari | Chrome On Android | Opera | IE | |
---|---|---|---|---|---|
Web Storage | Y | Y | Y (2+) | Y | Y |
IndexedDB | Y | N | N | N | N |
WebSQL | N | Y | Y (2+) | Y | N |
App Cache | Y | Y | Y (2.1+) | Y | N |
File System API | N | N | Y (3+) | N | N |
For most apps, you can get them packaged up for distribution in a matter of a minute or two. Just go to appmator.appspot.com!
Because we're using the application cache, you may want to request the "unlimitedStorage" permission. Just paste it in manually to the manifest.json file.
{ "name": "Great App Name", "description": "Pithy description", "version": "0.0.0.1", "icons": { "128": "icon_128.png" }, permissions : [ "unlimitedStorage" ], "offline_enabled": true, "app": { ... } }
Web Storage (LocalStorage and SessionStorage)
Questions?