Skip to content

Instantly share code, notes, and snippets.

@felquis
Last active December 16, 2015 11:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save felquis/c4ea346d092a1f9d91fe to your computer and use it in GitHub Desktop.
Save felquis/c4ea346d092a1f9d91fe to your computer and use it in GitHub Desktop.
Service Worker template
/*
:)
*/
// This polyfill provides Cache.add(), Cache.addAll(), and CacheStorage.match(),
// which are not implemented in Chrome 40.
// https://github.com/coonsta/cache-polyfill
importScripts('js/dependencies/cache-polyfill.js')
// Increase!!
var version = '7776'
// While overkill for this specific sample in which there is only one cache,
// this is one best practice that can be followed in general to keep track of
// multiple caches used by a given service worker, and keep them all versioned.
// It maps a shorthand identifier for a cache to a specific, versioned cache name.
// Note that since global state is discarded in between service worker restarts, these
// variables will be reinitialized each time the service worker handles an event, and you
// should not attempt to change their values inside an event handler. (Treat them as constants.)
// If at any point you want to force pages that use this service worker to start using a fresh
// cache, then increment the CACHE_VERSION value. It will kick off the service worker update
// flow and the old cache(s) will be purged as part of the activate event handler when the
// updated service worker is activated.
var urlsToPrefetch = [
'/',
// don't cache these files
// '/sw.js',
// '/js/service-worker.js',
'/styles/main.css'
'/scripts/main.js',
'/images/logo.png',
'images/icon/search.svg'
// put your files here
]
self.addEventListener('install', function(event) {
// console.log('WORKER: install event in progress.')
event.waitUntil(
/* The caches built-in is a promise-based API that helps you cache responses,
as well as finding and deleting them.
*/
caches
/* You can open a cache by name, and this method returns a promise. We use
a versioned cache name here so that we can remove old cache entries in
one fell swoop later, when phasing out an older service worker.
*/
.open(version)
.then(function(cache) {
/* After the cache is opened, we can fill it with the offline fundamentals.
The method below will add all resources we've indicated to the cache,
after making HTTP requests for each of them.
*/
return cache.addAll(urlsToPrefetch)
})
.then(function(as) {
// console.log('WORKER: install completed', as)
return self.skipWaiting();
})
)
})
self.addEventListener('fetch', function(event) {
// console.log('WORKER: fetch event in progress.')
/* We should only cache GET requests, and deal with the rest of method in the
client-side, by handling failed POST,PUT,PATCH,etc. requests.
here we also ignore requests from APIs
*/
if (event.request.method !== 'GET' ||
event.request.url.indexOf('api.from-my-domain.com') !== -1 ||
event.request.url.indexOf('www.google-analytics.com') !== -1 ||
event.request.url.indexOf('register-service-worker') !== -1 ||
event.request.url.indexOf('connect.facebook.net') !== -1) {
/* If we don't block the event as shown below, then the request will go to
the network as usual.
*/
// console.log(event.request.method, event.request.url)
return
}
/* Similar to event.waitUntil in that it blocks the fetch event on a promise.
Fulfillment result will be used as the response, and rejection will end in a
HTTP response indicating failure.
*/
event.respondWith( fetchRespondWith(event) )
})
function fetchRespondWith(event) {
// console.log('request: ', event.request.url, event.request)
return caches
/* This method returns a promise that resolves to a cache entry matching
the request. Once the promise is settled, we can then provide a response
to the fetch request.
*/
.match(event.request)
.then(function(cached) {
/* Even if the response is in our cache, we go to the network as well.
This pattern is known for producing 'eventually fresh' responses,
where we return cached responses immediately, and meanwhile pull
a network response and store that in the cache.
Read more:
https://ponyfoo.com/articles/progressive-networking-serviceworker
*/
var networked = fetch(event.request)
// We handle the network request with success and failure scenarios.
.then(fetchedFromNetwork(event), unableToResolve)
// We should catch errors on the fetchedFromNetwork handler as well.
.catch(unableToResolve)
/* We return the cached response immediately if there is one, and fall
back to waiting on the network as usual.
*/
// console.log('WORKER: fetch event', cached ? '(cached)' : '(network)', event.request.url)
return cached || networked
})
}
function fetchedFromNetwork(event) {
return function transform(response) {
/* We copy the response before replying to the network request.
This is the response that will be stored on the ServiceWorker cache.
*/
var cacheCopy = response.clone()
// // console.log('WORKER: fetch response from network.', event.request.url)
caches
// We open a cache to store the response for this request.
.open(version)
.then(function add(cache) {
/* We store the response for this request. It'll later become
available to caches.match(event.request) calls, when looking
for cached responses.
*/
cache.put(event.request, cacheCopy)
})
.then(function() {
// console.log('WORKER: fetch response stored in cache.', event.request.url)
})
// Return the response so that the promise is settled in fulfillment.
return response
}
}
/*
Ao cair aqui significa que não foi possivel responder para uma URL nem
com o conexão nem via cache.. isso significa que interceptamos esses
requests e mostramos uma tela de offline, com um botão que trás
ações úteis para recomeçar as atividades
*/
function unableToResolve (request) {
/*
Ao tentar visualizar uma tela que não está offline
a usuária irá ver a rota de /offline renderizada
*/
caches.open(version).then(function(cache) {
// console.log('unableToResolve', request)
return cache.match('/offline').then(function(response) {
// Cache hit - return the response from the cached version
if (response) {
return response;
}
})
})
}
self.addEventListener('activate', function(event) {
/* Just like with the install event, event.waitUntil blocks activate on a promise.
Activation will fail unless the promise is fulfilled.
*/
// console.log('WORKER: activate event in progress.')
event.waitUntil(
caches
/* This method returns a promise which will resolve to an array of available
cache keys.
*/
.keys()
.then(function (keys) {
// We return a promise that settles when all outdated caches are deleted.
return Promise.all(
keys
.filter(function (key) {
// Filter by keys that don't start with the latest version prefix.
return !key.startsWith(version)
})
.map(function (key) {
/* Return a promise that's fulfilled
when each outdated cache is deleted.
*/
return caches.delete(key)
})
)
})
.then(function(alo) {
// console.log('WORKER: activate completed.', alo)
self.clients.claim()
})
)
})
// EM CONSIDERAÇÃO
// self.addEventListener('push', function(event) {
// console.log('Push: ', event)
// registration.showNotification('New email', {
// body: 'From example@demo.com',
// tag: 'new-email'
// });
// return
// event.waitUntil(
// caches.open(version).then(function(cache) {
// return fetch('https://my-domain.com/notification/last', {
// method: 'post',
// mode: 'cors',
// credentials: 'include',
// headers: {
// 'Accept': 'application/json',
// 'Content-Type': 'application/json',
// 'Authorization': '' // must get session from IndexedDB
// }
// // body: JSON.stringify({
// // name: 'Hubot',
// // login: 'hubot',
// // })
// }).then(function(response) {
// cache.put('/inbox.json', response.clone());
// return response.json();
// });
// }).then(function(emails) {
// registration.showNotification('New email', {
// body: 'From email@example.com',
// tag: 'new-email'
// });
// })
// )
// if (event.data.text() == 'new-email') {
// event.waitUntil(
// caches.open(version).then(function(cache) {
// return fetch('/inbox.json').then(function(response) {
// cache.put('/inbox.json', response.clone());
// return response.json();
// });
// }).then(function(emails) {
// registration.showNotification('New email', {
// body: 'From ' + emails[0].from.name,
// tag: 'new-email'
// });
// })
// );
// }
// });
// self.addEventListener('notificationclick', function(event) {
// console.log('notification click: ', event)
// // Android doesn't close the notification when you click on it
// // See: http://crbug.com/463146
// event.notification.close();
// debugger
// // if (event.notification.tag == 'new-email') {
// // }
// });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment