Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save cdowebcast/716d1af4a8933944efa28a5eab439ede to your computer and use it in GitHub Desktop.
Save cdowebcast/716d1af4a8933944efa28a5eab439ede to your computer and use it in GitHub Desktop.
Cache service worker
/* global caches, Promise */
//Arquivo apenas para facilitar a edição do javascript, deve ser colado no .php// Não minificar
importScripts('serviceworker-cache-polyfill.js');
// 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 CACHE_SCOPE = location.origin + "/";
var CACHE_VERSION = 0;
var CACHE_VERSION_OLD = 0;
var CURRENT_CACHES = {
'perma-cache': 'perma-cache', //Cache que não é apagado através das versões.
'prefetch-cache': 'prefetch-cache-v' + CACHE_VERSION, //Cache prefetch
'general-cache': 'general-cache-v' + CACHE_VERSION,
};
var logger = false;
self.addEventListener('activate', function (event) {
//console.log('Handling activate event:', event);
// Delete all caches that aren't named in CURRENT_CACHES.
// While there is only one cache in this example, the same logic will handle the case where
// there are multiple versioned caches.
var expectedCacheNames = Object.keys(CURRENT_CACHES).map(function (key) {
return CURRENT_CACHES[key];
});
event.waitUntil(
caches.keys().then(function (cacheNames) {
return Promise.all(
cacheNames.map(function (cacheName) {
if (expectedCacheNames.indexOf(cacheName) === -1) {
// If this cache name isn't present in the array of "expected" cache names, then delete it.
//console.log('Deleting out of date cache:', cacheName);
return caches.delete(cacheName);
}
})
);
})
);
// `claim()` sets this worker as the active worker for all clients that
// match the workers scope and triggers an `oncontrollerchange` event for
// the clients.
return self.clients.claim();
});
self.addEventListener('message', function (event) {
//console.log('Handling message event:', event);
var cacheName = (undefined !== event.data.cachename && event.data.cachename in CURRENT_CACHES) ? event.data.cachename : "general-cache";
// //console.log(cacheName);
caches.open(CURRENT_CACHES[cacheName]).then(function (cache) {
switch (event.data.command) {
// This command returns a list of the URLs corresponding to the Request objects
// that serve as keys for the current cache.
case 'keys':
cache.keys().then(function (requests) {
var urls = requests.map(function (request) {
return request.url;
});
// event.ports[0] corresponds to the MessagePort that was transferred as part of the controlled page's
// call to controller.postMessage(). Therefore, event.ports[0].postMessage() will trigger the onmessage
// handler from the controlled page.
// It's up to you how to structure the messages that you send back; this is just one example.
event.ports[0].postMessage({
error: null,
urls: urls.sort()
});
});
break;
// This command adds a new request/response pair to the cache.
case 'add':
// If event.data.url isn't a valid URL, new Request() will throw a TypeError which will be handled
// by the outer .catch().
// Hardcode {mode: 'no-cors} since the default for new Requests constructed from strings is to require
// CORS, and we don't have any way of knowing whether an arbitrary URL that a user entered supports CORS.
var request = new Request(event.data.url, {mode: 'no-cors'});
cache.add(request).then(function () {
event.ports[0].postMessage({
error: null
});
});
break;
// This command removes a request/response pair from the cache (assuming it exists).
case 'delete':
var request = new Request(event.data.url, {mode: 'no-cors'});
cache.delete(request).then(function (success) {
event.ports[0].postMessage({
error: success ? null : 'Item was not found in the cache.'
});
});
break;
case 'deleteAll':
caches.keys().then(function (cacheNames) {
return Promise.all(
cacheNames.map(function (cacheName) {
if (cacheName !== CURRENT_CACHES['perma-cache']) {
// If this cache name isn't present in the array of "expected" cache names, then delete it.
//console.log('Deleting cache:', cacheName);
return caches.delete(cacheName);
}
})
);
})
var request = new Request(event.data.url, {mode: 'no-cors'});
cache.delete(request).then(function (success) {
event.ports[0].postMessage({
error: success ? null : 'Item was not found in the cache.'
});
});
break;
case 'rev':
var isSame = true;
if ((CACHE_VERSION != CACHE_VERSION_OLD) || (CACHE_VERSION != event.data.rev)) {
isSame = false;
CACHE_VERSION = event.data.rev;
CURRENT_CACHES = {
'perma-cache': 'perma-cache',
'general-cache': 'general-cache-v' + CACHE_VERSION
};
var expectedCacheNames = Object.keys(CURRENT_CACHES).map(function (key) {
return CURRENT_CACHES[key];
});
caches.keys().then(function (cacheNames) {
return Promise.all(
cacheNames.map(function (cacheName) {
if (expectedCacheNames.indexOf(cacheName) === -1) {
// If this cache name isn't present in the array of "expected" cache names, then delete it.
//console.log('Deleting old cache:', cacheName);
return caches.delete(cacheName);
}
})
);
});
}
event.ports[0].postMessage({
error: null,
isSame: isSame
});
break;
default:
// This will be handled by the outer .catch().
throw 'Unknown command: ' + event.data.command;
}
}).catch(function (error) {
// If the promise rejects, handle it by returning a standardized error message to the controlled page.
console.error('Message handling failed:', error);
event.ports[0].postMessage({
error: error.toString()
});
});
});
// Urls to Prefetch :)
var urlsToPrefetch = [
//JS
"plugin/angular/angular.min.js", "plugin/requirejs/require.min.js","app/main.js","app/app.js","app/includes.js"
//CSS
,"styles/css/print.css","styles/css/bootstrap.css","styles/css/fixes.css",
//Html
,"app/layout/partials/menu.tpl.html", "app/layout/public.tpl.html",
//JSON
,"app/i18n/languages.json","app/i18n/pt-br.json", "app/i18n/en.json"
];
self.addEventListener('sync', function (event) {
logger && console.log('Handling sync event:', event);
});
self.addEventListener('install', function (event) {
logger && console.log('Handling install event:', event);
//var now = Date.now();
// All of these logging statements should be visible via the "Inspect" interface
// for the relevant SW accessed via chrome://serviceworker-internals
if (urlsToPrefetch.length > 0) {
logger && console.log('Handling install event. Resources to prefetch:', urlsToPrefetch.length , "resources");
event.waitUntil(
caches.open(CURRENT_CACHES['prefetch-cache']).then(function (cache) {
var cachePromises = urlsToPrefetch.map(function (urlToPrefetch) {
urlToPrefetch += '?v=' + CACHE_VERSION;
// This constructs a new URL object using the service worker's script location as the base
// for relative URLs.
//var url = new URL(urlToPrefetch + '?v=' + CACHE_VERSION, location.href);
var url = new URL(urlToPrefetch, location.href);
// Append a cache-bust=TIMESTAMP URL parameter to each URL's query string.
// This is particularly important when precaching resources that are later used in the
// fetch handler as responses directly, without consulting the network (i.e. cache-first).
// If we were to get back a response from the HTTP browser cache for this precaching request
// then that stale response would be used indefinitely, or at least until the next time
// the service worker script changes triggering the install flow.
//url.search += (url.search ? '&' : '?') + 'v=' + CACHE_VERSION;
// It's very important to use {mode: 'no-cors'} if there is any chance that
// the resources being fetched are served off of a server that doesn't support
// CORS (http://en.wikipedia.org/wiki/Cross-origin_resource_sharing).
// In this example, www.chromium.org doesn't support CORS, and the fetch()
// would fail if the default mode of 'cors' was used for the fetch() request.
// The drawback of hardcoding {mode: 'no-cors'} is that the response from all
// cross-origin hosts will always be opaque
// (https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#cross-origin-resources)
// and it is not possible to determine whether an opaque response represents a success or failure
// (https://github.com/whatwg/fetch/issues/14).
var request = new Request(url, {mode: 'no-cors'});
return fetch(request).then(function (response) {
logger && console.log('Add to Cache (Prefetch)', url.href);
if (!response || response.status !== 200 || response.type !== 'basic') {
throw new Error('request for ' + urlToPrefetch +
' failed with status ' + response.statusText);
}
//var responseToCache = response.clone();
// Use the original URL without the cache-busting parameter as the key for cache.put().
// return cache.put(urlToPrefetch, responseToCache);
return cache.put(urlToPrefetch, response);
}).catch(function (error) {
logger && console.error('Not caching ' + urlToPrefetch + ' due to ' + error);
});
});
return Promise.all(cachePromises).then(function () {
logger && console.log('Pre-fetching complete.');
});
}).catch(function (error) {
logger && console.error('Pre-fetching failed:', error);
}));
}
// Perform install steps
// if (urlsToPrefetch.length > 0) {
// event.waitUntil(
// caches.open(CURRENT_CACHES['perma-cache'])
// .then(function (cache) {
// return cache.addAll(urlsToPrefetch);
// })
// );
// }
// `skipWaiting()` forces the waiting ServiceWorker to become the
// active ServiceWorker, triggering the `onactivate` event.
// Together with `Clients.claim()` this allows a worker to take effect
// immediately in the client(s).
return self.skipWaiting();
});
//self.addEventListener('fetch', function(event) {
// //console.log(event.request);
// event.respondWith(
// caches.match(event.request)
// .then(function(response) {
// if (response) {
// //console.log('From Service Worker:',response);
// return response;
// }
//
// return fetch(event.request);
// }
// )
// );
//});
self.addEventListener('fetch', function (event) {
//console.log(event);
if (event.request.method === "GET") {
var qSFilter = "" + ((event.request.url).split('?'))[0];//Filtrar Quetry Stirng
//console.log(event.request.url, qSFilter, qSFilter.split(CACHE_SCOPE), CACHE_SCOPE);
var leUrl = (qSFilter.split(CACHE_SCOPE))[1];
//Adiciona os arquivos selecionados ao cache
//console.log(leUrl, /^(app|style|plugin).*(js|css|html|jpe?g|png|gif|json|woff2?)$/.test(leUrl));
if (/^(app|style|plugin).*(js|css|html|jpe?g|png|gif|json|woff2?)$/.test(leUrl)
|| /^backend\/server\/file\/i18n\/((?!client).+)\//.test(leUrl)
|| /^backend\/server\/static\/images\/.*$/.test(leUrl)
|| /^backend\/server\/static\/style.*$/.test(leUrl)
) {
var url = new URL(leUrl + '?v=' + CACHE_VERSION, location.href);
var synthetic = new Request(url, {mode: 'no-cors'});
//console.log(event.request,response.clone(),synthetic);
event.respondWith(
// caches.match(event.request)
caches.match(synthetic)
.then(function (response) {
// Cache hit - return response
if (response) {
logger && console.log('From Cache', event.request.url);
return response;
}
// IMPORTANT: Clone the request. A request is a stream and
// can only be consumed once. Since we are consuming this
// once by cache and once by the browser for fetch, we need
// to clone the response
var fetchRequest = event.request.clone();
return fetch(fetchRequest).then(
function (response) {
// Check if we received a valid response
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// IMPORTANT: Clone the response. A response is a stream
// and because we want the browser to consume the response
// as well as the cache consuming the response, we need
// to clone it so we have 2 stream.
var responseToCache = response.clone();
caches.open(CURRENT_CACHES['general-cache'])
.then(function (cache) {
try {
logger && console.log('Add to Cache', event.request.url, qSFilter,leUrl);
cache.put(event.request, responseToCache);
} catch (e) {
console.error(e);
}
});
return response;
}
);
})
);
}
}
});
/**
* 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.
*
*/
if (!Cache.prototype.addAll) {
Cache.prototype.addAll = function addAll(requests) {
var cache = this;
// Since DOMExceptions are not constructable:
function NetworkError(message) {
this.name = 'NetworkError';
this.code = 19;
this.message = message;
}
NetworkError.prototype = Object.create(Error.prototype);
return Promise.resolve().then(function() {
if (arguments.length < 1) throw new TypeError();
// Simulate sequence<(Request or USVString)> binding:
var sequence = [];
requests = requests.map(function(request) {
if (request instanceof Request) {
return request;
}
else {
return String(request); // may throw TypeError
}
});
return Promise.all(
requests.map(function(request) {
if (typeof request === 'string') {
request = new Request(request);
}
var scheme = new URL(request.url).protocol;
if (scheme !== 'http:' && scheme !== 'https:') {
throw new NetworkError("Invalid scheme");
}
return fetch(request.clone());
})
);
}).then(function(responses) {
// TODO: check that requests don't overwrite one another
// (don't think this is possible to polyfill due to opaque responses)
return Promise.all(
responses.map(function(response, i) {
return cache.put(requests[i], response);
})
);
}).then(function() {
return undefined;
});
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment