Skip to content

Instantly share code, notes, and snippets.

@ymichael
Created May 31, 2017 06:44
Show Gist options
  • Save ymichael/6f0e2b78add837f2fa9fc32ff677a167 to your computer and use it in GitHub Desktop.
Save ymichael/6f0e2b78add837f2fa9fc32ff677a167 to your computer and use it in GitHub Desktop.
/*
* AUTOGENERATED FROM GENERATE-SERVICE-WORKER
*/
const $VERSION = '26f8b6b6d92f0e7d7b7dc5ccae53d58f';
const $DEBUG = false;
const $Cache = {
"precache": [
"https://s.pinimg.com/webapp/js/entryChunk-react-common-5c5fb1fe655d0e4956c2.js",
"https://s.pinimg.com/webapp/js/vendor-react-6f23018b1ba21a553ff6.js",
"https://s.pinimg.com/webapp/js/entryChunk-core-83d9ab9b588db714a657.js",
"https://s.pinimg.com/webapp/js/external-core-2-05e6d3187f10f7f103ab.js",
"https://s.pinimg.com/webapp/js/external-core-1-95974d515de8fc25ba4e.js",
"https://s.pinimg.com/webapp/js/external-core-1-slim-345a3f71f5ea89c81fd7.js"
],
"strategy": [
{
"type": "prefer-cache",
"matches": [
"webapp/js/.*\\.js",
".*\\.css"
]
}
]
};
const $Notifications = {
"fallbackURL": "_/_/push/web_push_content/",
"default": {
"title": "Fresh Pins!",
"body": "You’ve got new Pins waiting for you on Pinterest.",
"icon": "https://s.pinimg.com/images/favicon_red_192.png",
"tag": "pinterest-push-notification-tag"
}
};
const $Log = {
"notificationClicked": "_/_/push/web_push_click/"
};
if (!$Cache) {
self.addEventListener('install', (event) => {
event.waitUntil(self.skipWaiting());
});
}
function print(fn) {
return function (message, group) {
if ($DEBUG) {
if (group && logger.groups[group]) {
logger.groups[group].push({
fn: fn,
message: message
});
} else {
console[fn].call(console, message);
}
}
};
}
const logger = {
groups: {},
group: group => {
logger.groups[group] = [];
},
groupEnd: group => {
const groupLogs = logger.groups[group];
if (groupLogs && groupLogs.length > 0) {
console.groupCollapsed(group);
groupLogs.forEach(log => {
console[log.fn].call(console, log.message);
});
console.groupEnd();
}
delete logger.groups[group];
},
log: print('log'),
warn: print('warn'),
error: print('error')
};
/* -------- CACHE --------- */
const CURRENT_CACHE = `SW_CACHE:${$VERSION}`;
const STATIC_CACHE = 'static';
const AVAILABLE_CACHES = [CURRENT_CACHE, STATIC_CACHE];
const isValidResponse = res => (res.ok || (res.status === 0 && res.type === 'opaque'));
/* -------- CACHE LISTENERS --------- */
self.addEventListener('install', handleInstall);
self.addEventListener('activate', handleActivate);
if ($Cache.precache || $Cache.strategy) {
self.addEventListener('fetch', handleFetch);
}
/* -------- CACHE HANDLERS --------- */
function handleInstall(event) {
logger.log('Entering install handler.');
self.skipWaiting();
if ($Cache.precache) {
event.waitUntil(precache());
}
}
function handleActivate(event) {
logger.log('Entering activate handler.');
const cachesCleared = caches.keys().then(cacheNames => {
logger.group('cleanup');
return Promise.all(cacheNames.map(cacheName => {
if (!AVAILABLE_CACHES.includes(cacheName)) {
logger.log(`Deleting cache key: ${cacheName}`, 'cleanup');
return caches.delete(cacheName);
}
return Promise.resolve();
})).then(() => logger.groupEnd('cleanup'));
});
event.waitUntil(cachesCleared);
}
function handleFetch(event) {
if (event.request.method === 'GET') {
const strategy = getStrategyForUrl(event.request.url);
if (strategy) {
logger.group(event.request.url);
logger.log(`Using strategy ${strategy.type}.`, event.request.url);
event.respondWith(
applyEventStrategy(strategy, event).then(response => {
logger.groupEnd(event.request.url);
return response;
})
);
}
}
}
/* -------- CACHE HELPERS --------- */
function applyEventStrategy(strategy, event) {
const request = event.request;
switch (strategy.type) {
case 'offline-only':
return fetchAndCache(request, strategy)().catch(getFromCache(request));
case 'fallback-only':
return fetchAndCache(request, strategy)().then(fallbackToCache(request));
case 'prefer-cache':
return getFromCache(request)().catch(fetchAndCache(request, strategy));
case 'race':
return getFromFastest(request, strategy)();
default:
return Promise.reject(`Strategy not supported: ${strategy.type}`);
}
}
function insertInCache(request, response, strategy) {
logger.log('Inserting in cache.', request.url);
const cacheName = strategy.keepAlive ? STATIC_CACHE : CURRENT_CACHE;
return caches.open(cacheName)
.then(cache => cache.put(request, response));
}
function getFromCache(request) {
return () => {
return caches.match(request).then(response => {
if (response) {
logger.log('Found entry in cache.', request.url);
return response;
}
logger.log('No entry found in cache.', request.url);
throw new Error(`No cache entry found for ${request.url}`);
});
};
}
function getStrategyForUrl(url) {
if ($Cache.strategy) {
return $Cache.strategy.find(strategy => {
return strategy.matches.some(match => {
const regex = new RegExp(match);
return regex.test(url);
});
});
}
return null;
}
function fetchAndCache(request, strategy) {
return () => {
logger.log('Fetching remote data.', request.url);
return fetch(request).then(response => {
if (isValidResponse(response)) {
logger.log('Caching remote response.', request.url);
insertInCache(request, response.clone(), strategy);
} else {
logger.log('Fetch error.', request.url);
}
return response;
});
};
}
function fallbackToCache(request) {
return (response) => {
if (!isValidResponse(response)) {
return getFromCache(request)();
}
return response;
};
}
function getFromFastest(request, strategy) {
return () => new Promise((resolve, reject) => {
var errors = 0;
function raceReject() {
errors += 1;
if (errors === 2) {
reject(new Error('Network and cache both failed.'));
}
}
function raceResolve(response) {
if (response instanceof Response) {
resolve(response);
} else {
raceReject();
}
}
getFromCache(request)()
.then(raceResolve, raceReject);
fetchAndCache(request, strategy)()
.then(raceResolve, raceReject);
});
}
function precache() {
logger.group('precaching');
return caches.open(CURRENT_CACHE).then(cache => {
return Promise.all(
$Cache.precache.map(urlToPrefetch => {
logger.log(urlToPrefetch, 'precaching');
const cacheBustedUrl = new URL(urlToPrefetch, location.href);
cacheBustedUrl.search += (cacheBustedUrl.search ? '&' : '?') + `cache-bust=${Date.now()}`;
const request = new Request(cacheBustedUrl, { mode: 'no-cors' });
return fetch(request).then(response => {
if (!isValidResponse(response)) {
logger.error(`Failed for ${urlToPrefetch}.`, 'precaching');
return undefined;
}
return cache.put(urlToPrefetch, response);
});
})
);
}).then(() => logger.groupEnd('precaching'));
}
'use strict';
/* -------- NOTIFICATIONS --------- */
self.addEventListener('push', handleNotificationPush);
self.addEventListener('notificationclick', handleNotificationClick);
/* -------- NOTIFICATIONS HANDLERS --------- */
function handleNotificationPush(event) {
logger.log('Push notification received');
if ($Log.notificationReceived) {
event.waitUntil(logNotificationReceived(event));
}
// Show notification or fallback
if (event.data && event.data.title) {
event.waitUntil(showNotification(event.data));
} else if ($Notifications.fallbackURL) {
event.waitUntil(
self.registration.pushManager.getSubscription()
.then(fetchNotification)
.then(convertResponseToJson)
.then(showNotification)
.catch(showNotification)
);
} else {
logger.warn('No notification.data and no fallbackURL.');
event.waitUntil(showNotification());
}
}
function handleNotificationClick(event) {
logger.log('Push notification clicked.', event.notification.tag);
if ($Log.notificationClicked) {
event.waitUntil(logNotificationClick(event));
}
// Open the url if provided
if (event.notification.data && event.notification.data.url) {
const url = event.notification.data.url;
event.waitUntil(openWindow(url));
} else if (event.notification.tag.indexOf(':') !== -1) {
// TODO: Deprecate
const url = event.notification.tag.split(':')[2] || '/';
event.waitUntil(openWindow(url));
} else {
logger.warn('Cannot route click with no data.url property. Using "/".', event.notification.tag);
event.waitUntil(openWindow('/'));
}
event.notification.close();
logger.groupEnd(event.notification.tag);
}
/* -------- NOTIFICATIONS HELPERS --------- */
function showNotification(data) {
if (!data || !data.tag) {
// eslint-disable-next-line no-param-reassign
data = $Notifications.default;
}
logger.group(data.tag);
logger.log('Show notification.', data.tag);
return self.registration
.showNotification(data.title, data)
.then(delayDismissNotification);
}
function fetchNotification(subscription) {
if (!subscription) {
logger.warn('No subscription found.');
throw new Error('No subscription found.');
}
logger.log('Fetching remote notification data.');
const queries = {
endpoint: subscription.endpoint
};
const url = formatUrl($Notifications.fallbackURL, queries);
return fetch(url, { credentials: 'include' });
}
function convertResponseToJson(response) {
if (response.status !== 200) {
throw new Error('Notification data fetch failed.');
}
return response.json();
}
function delayDismissNotification() {
setTimeout(function serviceWorkerDismissNotification() {
self.registration.getNotifications()
.then(notifications => {
notifications.forEach(notification => {
notification.close();
logger.log('Dismissing notification.', notification.tag);
logger.groupEnd(notification.tag);
});
});
}, $Notifications.duration || 5000);
}
function openWindow(url) {
if (clients.openWindow) {
return clients.openWindow(url);
}
return Promise.resolve();
}
function logNotificationReceived(event) {
return logAction(event, $Log.notificationReceived);
}
function logNotificationClick(event) {
return logAction(event.notification, $Log.notificationClicked);
}
function logAction(notification, url) {
logger.log(`Send log event to ${url}.`, notification.tag);
return self.registration.pushManager.getSubscription().then((subscription) => {
const query = {
endpoint: subscription.endpoint,
tag: notification.tag
};
return fetch(formatUrl(url, query), { credentials: 'include' });
});
}
function formatUrl(url, queries) {
const prefix = url.includes('?') ? '&' : '?';
const query = Object.keys(queries).map(function (key) {
return `${key}=${queries[key]}`;
}).join('&');
return url + prefix + query;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment