Skip to content
This repository was archived by the owner on Nov 27, 2018. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,9 @@ module.exports = function(config) {
console.log('\nCreating files…');

return new Promise(function(resolve, reject) {
var stream = gulp.src([__dirname + '/../templates/**'])
var contents = __dirname + '/../templates/**';
var workerTemplate = __dirname + '/../templates/app/offline-worker.tmpl';
var stream = gulp.src([contents, '!' + workerTemplate])
.pipe(rename(function (path) {
// NPM can't include a .gitignore file so we have to rename it.
if (path.basename === 'gitignore') {
Expand Down
1 change: 1 addition & 0 deletions lib/offline.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ module.exports = function(config) {
cacheId: cacheId,
ignoreUrlParametersMatching: config.ignoreUrlParametersMatching || [/./],
maximumFileSizeToCacheInBytes: Infinity,
templateFilePath: __dirname + '/../templates/app/offline-worker.tmpl',
});
}));
});
Expand Down
168 changes: 168 additions & 0 deletions templates/app/offline-worker.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */

<% if (importScripts) { %>
importScripts(<%= importScripts %>);
<% } %>

(function (self) {
'use strict';

// On install, cache resources and skip waiting so the worker won't
// wait for clients to be closed before becoming active.
self.addEventListener('install', function (event) {
event.waitUntil(oghliner.cacheResources().then(function () {
if (typeof self.skipWaiting === 'function') {
return self.skipWaiting();
}
}));
});

// On activation, delete old caches and start controlling the clients
// without waiting for them to reload.
self.addEventListener('activate', function (event) {
event.waitUntil(oghliner.activateNewContent().then(function () {
if (self.clients && typeof self.clients.claim === "function") {
return self.clients.claim();
}
}));
});

<% if (handleFetch) { %>
// Retrieves the request following oghliner strategy.
self.addEventListener('fetch', function (event) {
if (event.request.method === 'GET') {
event.respondWith(oghliner.get(event.request));
}
else {
event.respondWith(self.fetch(event.request));
}
});
<% } %>

var oghliner = self.oghliner = {

// This is the unique prefix for all the caches controlled by this worker.
CACHE_PREFIX: 'sw-precache-<%= version %>:<%= cacheId %>:' + (self.registration ? self.registration.scope : '') + ':',

// This is the unique name for the cache controlled by this version of the worker.
get CACHE_NAME() {
return this.CACHE_PREFIX + 'offline-cache';
},

// In order to avoid deleting the former content during installation, new contents are
// cached here until activation.
get TEMP_CACHE_NAME() {
return this.CACHE_NAME + ':new';
},

// This is a list of resources that will be cached. Hashes are needed to include
// byte-level changes when these files change and thus, make the browser to update
// the service worker.
RESOURCES: [
['./'],
<% JSON.parse(precacheConfig).forEach(function (pathAndHash) {
%> ['<%= pathAndHash[0] %>', '<%= pathAndHash[1] %>'],
<% }); %>
],

// Adds the resources to the cache controlled by this worker.
cacheResources: function () {
var _this = this;
var now = Date.now();
var baseUrl = self.location;
return _this.openNewCache()
.then(function (cache) {
return Promise.all(_this.RESOURCES.map(function (resource) {
resource = resource[0];

// Bust the request to get a fresh response
var url = new URL(resource, baseUrl);
var bustParameter = (url.search ? '&' : '') + '__bust=' + now;
var bustedUrl = new URL(url.toString());
bustedUrl.search += bustParameter;

// But cache the response for the original request
var requestConfig = { credentials: 'same-origin' };
var originalRequest = new Request(url.toString(), requestConfig);
var bustedRequest = new Request(bustedUrl.toString(), requestConfig);
return fetch(bustedRequest).then(function (response) {
if (response.ok) {
return cache.put(originalRequest, response);
}
console.error('Error fetching ' + url + ', status was ' + response.status);
});
}));
});
},

// Get a response from the current offline cache or from the network.
get: function (request) {
return this.openCache()
.then(function (cache) {
return cache.match(request);
})
.then(function (response) {
if (response) {
return response;
}
return self.fetch(request);
});
},

// Open and cache the offline cache promise to improve the performance when
// serving from the offline-cache.
openCache: function () {
if (!this._cache) {
this._cache = self.caches.open(this.CACHE_NAME);
}
return this._cache;
},

// Called during installation, recreates a cache to put the new content without
// overwritting the old one.
openNewCache: function () {
var tempCache = this.TEMP_CACHE_NAME;
return self.caches.delete(tempCache).then(function () {
return self.caches.open(tempCache);
});
},

// Called during activation, copies the new contents to the current cache.
activateNewContent: function () {
var tempCacheName = this.TEMP_CACHE_NAME;
var currentCacheName = this.CACHE_NAME;

return prepareCaches(currentCacheName, tempCacheName).then(function (caches) {
var tempCache = caches[1];
var currentCache = caches[0];

return getEntries(tempCache).then(function (entries) {
return Promise.all(entries.map(function (entry) {
return currentCache.put(entry.request, entry.response);
}));
});
}).then(function () {
self.caches.delete(tempCacheName);
});

function prepareCaches(target, source) {
return self.caches.delete(target).then(function () {
return Promise.all([target, source].map(function (cacheName) {
return self.caches.open(cacheName);
}));
});
}

function getEntries(cache) {
return cache.keys().then(function (requests) {
return Promise.all(requests.map(function (request) {
return cache.match(request).then(function (response) {
return { request: request, response: response };
});
}));
});
}
}
};
}(self));