Skip to content

Commit

Permalink
Change strategy for Service Worker cache to 'Fastest'
Browse files Browse the repository at this point in the history
  • Loading branch information
Lim Chee Aun committed Mar 26, 2016
1 parent de25ca4 commit 0a0bd41
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 252 deletions.
11 changes: 0 additions & 11 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,6 @@ module.exports = function(grunt) {
}
}
},
swPrecache: {
production: {
options: {
staticFileGlobs: [
'js/scripts.js',
'index.html',
'bus-arrival/index.html'
]
}
}
},
aws: awsCreds,
s3: {
options: {
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ Few more `grunt` goodies:
- `grunt svg_sprite` - generate the SVG sprite
- `grunt inline` - inline the JS and CSS files into the HTML
- `grunt s3` - uploads data to a S3 bucket
- `grunt swPrecache` - generate/update the Service Worker file
- `grunt stats` - get the stats for total number of bus stops and services

The icon is from [The Noun Project](http://thenounproject.com/noun/bus/#icon-No97). The color scheme and markers are *inspired* by [Gothere.sg](http://gothere.sg/).
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"grunt-contrib-watch": "~1.0.0",
"grunt-inline": "~0.3.6",
"grunt-svg-sprite": "~1.2.19",
"sw-precache": "~3.1.1",
"tokml": "~0.4.0"
},
"author": "Lim Chee Aun",
Expand Down
274 changes: 54 additions & 220 deletions service-worker.js
Original file line number Diff line number Diff line change
@@ -1,225 +1,59 @@
/**
* Copyright 2015 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// This generated service worker JavaScript will precache your site's resources.
// The code needs to be saved in a .js file at the top-level of your site, and registered
// from your pages in order to be used. See
// https://github.com/googlechrome/sw-precache/blob/master/demo/app/js/service-worker-registration.js
// for an example of how you can register this script and handle various service worker events.

/* eslint-env worker, serviceworker */
/* eslint-disable indent, no-unused-vars, no-multiple-empty-lines, max-nested-callbacks, space-before-function-paren */
'use strict';



/* eslint-disable quotes, comma-spacing */
var PrecacheConfig = [["bus-arrival/index.html","c0cae7d4618d426824d2b38e05ab9e2b"],["index.html","c4437c15fdc37ac47b2eba56ca386a85"],["js/scripts.js","e2f0de5cd9039e70b1f650c27ef78a46"]];
/* eslint-enable quotes, comma-spacing */
var CacheNamePrefix = 'sw-precache-v1--' + (self.registration ? self.registration.scope : '') + '-';


var IgnoreUrlParametersMatching = [/^utm_/];



var addDirectoryIndex = function (originalUrl, index) {
var url = new URL(originalUrl);
if (url.pathname.slice(-1) === '/') {
url.pathname += index;
}
return url.toString();
};

var getCacheBustedUrl = function (url, now) {
now = now || Date.now();

var urlWithCacheBusting = new URL(url);
urlWithCacheBusting.search += (urlWithCacheBusting.search ? '&' : '') + 'sw-precache=' + now;

return urlWithCacheBusting.toString();
};

var populateCurrentCacheNames = function (precacheConfig,
cacheNamePrefix, baseUrl) {
var absoluteUrlToCacheName = {};
var currentCacheNamesToAbsoluteUrl = {};

precacheConfig.forEach(function(cacheOption) {
var absoluteUrl = new URL(cacheOption[0], baseUrl).toString();
var cacheName = cacheNamePrefix + absoluteUrl + '-' + cacheOption[1];
currentCacheNamesToAbsoluteUrl[cacheName] = absoluteUrl;
absoluteUrlToCacheName[absoluteUrl] = cacheName;
});

return {
absoluteUrlToCacheName: absoluteUrlToCacheName,
currentCacheNamesToAbsoluteUrl: currentCacheNamesToAbsoluteUrl
};
};

var stripIgnoredUrlParameters = function (originalUrl,
ignoreUrlParametersMatching) {
var url = new URL(originalUrl);

url.search = url.search.slice(1) // Exclude initial '?'
.split('&') // Split into an array of 'key=value' strings
.map(function(kv) {
return kv.split('='); // Split each 'key=value' string into a [key, value] array
})
.filter(function(kv) {
return ignoreUrlParametersMatching.every(function(ignoredRegex) {
return !ignoredRegex.test(kv[0]); // Return true iff the key doesn't match any of the regexes.
});
})
.map(function(kv) {
return kv.join('='); // Join each [key, value] array into a 'key=value' string
})
.join('&'); // Join the array of 'key=value' strings into a string with '&' in between each

return url.toString();
};


var mappings = populateCurrentCacheNames(PrecacheConfig, CacheNamePrefix, self.location);
var AbsoluteUrlToCacheName = mappings.absoluteUrlToCacheName;
var CurrentCacheNamesToAbsoluteUrl = mappings.currentCacheNamesToAbsoluteUrl;

function deleteAllCaches() {
return caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
return caches.delete(cacheName);
})
);
});
}

self.addEventListener('install', function(event) {
var now = Date.now();

event.waitUntil(
caches.keys().then(function(allCacheNames) {
return Promise.all(
Object.keys(CurrentCacheNamesToAbsoluteUrl).filter(function(cacheName) {
return allCacheNames.indexOf(cacheName) === -1;
}).map(function(cacheName) {
var urlWithCacheBusting = getCacheBustedUrl(CurrentCacheNamesToAbsoluteUrl[cacheName],
now);

return caches.open(cacheName).then(function(cache) {
var request = new Request(urlWithCacheBusting, {credentials: 'same-origin'});
return fetch(request).then(function(response) {
if (response.ok) {
return cache.put(CurrentCacheNamesToAbsoluteUrl[cacheName], response);
}

console.error('Request for %s returned a response with status %d, so not attempting to cache it.',
urlWithCacheBusting, response.status);
// Get rid of the empty cache if we can't add a successful response to it.
return caches.delete(cacheName);
});
});
})
).then(function() {
return Promise.all(
allCacheNames.filter(function(cacheName) {
return cacheName.indexOf(CacheNamePrefix) === 0 &&
!(cacheName in CurrentCacheNamesToAbsoluteUrl);
}).map(function(cacheName) {
return caches.delete(cacheName);
})
);
});
}).then(function() {
if (typeof self.skipWaiting === 'function') {
// Force the SW to transition from installing -> active state
self.skipWaiting();
}
})
);
self.addEventListener('install', function(event){
console.log('Install');
});

if (self.clients && (typeof self.clients.claim === 'function')) {
self.addEventListener('activate', function(event) {
event.waitUntil(self.clients.claim());
});
}

self.addEventListener('message', function(event) {
if (event.data.command === 'delete_all') {
console.log('About to delete all caches...');
deleteAllCaches().then(function() {
console.log('Caches deleted.');
event.ports[0].postMessage({
error: null
});
}).catch(function(error) {
console.log('Caches not deleted:', error);
event.ports[0].postMessage({
error: error
});
});
}
self.addEventListener('activate', function(event){
console.log('Activate');
});


self.addEventListener('fetch', function(event) {
if (event.request.method === 'GET') {
var urlWithoutIgnoredParameters = stripIgnoredUrlParameters(event.request.url,
IgnoreUrlParametersMatching);

var cacheName = AbsoluteUrlToCacheName[urlWithoutIgnoredParameters];
var directoryIndex = 'index.html';
if (!cacheName && directoryIndex) {
urlWithoutIgnoredParameters = addDirectoryIndex(urlWithoutIgnoredParameters, directoryIndex);
cacheName = AbsoluteUrlToCacheName[urlWithoutIgnoredParameters];
}

var navigateFallback = '';
// Ideally, this would check for event.request.mode === 'navigate', but that is not widely
// supported yet:
// https://code.google.com/p/chromium/issues/detail?id=540967
// https://bugzilla.mozilla.org/show_bug.cgi?id=1209081
if (!cacheName && navigateFallback && event.request.headers.has('accept') &&
event.request.headers.get('accept').includes('text/html')) {
var navigateFallbackUrl = new URL(navigateFallback, self.location);
cacheName = AbsoluteUrlToCacheName[navigateFallbackUrl.toString()];
}

if (cacheName) {
event.respondWith(
// Rely on the fact that each cache we manage should only have one entry, and return that.
caches.open(cacheName).then(function(cache) {
return cache.keys().then(function(keys) {
return cache.match(keys[0]).then(function(response) {
if (response) {
return response;
}
// If for some reason the response was deleted from the cache,
// raise and exception and fall back to the fetch() triggered in the catch().
throw Error('The cache ' + cacheName + ' is empty.');
});
});
}).catch(function(e) {
console.warn('Couldn\'t serve response for "%s" from cache: %O', event.request.url, e);
return fetch(event.request);
})
);
}
}
var cacheName = 'busroutersg-v1';
// caches.delete('busroutersg-v1'); // Delete the old one
var successResponses = /^0|([123]\d\d)|(40[14567])|410$/;

function fetchAndCache(request){
return fetch(request.clone()).then(function(response){
if (request.method == 'GET' && response && successResponses.test(response.status) && (response.type == 'basic' && !/\.json$/.test(request.url) || /\.(js|png|ttf|woff|woff2)$/i.test(request.url) || /fonts\.googleapis\.com/i.test(request.url))){
console.log('Cache', request.url);
caches.open(cacheName).then(function(cache){
cache.put(request, response);
});
}
return response.clone();
});
};

function cacheOnly(request){
return caches.open(cacheName).then(function(cache){
return cache.match(request);
});
};

// Fastest strategy from https://github.com/GoogleChrome/sw-toolbox
self.addEventListener('fetch', function(event){
var request = event.request;
var url = request.url;
event.respondWith(new Promise(function(resolve, reject){
var rejected = false;
var reasons = [];

var maybeReject = function(reason){
reasons.push(reason.toString());
if (rejected){
reject(new Error('Both cache and network failed: "' + reasons.join('", "') + '"'));
} else {
rejected = true;
}
};

var maybeResolve = function(result){
if (result instanceof Response){
resolve(result);
} else {
maybeReject('No result returned');
}
};

fetchAndCache(request.clone()).then(maybeResolve, maybeReject);
cacheOnly(request).then(maybeResolve, maybeReject);
}));
});

19 changes: 0 additions & 19 deletions tasks/sw-precache.js

This file was deleted.

0 comments on commit 0a0bd41

Please sign in to comment.