Stephan Hochdörfer // 01.07.2014
<!DOCTYPE html> <html lang="en">
<!DOCTYPE html> <html lang="en" manifest="cache.manifest">
CACHE MANIFEST #2014-05-22 js/app.js css/app.css favicon.ico http://someotherdomain.com/image.png
CACHE MANIFEST # 2014-05-22 NETWORK: data.php CACHE: /main/home /main/app.js /settings/home /settings/app.js http://myhost/logo.png http://myhost/check.png http://myhost/cross.png
CACHE MANIFEST # 2014-05-22 FALLBACK: / /offline.html NETWORK: *
// events fired by window.applicationCache window.applicationCache.onchecking = function(e) { log("Checking for updates"); } window.applicationCache.onnoupdate = function(e) { log("No updates"); } window.applicationCache.onupdateready = function(e) { log("Update ready"); } window.applicationCache.onobsolete = function(e) { log("Obsolete"); }
window.applicationCache.ondownloading = function(e) { log("Downloading"); } window.applicationCache.oncached = function(e) { log("Cached"); } window.applicationCache.onerror = function(e) { log("Error"); } window.applicationCache.onprogress = function(e) { log("Progress: Downloading file " + counter++); };
// Check if a new cache version is available window.addEventListener('load', function(e) { window.applicationCache.addEventListener('updateready', function(e) { if(window.applicationCache.status == window.applicationCache.UPDATEREADY) { window.applicationCache.swapCache(); if (confirm('New version is available. Load it?')) { window.location.reload(); } } }, false); }, false);
Dateien werden immer(!)
vom lokalen Cache ausgeliefert.
Der lokale Cache wird nur dann aktualisiert
wenn sich die manifest Datei geändert hat.
Nicht ladbare Dateien aus der CACHE Sektion
führen dazu dass der Cache invalide ist.
Kann die manifest Datei nicht geladen werden,
erfolgt kein Caching!
Nicht gecachte Ressourcen werden auf
einer gecachten Seite nicht angezeigt.
Nach Aktualisierung des Caches muss
die Seite neu geladen werden!
Mit expires Header arbeiten um das
Cachen des manifests zu verhinden!
<!DOCTYPE HTML> <html> <head> <title>Data URI Schema</title> <style type="text/css"> ul.checklist li { margin-left: 20px; background: white url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==') no-repeat scroll left top; } </style> </head> <body> </body> </html>
<!DOCTYPE HTML> <html> <head> <title>Data URI Schema</title> </head> <body> <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot"> </body> </html>
Nicht getrennt vom Dokument. Dokument
muss jedes Mal neu geladen werden.
Ändert sich der Inhalt muss dieser
erneut codiert und eingebettet werden.
Kein Support für IE7.
IE8 erlaubt max. 30K an Daten.
Base64 kodierte Strings sind um etwa
1/3 größer als die binäre Repräsentation.
Komfortable Art Daten offline zu
speichern: Key/Value Speicher
localStorage vs. sessionStorage
function add(item) { try { // for a new item set id if((typeof item.id === "undefined") || (null == item.id) || ("" == item.id)) { item.id = get_lastIndex() + 1; } // store object as string localStorage.setItem(item.id, JSON.stringify(item) ); // update the index set_lastIndex(item.id); } catch(ex) { console.log(ex); } }
function modify(item) { try { // store object as string localStorage.setItem(item.id, JSON.stringify(item) ); } catch(ex) { console.log(ex); } }
function remove (id) { try { localStorage.removeItem(id); } catch(ex) { console.log(ex); } }
function read() { try { var lastIdx = get_lastIndex(); for(var i = 1; i <= lastIdx; i++) { if(null !== localStorage.getItem(i)) { // parse and render item var item = JSON.parse( localStorage.getItem(i) ); } } } catch(ex) { console.log(ex); } }
Ersetze „localStorage“ durch „sessionStorage“
function add(item) { try { // for a new item set id if((typeof item.id === "undefined") || (null == item.id) || ("" == item.id)) { item.id = get_lastIndex() + 1; } // store object as string sessionStorage.setItem(item.id, JSON.stringify(item) ); // update the index set_lastIndex(item.id); } catch(ex) { console.log(ex); } }
var value = "my value"; // method call localStorage.setItem("key", value); // Array accessor localStorage[key] = value; // Property accessor localStorage.key = value;
Eine lokale SQL Datenbank auf SQLite Basis.
var onError = function(tx, ex) { alert("Error: " + ex.message); }; var onSuccess = function(tx, results) { var len = results.rows.length; for(var i = 0; i < len; i++) { // render found todo item render(results.rows.item(i)); } };
// initalize the database connection var db = openDatabase('todo', '1.0', 'Todo Database', 5 * 1024 * 1024 ); db.transaction(function (tx) { tx.executeSql( 'CREATE TABLE IF NOT EXISTS todo '+ '(id INTEGER PRIMARY KEY ASC, todo TEXT)', [], onSuccess, onError ); });
function m1(t){ t.executeSql("create table tbl1...") } function m2(t){ t.executeSql("alter table tbl1...") } function m3(t){ t.executeSql("alter table tbl1...") } if(db.version == "") { db.changeVersion("", "1", m1, null, function() { db.changeVersion("1", "2", m2, null, function() { db.changeVersion("2", "3", m3); }); }); } if(db.version == "1") { db.changeVersion("1", "2", m2, null, function() { db.changeVersion("2", "3", m3); }); } if(db.version == "2") { db.changeVersion("2", "3", m3); }
function add(item) { db.transaction(function(tx) { tx.executeSql( 'INSERT INTO todo (todo) VALUES (?)', [ item.todo ], onSuccess, onError ); }); }
function modify(item) { db.transaction(function(tx) { tx.executeSql( 'UPDATE todo SET todo = ? WHERE id = ?', [ item.todo item.id ], onSuccess, onError ); }); }
function remove(id) { db.transaction(function (tx) { tx.executeSql( 'DELETE FROM todo WHERE id = ?', [ id ], onSuccess, onError ); }); }
function read() { db.transaction(function (tx) { tx.executeSql( 'SELECT * FROM todo', [], onSuccess, onError ); }); }
Kompromiss aus Web Storage
und Web SQL Database.
// different browsers, different naming conventions var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB; var IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction; var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange;
var db = null; var request = indexedDB.open("todo"); request.onfailure = onError; request.onsuccess = function(e) { db = request.result; var v = "1.0"; if(v != db.version) { var verRequest = db.setVersion(v); verRequest.onfailure = onError; verRequest.onsuccess = function(e) { var store = db.createObjectStore("todo", { keyPath: "id", autoIncrement: true }); e.target.transaction.oncomplete = function() {}; }; } };
function add(item) { try { var trans = db.transaction(["todo"], IDBTransaction.READ_WRITE); var store = trans.objectStore("todo"); var request = store.put({ "todo": item.todo, }); } catch(ex) { onError(ex); } }
function modify(item) { try { var trans = db.transaction(["todo"], IDBTransaction.READ_WRITE); var store = trans.objectStore("todo"); var request = store.put(item); } catch(ex) { onError(ex); } }
function remove(id) { try { var trans = db.transaction(["todo"], IDBTransaction.READ_WRITE); var store = trans.objectStore("todo"); var request = store.delete(id); } catch(ex) { onError(ex); } }
function read () { try { var trans = db.transaction(["todo"], IDBTransaction.READ); var store = trans.objectStore("todo"); var keyRange = IDBKeyRange.lowerBound(0); var cursorRequest = store.openCursor(keyRange); cursorRequest.onsuccess = function(e) { var result = e.target.result; if(!!result == false) { return; } // @TODO: render result.value result.continue(); }; } catch(ex) { onError(ex); } }
try { var index = db.openIndex('todo'); var item = index.get(1337); } catch(ex) { onError(ex); }
try { // create non-unique index db.createIndex("Created", "createdDate", {"unique": false}); // create unique index db.createIndex("OtherKey", "otherField", {"unique": true}); // create multi-column index (not working in IE10!) db.createIndex("MultiIndex", ["field1", "field2"]); } catch(ex) { onError(ex); }
db.js als Wrapper verwenden!
var server; db.open( { server: 'todo', version: 1, schema: { todo: { key: { keyPath: 'id' , autoIncrement: true}, indexes: { Created: { } } } } }).done(function(s) { server = s; });
server.todo.add( { todo: 'This is a sample todo item' }).done(function(item) { // item stored });
server.todo.query() .execute() .done(function(results) { // do something with the results });
var onError = function(e) { var msg = ''; switch(e.code) { case FileError.QUOTA_EXCEEDED_ERR: msg = 'QUOTA_EXCEEDED_ERR'; break; case FileError.NOT_FOUND_ERR: msg = 'NOT_FOUND_ERR'; break; case FileError.SECURITY_ERR: msg = 'SECURITY_ERR'; break; case FileError.INVALID_MODIFICATION_ERR: msg = 'INVALID_MODIFICATION_ERR'; break; case FileError.INVALID_STATE_ERR: msg = 'INVALID_STATE_ERR'; break; default: msg = 'Unknown Error'; break; }; alert("Error: " + msg); };
// File system has been prefixed as of Google Chrome 12 window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem; window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder; var size = 5 * 1024*1024; // 5MB
// request quota for persistent store window.webkitStorageInfo.requestQuota( PERSISTENT, size, function(grantedBytes) { window.requestFileSystem( PERSISTENT, grantedBytes, function(fs) { // @TODO: access filesystem } } } }
function add(item) { window.webkitStorageInfo.requestQuota( PERSISTENT, size, function(grantedBytes) { window.requestFileSystem( PERSISTENT, grantedBytes, function(fs) { writeToFile(fs, item); }, onError ); }, function(e) { onError(e); } ); }
function writeToFile(fs, item) { fs.root.getFile( 'todo.txt', { create: true }, function(fileEntry) { fileEntry.createWriter( function(fileWriter) { var blob = new Blob([JSON.stringify(item)+"\n"]); fileWriter.seek(fileWriter.length); fileWriter.write(blob); }, onError ); }, onError ); }
function read() { window.webkitStorageInfo.requestQuota( PERSISTENT, size, function(grantedBytes) { window.requestFileSystem( PERSISTENT, grantedBytes, function(fs){ readFromFile(fs); }, onError ); }, function(e) { onError(e); } ); }
function readFromFile(fs) { fs.root.getFile( 'todo.txt', { create: true }, function(fileEntry) { fileEntry.file(function(file){ var reader = new FileReader(); reader.onloadend = function(e) { if (evt.target.readyState == FileReader.DONE) { // process this.result } }; reader.readAsText(file); }); }, onError ); }
Web Storage | Web SQL DB | IndexedDB | File API | |
IE | 8.0 | 10.0 | 10.0 | - |
Firefox | 11.0 | 11.0 | 11.0 | 19.0 |
Chrome | 18.0 | 18.0 | 18.0 | 18 |
Safari | 5.1 | 5.1 | - | - |
iOS | 3.2 | 3.2 | - | - |
Android | 2.1 | 2.1 | - | - |
Web Storage | Web SQL DB | IndexedDB | File API | |
IE | 10 MB | 500 MB | 500 MB | |
Firefox | 10 MB | 50 MB | 50 MB | |
Chrome | 5 MB | ∞ | ∞ | ∞ |
Safari | 5 MB | 5 MB | 5 MB | |
iOS | 5 MB | 5 MB | 5 MB | |
Android | 5 MB | ? | ? |
document.body.addEventListener("online", function () { // browser is online! } document.body.addEventListener("offline", function () { // browser is not online! }
$.ajax({ dataType: 'json', url: 'http://myapp.com/ping', success: function(data){ // ping worked }, error: function() { // ping failed -> Server not reachable } });
PouchDB, the JavaScript Database that syncs!
var db = new PouchDB('todo'); db.put({ _id: 1, todo: 'Get some work done...', }); db.replicate.to('http://myapp.com/mydb');
Vielen Dank!