Progressive Web Apps
@PatrickKettner
Progressive What Apps?
@PatrickKettner
calm a baby
2G Ruined My Day45M Ruined My Day
When you don’t have a better
idea, buy a novelty domain
https://HushLittleBa.by
Baby soothing goodness in <6k
ServiceWorker
let CURRENT_CACHE = {
prefetch: 'prefetch-cache-v1'
};
self.addEventListener('install', event => {
let prefetch = ['/T0t411y-R4D-Sans']
event.waitUntil(caches.open(CURRENT_CACHE['prefetch'])
.then(cache =>
cache.addAll(prefetch.map((url) =>
new Request(url, { 'mode': 'no-cors' })
))
)
)
})
let CURRENT_CACHE = {
prefetch: 'prefetch-cache-v1'
};
self.addEventListener('install', event => {
let prefetch = ['/T0t411y-R4D-Sans']
event.waitUntil(caches.open(CURRENT_CACHE['prefetch'])
.then(cache =>
cache.addAll(prefetch.map((url) =>
new Request(url, { 'mode': 'no-cors' })
))
)
)
})
let CURRENT_CACHE = {
prefetch: 'prefetch-cache-v1'
};
self.addEventListener('install', event => {
let prefetch = ['/T0t411y-R4D-Sans']
event.waitUntil(caches.open(CURRENT_CACHE['prefetch'])
.then(cache =>
cache.addAll(prefetch.map((url) =>
new Request(url, { 'mode': 'no-cors' })
))
)
)
})
let CURRENT_CACHE = {
prefetch: 'prefetch-cache-v1'
};
self.addEventListener('install', event => {
let prefetch = ['/T0t411y-R4D-Sans']
event.waitUntil(caches.open(CURRENT_CACHE['prefetch'])
.then(cache =>
cache.addAll(prefetch.map((url) =>
new Request(url, { 'mode': 'no-cors' })
))
)
)
})
let CURRENT_CACHE = {
prefetch: 'prefetch-cache-v1'
};
self.addEventListener('install', event => {
let prefetch = ['/T0t411y-R4D-Sans']
event.waitUntil(caches.open(CURRENT_CACHE['prefetch'])
.then(cache =>
cache.addAll(prefetch.map((url) =>
new Request(url, { 'mode': 'no-cors' })
))
)
)
})
let CURRENT_CACHE = {
prefetch: 'prefetch-cache-v1'
};
self.addEventListener('install', event => {
let prefetch = ['/T0t411y-R4D-Sans']
event.waitUntil(caches.open(CURRENT_CACHE['prefetch'])
.then(cache =>
cache.addAll(prefetch.map((url) =>
new Request(url, { 'mode': 'no-cors' })
))
)
)
})
self.addEventListener('fetch', event => {
event.respondWith(caches.match(event.request)
.then((response) => {
if (response) {
return response;
}
return fetch(event.request)
.then(response => response);
}));
});
self.addEventListener('fetch', event => {
event.respondWith(caches.match(event.request)
.then((response) => {
if (response) {
return response;
}
return fetch(event.request)
.then(response => response);
}));
});
self.addEventListener('fetch', event => {
event.respondWith(caches.match(event.request)
.then((response) => {
if (response) {
return response;
}
return fetch(event.request)
.then(response => response);
}));
});
:(
AppCache
@JaffaTheCake
ServiceWorker > AppCache
...but it covers our use case
if('serviceWorker' in navigator) {
navigator.serviceWorker.register('/serviceworker.js')
} else if ('applicationCache' in window) {
// add AppCache
}
<html lang="en" manifest="/offline.appcache">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
...
<html lang="en" manifest="/offline.appcache">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
...
if('serviceWorker' in navigator) {
navigator.serviceWorker.register('/serviceworker.js')
} else if ('applicationCache' in window) {
// add AppCache
}
let iframe = document.createElement('iframe')
iframe.style.display = 'none'
iframe.src = '/load-appcache.html'
document.body.appendChild(iframe)
let iframe = document.createElement('iframe')
iframe.style.display = 'none'
iframe.src = '/load-appcache.html'
document.body.appendChild(iframe)
let iframe = document.createElement('iframe')
iframe.style.display = 'none'
iframe.src = '/load-appcache.html'
document.body.appendChild(iframe)
let iframe = document.createElement('iframe')
iframe.style.display = 'none'
iframe.src = '/load-appcache.html'
document.body.appendChild(iframe)
<html manifest="/offline.appcache">
<head>
<title>loading douchebags</title>
</head>
<body></body>
</html>
CACHE MANIFEST
/
/download
/css/main.css
/img/logo.svg
/js/build.js
NETWORK:
*
CACHE MANIFEST
/
/download
/css/main.css
/img/logo.svg
/js/build.js
NETWORK:
*
CACHE MANIFEST
/
/download
/css/main.css
/img/logo.svg
/js/build.js
NETWORK:
*
CACHE MANIFEST
/
/download
/css/main.css
/img/logo.svg
/js/build.js
NETWORK:
*
20.7 seconds
190 milliseconds
@JaffaTheCake
radical new way to
create web sites
radical new way to
update web sites
WebWorkers
commit 90b52e847359ae902d3f7ce7bc511cadfbc29ea8
Author: Alexey Proskuryakov <ap@webkit.org>
Date: Thu Nov 6 07:04:47 +0000
Implement Worker global object
2008!
WebWorkers?
single threaded
by default
everything is fighting
for CPU time
lots of number crunchin’
===
hardcore jank
Offload tasks to
background thread
super expensive fns
become pretty cheap
@PatrickKettner
@PatrickKettner
@PatrickKettner
https://github.com/nolan
lawson/pokedex.org
Web App Manifest
{
"lang": "en",
"dir": "ltr",
"name": "Super Racer 2000",
"description": "The ultimate futuristic racing game from the future!",
"short_name": "Racer2K",
"icons": [{
"src": "icon/lowres.webp",
"sizes": "64x64",
"type": "image/webp"
},{
"src": "icon/lowres.png",
"sizes": "64x64"
}, {
"src": "icon/hd_hi",
"sizes": "128x128"
}],
"scope": "/racer/",
"start_url": "/racer/start.html",
"display": "fullscreen",
"orientation": "landscape",
"theme_color": "aliceblue",
"background_color": "red"
}
{
"lang": "en",
"dir": "ltr",
"name": "Super Racer 2000",
"description": "The ultimate futuristic racing game from the future!",
"short_name": "Racer2K",
"icons": [{
"src": "icon/lowres.webp",
"sizes": "64x64",
"type": "image/webp"
},{
"src": "icon/lowres.png",
"sizes": "64x64"
}, {
"src": "icon/hd_hi",
"sizes": "128x128"
}],
"scope": "/racer/",
"start_url": "/racer/start.html",
"display": "fullscreen",
"orientation": "landscape",
"theme_color": "aliceblue",
"background_color": "red"
}
{
"lang": "en",
"dir": "ltr",
"name": "Super Racer 2000",
"description": "The ultimate futuristic racing game from the future!",
"short_name": "Racer2K",
"icons": [{
"src": "icon/lowres.webp",
"sizes": "64x64",
"type": "image/webp"
},{
"src": "icon/lowres.png",
"sizes": "64x64"
}, {
"src": "icon/hd_hi",
"sizes": "128x128"
}],
"scope": "/racer/",
"start_url": "/racer/start.html",
"display": "fullscreen",
"orientation": "landscape",
"theme_color": "aliceblue",
"background_color": "red"
}
{
"lang": "en",
"dir": "ltr",
"name": "Super Racer 2000",
"description": "The ultimate futuristic racing game from the future!",
"short_name": "Racer2K",
"icons": [{
"src": "icon/lowres.webp",
"sizes": "64x64",
"type": "image/webp"
},{
"src": "icon/lowres.png",
"sizes": "64x64"
}, {
"src": "icon/hd_hi",
"sizes": "128x128"
}],
"scope": "/racer/",
"start_url": "/racer/start.html",
"display": "fullscreen",
"orientation": "landscape",
"theme_color": "aliceblue",
"background_color": "red"
}
{
"lang": "en",
"dir": "ltr",
"name": "Super Racer 2000",
"description": "The ultimate futuristic racing game from the future!",
"short_name": "Racer2K",
"icons": [{
"src": "icon/lowres.webp",
"sizes": "64x64",
"type": "image/webp"
},{
"src": "icon/lowres.png",
"sizes": "64x64"
}, {
"src": "icon/hd_hi",
"sizes": "128x128"
}],
"scope": "/racer/",
"start_url": "/racer/start.html",
"display": "fullscreen",
"orientation": "landscape",
"theme_color": "aliceblue",
"background_color": "red"
}
{
"lang": "en",
"dir": "ltr",
"name": "Super Racer 2000",
"description": "The ultimate futuristic racing game from the future!",
"short_name": "Racer2K",
"icons": [{
"src": "icon/lowres.webp",
"sizes": "64x64",
"type": "image/webp"
},{
"src": "icon/lowres.png",
"sizes": "64x64"
}, {
"src": "icon/hd_hi",
"sizes": "128x128"
}],
"scope": "/racer/",
"start_url": "/racer/start.html",
"display": "fullscreen",
"orientation": "landscape",
"theme_color": "aliceblue",
"background_color": "red"
}
{
"lang": "en",
"dir": "ltr",
"name": "Super Racer 2000",
"description": "The ultimate futuristic racing game from the future!",
"short_name": "Racer2K",
"icons": [{
"src": "icon/lowres.webp",
"sizes": "64x64",
"type": "image/webp"
},{
"src": "icon/lowres.png",
"sizes": "64x64"
}, {
"src": "icon/hd_hi",
"sizes": "128x128"
}],
"scope": "/racer/",
"start_url": "/racer/start.html",
"display": "fullscreen",
"orientation": "landscape",
"theme_color": "aliceblue",
"background_color": "red"
}
{
"lang": "en",
"dir": "ltr",
"name": "Super Racer 2000",
"description": "The ultimate futuristic racing game from the future!",
"short_name": "Racer2K",
"icons": [{
"src": "icon/lowres.webp",
"sizes": "64x64",
"type": "image/webp"
},{
"src": "icon/lowres.png",
"sizes": "64x64"
}, {
"src": "icon/hd_hi",
"sizes": "128x128"
}],
"scope": "/racer/",
"start_url": "/racer/start.html",
"display": "fullscreen",
"orientation": "landscape",
"theme_color": "aliceblue",
"background_color": "red"
}
{
"lang": "en",
"dir": "ltr",
"name": "Super Racer 2000",
"description": "The ultimate futuristic racing game from the future!",
"short_name": "Racer2K",
"icons": [{
"src": "icon/lowres.webp",
"sizes": "64x64",
"type": "image/webp"
},{
"src": "icon/lowres.png",
"sizes": "64x64"
}, {
"src": "icon/hd_hi",
"sizes": "128x128"
}],
"scope": "/racer/",
"start_url": "/racer/start.html",
"display": "fullscreen",
"orientation": "landscape",
"theme_color": "aliceblue",
"background_color": "red"
}
"related_applications": [{
”platform": ”play",
”url": "https://play.google.com/store/apps/details?id=com.example.app1",
”id": "com.example.app1"
},{
"platform": ”itunes",
”url": "https://itunes.apple.com/app/example-app1/id123456789"
}, {
”platform": ”windows",
”url": "ms-windows-store://pdp?PFN=Example.App_pfhei292v8qza"
}],
"prefer_related_applications": false,
"related_applications": [{
”platform": ”play",
”url": "https://play.google.com/store/apps/details?id=com.example.app1",
”id": "com.example.app1"
},{
"platform": ”itunes",
”url": "https://itunes.apple.com/app/example-app1/id123456789"
}, {
”platform": ”windows",
”url": "ms-windows-store://pdp?PFN=Example.App_pfhei292v8qza"
}],
"prefer_related_applications": false,
<html lang=en dir=ltr>
<head>
<title>Racer2K</title>
<meta name=application-name content="Super Racer 2000”>
<meta name=description content="The ultimate futuristic game"/>
<meta name=apple-mobile-web-app-capable content=yes>
<link rel=manifest href="manifest.webmanifest">
<meta name=screen-orientation content=landscape>
<meta name=theme-color content=aliceblue>
<link rel=favicon href="icon/lowres.png">
<meta name="msApplication-PackageFamilyName" content="Example…
<link rel=alternate href="ios-app://id123456789/racer/begin">
<link rel=apple-touch-icon sizes=64x64 href="icon/lowres.png">
<link rel=apple-touch-icon sizes=128x128 href="icon/hd_hi.png">
<link rel=start href="/racer/start.html">
<html lang=en dir=ltr>
<head>
<title>Racer2K</title>
<meta name=application-name content="Super Racer 2000”>
<meta name=description content="The ultimate futuristic game”>
<meta name=apple-mobile-web-app-capable content=yes>
<link rel=manifest href="manifest.webmanifest">
<meta name=screen-orientation content=landscape>
<meta name=theme-color content=aliceblue>
<link rel=favicon href="icon/lowres.png">
<meta name="msApplication-PackageFamilyName" content="Example…
<link rel=alternate href="ios-app://id123456789/racer/begin">
<link rel=apple-touch-icon sizes=64x64 href="icon/lowres.png">
<link rel=apple-touch-icon sizes=128x128 href="icon/hd_hi.png">
<link rel=start href="/racer/start.html">
{
"lang": "en",
"dir": "ltr",
"name": "Super Racer 2000",
"description": "The ultimate futuristic racing game from the future!",
"short_name": "Racer2K",
"icons": [{
"src": "icon/lowres.webp",
"sizes": "64x64",
"type": "image/webp"
},{
"src": "icon/lowres.png",
"sizes": "64x64"
}, {
"src": "icon/hd_hi",
"sizes": "128x128"
}],
"scope": "/racer/",
"start_url": "/racer/start.html",
"display": "fullscreen",
"orientation": "landscape",
"theme_color": "aliceblue",
"background_color": "red"
}
<html lang=en dir=ltr>
<head>
<title>Racer2K</title>
<meta name=application-name content="Super Racer 2000”>
<meta name=description content="The ultimate futuristic game”>
<meta name=apple-mobile-web-app-capable content=yes>
<link rel=manifest href="manifest.webmanifest">
<meta name=screen-orientation content=landscape>
<meta name=theme-color content=aliceblue>
<link rel=favicon href="icon/lowres.png">
<meta name="msApplication-PackageFamilyName" content="Example…
<link rel=alternate href="ios-app://id123456789/racer/begin">
<link rel=apple-touch-icon sizes=64x64 href="icon/lowres.png">
<link rel=apple-touch-icon sizes=128x128 href="icon/hd_hi.png">
<link rel=start href="/racer/start.html">
<html lang=en dir=ltr>
<head>
<title>Racer2K</title>
<meta name=application-name content="Super Racer 2000”>
<meta name=description content="The ultimate futuristic game”>
<meta name=apple-mobile-web-app-capable content=yes>
<link rel=manifest href="manifest.webmanifest">
<meta name=screen-orientation content=landscape>
<meta name=theme-color content=aliceblue>
<link rel=favicon href="icon/lowres.png">
<meta name="msApplication-PackageFamilyName" content="Example…
<link rel=alternate href="ios-app://id123456789/racer/begin">
<link rel=apple-touch-icon sizes=64x64 href="icon/lowres.png">
<link rel=apple-touch-icon sizes=128x128 href="icon/hd_hi.png">
<link rel=start href="/racer/start.html">
let cheerio = require(’cheerio')
let cld = require('cld')
let getLang = (html, callback) => {
let $ = cheerio.load(html)
let lang = $('html[lang]').attr(‘lang')
if (!lang) {
lang = $('html[xml:lang]').attr(‘xml:lang’)
}
if (!lang) {
lang = $(’meta[name:”dc.language”]').attr(‘content’)
}
...
let cheerio = require(’cheerio')
let cld = require('cld')
let getLang = (html, callback) => {
let $ = cheerio.load(html)
let lang = $('html[lang]').attr(‘lang')
if (!lang) {
lang = $('html[xml:lang]').attr(‘xml:lang’)
}
if (!lang) {
lang = $(’meta[name:”dc.language”]').attr(‘content’)
}
...
let cheerio = require(’cheerio')
let cld = require('cld')
let getLang = (html, callback) => {
let $ = cheerio.load(html)
let lang = $('html[lang]').attr(‘lang')
if (!lang) {
lang = $('html[xml:lang]').attr(‘xml:lang’)
}
if (!lang) {
lang = $(’meta[name:”dc.language”]').attr(‘content’)
}
...
let cheerio = require(’cheerio')
let cld = require('cld')
let getLang = (html, callback) => {
let $ = cheerio.load(html)
let lang = $('html[lang]').attr(‘lang')
if (!lang) {
lang = $('html[xml:lang]').attr(‘xml:lang’)
}
if (!lang) {
lang = $(’meta[name:”dc.language”]').attr(‘content’)
}
...
let cheerio = require(’cheerio')
let cld = require('cld')
let getLang = (html, callback) => {
let $ = cheerio.load(html)
let lang = $('html[lang]').attr(‘lang')
if (!lang) {
lang = $('html[xml:lang]').attr(‘xml:lang’)
}
if (!lang) {
lang = $(’meta[name:”dc.language”]').attr(‘content’)
}
...
let cheerio = require(’cheerio')
let cld = require('cld')
let getLang = (html, callback) => {
let $ = cheerio.load(html)
let lang = $('html[lang]').attr(‘lang')
if (!lang) {
lang = $('html[xml:lang]').attr(‘xml:lang’)
}
if (!lang) {
lang = $(’meta[name:”dc.language”]').attr(‘content’)
}
...
cld.detect(html,{isHTML: true},(err, result) => {
if (!err && result.reliable) {
lang = result.language.code
}
callback(err, lang)
}
let url = require(’url')
let async = require(’async’)
let icojs = require(’icojs')
let cheerio = require(’cheerio')
let request= require(’request')
let imgsize = require(’image-size')
let imgtype = require(’image-type')
let mime = require(’./mime-types’).lookup
let FilteType = require(’file-type')
let flatten = require(’lodash/flattenDeep')
let filter = require(’lodash/filter')
let icons = (obj, callback) => {
let $ = cheerio.load(html)
let favicon = $(’rel[“shortcut icon”]').attr(‘href')
<link rel=favicon href="icon.ico">
<link rel=apple-touch-icon sizes=64x64 href="icon/lowres.png">
<link rel=apple-touch-icon sizes=128x128 href="icon/hd_hi.png”><meta
name="msApplication-TileImage" content=”icon/tile.png”>
<meta name="msApplication-config" content=”tileConfig.xml”>
<link rel=favicon href="icon.ico">
<link rel=apple-touch-icon sizes=64x64 href="icon/lowres.png">
<link rel=apple-touch-icon sizes=128x128 href="icon/hd_hi.png">
<meta name="msApplication-TileImage" content=”icon/tile.png”>
<meta name="msApplication-config" content=”tileConfig.xml”>
<link rel=favicon href="icon.ico">
<link rel=apple-touch-icon sizes=64x64 href="icon/lowres.png">
<link rel=apple-touch-icon sizes=128x128 href="icon/hd_hi.png">
<meta name="msApplication-TileImage" content=”icon/tile.png”>
<meta name="msApplication-config" content=”tileConfig.xml”>
<link rel=favicon href="icon.ico">
<link rel=apple-touch-icon sizes=64x64 href="icon/lowres.png">
<link rel=apple-touch-icon sizes=128x128 href="icon/hd_hi.png">
<meta name="msApplication-TileImage" content=”icon/tile.png”>
<meta name="msApplication-config" content=”tileConfig.xml”>
https://WebManife.st
<html lang=en dir=ltr>
<head>
<title>Racer2K</title>
<meta name=application-name content="Super Racer 2000”>
<meta name=description content="The ultimate futuristic game"/>
<meta name=apple-mobile-web-app-capable content=yes>
<link rel=manifest href="manifest.webmanifest">
<meta name=screen-orientation content=landscape>
<meta name=theme-color content=aliceblue>
<link rel=favicon href="icon/lowres.png">
<meta name="msApplication-PackageFamilyName" content="Example…
<link rel=alternate href="ios-app://id123456789/racer/begin">
<link rel=apple-touch-icon sizes=64x64 href="icon/lowres.png">
<link rel=apple-touch-icon sizes=128x128 href="icon/hd_hi.png">
<link rel=start href="/racer/start.html">
https://WebManife.st/validator
https://WebManife.st/spec
<html lang=en dir=ltr>
<head>
<title>Racer2K</title>
<meta name=application-name content="Super Racer 2000”>
<meta name=description content="The ultimate futuristic game"/>
<meta name=apple-mobile-web-app-capable content=yes>
<link rel=manifest href="manifest.webmanifest">
<meta name=screen-orientation content=landscape>
<meta name=theme-color content=aliceblue>
<link rel=favicon href="icon/lowres.png">
<meta name="msApplication-PackageFamilyName" content="Example…
<link rel=alternate href="ios-app://id123456789/racer/begin">
<link rel=apple-touch-icon sizes=64x64 href="icon/lowres.png">
<link rel=apple-touch-icon sizes=128x128 href="icon/hd_hi.png">
<link rel=start href="/racer/start.html">
let methodData = [{
supportedMethods: [’basic-card'],
data: {
supportedNetworks: [’visa', ’mastercard', ’amex'],
supportedTypes: [’credit']
}
}];
let details = {
total: {
label: ’Total Due',
amount: { currency: ’USD', value : ’109.00' }
}};
var options = {
requestShipping: true
};
let methodData = [{
supportedMethods: [’basic-card'],
data: {
supportedNetworks: [’visa', ’mastercard', ’amex'],
supportedTypes: [’credit']
}
}];
let details = {
total: {
label: ’Total Due',
amount: { currency: ’USD', value : ’109.00' }
}};
var options = {
requestShipping: true
};
let methodData = [{
supportedMethods: [’basic-card'],
data: {
supportedNetworks: [’visa', ’mastercard', ’amex'],
supportedTypes: [’credit']
}
}];
let details = {
total: {
label: ’Total Due',
amount: { currency: ’USD', value : ’109.00' }
}};
var options = {
requestShipping: true
};
if (’PaymentRequest’ in window) {
let payment = new PaymentRequest(methodData, details, options)
}
if (’PaymentRequest’ in window) {
let payment = new PaymentRequest(methodData, details, options)
} else {
window.location.href = ‘/old-checkout’
}
if (’Windows’ in window) {
...
}
bluetooth
Windows​.Devices​.Bluetooth
media keys
Windows.​Media.​Dial​Protocol
ocr
Windows.​Media.​Ocr
cortana
Windows​.ApplicationModel​.VoiceCommands
IndexedDB
commit 4334db65c2718d304d4792fbeb46f96e9d798b76
Author: Ben Turner <bent@mozilla.com>
Date: Wed Jun 23 19:46:00 2010 +0000
Implement Asynchronous parts of the Indexed Database
API
localStorage.setItem(’foo', ’bar');
localStorage.getItem(’foo’) //bar
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
if (!window.indexedDB) {
alert("Sorry!Your browser doesn't support IndexedDB");
}
var database;
var request = window.indexedDB.open("foobar",1);
request.onerror = function(event) {
console.log(event.target.errorCode);
};
request.onsuccess = function(event) {
database=request.result;
};
request.onupgradeneeded = function(event) {
var db = event.target.result;
var objectStore = db.createObjectStore("notes", { keyPath: "id",autoIncrement:true});
};
var note={title:"foo", body:"bar", date:”06/21/2016”};
var transaction = database.transaction(["hello"], "readwrite");
var objectStore = transaction.objectStore("world");
var request=objectStore.put(note);
request.onsuccess = function(event) {
//do something here
};
var objectStore = database.transaction("hello").objectStore("world");
objectStore.openCursor().onsuccess = function(event) {
var cursor = event.target.result;
if (cursor) {
alert("Note id: "+cursor.key+", Title: "+cursor.value.title);
cursor.continue();
}
};
window.name
document.cookie
Flash Cache
IndexedDB
localStorage
AppCache
Cache API
WebSQL
window.name
document.cookie
Flash Cache
IndexedDB
localStorage
AppCache
Cache API
WebSQL
window.name
document.cookie
Flash Cache
IndexedDB
localStorage
AppCache
Cache API
WebSQL
window.name
document.cookie
Flash Cache
IndexedDB
localStorage
AppCache
Cache API
WebSQL
var db = new Dexie('MyDatabase');
db.friends
.where(‘age’)
.above(75)
.each((friend) => {
console.log(friend.name);
});
localForage.setItem(’foo', ’bar');
localForage.getItem(’foo’, (err, val) => {
console.log(val)
}
))
with a G
demo.agektmr.com/storage
don’t wait for a rearch
use the new shiny.
make awesome stuff.
github.com/PatrickKettner
twitter.com/PatrickKettner
facebook.com/PatrickKettner
PatKet@microsoft.com
Thanks!

Progressive What Apps?

  • 1.
  • 2.
  • 13.
  • 18.
    2G Ruined MyDay45M Ruined My Day
  • 21.
    When you don’thave a better idea, buy a novelty domain
  • 22.
  • 23.
  • 24.
    let CURRENT_CACHE ={ prefetch: 'prefetch-cache-v1' }; self.addEventListener('install', event => { let prefetch = ['/T0t411y-R4D-Sans'] event.waitUntil(caches.open(CURRENT_CACHE['prefetch']) .then(cache => cache.addAll(prefetch.map((url) => new Request(url, { 'mode': 'no-cors' }) )) ) ) })
  • 25.
    let CURRENT_CACHE ={ prefetch: 'prefetch-cache-v1' }; self.addEventListener('install', event => { let prefetch = ['/T0t411y-R4D-Sans'] event.waitUntil(caches.open(CURRENT_CACHE['prefetch']) .then(cache => cache.addAll(prefetch.map((url) => new Request(url, { 'mode': 'no-cors' }) )) ) ) })
  • 26.
    let CURRENT_CACHE ={ prefetch: 'prefetch-cache-v1' }; self.addEventListener('install', event => { let prefetch = ['/T0t411y-R4D-Sans'] event.waitUntil(caches.open(CURRENT_CACHE['prefetch']) .then(cache => cache.addAll(prefetch.map((url) => new Request(url, { 'mode': 'no-cors' }) )) ) ) })
  • 27.
    let CURRENT_CACHE ={ prefetch: 'prefetch-cache-v1' }; self.addEventListener('install', event => { let prefetch = ['/T0t411y-R4D-Sans'] event.waitUntil(caches.open(CURRENT_CACHE['prefetch']) .then(cache => cache.addAll(prefetch.map((url) => new Request(url, { 'mode': 'no-cors' }) )) ) ) })
  • 28.
    let CURRENT_CACHE ={ prefetch: 'prefetch-cache-v1' }; self.addEventListener('install', event => { let prefetch = ['/T0t411y-R4D-Sans'] event.waitUntil(caches.open(CURRENT_CACHE['prefetch']) .then(cache => cache.addAll(prefetch.map((url) => new Request(url, { 'mode': 'no-cors' }) )) ) ) })
  • 29.
    let CURRENT_CACHE ={ prefetch: 'prefetch-cache-v1' }; self.addEventListener('install', event => { let prefetch = ['/T0t411y-R4D-Sans'] event.waitUntil(caches.open(CURRENT_CACHE['prefetch']) .then(cache => cache.addAll(prefetch.map((url) => new Request(url, { 'mode': 'no-cors' }) )) ) ) })
  • 30.
    self.addEventListener('fetch', event =>{ event.respondWith(caches.match(event.request) .then((response) => { if (response) { return response; } return fetch(event.request) .then(response => response); })); });
  • 31.
    self.addEventListener('fetch', event =>{ event.respondWith(caches.match(event.request) .then((response) => { if (response) { return response; } return fetch(event.request) .then(response => response); })); });
  • 32.
    self.addEventListener('fetch', event =>{ event.respondWith(caches.match(event.request) .then((response) => { if (response) { return response; } return fetch(event.request) .then(response => response); })); });
  • 36.
  • 37.
  • 38.
  • 39.
  • 41.
    ...but it coversour use case
  • 43.
    if('serviceWorker' in navigator){ navigator.serviceWorker.register('/serviceworker.js') } else if ('applicationCache' in window) { // add AppCache }
  • 44.
    <html lang="en" manifest="/offline.appcache"> <head> <metacharset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> ...
  • 45.
    <html lang="en" manifest="/offline.appcache"> <head> <metacharset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> ...
  • 46.
    if('serviceWorker' in navigator){ navigator.serviceWorker.register('/serviceworker.js') } else if ('applicationCache' in window) { // add AppCache }
  • 47.
    let iframe =document.createElement('iframe') iframe.style.display = 'none' iframe.src = '/load-appcache.html' document.body.appendChild(iframe)
  • 48.
    let iframe =document.createElement('iframe') iframe.style.display = 'none' iframe.src = '/load-appcache.html' document.body.appendChild(iframe)
  • 49.
    let iframe =document.createElement('iframe') iframe.style.display = 'none' iframe.src = '/load-appcache.html' document.body.appendChild(iframe)
  • 50.
    let iframe =document.createElement('iframe') iframe.style.display = 'none' iframe.src = '/load-appcache.html' document.body.appendChild(iframe)
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 62.
    radical new wayto create web sites
  • 63.
    radical new wayto update web sites
  • 64.
  • 65.
    commit 90b52e847359ae902d3f7ce7bc511cadfbc29ea8 Author: AlexeyProskuryakov <ap@webkit.org> Date: Thu Nov 6 07:04:47 +0000 Implement Worker global object
  • 66.
  • 71.
  • 72.
  • 73.
  • 74.
    lots of numbercrunchin’ === hardcore jank
  • 75.
  • 76.
  • 83.
  • 84.
  • 86.
  • 87.
  • 88.
  • 90.
    { "lang": "en", "dir": "ltr", "name":"Super Racer 2000", "description": "The ultimate futuristic racing game from the future!", "short_name": "Racer2K", "icons": [{ "src": "icon/lowres.webp", "sizes": "64x64", "type": "image/webp" },{ "src": "icon/lowres.png", "sizes": "64x64" }, { "src": "icon/hd_hi", "sizes": "128x128" }], "scope": "/racer/", "start_url": "/racer/start.html", "display": "fullscreen", "orientation": "landscape", "theme_color": "aliceblue", "background_color": "red" }
  • 91.
    { "lang": "en", "dir": "ltr", "name":"Super Racer 2000", "description": "The ultimate futuristic racing game from the future!", "short_name": "Racer2K", "icons": [{ "src": "icon/lowres.webp", "sizes": "64x64", "type": "image/webp" },{ "src": "icon/lowres.png", "sizes": "64x64" }, { "src": "icon/hd_hi", "sizes": "128x128" }], "scope": "/racer/", "start_url": "/racer/start.html", "display": "fullscreen", "orientation": "landscape", "theme_color": "aliceblue", "background_color": "red" }
  • 92.
    { "lang": "en", "dir": "ltr", "name":"Super Racer 2000", "description": "The ultimate futuristic racing game from the future!", "short_name": "Racer2K", "icons": [{ "src": "icon/lowres.webp", "sizes": "64x64", "type": "image/webp" },{ "src": "icon/lowres.png", "sizes": "64x64" }, { "src": "icon/hd_hi", "sizes": "128x128" }], "scope": "/racer/", "start_url": "/racer/start.html", "display": "fullscreen", "orientation": "landscape", "theme_color": "aliceblue", "background_color": "red" }
  • 93.
    { "lang": "en", "dir": "ltr", "name":"Super Racer 2000", "description": "The ultimate futuristic racing game from the future!", "short_name": "Racer2K", "icons": [{ "src": "icon/lowres.webp", "sizes": "64x64", "type": "image/webp" },{ "src": "icon/lowres.png", "sizes": "64x64" }, { "src": "icon/hd_hi", "sizes": "128x128" }], "scope": "/racer/", "start_url": "/racer/start.html", "display": "fullscreen", "orientation": "landscape", "theme_color": "aliceblue", "background_color": "red" }
  • 94.
    { "lang": "en", "dir": "ltr", "name":"Super Racer 2000", "description": "The ultimate futuristic racing game from the future!", "short_name": "Racer2K", "icons": [{ "src": "icon/lowres.webp", "sizes": "64x64", "type": "image/webp" },{ "src": "icon/lowres.png", "sizes": "64x64" }, { "src": "icon/hd_hi", "sizes": "128x128" }], "scope": "/racer/", "start_url": "/racer/start.html", "display": "fullscreen", "orientation": "landscape", "theme_color": "aliceblue", "background_color": "red" }
  • 95.
    { "lang": "en", "dir": "ltr", "name":"Super Racer 2000", "description": "The ultimate futuristic racing game from the future!", "short_name": "Racer2K", "icons": [{ "src": "icon/lowres.webp", "sizes": "64x64", "type": "image/webp" },{ "src": "icon/lowres.png", "sizes": "64x64" }, { "src": "icon/hd_hi", "sizes": "128x128" }], "scope": "/racer/", "start_url": "/racer/start.html", "display": "fullscreen", "orientation": "landscape", "theme_color": "aliceblue", "background_color": "red" }
  • 96.
    { "lang": "en", "dir": "ltr", "name":"Super Racer 2000", "description": "The ultimate futuristic racing game from the future!", "short_name": "Racer2K", "icons": [{ "src": "icon/lowres.webp", "sizes": "64x64", "type": "image/webp" },{ "src": "icon/lowres.png", "sizes": "64x64" }, { "src": "icon/hd_hi", "sizes": "128x128" }], "scope": "/racer/", "start_url": "/racer/start.html", "display": "fullscreen", "orientation": "landscape", "theme_color": "aliceblue", "background_color": "red" }
  • 97.
    { "lang": "en", "dir": "ltr", "name":"Super Racer 2000", "description": "The ultimate futuristic racing game from the future!", "short_name": "Racer2K", "icons": [{ "src": "icon/lowres.webp", "sizes": "64x64", "type": "image/webp" },{ "src": "icon/lowres.png", "sizes": "64x64" }, { "src": "icon/hd_hi", "sizes": "128x128" }], "scope": "/racer/", "start_url": "/racer/start.html", "display": "fullscreen", "orientation": "landscape", "theme_color": "aliceblue", "background_color": "red" }
  • 98.
    { "lang": "en", "dir": "ltr", "name":"Super Racer 2000", "description": "The ultimate futuristic racing game from the future!", "short_name": "Racer2K", "icons": [{ "src": "icon/lowres.webp", "sizes": "64x64", "type": "image/webp" },{ "src": "icon/lowres.png", "sizes": "64x64" }, { "src": "icon/hd_hi", "sizes": "128x128" }], "scope": "/racer/", "start_url": "/racer/start.html", "display": "fullscreen", "orientation": "landscape", "theme_color": "aliceblue", "background_color": "red" }
  • 99.
    "related_applications": [{ ”platform": ”play", ”url":"https://play.google.com/store/apps/details?id=com.example.app1", ”id": "com.example.app1" },{ "platform": ”itunes", ”url": "https://itunes.apple.com/app/example-app1/id123456789" }, { ”platform": ”windows", ”url": "ms-windows-store://pdp?PFN=Example.App_pfhei292v8qza" }], "prefer_related_applications": false,
  • 100.
    "related_applications": [{ ”platform": ”play", ”url":"https://play.google.com/store/apps/details?id=com.example.app1", ”id": "com.example.app1" },{ "platform": ”itunes", ”url": "https://itunes.apple.com/app/example-app1/id123456789" }, { ”platform": ”windows", ”url": "ms-windows-store://pdp?PFN=Example.App_pfhei292v8qza" }], "prefer_related_applications": false,
  • 101.
    <html lang=en dir=ltr> <head> <title>Racer2K</title> <metaname=application-name content="Super Racer 2000”> <meta name=description content="The ultimate futuristic game"/> <meta name=apple-mobile-web-app-capable content=yes> <link rel=manifest href="manifest.webmanifest"> <meta name=screen-orientation content=landscape> <meta name=theme-color content=aliceblue> <link rel=favicon href="icon/lowres.png"> <meta name="msApplication-PackageFamilyName" content="Example… <link rel=alternate href="ios-app://id123456789/racer/begin"> <link rel=apple-touch-icon sizes=64x64 href="icon/lowres.png"> <link rel=apple-touch-icon sizes=128x128 href="icon/hd_hi.png"> <link rel=start href="/racer/start.html">
  • 102.
    <html lang=en dir=ltr> <head> <title>Racer2K</title> <metaname=application-name content="Super Racer 2000”> <meta name=description content="The ultimate futuristic game”> <meta name=apple-mobile-web-app-capable content=yes> <link rel=manifest href="manifest.webmanifest"> <meta name=screen-orientation content=landscape> <meta name=theme-color content=aliceblue> <link rel=favicon href="icon/lowres.png"> <meta name="msApplication-PackageFamilyName" content="Example… <link rel=alternate href="ios-app://id123456789/racer/begin"> <link rel=apple-touch-icon sizes=64x64 href="icon/lowres.png"> <link rel=apple-touch-icon sizes=128x128 href="icon/hd_hi.png"> <link rel=start href="/racer/start.html">
  • 103.
    { "lang": "en", "dir": "ltr", "name":"Super Racer 2000", "description": "The ultimate futuristic racing game from the future!", "short_name": "Racer2K", "icons": [{ "src": "icon/lowres.webp", "sizes": "64x64", "type": "image/webp" },{ "src": "icon/lowres.png", "sizes": "64x64" }, { "src": "icon/hd_hi", "sizes": "128x128" }], "scope": "/racer/", "start_url": "/racer/start.html", "display": "fullscreen", "orientation": "landscape", "theme_color": "aliceblue", "background_color": "red" }
  • 104.
    <html lang=en dir=ltr> <head> <title>Racer2K</title> <metaname=application-name content="Super Racer 2000”> <meta name=description content="The ultimate futuristic game”> <meta name=apple-mobile-web-app-capable content=yes> <link rel=manifest href="manifest.webmanifest"> <meta name=screen-orientation content=landscape> <meta name=theme-color content=aliceblue> <link rel=favicon href="icon/lowres.png"> <meta name="msApplication-PackageFamilyName" content="Example… <link rel=alternate href="ios-app://id123456789/racer/begin"> <link rel=apple-touch-icon sizes=64x64 href="icon/lowres.png"> <link rel=apple-touch-icon sizes=128x128 href="icon/hd_hi.png"> <link rel=start href="/racer/start.html">
  • 105.
    <html lang=en dir=ltr> <head> <title>Racer2K</title> <metaname=application-name content="Super Racer 2000”> <meta name=description content="The ultimate futuristic game”> <meta name=apple-mobile-web-app-capable content=yes> <link rel=manifest href="manifest.webmanifest"> <meta name=screen-orientation content=landscape> <meta name=theme-color content=aliceblue> <link rel=favicon href="icon/lowres.png"> <meta name="msApplication-PackageFamilyName" content="Example… <link rel=alternate href="ios-app://id123456789/racer/begin"> <link rel=apple-touch-icon sizes=64x64 href="icon/lowres.png"> <link rel=apple-touch-icon sizes=128x128 href="icon/hd_hi.png"> <link rel=start href="/racer/start.html">
  • 107.
    let cheerio =require(’cheerio') let cld = require('cld') let getLang = (html, callback) => { let $ = cheerio.load(html) let lang = $('html[lang]').attr(‘lang') if (!lang) { lang = $('html[xml:lang]').attr(‘xml:lang’) } if (!lang) { lang = $(’meta[name:”dc.language”]').attr(‘content’) } ...
  • 108.
    let cheerio =require(’cheerio') let cld = require('cld') let getLang = (html, callback) => { let $ = cheerio.load(html) let lang = $('html[lang]').attr(‘lang') if (!lang) { lang = $('html[xml:lang]').attr(‘xml:lang’) } if (!lang) { lang = $(’meta[name:”dc.language”]').attr(‘content’) } ...
  • 109.
    let cheerio =require(’cheerio') let cld = require('cld') let getLang = (html, callback) => { let $ = cheerio.load(html) let lang = $('html[lang]').attr(‘lang') if (!lang) { lang = $('html[xml:lang]').attr(‘xml:lang’) } if (!lang) { lang = $(’meta[name:”dc.language”]').attr(‘content’) } ...
  • 110.
    let cheerio =require(’cheerio') let cld = require('cld') let getLang = (html, callback) => { let $ = cheerio.load(html) let lang = $('html[lang]').attr(‘lang') if (!lang) { lang = $('html[xml:lang]').attr(‘xml:lang’) } if (!lang) { lang = $(’meta[name:”dc.language”]').attr(‘content’) } ...
  • 111.
    let cheerio =require(’cheerio') let cld = require('cld') let getLang = (html, callback) => { let $ = cheerio.load(html) let lang = $('html[lang]').attr(‘lang') if (!lang) { lang = $('html[xml:lang]').attr(‘xml:lang’) } if (!lang) { lang = $(’meta[name:”dc.language”]').attr(‘content’) } ...
  • 112.
    let cheerio =require(’cheerio') let cld = require('cld') let getLang = (html, callback) => { let $ = cheerio.load(html) let lang = $('html[lang]').attr(‘lang') if (!lang) { lang = $('html[xml:lang]').attr(‘xml:lang’) } if (!lang) { lang = $(’meta[name:”dc.language”]').attr(‘content’) } ...
  • 113.
    cld.detect(html,{isHTML: true},(err, result)=> { if (!err && result.reliable) { lang = result.language.code } callback(err, lang) }
  • 114.
    let url =require(’url') let async = require(’async’) let icojs = require(’icojs') let cheerio = require(’cheerio') let request= require(’request') let imgsize = require(’image-size') let imgtype = require(’image-type') let mime = require(’./mime-types’).lookup let FilteType = require(’file-type') let flatten = require(’lodash/flattenDeep') let filter = require(’lodash/filter') let icons = (obj, callback) => { let $ = cheerio.load(html) let favicon = $(’rel[“shortcut icon”]').attr(‘href')
  • 115.
    <link rel=favicon href="icon.ico"> <linkrel=apple-touch-icon sizes=64x64 href="icon/lowres.png"> <link rel=apple-touch-icon sizes=128x128 href="icon/hd_hi.png”><meta name="msApplication-TileImage" content=”icon/tile.png”> <meta name="msApplication-config" content=”tileConfig.xml”>
  • 116.
    <link rel=favicon href="icon.ico"> <linkrel=apple-touch-icon sizes=64x64 href="icon/lowres.png"> <link rel=apple-touch-icon sizes=128x128 href="icon/hd_hi.png"> <meta name="msApplication-TileImage" content=”icon/tile.png”> <meta name="msApplication-config" content=”tileConfig.xml”>
  • 117.
    <link rel=favicon href="icon.ico"> <linkrel=apple-touch-icon sizes=64x64 href="icon/lowres.png"> <link rel=apple-touch-icon sizes=128x128 href="icon/hd_hi.png"> <meta name="msApplication-TileImage" content=”icon/tile.png”> <meta name="msApplication-config" content=”tileConfig.xml”>
  • 118.
    <link rel=favicon href="icon.ico"> <linkrel=apple-touch-icon sizes=64x64 href="icon/lowres.png"> <link rel=apple-touch-icon sizes=128x128 href="icon/hd_hi.png"> <meta name="msApplication-TileImage" content=”icon/tile.png”> <meta name="msApplication-config" content=”tileConfig.xml”>
  • 119.
  • 122.
    <html lang=en dir=ltr> <head> <title>Racer2K</title> <metaname=application-name content="Super Racer 2000”> <meta name=description content="The ultimate futuristic game"/> <meta name=apple-mobile-web-app-capable content=yes> <link rel=manifest href="manifest.webmanifest"> <meta name=screen-orientation content=landscape> <meta name=theme-color content=aliceblue> <link rel=favicon href="icon/lowres.png"> <meta name="msApplication-PackageFamilyName" content="Example… <link rel=alternate href="ios-app://id123456789/racer/begin"> <link rel=apple-touch-icon sizes=64x64 href="icon/lowres.png"> <link rel=apple-touch-icon sizes=128x128 href="icon/hd_hi.png"> <link rel=start href="/racer/start.html">
  • 123.
  • 124.
    <html lang=en dir=ltr> <head> <title>Racer2K</title> <metaname=application-name content="Super Racer 2000”> <meta name=description content="The ultimate futuristic game"/> <meta name=apple-mobile-web-app-capable content=yes> <link rel=manifest href="manifest.webmanifest"> <meta name=screen-orientation content=landscape> <meta name=theme-color content=aliceblue> <link rel=favicon href="icon/lowres.png"> <meta name="msApplication-PackageFamilyName" content="Example… <link rel=alternate href="ios-app://id123456789/racer/begin"> <link rel=apple-touch-icon sizes=64x64 href="icon/lowres.png"> <link rel=apple-touch-icon sizes=128x128 href="icon/hd_hi.png"> <link rel=start href="/racer/start.html">
  • 133.
    let methodData =[{ supportedMethods: [’basic-card'], data: { supportedNetworks: [’visa', ’mastercard', ’amex'], supportedTypes: [’credit'] } }]; let details = { total: { label: ’Total Due', amount: { currency: ’USD', value : ’109.00' } }}; var options = { requestShipping: true };
  • 134.
    let methodData =[{ supportedMethods: [’basic-card'], data: { supportedNetworks: [’visa', ’mastercard', ’amex'], supportedTypes: [’credit'] } }]; let details = { total: { label: ’Total Due', amount: { currency: ’USD', value : ’109.00' } }}; var options = { requestShipping: true };
  • 135.
    let methodData =[{ supportedMethods: [’basic-card'], data: { supportedNetworks: [’visa', ’mastercard', ’amex'], supportedTypes: [’credit'] } }]; let details = { total: { label: ’Total Due', amount: { currency: ’USD', value : ’109.00' } }}; var options = { requestShipping: true };
  • 136.
    if (’PaymentRequest’ inwindow) { let payment = new PaymentRequest(methodData, details, options) }
  • 137.
    if (’PaymentRequest’ inwindow) { let payment = new PaymentRequest(methodData, details, options) } else { window.location.href = ‘/old-checkout’ }
  • 140.
    if (’Windows’ inwindow) { ... }
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
    commit 4334db65c2718d304d4792fbeb46f96e9d798b76 Author: BenTurner <bent@mozilla.com> Date: Wed Jun 23 19:46:00 2010 +0000 Implement Asynchronous parts of the Indexed Database API
  • 147.
  • 148.
    window.indexedDB = window.indexedDB|| window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction; window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange; if (!window.indexedDB) { alert("Sorry!Your browser doesn't support IndexedDB"); } var database; var request = window.indexedDB.open("foobar",1); request.onerror = function(event) { console.log(event.target.errorCode); }; request.onsuccess = function(event) { database=request.result; }; request.onupgradeneeded = function(event) { var db = event.target.result; var objectStore = db.createObjectStore("notes", { keyPath: "id",autoIncrement:true}); }; var note={title:"foo", body:"bar", date:”06/21/2016”}; var transaction = database.transaction(["hello"], "readwrite"); var objectStore = transaction.objectStore("world"); var request=objectStore.put(note); request.onsuccess = function(event) { //do something here }; var objectStore = database.transaction("hello").objectStore("world"); objectStore.openCursor().onsuccess = function(event) { var cursor = event.target.result; if (cursor) { alert("Note id: "+cursor.key+", Title: "+cursor.value.title); cursor.continue(); } };
  • 149.
  • 150.
  • 151.
  • 152.
  • 154.
    var db =new Dexie('MyDatabase'); db.friends .where(‘age’) .above(75) .each((friend) => { console.log(friend.name); });
  • 156.
  • 158.
  • 160.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.

Editor's Notes

  • #24  I know what you are thinking
  • #25  This is what it looks like Define a cache – you can have separate caches in SWs, one for blog content, another for comments, whatever Listen for the install event Prefetch our font, and anything else we know is guaranteed to want Wait until our cache is opened up, then we request each and every one of those files and add them to our cache
  • #26  This is what it looks like Define a cache – you can have separate caches in SWs, one for blog content, another for comments, whatever Listen for the install event Prefetch our font, and anything else we know is guaranteed to want Wait until our cache is opened up, then we request each and every one of those files and add them to our cache
  • #27  This is what it looks like Define a cache – you can have separate caches in SWs, one for blog content, another for comments, whatever Listen for the install event Prefetch our font, and anything else we know is guaranteed to want Wait until our cache is opened up, then we request each and every one of those files and add them to our cache
  • #28  This is what it looks like Define a cache – you can have separate caches in SWs, one for blog content, another for comments, whatever Listen for the install event Prefetch our font, and anything else we know is guaranteed to want Wait until our cache is opened up, then we request each and every one of those files and add them to our cache
  • #29  This is what it looks like Define a cache – you can have separate caches in SWs, one for blog content, another for comments, whatever Listen for the install event Prefetch our font, and anything else we know is guaranteed to want Wait until our cache is opened up, then we request each and every one of those files and add them to our cache
  • #30  This is what it looks like Define a cache – you can have separate caches in SWs, one for blog content, another for comments, whatever Listen for the install event Prefetch our font, and anything else we know is guaranteed to want Wait until our cache is opened up, then we request each and every one of those files and add them to our cache
  • #31  After we do that, we set up listener for FETCH event Every time anything on the site is requested from our domain, we see if it is in the cache Got it? Give it Don’t? go get it Instantaneous responses, even with dozens upon dozens of requests
  • #32  After we do that, we set up listener for FETCH event Every time anything on the site is requested from our domain, we see if it is in the cache Got it? Give it Don’t? go get it Instantaneous responses, even with dozens upon dozens of requests
  • #33  After we do that, we set up listener for FETCH event Every time anything on the site is requested from our domain, we see if it is in the cache Got it? Give it Don’t? go get it Instantaneous responses, even with dozens upon dozens of requests
  • #36  It IS the future, but support is pretty meh
  • #37  Which sucks, beause I want something today – not a year or three from now If only we had something widely supported today…
  • #38  I know what you are thinking
  • #39  Appcache is a douchebag Yes – it really is. It sucksin a lot of ways
  • #40  SW are WAY better, except for support
  • #41  SW are WAY better, except for support
  • #42  And it covers our basic usage of instant cache reading
  • #43  {{40% done – 20 minutes}} Appcache is everywhere
  • #44  Do basic namespace check
  • #45  You put it in your head
  • #46  You can’t add it programmatically. It HAS to be there on load
  • #47  Do basic namespace check
  • #48  We just load it in ANOTHER page Create iframe, load tiny page that is just the appcache manifest
  • #49  We just load it in ANOTHER page Create iframe, load tiny page that is just the appcache manifest
  • #50  We just load it in ANOTHER page Create iframe, load tiny page that is just the appcache manifest
  • #51  We just load it in ANOTHER page Create iframe, load tiny page that is just the appcache manifest
  • #52  Appcache takes over domain After initial load, firefox - NEXT
  • #53  Appcache is manifest driven Its just a big text file, with .appcache extension looks like this Blob of text that lists files, and a few rules Dunders are variables that we use to generate this file programatically Cache version important to break cache – just date epoch Assets is list of files to be added to the precache list
  • #54  Appcache is manifest driven Its just a big text file, with .appcache extension looks like this Blob of text that lists files, and a few rules Dunders are variables that we use to generate this file programatically Cache version important to break cache – just date epoch Assets is list of files to be added to the precache list
  • #55  Appcache is manifest driven Its just a big text file, with .appcache extension looks like this Blob of text that lists files, and a few rules Dunders are variables that we use to generate this file programatically Cache version important to break cache – just date epoch Assets is list of files to be added to the precache list
  • #56  Appcache is manifest driven Its just a big text file, with .appcache extension looks like this Blob of text that lists files, and a few rules Dunders are variables that we use to generate this file programatically Cache version important to break cache – just date epoch Assets is list of files to be added to the precache list
  • #59  Appcache is a douchebag Yes – it really is. It sucksin a lot of ways
  • #66  Added to webkit ri in 2008
  • #68  Chroem was in version 2!
  • #69  Chroem was in version 2!
  • #70  Chroem was in version 2!
  • #71  Chroem was in version 2!
  • #72  So what are they? It is a way to get around some browser limitations without breaking the web
  • #73  Means browser can only do one thing at a time Browser doesn’t know you won’t change page in scroll loop, So it has to execute code before it can paint the result of the scroll
  • #75  Scroll handlers, runaway scripts, underpowred machines… Crappy user expierence
  • #76  Webworkers lets us take certain tasks, fence them off from the main thread of the browser, and have the browser opportunistically execute that code without effecting the page’s painting
  • #91  Appcache takes over domain After initial load, firefox - NEXT
  • #92  Appcache takes over domain After initial load, firefox - NEXT
  • #93  Appcache takes over domain After initial load, firefox - NEXT
  • #94  Appcache takes over domain After initial load, firefox - NEXT
  • #95  Appcache takes over domain After initial load, firefox - NEXT
  • #96  Appcache takes over domain After initial load, firefox - NEXT
  • #97  Appcache takes over domain After initial load, firefox - NEXT
  • #98  Appcache takes over domain After initial load, firefox - NEXT
  • #99  Appcache takes over domain After initial load, firefox - NEXT
  • #100  Appcache takes over domain After initial load, firefox - NEXT
  • #101  Appcache takes over domain After initial load, firefox - NEXT
  • #102  Appcache takes over domain After initial load, firefox - NEXT
  • #103  Appcache takes over domain After initial load, firefox - NEXT
  • #104  Appcache takes over domain After initial load, firefox - NEXT
  • #105  Appcache takes over domain After initial load, firefox - NEXT
  • #106  Appcache takes over domain After initial load, firefox - NEXT
  • #108  Do basic namespace check
  • #109  Do basic namespace check
  • #110  Do basic namespace check
  • #111  Do basic namespace check
  • #112  Do basic namespace check
  • #113  Do basic namespace check
  • #114  Do basic namespace check
  • #115  Do basic namespace check
  • #116  Appcache takes over domain After initial load, firefox - NEXT
  • #117  Appcache takes over domain After initial load, firefox - NEXT
  • #118  Appcache takes over domain After initial load, firefox - NEXT
  • #119  Appcache takes over domain After initial load, firefox - NEXT
  • #120  Appcache takes over domain After initial load, firefox - NEXT
  • #123  Appcache takes over domain After initial load, firefox - NEXT
  • #124  Appcache takes over domain After initial load, firefox - NEXT
  • #125  Appcache takes over domain After initial load, firefox - NEXT
  • #126  Appcache takes over domain After initial load, firefox - NEXT
  • #127  Appcache takes over domain After initial load, firefox - NEXT
  • #128  Appcache takes over domain After initial load, firefox - NEXT
  • #129  Appcache takes over domain After initial load, firefox - NEXT
  • #130  Appcache takes over domain After initial load, firefox - NEXT
  • #131  Appcache takes over domain After initial load, firefox - NEXT
  • #132  Appcache takes over domain After initial load, firefox - NEXT
  • #134  Do basic namespace check
  • #135  Do basic namespace check
  • #136  Do basic namespace check
  • #137  Do basic namespace check
  • #138  Do basic namespace check
  • #141  Do basic namespace check
  • #142  So what are they? It is a way to get around some browser limitations without breaking the web
  • #143  So what are they? It is a way to get around some browser limitations without breaking the web
  • #144  So what are they? It is a way to get around some browser limitations without breaking the web
  • #145  So what are they? It is a way to get around some browser limitations without breaking the web
  • #147  Added to webkit ri in 2008
  • #163  Browser’s don’t know everything, we just pretend to Real world usage WILL change the spec. Be a part of it!
  • #164  Browser’s don’t know everything, we just pretend to Real world usage WILL change the spec. Be a part of it!
  • #165  Always try to push the experience one step further, and then another
  • #166  I even had to do a lot of extra work to get it at microsoft That is my acutal work email – LET ME HEAR FROM YOU! Change the world