diff --git a/.gitignore b/.gitignore index 8b49273..e118479 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ +# Build files +/node_modules + +# Dev environment /magento diff --git a/README.md b/README.md index 134105a..5e3010e 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,14 @@ A Docker development environment is included with the project: docker-compose run --rm cli bash /src/setup.sh docker-compose up -d +### npm dependencies + +The extension uses npm to manage some of its web dependencies. Dependencies are installed and updated using npm, then +copied into the `src/` directory using an npm script. To update the web dependencies, run: + + docker-compose run --rm node npm update + docker-compose run --rm node npm run build + ### Testing Service Workers on Chrome Chrome is very strict about security and only allows Service Workers on localhost, or on an HTTPS site with a valid certificate. To bypass these restrictions for testing, use the `--ignore-certificate-errors` and `--unsafely-treat-insecure-origin-as-secure` flags to run a less secure copy of Chrome: diff --git a/composer.json b/composer.json index 03de098..f2564fb 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "meanbee/magento2-serviceworker", "description": "A Magento 2 extension that adds Service Worker support.", "type": "magento2-module", - "version": "1.0.0", + "version": "1.1.0", "license": [ "MIT" ], @@ -20,9 +20,9 @@ }, "require": { "magento/framework": "^100.1.2", - "magento/module-backend": "^100.1.2", - "magento/module-config": "^100.1.2", - "magento/module-cms": "^101.0.3" + "magento/module-backend": "^100.1.1", + "magento/module-config": "^100.1.1", + "magento/module-cms": "^101.0.2" }, "autoload": { "files": [ diff --git a/docker-compose.yml b/docker-compose.yml index 25b24f1..e2357af 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -81,6 +81,15 @@ services: links: - db + node: + image: node:8 + working_dir: /src + command: /bin/true + volumes: + - ~/.npm:/.npm + volumes_from: + - appdata + db: image: mariadb:10 ports: @@ -107,7 +116,7 @@ services: - SYNC_VERBOSE=1 - SYNC_MAX_INOTIFY_WATCHES=64000 privileged: true - restart: always + restart: on-failure:5 dbdata: image: tianon/true diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..90b18ee --- /dev/null +++ b/package-lock.json @@ -0,0 +1,12 @@ +{ + "name": "@meanbee/magento2-serviceworker", + "version": "1.1.0", + "lockfileVersion": 1, + "dependencies": { + "workbox-google-analytics": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-1.0.0.tgz", + "integrity": "sha1-tdiA6WLqg6ueqY3nwSEpdlM1f3A=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..8661663 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "@meanbee/magento2-serviceworker", + "version": "1.1.0", + "description": "A Magento 2 extension that adds Service Worker support.", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build:clean": "rm -rf src/view/frontend/web/js/lib/*", + "build:copy": "find node_modules/workbox-google-analytics -name 'workbox-google-analytics.prod.*' -exec install -t src/view/frontend/web/js/lib {} +", + "build": "npm run build:clean && npm run build:copy" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/meanbee/magento2-serviceworker.git" + }, + "author": "Tomas Gerulaitis ", + "license": "MIT", + "bugs": { + "url": "https://github.com/meanbee/magento2-serviceworker/issues" + }, + "homepage": "https://github.com/meanbee/magento2-serviceworker#readme", + "dependencies": { + "workbox-google-analytics": "^1.0.0" + } +} diff --git a/src/Block/Js.php b/src/Block/Js.php index 4011ef9..1c00427 100644 --- a/src/Block/Js.php +++ b/src/Block/Js.php @@ -53,4 +53,24 @@ public function getUrlBlacklist() { return $this->config->getUrlBlacklist(); } + + /** + * Check if Offline Google Analytics features are enabled. + * + * @return bool + */ + public function isGaOfflineEnabled() + { + return $this->config->isGaOfflineEnabled(); + } + + /** + * Get the URL to the Offline Google Analytics helper script. + * + * @return string + */ + public function getGaJsUrl() + { + return $this->getViewFileUrl("Meanbee_ServiceWorker::js/lib/workbox-google-analytics.prod.v1.0.0.js"); + } } diff --git a/src/Helper/Config.php b/src/Helper/Config.php index 15dcf4c..d21c5d8 100644 --- a/src/Helper/Config.php +++ b/src/Helper/Config.php @@ -9,6 +9,7 @@ class Config extends \Magento\Framework\App\Helper\AbstractHelper const XML_PATH_ENABLE = "web/serviceworker/enable"; const XML_PATH_OFFLINE_PAGE = "web/serviceworker/offline_page"; const XML_PATH_URL_BLACKLIST = "web/serviceworker/url_blacklist"; + const XML_PATH_GA_OFFLINE_ENABLE = "web/serviceworker/ga_offline_enable"; const PATH_WILDCARD_SYMBOL = "*"; @@ -81,4 +82,16 @@ public function getUrlBlacklist() return $data; } + + /** + * Check if Offline Google Analytics features are enabled. + * + * @param string $store + * + * @return bool + */ + public function isGaOfflineEnabled($store = null) + { + return $this->scopeConfig->isSetFlag(static::XML_PATH_GA_OFFLINE_ENABLE, ScopeInterface::SCOPE_STORE, $store); + } } diff --git a/src/etc/adminhtml/system.xml b/src/etc/adminhtml/system.xml index e231096..f7b442f 100644 --- a/src/etc/adminhtml/system.xml +++ b/src/etc/adminhtml/system.xml @@ -18,6 +18,11 @@ + + + + Magento\Config\Model\Config\Source\Yesno + diff --git a/src/etc/config.xml b/src/etc/config.xml index b5ffa00..5365e21 100644 --- a/src/etc/config.xml +++ b/src/etc/config.xml @@ -9,6 +9,7 @@ customer/account/create* customer/account/login* ]]> + 1 diff --git a/src/etc/module.xml b/src/etc/module.xml index 413266a..3b5933f 100644 --- a/src/etc/module.xml +++ b/src/etc/module.xml @@ -1,6 +1,6 @@ - + diff --git a/src/view/frontend/templates/serviceworker.js.phtml b/src/view/frontend/templates/serviceworker.js.phtml index 5e25a71..5a952a4 100644 --- a/src/view/frontend/templates/serviceworker.js.phtml +++ b/src/view/frontend/templates/serviceworker.js.phtml @@ -5,6 +5,12 @@ const version = 'getVersion() ?>'; const offlinePage = 'getOfflinePageUrl() ?>'; const urlBlacklist = getUrlBlacklist()) ?>; +isGaOfflineEnabled()): ?> +importScripts('getGaJsUrl() ?>'); + +workbox.googleAnalytics.initialize(); + + // Functions // ##################################### diff --git a/src/view/frontend/web/js/lib/workbox-google-analytics.prod.v1.0.0.js b/src/view/frontend/web/js/lib/workbox-google-analytics.prod.v1.0.0.js new file mode 100755 index 0000000..373c715 --- /dev/null +++ b/src/view/frontend/web/js/lib/workbox-google-analytics.prod.v1.0.0.js @@ -0,0 +1,45 @@ +/* + Copyright 2016 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.workbox = this.workbox || {}; +this.workbox.googleAnalytics = (function () { +'use strict'; + +var constants = {CACHE_NAME:'offline-google-analytics',IDB:{NAME:'offline-google-analytics',STORE:'urls',VERSION:1},MAX_ANALYTICS_BATCH_SIZE:20,STOP_RETRYING_AFTER:172800000,URL:{ANALYTICS_JS_PATH:'/analytics.js',COLLECT_PATH:'/collect',HOST:'www.google-analytics.com'}}; + +function createCommonjsModule(fn, module) { + return module = { exports: {} }, fn(module, module.exports), module.exports; +} + +var idb=createCommonjsModule(function(a){'use strict';(function(){function b(r){return Array.prototype.slice.call(r)}function c(r){return new Promise(function(s,t){r.onsuccess=function(){s(r.result);},r.onerror=function(){t(r.error);};})}function d(r,s,t){var u,v=new Promise(function(w,x){u=r[s].apply(r,t),c(u).then(w,x);});return v.request=u,v}function e(r,s,t){var u=d(r,s,t);return u.then(function(v){return v?new k(v,u.request):void 0})}function f(r,s,t){t.forEach(function(u){Object.defineProperty(r.prototype,u,{get:function(){return this[s][u]},set:function(v){this[s][u]=v;}});});}function g(r,s,t,u){u.forEach(function(v){v in t.prototype&&(r.prototype[v]=function(){return d(this[s],v,arguments)});});}function h(r,s,t,u){u.forEach(function(v){v in t.prototype&&(r.prototype[v]=function(){return this[s][v].apply(this[s],arguments)});});}function i(r,s,t,u){u.forEach(function(v){v in t.prototype&&(r.prototype[v]=function(){return e(this[s],v,arguments)});});}function j(r){this._index=r;}function k(r,s){this._cursor=r,this._request=s;}function l(r){this._store=r;}function m(r){this._tx=r,this.complete=new Promise(function(s,t){r.oncomplete=function(){s();},r.onerror=function(){t(r.error);},r.onabort=function(){t(r.error);};});}function n(r,s,t){this._db=r,this.oldVersion=s,this.transaction=new m(t);}function o(r){this._db=r;}f(j,'_index',['name','keyPath','multiEntry','unique']),g(j,'_index',IDBIndex,['get','getKey','getAll','getAllKeys','count']),i(j,'_index',IDBIndex,['openCursor','openKeyCursor']),f(k,'_cursor',['direction','key','primaryKey','value']),g(k,'_cursor',IDBCursor,['update','delete']),['advance','continue','continuePrimaryKey'].forEach(function(r){r in IDBCursor.prototype&&(k.prototype[r]=function(){var s=this,t=arguments;return Promise.resolve().then(function(){return s._cursor[r].apply(s._cursor,t),c(s._request).then(function(u){return u?new k(u,s._request):void 0})})});}),l.prototype.createIndex=function(){return new j(this._store.createIndex.apply(this._store,arguments))},l.prototype.index=function(){return new j(this._store.index.apply(this._store,arguments))},f(l,'_store',['name','keyPath','indexNames','autoIncrement']),g(l,'_store',IDBObjectStore,['put','add','delete','clear','get','getAll','getKey','getAllKeys','count']),i(l,'_store',IDBObjectStore,['openCursor','openKeyCursor']),h(l,'_store',IDBObjectStore,['deleteIndex']),m.prototype.objectStore=function(){return new l(this._tx.objectStore.apply(this._tx,arguments))},f(m,'_tx',['objectStoreNames','mode']),h(m,'_tx',IDBTransaction,['abort']),n.prototype.createObjectStore=function(){return new l(this._db.createObjectStore.apply(this._db,arguments))},f(n,'_db',['name','version','objectStoreNames']),h(n,'_db',IDBDatabase,['deleteObjectStore','close']),o.prototype.transaction=function(){return new m(this._db.transaction.apply(this._db,arguments))},f(o,'_db',['name','version','objectStoreNames']),h(o,'_db',IDBDatabase,['close']),['openCursor','openKeyCursor'].forEach(function(r){[l,j].forEach(function(s){s.prototype[r.replace('open','iterate')]=function(){var t=b(arguments),u=t[t.length-1],v=this._store||this._index,w=v[r].apply(v,t.slice(0,-1));w.onsuccess=function(){u(w.result);};};});}),[j,l].forEach(function(r){r.prototype.getAll||(r.prototype.getAll=function(s,t){var u=this,v=[];return new Promise(function(w){u.iterateCursor(s,function(x){return x?(v.push(x.value),void 0!==t&&v.length==t?void w(v):void x.continue()):void w(v)});})});});var q={open:function(r,s,t){var u=d(indexedDB,'open',[r,s]),v=u.request;return v.onupgradeneeded=function(w){t&&t(new n(v.result,w.oldVersion,v.transaction));},u.then(function(w){return new o(w)})},delete:function(r){return d(indexedDB,'deleteDatabase',[r])}};a.exports=q;})();}); + +class IDBHelper{constructor(a,b,c){if(a==void 0||b==void 0||c==void 0)throw Error('name, version, storeName must be passed to the constructor.');this._name=a,this._version=b,this._storeName=c;}_getDb(){return this._dbPromise?this._dbPromise:(this._dbPromise=idb.open(this._name,this._version,(a)=>{a.createObjectStore(this._storeName);}).then((a)=>{return a}),this._dbPromise)}close(){return this._dbPromise?this._dbPromise.then((a)=>{a.close(),this._dbPromise=null;}):void 0}put(a,b){return this._getDb().then((c)=>{const d=c.transaction(this._storeName,'readwrite'),e=d.objectStore(this._storeName);return e.put(b,a),d.complete})}delete(a){return this._getDb().then((b)=>{const c=b.transaction(this._storeName,'readwrite'),d=c.objectStore(this._storeName);return d.delete(a),c.complete})}get(a){return this._getDb().then((b)=>{return b.transaction(this._storeName).objectStore(this._storeName).get(a)})}getAllValues(){return this._getDb().then((a)=>{return a.transaction(this._storeName).objectStore(this._storeName).getAll()})}getAllKeys(){return this._getDb().then((a)=>{return a.transaction(this._storeName).objectStore(this._storeName).getAllKeys()})}} + +const idbHelper=new IDBHelper(constants.IDB.NAME,constants.IDB.VERSION,constants.IDB.STORE);var enqueueRequest = ((a,b)=>{const c=new URL(a.url);return a.text().then((d)=>{return d&&(c.search=d),idbHelper.put(c.toString(),b||Date.now())})}); + +class LogGroup{constructor({title:a,isPrimary:b}={}){this._isPrimary=b||!1,this._groupTitle=a||'',this._logs=[],this._childGroups=[],this._isFirefox=!1,/Firefox\/\d*\.\d*/.exec(navigator.userAgent)&&(this._isFirefox=!0),this._isEdge=!1,/Edge\/\d*\.\d*/.exec(navigator.userAgent)&&(this._isEdge=!0);}addLog(a){this._logs.push(a);}addChildGroup(a){0===a._logs.length||this._childGroups.push(a);}print(){return this._isEdge?void this._printEdgeFriendly():void(this._openGroup(),this._logs.forEach((a)=>{this._printLogDetails(a);}),this._childGroups.forEach((a)=>{a.print();}),this._closeGroup())}_printEdgeFriendly(){this._logs.forEach((a)=>{let c=a.message;'string'==typeof c&&(c=c.replace(/%c/g,''));const d=[c];a.error&&d.push(a.error),a.args&&d.push(a.args);const e=a.logFunc||console.log;e(...d);}),this._childGroups.forEach((a)=>{a.print();});}_printLogDetails(a){const b=a.logFunc?a.logFunc:console.log;let c=a.message,d=[c];a.colors&&!this._isEdge&&(d=d.concat(a.colors)),a.args&&(d=d.concat(a.args)),b(...d);}_openGroup(){if(this._isPrimary){if(0===this._childGroups.length)return;const a=this._logs.shift();if(this._isFirefox)return void this._printLogDetails(a);a.logFunc=console.group,this._printLogDetails(a);}else console.groupCollapsed(this._groupTitle);}_closeGroup(){this._isPrimary&&0===this._childGroups.length||console.groupEnd();}} + +function isServiceWorkerGlobalScope(){return'ServiceWorkerGlobalScope'in self&&self instanceof ServiceWorkerGlobalScope}function isDevBuild(){return`dev`==`prod`}function isLocalhost(){return!!('localhost'===location.hostname||'[::1]'===location.hostname||location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/))}var environment = {isDevBuild,isLocalhost,isServiceWorkerGlobalScope}; + +self.workbox=self.workbox||{},self.workbox.LOG_LEVEL=self.workbox.LOG_LEVEL||{none:-1,verbose:0,debug:1,warn:2,error:3};const LIGHT_GREY=`#bdc3c7`; const DARK_GREY=`#7f8c8d`; const LIGHT_GREEN=`#2ecc71`; const LIGHT_YELLOW=`#f1c40f`; const LIGHT_RED=`#e74c3c`; const LIGHT_BLUE=`#3498db`;class LogHelper{constructor(){this._defaultLogLevel=environment.isDevBuild()?self.workbox.LOG_LEVEL.debug:self.workbox.LOG_LEVEL.warn;}log(a){this._printMessage(self.workbox.LOG_LEVEL.verbose,a);}debug(a){this._printMessage(self.workbox.LOG_LEVEL.debug,a);}warn(a){this._printMessage(self.workbox.LOG_LEVEL.warn,a);}error(a){this._printMessage(self.workbox.LOG_LEVEL.error,a);}_printMessage(a,b){if(this._shouldLogMessage(a,b)){const c=this._getAllLogGroups(a,b);c.print();}}_getAllLogGroups(a,b){const c=new LogGroup({isPrimary:!0,title:'workbox log.'}),d=this._getPrimaryMessageDetails(a,b);if(c.addLog(d),b.error){const f={message:b.error,logFunc:console.error};c.addLog(f);}const e=new LogGroup({title:'Extra Information.'});if(b.that&&b.that.constructor&&b.that.constructor.name){const f=b.that.constructor.name;e.addLog(this._getKeyValueDetails('class',f));}return b.data&&('object'!=typeof b.data||b.data instanceof Array?e.addLog(this._getKeyValueDetails('additionalData',b.data)):Object.keys(b.data).forEach((f)=>{e.addLog(this._getKeyValueDetails(f,b.data[f]));})),c.addChildGroup(e),c}_getKeyValueDetails(a,b){return{message:`%c${a}: `,colors:[`color: ${LIGHT_BLUE}`],args:b}}_getPrimaryMessageDetails(a,b){let c,d;a===self.workbox.LOG_LEVEL.verbose?(c='Info',d=LIGHT_GREY):a===self.workbox.LOG_LEVEL.debug?(c='Debug',d=LIGHT_GREEN):a===self.workbox.LOG_LEVEL.warn?(c='Warn',d=LIGHT_YELLOW):a===self.workbox.LOG_LEVEL.error?(c='Error',d=LIGHT_RED):void 0;let e=`%c🔧 %c[${c}]`;const f=[`color: ${LIGHT_GREY}`,`color: ${d}`];let g;return'string'==typeof b?g=b:b.message&&(g=b.message),g&&(g=g.replace(/\s+/g,' '),e+=`%c ${g}`,f.push(`color: ${DARK_GREY}; font-weight: normal`)),{message:e,colors:f}}_shouldLogMessage(a,b){if(!b)return!1;let c=this._defaultLogLevel;return self&&self.workbox&&'number'==typeof self.workbox.logLevel&&(c=self.workbox.logLevel),c===self.workbox.LOG_LEVEL.none||a{return a=a||{},idbHelper$1.getAllKeys().then((b)=>{return Promise.all(b.map((c)=>{return idbHelper$1.get(c).then((d)=>{const e=Date.now()-d,f=new URL(c);if(e>constants.STOP_RETRYING_AFTER)return;if(!('searchParams'in f))return;let g=a.parameterOverrides||{};g.qt=e,Object.keys(g).sort().forEach((i)=>{f.searchParams.set(i,g[i]);});let h=a.hitFilter;if('function'==typeof h)try{h(f.searchParams);}catch(i){return}return fetch(f.toString())}).then(()=>idbHelper$1.delete(c))}))})}); + +const initialize=(a)=>{a=a||{};let b=!1;self.addEventListener('fetch',(c)=>{const d=new URL(c.request.url),e=c.request;if(d.hostname===constants.URL.HOST)if(d.pathname===constants.URL.COLLECT_PATH){const f=e.clone();c.respondWith(fetch(e).then((g)=>{return b&&replayQueuedRequests(a),b=!1,g},()=>{return logHelper.log('Enqueuing failed request...'),b=!0,enqueueRequest(f).then(()=>Response.error())}));}else d.pathname===constants.URL.ANALYTICS_JS_PATH&&c.respondWith(caches.open(constants.CACHE_NAME).then((f)=>{return fetch(e).then((g)=>{return f.put(e,g.clone()).then(()=>g)}).catch((g)=>{return logHelper.error(g),f.match(e)})}));}),replayQueuedRequests(a);};var index = {initialize}; + +return index; + +}()); +//# sourceMappingURL=workbox-google-analytics.prod.v1.0.0.js.map diff --git a/src/view/frontend/web/js/lib/workbox-google-analytics.prod.v1.0.0.js.map b/src/view/frontend/web/js/lib/workbox-google-analytics.prod.v1.0.0.js.map new file mode 100755 index 0000000..abe14c8 --- /dev/null +++ b/src/view/frontend/web/js/lib/workbox-google-analytics.prod.v1.0.0.js.map @@ -0,0 +1 @@ +{"version":3,"file":"workbox-google-analytics.prod.v1.0.0.js","sources":["../../src/lib/constants.js","../../../../node_modules/idb/lib/idb.js","../../../../lib/idb-helper.js","../../src/lib/enqueue-request.js","../../../../lib/log-group.js","../../../../lib/environment.js","../../../../lib/log-helper.js","../../src/lib/replay-queued-requests.js","../../src/index.js"],"sourcesContent":["/*\n Copyright 2016 Google Inc. All Rights Reserved.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n\nexport default {\n CACHE_NAME: 'offline-google-analytics',\n IDB: {\n NAME: 'offline-google-analytics',\n STORE: 'urls',\n VERSION: 1,\n },\n MAX_ANALYTICS_BATCH_SIZE: 20,\n STOP_RETRYING_AFTER: 1000 * 60 * 60 * 48, // Two days, in milliseconds.\n URL: {\n ANALYTICS_JS_PATH: '/analytics.js',\n COLLECT_PATH: '/collect',\n HOST: 'www.google-analytics.com',\n },\n};\n","'use strict';\n\n(function() {\n function toArray(arr) {\n return Array.prototype.slice.call(arr);\n }\n\n function promisifyRequest(request) {\n return new Promise(function(resolve, reject) {\n request.onsuccess = function() {\n resolve(request.result);\n };\n\n request.onerror = function() {\n reject(request.error);\n };\n });\n }\n\n function promisifyRequestCall(obj, method, args) {\n var request;\n var p = new Promise(function(resolve, reject) {\n request = obj[method].apply(obj, args);\n promisifyRequest(request).then(resolve, reject);\n });\n\n p.request = request;\n return p;\n }\n\n function promisifyCursorRequestCall(obj, method, args) {\n var p = promisifyRequestCall(obj, method, args);\n return p.then(function(value) {\n if (!value) return;\n return new Cursor(value, p.request);\n });\n }\n\n function proxyProperties(ProxyClass, targetProp, properties) {\n properties.forEach(function(prop) {\n Object.defineProperty(ProxyClass.prototype, prop, {\n get: function() {\n return this[targetProp][prop];\n },\n set: function(val) {\n this[targetProp][prop] = val;\n }\n });\n });\n }\n\n function proxyRequestMethods(ProxyClass, targetProp, Constructor, properties) {\n properties.forEach(function(prop) {\n if (!(prop in Constructor.prototype)) return;\n ProxyClass.prototype[prop] = function() {\n return promisifyRequestCall(this[targetProp], prop, arguments);\n };\n });\n }\n\n function proxyMethods(ProxyClass, targetProp, Constructor, properties) {\n properties.forEach(function(prop) {\n if (!(prop in Constructor.prototype)) return;\n ProxyClass.prototype[prop] = function() {\n return this[targetProp][prop].apply(this[targetProp], arguments);\n };\n });\n }\n\n function proxyCursorRequestMethods(ProxyClass, targetProp, Constructor, properties) {\n properties.forEach(function(prop) {\n if (!(prop in Constructor.prototype)) return;\n ProxyClass.prototype[prop] = function() {\n return promisifyCursorRequestCall(this[targetProp], prop, arguments);\n };\n });\n }\n\n function Index(index) {\n this._index = index;\n }\n\n proxyProperties(Index, '_index', [\n 'name',\n 'keyPath',\n 'multiEntry',\n 'unique'\n ]);\n\n proxyRequestMethods(Index, '_index', IDBIndex, [\n 'get',\n 'getKey',\n 'getAll',\n 'getAllKeys',\n 'count'\n ]);\n\n proxyCursorRequestMethods(Index, '_index', IDBIndex, [\n 'openCursor',\n 'openKeyCursor'\n ]);\n\n function Cursor(cursor, request) {\n this._cursor = cursor;\n this._request = request;\n }\n\n proxyProperties(Cursor, '_cursor', [\n 'direction',\n 'key',\n 'primaryKey',\n 'value'\n ]);\n\n proxyRequestMethods(Cursor, '_cursor', IDBCursor, [\n 'update',\n 'delete'\n ]);\n\n // proxy 'next' methods\n ['advance', 'continue', 'continuePrimaryKey'].forEach(function(methodName) {\n if (!(methodName in IDBCursor.prototype)) return;\n Cursor.prototype[methodName] = function() {\n var cursor = this;\n var args = arguments;\n return Promise.resolve().then(function() {\n cursor._cursor[methodName].apply(cursor._cursor, args);\n return promisifyRequest(cursor._request).then(function(value) {\n if (!value) return;\n return new Cursor(value, cursor._request);\n });\n });\n };\n });\n\n function ObjectStore(store) {\n this._store = store;\n }\n\n ObjectStore.prototype.createIndex = function() {\n return new Index(this._store.createIndex.apply(this._store, arguments));\n };\n\n ObjectStore.prototype.index = function() {\n return new Index(this._store.index.apply(this._store, arguments));\n };\n\n proxyProperties(ObjectStore, '_store', [\n 'name',\n 'keyPath',\n 'indexNames',\n 'autoIncrement'\n ]);\n\n proxyRequestMethods(ObjectStore, '_store', IDBObjectStore, [\n 'put',\n 'add',\n 'delete',\n 'clear',\n 'get',\n 'getAll',\n 'getKey',\n 'getAllKeys',\n 'count'\n ]);\n\n proxyCursorRequestMethods(ObjectStore, '_store', IDBObjectStore, [\n 'openCursor',\n 'openKeyCursor'\n ]);\n\n proxyMethods(ObjectStore, '_store', IDBObjectStore, [\n 'deleteIndex'\n ]);\n\n function Transaction(idbTransaction) {\n this._tx = idbTransaction;\n this.complete = new Promise(function(resolve, reject) {\n idbTransaction.oncomplete = function() {\n resolve();\n };\n idbTransaction.onerror = function() {\n reject(idbTransaction.error);\n };\n idbTransaction.onabort = function() {\n reject(idbTransaction.error);\n };\n });\n }\n\n Transaction.prototype.objectStore = function() {\n return new ObjectStore(this._tx.objectStore.apply(this._tx, arguments));\n };\n\n proxyProperties(Transaction, '_tx', [\n 'objectStoreNames',\n 'mode'\n ]);\n\n proxyMethods(Transaction, '_tx', IDBTransaction, [\n 'abort'\n ]);\n\n function UpgradeDB(db, oldVersion, transaction) {\n this._db = db;\n this.oldVersion = oldVersion;\n this.transaction = new Transaction(transaction);\n }\n\n UpgradeDB.prototype.createObjectStore = function() {\n return new ObjectStore(this._db.createObjectStore.apply(this._db, arguments));\n };\n\n proxyProperties(UpgradeDB, '_db', [\n 'name',\n 'version',\n 'objectStoreNames'\n ]);\n\n proxyMethods(UpgradeDB, '_db', IDBDatabase, [\n 'deleteObjectStore',\n 'close'\n ]);\n\n function DB(db) {\n this._db = db;\n }\n\n DB.prototype.transaction = function() {\n return new Transaction(this._db.transaction.apply(this._db, arguments));\n };\n\n proxyProperties(DB, '_db', [\n 'name',\n 'version',\n 'objectStoreNames'\n ]);\n\n proxyMethods(DB, '_db', IDBDatabase, [\n 'close'\n ]);\n\n // Add cursor iterators\n // TODO: remove this once browsers do the right thing with promises\n ['openCursor', 'openKeyCursor'].forEach(function(funcName) {\n [ObjectStore, Index].forEach(function(Constructor) {\n Constructor.prototype[funcName.replace('open', 'iterate')] = function() {\n var args = toArray(arguments);\n var callback = args[args.length - 1];\n var nativeObject = this._store || this._index;\n var request = nativeObject[funcName].apply(nativeObject, args.slice(0, -1));\n request.onsuccess = function() {\n callback(request.result);\n };\n };\n });\n });\n\n // polyfill getAll\n [Index, ObjectStore].forEach(function(Constructor) {\n if (Constructor.prototype.getAll) return;\n Constructor.prototype.getAll = function(query, count) {\n var instance = this;\n var items = [];\n\n return new Promise(function(resolve) {\n instance.iterateCursor(query, function(cursor) {\n if (!cursor) {\n resolve(items);\n return;\n }\n items.push(cursor.value);\n\n if (count !== undefined && items.length == count) {\n resolve(items);\n return;\n }\n cursor.continue();\n });\n });\n };\n });\n\n var exp = {\n open: function(name, version, upgradeCallback) {\n var p = promisifyRequestCall(indexedDB, 'open', [name, version]);\n var request = p.request;\n\n request.onupgradeneeded = function(event) {\n if (upgradeCallback) {\n upgradeCallback(new UpgradeDB(request.result, event.oldVersion, request.transaction));\n }\n };\n\n return p.then(function(db) {\n return new DB(db);\n });\n },\n delete: function(name) {\n return promisifyRequestCall(indexedDB, 'deleteDatabase', [name]);\n }\n };\n\n if (typeof module !== 'undefined') {\n module.exports = exp;\n }\n else {\n self.idb = exp;\n }\n}());\n","/*\n Copyright 2016 Google Inc. All Rights Reserved.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n\n/* eslint-disable require-jsdoc */\n\nimport idb from 'idb';\n\n/**\n * A wrapper to store for an IDB connection to a particular ObjectStore.\n *\n * @private\n */\nclass IDBHelper {\n constructor(name, version, storeName) {\n if (name == undefined || version == undefined || storeName == undefined) {\n throw Error('name, version, storeName must be passed to the ' +\n 'constructor.');\n }\n\n this._name = name;\n this._version = version;\n this._storeName = storeName;\n }\n\n /**\n * Returns a promise that resolves with an open connection to IndexedDB,\n * either existing or newly opened.\n *\n * @private\n * @return {Promise}\n */\n _getDb() {\n if (this._dbPromise) {\n return this._dbPromise;\n }\n\n this._dbPromise = idb.open(this._name, this._version, (upgradeDB) => {\n upgradeDB.createObjectStore(this._storeName);\n })\n .then((db) => {\n return db;\n });\n\n return this._dbPromise;\n }\n\n close() {\n if (!this._dbPromise) {\n return;\n }\n\n return this._dbPromise\n .then((db) => {\n db.close();\n this._dbPromise = null;\n });\n }\n\n /**\n * Wrapper on top of the idb wrapper, which simplifies saving the key/value\n * pair to the object store.\n * Returns a Promise that fulfills when the transaction completes.\n *\n * @private\n * @param {String} key\n * @param {Object} value\n * @return {Promise}\n */\n put(key, value) {\n return this._getDb().then((db) => {\n const tx = db.transaction(this._storeName, 'readwrite');\n const objectStore = tx.objectStore(this._storeName);\n objectStore.put(value, key);\n return tx.complete;\n });\n }\n\n /**\n * Wrapper on top of the idb wrapper, which simplifies deleting an entry\n * from the object store.\n * Returns a Promise that fulfills when the transaction completes.\n *\n * @private\n * @param {String} key\n * @return {Promise}\n */\n delete(key) {\n return this._getDb().then((db) => {\n const tx = db.transaction(this._storeName, 'readwrite');\n const objectStore = tx.objectStore(this._storeName);\n objectStore.delete(key);\n return tx.complete;\n });\n }\n\n /**\n * Wrapper on top of the idb wrapper, which simplifies getting a key's value\n * from the object store.\n * Returns a promise that fulfills with the value.\n *\n * @private\n * @param {String} key\n * @return {Promise}\n */\n get(key) {\n return this._getDb().then((db) => {\n return db.transaction(this._storeName)\n .objectStore(this._storeName)\n .get(key);\n });\n }\n\n /**\n * Wrapper on top of the idb wrapper, which simplifies getting all the values\n * in an object store.\n * Returns a promise that fulfills with all the values.\n *\n * @private\n * @return {Promise>}\n */\n getAllValues() {\n return this._getDb().then((db) => {\n return db.transaction(this._storeName)\n .objectStore(this._storeName)\n .getAll();\n });\n }\n\n /**\n * Wrapper on top of the idb wrapper, which simplifies getting all the keys\n * in an object store.\n * Returns a promise that fulfills with all the keys.\n *\n * @private\n * @param {String} storeName\n * @return {Promise>}\n */\n getAllKeys() {\n return this._getDb().then((db) => {\n return db.transaction(this._storeName)\n .objectStore(this._storeName)\n .getAllKeys();\n });\n }\n}\n\nexport default IDBHelper;\n","/*\n Copyright 2016 Google Inc. All Rights Reserved.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n http://www.apache.org/licenses/LICENSE-2.0\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n */\n\n/* eslint-env worker, serviceworker */\n\nimport IDBHelper from '../../../../lib/idb-helper.js';\nimport constants from './constants.js';\n\nconst idbHelper = new IDBHelper(constants.IDB.NAME, constants.IDB.VERSION,\n constants.IDB.STORE);\n\n/**\n * Adds a URL to IndexedDB, along with the current timestamp.\n *\n * If the request has a body, that body will be used as the URL's search\n * parameters when saving the URL to IndexedDB.\n *\n * If no `time` parameter is provided, Date.now() will be used.\n *\n * @private\n * @param {Request} request\n* @param {Number} [time]\n * @return {Promise.} A promise that resolves when IndexedDB is updated.\n */\nexport default (request, time) => {\n const url = new URL(request.url);\n return request.text().then((body) => {\n // If there's a request body, then use it as the URL's search value.\n // This is most likely because the original request was an HTTP POST\n // that uses the beacon transport.\n if (body) {\n url.search = body;\n }\n\n return idbHelper.put(url.toString(), time || Date.now());\n });\n};\n","/* eslint-disable no-console */\n\n/**\n * A simple helper to manage the print of a set of logs\n */\nclass LogGroup {\n /**\n * @param {object} input\n * @param {string} input.title\n * @param {boolean} input.isPrimary\n */\n constructor({title, isPrimary} = {}) {\n this._isPrimary = isPrimary || false;\n this._groupTitle = title || '';\n this._logs = [];\n this._childGroups = [];\n\n this._isFirefox = false;\n if (/Firefox\\/\\d*\\.\\d*/.exec(navigator.userAgent)) {\n this._isFirefox = true;\n }\n\n this._isEdge = false;\n if (/Edge\\/\\d*\\.\\d*/.exec(navigator.userAgent)) {\n this._isEdge = true;\n }\n }\n\n /**\n *@param {object} logDetails\n */\n addLog(logDetails) {\n this._logs.push(logDetails);\n }\n\n /**\n * @param {object} group\n */\n addChildGroup(group) {\n if (group._logs.length === 0) {\n return;\n }\n\n this._childGroups.push(group);\n }\n\n /**\n * prints out this log group to the console.\n */\n print() {\n if (this._isEdge) {\n this._printEdgeFriendly();\n return;\n }\n\n this._openGroup();\n\n this._logs.forEach((logDetails) => {\n this._printLogDetails(logDetails);\n });\n\n this._childGroups.forEach((group) => {\n group.print();\n });\n\n this._closeGroup();\n }\n\n /**\n * This prints a simpler log for Edge which has poor group support.\n * For more details see:\n * https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11363242/\n */\n _printEdgeFriendly() {\n // Edge has no support for colors at all and poor support for groups.\n this._logs.forEach((logDetails, index) => {\n // Message can be an object - i.e. an error.\n let message = logDetails.message;\n if (typeof message === 'string') {\n // Replace the %c value with an empty string.\n message = message.replace(/%c/g, '');\n }\n const logArgs = [message];\n if (logDetails.error) {\n logArgs.push(logDetails.error);\n }\n if (logDetails.args) {\n logArgs.push(logDetails.args);\n }\n const logFunc = logDetails.logFunc || console.log;\n logFunc(...logArgs);\n });\n\n this._childGroups.forEach((group, index) => {\n group.print();\n });\n }\n\n /**\n * Prints the specific logDetails object.\n * @param {object} logDetails\n */\n _printLogDetails(logDetails) {\n const logFunc = logDetails.logFunc ? logDetails.logFunc : console.log;\n let message = logDetails.message;\n let allArguments = [message];\n if (logDetails.colors && !this._isEdge) {\n allArguments = allArguments.concat(logDetails.colors);\n }\n if (logDetails.args) {\n allArguments = allArguments.concat(logDetails.args);\n }\n logFunc(...allArguments);\n }\n\n /**\n * Opens a console group - managing differences in Firefox.\n */\n _openGroup() {\n if (this._isPrimary) {\n // Only start a group is there are child groups\n if (this._childGroups.length === 0) {\n return;\n }\n\n const logDetails = this._logs.shift();\n if (this._isFirefox) {\n // Firefox doesn't support colors logs in console.group.\n this._printLogDetails(logDetails);\n return;\n }\n\n // Print the colored message with console.group\n logDetails.logFunc = console.group;\n this._printLogDetails(logDetails);\n } else {\n console.groupCollapsed(this._groupTitle);\n }\n }\n\n /**\n * Closes a console group\n */\n _closeGroup() {\n // Only close a group if there was a child group opened\n if (this._isPrimary && this._childGroups.length === 0) {\n return;\n }\n\n console.groupEnd();\n }\n}\n\nexport default LogGroup;\n","/*\n Copyright 2016 Google Inc. All Rights Reserved.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n\n/**\n * @private\n * @return {boolean} True, if we're running in the service worker global scope.\n * False otherwise.\n */\nfunction isServiceWorkerGlobalScope() {\n return ('ServiceWorkerGlobalScope' in self &&\n self instanceof ServiceWorkerGlobalScope);\n}\n\n/**\n * @private\n * @return {boolean} True, if we're running a development bundle.\n * False otherwise.\n */\nfunction isDevBuild() {\n // `BUILD_PROCESS_REPLACE::BUILD_TARGET` is replaced during the build process.\n return `BUILD_PROCESS_REPLACE::BUILD_TARGET` === `dev`;\n}\n\n/**\n * @private\n * @return {boolean} True, if we're running on localhost or the equivalent IP\n * address. False otherwise.\n */\nfunction isLocalhost() {\n return Boolean(\n location.hostname === 'localhost' ||\n // [::1] is the IPv6 localhost address.\n location.hostname === '[::1]' ||\n // 127.0.0.1/8 is considered localhost for IPv4.\n location.hostname.match(\n /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n )\n );\n}\n\nexport default {\n isDevBuild,\n isLocalhost,\n isServiceWorkerGlobalScope,\n};\n","/*\n Copyright 2016 Google Inc. All Rights Reserved.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n\n/* eslint-disable no-console */\n\nimport LogGroup from './log-group';\nimport environment from './environment';\n\nself.workbox = self.workbox || {};\nself.workbox.LOG_LEVEL = self.workbox.LOG_LEVEL || {\n none: -1,\n verbose: 0,\n debug: 1,\n warn: 2,\n error: 3,\n};\n\nconst LIGHT_GREY = `#bdc3c7`;\nconst DARK_GREY = `#7f8c8d`;\nconst LIGHT_GREEN = `#2ecc71`;\nconst LIGHT_YELLOW = `#f1c40f`;\nconst LIGHT_RED = `#e74c3c`;\nconst LIGHT_BLUE = `#3498db`;\n\n/**\n * A class that will only log given the current log level\n * defined by the developer.\n *\n * Define custom log level by setting `self.workbox.logLevel`.\n *\n * @example\n *\n * self.workbox.logLevel = self.workbox.LOG_LEVEL.verbose;\n *\n * @private\n */\nclass LogHelper {\n /**\n * LogHelper constructor.\n */\n constructor() {\n this._defaultLogLevel = environment.isDevBuild() ?\n self.workbox.LOG_LEVEL.debug :\n self.workbox.LOG_LEVEL.warn;\n }\n\n /**\n * The most verbose log level.\n *\n * @param {Object} options The options of the log.\n */\n log(options) {\n this._printMessage(self.workbox.LOG_LEVEL.verbose, options);\n }\n\n /**\n * Useful for logs that are more exceptional that log()\n * but not severe.\n *\n * @param {Object} options The options of the log.\n */\n debug(options) {\n this._printMessage(self.workbox.LOG_LEVEL.debug, options);\n }\n\n /**\n * Warning messages.\n *\n * @param {Object} options The options of the log.\n */\n warn(options) {\n this._printMessage(self.workbox.LOG_LEVEL.warn, options);\n }\n\n /**\n * Error logs.\n *\n * @param {Object} options The options of the log.\n */\n error(options) {\n this._printMessage(self.workbox.LOG_LEVEL.error, options);\n }\n\n /**\n * Method to print to the console.\n * @param {number} logLevel\n * @param {Object} logOptions\n */\n _printMessage(logLevel, logOptions) {\n if (!this._shouldLogMessage(logLevel, logOptions)) {\n return;\n }\n\n const logGroups = this._getAllLogGroups(logLevel, logOptions);\n logGroups.print();\n }\n\n /**\n * Print a user friendly log to the console.\n * @param {numer} logLevel A number from self.workbox.LOG_LEVEL\n * @param {Object} logOptions Arguments to print to the console\n * @return {LogGroup} Returns a log group to print to the console.\n */\n _getAllLogGroups(logLevel, logOptions) {\n const topLogGroup = new LogGroup({\n isPrimary: true,\n title: 'workbox log.',\n });\n\n const primaryMessage = this._getPrimaryMessageDetails(logLevel, logOptions);\n topLogGroup.addLog(primaryMessage);\n\n if (logOptions.error) {\n const errorMessage = {\n message: logOptions.error,\n logFunc: console.error,\n };\n topLogGroup.addLog(errorMessage);\n }\n\n const extraInfoGroup = new LogGroup({title: 'Extra Information.'});\n if (logOptions.that && logOptions.that.constructor &&\n logOptions.that.constructor.name) {\n const className = logOptions.that.constructor.name;\n extraInfoGroup.addLog(\n this._getKeyValueDetails('class', className)\n );\n }\n\n if (logOptions.data) {\n if (typeof logOptions.data === 'object' &&\n !(logOptions.data instanceof Array)) {\n Object.keys(logOptions.data).forEach((keyName) => {\n extraInfoGroup.addLog(\n this._getKeyValueDetails(keyName, logOptions.data[keyName])\n );\n });\n } else {\n extraInfoGroup.addLog(\n this._getKeyValueDetails('additionalData', logOptions.data)\n );\n }\n }\n\n topLogGroup.addChildGroup(extraInfoGroup);\n\n return topLogGroup;\n }\n\n /**\n * This is a helper function to wrap key value pairss to a colored key\n * value string.\n * @param {string} key\n * @param {string} value\n * @return {Object} The object containing a message, color and Arguments\n * for the console.\n */\n _getKeyValueDetails(key, value) {\n return {\n message: `%c${key}: `,\n colors: [`color: ${LIGHT_BLUE}`],\n args: value,\n };\n }\n\n /**\n * Helper method to color the primary message for the log\n * @param {number} logLevel One of self.workbox.LOG_LEVEL\n * @param {Object} logOptions Arguments to print to the console\n * @return {Object} Object containing the message and color info to print.\n */\n _getPrimaryMessageDetails(logLevel, logOptions) {\n let logLevelName;\n let logLevelColor;\n switch (logLevel) {\n case self.workbox.LOG_LEVEL.verbose:\n logLevelName = 'Info';\n logLevelColor = LIGHT_GREY;\n break;\n case self.workbox.LOG_LEVEL.debug:\n logLevelName = 'Debug';\n logLevelColor = LIGHT_GREEN;\n break;\n case self.workbox.LOG_LEVEL.warn:\n logLevelName = 'Warn';\n logLevelColor = LIGHT_YELLOW;\n break;\n case self.workbox.LOG_LEVEL.error:\n logLevelName = 'Error';\n logLevelColor = LIGHT_RED;\n break;\n }\n\n let primaryLogMessage = `%c🔧 %c[${logLevelName}]`;\n const primaryLogColors = [\n `color: ${LIGHT_GREY}`,\n `color: ${logLevelColor}`,\n ];\n\n let message;\n if(typeof logOptions === 'string') {\n message = logOptions;\n } else if (logOptions.message) {\n message = logOptions.message;\n }\n\n if (message) {\n message = message.replace(/\\s+/g, ' ');\n primaryLogMessage += `%c ${message}`;\n primaryLogColors.push(`color: ${DARK_GREY}; font-weight: normal`);\n }\n\n return {\n message: primaryLogMessage,\n colors: primaryLogColors,\n };\n }\n\n /**\n * Test if the message should actually be logged.\n * @param {number} logLevel The level of the current log to be printed.\n * @param {Object|String} logOptions The options to log.\n * @return {boolean} Returns true of the message should be printed.\n */\n _shouldLogMessage(logLevel, logOptions) {\n if (!logOptions) {\n return false;\n }\n\n let minValidLogLevel = this._defaultLogLevel;\n if (self && self.workbox && typeof self.workbox.logLevel === 'number') {\n minValidLogLevel = self.workbox.logLevel;\n }\n\n if (minValidLogLevel === self.workbox.LOG_LEVEL.none ||\n logLevel < minValidLogLevel) {\n return false;\n }\n\n return true;\n }\n}\n\nexport default new LogHelper();\n","/*\n Copyright 2016 Google Inc. All Rights Reserved.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n http://www.apache.org/licenses/LICENSE-2.0\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n */\n\n/* eslint-env worker, serviceworker */\n\nimport IDBHelper from '../../../../lib/idb-helper.js';\nimport constants from './constants.js';\n\nconst idbHelper = new IDBHelper(constants.IDB.NAME, constants.IDB.VERSION,\n constants.IDB.STORE);\n\n/**\n * Replays all the queued requests found in IndexedDB, by calling fetch()\n * with an additional parameter indicating the offset from the original time.\n *\n * Returns a promise that resolves when the replaying is complete.\n *\n * @private\n * @param {Object=} config Optional configuration arguments.\n * @param {Object=} config.parameterOverrides Optional\n * [Measurement Protocol parameters](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters),\n * expressed as key/value pairs, to be added to replayed\n * Google Analytics requests. This can be used to, e.g., set\n * a custom dimension indicating that the request was\n * replayed.\n * @param {Function=} config.hitFilter Optional\n * A function that allows you to modify the hit parameters\n * prior to replaying the hit. The function is invoked with\n * the original hit's URLSearchParams object as its only\n * argument. To abort the hit and prevent it from being\n * replayed, throw an error.\n * @return {Promise.}\n */\n export default (config) => {\n config = config || {};\n\n return idbHelper.getAllKeys().then((urls) => {\n return Promise.all(urls.map((url) => {\n return idbHelper.get(url).then((hitTime) => {\n const queueTime = Date.now() - hitTime;\n const newUrl = new URL(url);\n\n // Do not attempt to replay hits that are too old.\n if (queueTime > constants.STOP_RETRYING_AFTER) {\n return;\n }\n\n // Do not attempt to replay hits in browsers without\n // URLSearchParams support.\n if (!('searchParams' in newUrl)) {\n return;\n }\n\n let parameterOverrides = config.parameterOverrides || {};\n parameterOverrides.qt = queueTime;\n\n // Call sort() on the keys so that there's a reliable order of calls\n // to searchParams.set(). This isn't important in terms of\n // functionality, but it will make testing easier, since the\n // URL serialization depends on the order in which .set() is called.\n Object.keys(parameterOverrides).sort().forEach((parameter) => {\n newUrl.searchParams.set(parameter, parameterOverrides[parameter]);\n });\n\n // If the hitFilter config option was passed and is a function,\n // invoke it with searchParams as its argument allowing the function\n // to modify the hit prior to sending it. The function can also\n // throw an error to abort the hit if needed.\n let hitFilter = config.hitFilter;\n if (typeof hitFilter === 'function') {\n try {\n hitFilter(newUrl.searchParams);\n } catch (err) {\n return;\n }\n }\n\n return fetch(newUrl.toString());\n }).then(() => idbHelper.delete(url));\n }));\n });\n};\n","/*\n Copyright 2016 Google Inc. All Rights Reserved.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n http://www.apache.org/licenses/LICENSE-2.0\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n */\n\n/* eslint-env worker, serviceworker */\n\nimport constants from './lib/constants.js';\nimport enqueueRequest from './lib/enqueue-request.js';\nimport logHelper from '../../../lib/log-helper.js';\nimport replayQueuedRequests from './lib/replay-queued-requests.js';\n\n/**\n * In order to use the library, call\n * `workbox.googleAnalytics.initialize()`.\n * It will take care of setting up service worker `fetch` handlers to ensure\n * that the Google Analytics JavaScript is available offline, and that any\n * Google Analytics requests made while offline are saved (using `IndexedDB`)\n * and retried the next time the service worker starts up.\n *\n * @example\n * // This code should live inside your service worker JavaScript, ideally\n * // before any other 'fetch' event handlers are defined:\n *\n * // First, import the library into the service worker global scope:\n * importScripts('path/to/offline-google-analytics-import.js');\n *\n * // Then, call workbox.googleAnalytics.initialize():\n * workbox.googleAnalytics.initialize();\n *\n * // At this point, implement any other service worker caching strategies\n * // appropriate for your web app.\n *\n * @example\n * // If you need to specify parameters to be sent with each hit, you can use\n * // the `parameterOverrides` configuration option. This is useful in cases\n * // where you want to set a custom dimension on all hits sent by the service\n * // worker to differentiate them in your reports later.\n * workbox.googleAnalytics.initialize({\n * parameterOverrides: {\n * cd1: 'replay'\n * }\n * });\n *\n * @example\n * // In situations where you need to programmatically modify a hit's\n * // parameters you can use the `hitFilter` option. One example of when this\n * // might be useful is if you wanted to track the amount of time that elapsed\n * // between when the hit was attempted and when it was successfully replayed.\n * workbox.googleAnalytics.initialize({\n * hitFilter: searchParams =>\n * // Sets the `qt` param as a custom metric.\n * const qt = searchParams.get('qt');\n * searchParams.set('cm1', qt);\n * }\n * });\n *\n * @module workbox-google-analytics\n */\n\n/**\n * @alias workbox.googleAnalytics.initialize\n * @param {Object=} config\n * @param {Object=} config.parameterOverrides\n * [Measurement Protocol parameters](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters),\n * expressed as key/value pairs, to be added to replayed\n * Google Analytics requests. This can be used to, e.g., set\n * a custom dimension indicating that the request was\n * replayed.\n * @param {Function=} config.hitFilter\n * A function that allows you to modify the hit parameters\n * prior to replaying the hit. The function is invoked with\n * the original hit's URLSearchParams object as its only\n * argument. To abort the hit and prevent it from being\n * replayed, throw an error.\n * @memberof module:workbox-google-analytics\n */\nconst initialize = (config) => {\n config = config || {};\n\n // Stores whether or not the previous /collect request failed.\n let previousHitFailed = false;\n\n self.addEventListener('fetch', (event) => {\n const url = new URL(event.request.url);\n const request = event.request;\n\n if (url.hostname === constants.URL.HOST) {\n if (url.pathname === constants.URL.COLLECT_PATH) {\n // If this is a /collect request, then use a network-first strategy,\n // falling back to queueing the request in IndexedDB.\n\n // Make a clone of the request before we use it, in case we need\n // to read the request body later on.\n const clonedRequest = request.clone();\n\n event.respondWith(\n fetch(request).then((response) => {\n if (previousHitFailed) {\n replayQueuedRequests(config);\n }\n previousHitFailed = false;\n return response;\n }, (error) => {\n logHelper.log('Enqueuing failed request...');\n previousHitFailed = true;\n return enqueueRequest(clonedRequest).then(() => Response.error());\n })\n );\n } else if (url.pathname === constants.URL.ANALYTICS_JS_PATH) {\n // If this is a request for the Google Analytics JavaScript library,\n // use the network first, falling back to the previously cached copy.\n event.respondWith(\n caches.open(constants.CACHE_NAME).then((cache) => {\n return fetch(request).then((response) => {\n return cache.put(request, response.clone()).then(() => response);\n }).catch((error) => {\n logHelper.error(error);\n return cache.match(request);\n });\n })\n );\n }\n }\n });\n\n replayQueuedRequests(config);\n};\n\nexport default {initialize};\n"],"names":["CACHE_NAME","IDB","NAME","STORE","VERSION","MAX_ANALYTICS_BATCH_SIZE","STOP_RETRYING_AFTER","URL","ANALYTICS_JS_PATH","COLLECT_PATH","HOST","prototype","slice","call","arr","request","onsuccess","resolve","result","onerror","reject","error","obj","method","apply","args","promisifyRequest","then","p","promisifyRequestCall","value","properties","forEach","Object","defineProperty","ProxyClass","prop","get","targetProp","set","val","arguments","_index","index","_cursor","cursor","_request","_store","store","_tx","idbTransaction","complete","oncomplete","onabort","_db","db","oldVersion","transaction","proxyProperties","Index","proxyRequestMethods","IDBIndex","proxyCursorRequestMethods","Cursor","IDBCursor","methodName","ObjectStore","createIndex","IDBObjectStore","proxyMethods","Transaction","objectStore","IDBTransaction","UpgradeDB","createObjectStore","IDBDatabase","DB","Constructor","funcName","replace","toArray","length","nativeObject","callback","getAll","instance","iterateCursor","query","items","push","count","continue","open","indexedDB","name","version","onupgradeneeded","upgradeCallback","event","delete","module","exp","constructor","storeName","_name","_version","_storeName","_getDb","_dbPromise","idb","upgradeDB","close","put","tx","key","getAllValues","getAllKeys","constants","url","text","search","body","idbHelper","toString","time","Date","now","title","isPrimary","_isPrimary","_groupTitle","_logs","_childGroups","_isFirefox","exec","navigator","userAgent","_isEdge","addLog","logDetails","addChildGroup","group","print","_printEdgeFriendly","_openGroup","_printLogDetails","_closeGroup","message","logArgs","logFunc","console","log","colors","allArguments","concat","shift","groupCollapsed","groupEnd","self","hostname","location","match","isDevBuild","isLocalhost","isServiceWorkerGlobalScope","workbox","LOG_LEVEL","none","verbose","debug","warn","DARK_GREY","LIGHT_GREEN","LIGHT_YELLOW","LIGHT_RED","LIGHT_BLUE","_defaultLogLevel","environment","_printMessage","options","_shouldLogMessage","logLevel","logOptions","_getAllLogGroups","logGroups","_getPrimaryMessageDetails","topLogGroup","primaryMessage","errorMessage","that","extraInfoGroup","_getKeyValueDetails","className","data","keys","keyName","LIGHT_GREY","logLevelName","logLevelColor","primaryLogColors","primaryLogMessage","minValidLogLevel","config","all","urls","map","hitTime","queueTime","parameterOverrides","qt","sort","newUrl","searchParams","parameter","hitFilter","addEventListener","pathname","clone","respondWith","fetch","replayQueuedRequests","response","enqueueRequest","clonedRequest","Response","caches","catch","cache","initialize"],"mappings":"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"} \ No newline at end of file