gaf-mobile
Version:
GAF mobile Web site
133 lines (117 loc) • 4.07 kB
JavaScript
/**
* This script will be imported in our service worker which would add a `fetch`
* event handler for intercepting tracking-related requests. For the tracking
* libraries (GTM, QTS, TTREF), the handler will observe the network-first
* strategy, falling back to the previously cached copy. For QTS tracking
* requests, this should handle network failures such as being offline.
* Failed requests are stored in IndexedDB. Every time the service worker
* starts up, this would try to replay the failed requests.
*/
// TODO: Implement some kind of garbage collection for failed requests stored
// in IndexedDB
/* global self */
;
var IDB = {
NAME: 'offline-qts',
STORE: 'requests',
VERSION: 1
};
var CACHE_NAME = 'offline-tracking-gaf-mobile';
var QTS_HOSTNAME = 't.freelancer.com';
var QTS_LIB_HOSTNAME = 'd2werhn82xczly.cloudfront.net';
var QTS_LIB_PATHNAME = '/20140328/main.min.js';
var GTM_HOSTNAME = 'www.googletagmanager.com';
var GTM_PATHNAME = '/gtm.js';
var TTREF_PATHNAME = '/build/js/ttref.js';
var idb = (self.IDBHelper)(IDB.NAME, IDB.VERSION, function(db) {
// upgrade callback
return db.createObjectStore(IDB.STORE);
});
/**
* Upserts the failed tracking requests to IndexedDB. We will do the following
* manipulations to the QTS request:
* 1. Push back the original event name to `real_event_name` param
* 2. Change event name to `mobile_offline_event`
* 3. Add a `real_timestamp` param
*/
var enqueueOfflineQts = function(url) {
var now = Math.round(Date.now() / 1000);
var newUrl = url;
var search = newUrl.search;
var searchMap = {};
if (search.charAt(0) === '?') {
search = search.slice(1);
}
// TODO: Use URLSearchParams when widely supported (Chrome 49+ and FF 29+)
var pairs = (search || '').split('&');
pairs.forEach(function(pair) {
var index = pair.indexOf('=');
if (index > -1) {
var key = pair.slice(0, index);
var value = decodeURIComponent(pair.slice(index + 1));
searchMap[key] = value;
}
});
searchMap.real_event_name = searchMap.en;
searchMap.en = 'mobile_offline_event';
searchMap.real_timestamp = now.toString();
var newPairs = [], key, value;
for (key in searchMap) {
value = encodeURIComponent(searchMap[key]);
newPairs.push(key + '=' + value);
}
newUrl.search = ('?' + newPairs.join('&'));
return idb.put(IDB.STORE, newUrl.toString(), now);
};
/**
* Resends requests stored in IndexedDB. If the request was successfully sent,
* the entry is deleted from the store.
*/
var resendOfflineQts = function() {
if (self.fetch) {
return idb.getAllKeys(IDB.STORE).then(function(urls) {
return Promise.all(urls.map(function(url) {
return self.fetch(url, { method: 'GET', mode: 'no-cors' })
.then(function() {
return idb.delete(IDB.STORE, url);
});
}));
});
}
};
/**
* Handles requests to either the tracking libraries or QTS requests.
*/
self.addEventListener('fetch', function(event) {
if (!self.fetch || !self.caches) { // Fetch or Cache API is not supported
return;
}
var request = event.request;
var url = new URL(request.url);
if (request.method === 'GET') {
if (url.hostname === QTS_HOSTNAME) {
event.respondWith(
self.fetch(request).catch(function() {
enqueueOfflineQts(url);
})
);
} else if ((url.pathname === TTREF_PATHNAME) ||
(url.hostname === GTM_HOSTNAME && url.pathname === GTM_PATHNAME) ||
(url.hostname === QTS_LIB_HOSTNAME && url.pathname === QTS_LIB_PATHNAME)
) {
event.respondWith(
self.caches.open(CACHE_NAME).then(function(cache) {
return self.fetch(request).then(function(response) {
return cache.put(request, response.clone()).then(function() {
return response;
});
}).catch(function() {
return cache.match(request);
});
})
);
}
}
});
// Resend locally stored offline QTS requests every time SW starts up
resendOfflineQts();