diff --git a/dist/offliner-client.js b/dist/offliner-client.js index d6dbd1b..7aebb58 100644 --- a/dist/offliner-client.js +++ b/dist/offliner-client.js @@ -79,21 +79,78 @@ * @return {Promise} A promise resolving if the installation success. */ install: function () { - this._installMessageHandlers(); + if (!('serviceWorker' in navigator)) { + return Promise.reject(new Error('serviceworkers-not-supported')); + } + return navigator.serviceWorker.register(workerURL, { scope: root - }); + }).then(function (registration) { + return this.connect().then(function () { + return registration; + }); + }.bind(this)); }, /** - * If you are using offliner as a serviceworkerware middleware, instead - * of calling {{#crossLink OfflinerClient/install:method}}{{/crossLink}}, - * call `connect()` to avoid registering the worker. + * Keeps the promise of connect. + * + * @property _connecting + * @type Promise + * @private + */ + _connecting: null, + + /** + * Connects the client with offliner allowing the client to control offliner + * and receive events. * * @method connect + * @return {Promise} A promise resolving once connection has been stablished + * with the worker and communication is possible. */ connect: function () { - this._installMessageHandlers(); + if (!this._connecting) { this._connecting = this._connect(); } + return this._connecting; + }, + + /** + * The actual implementation for {{#crossLink "connect:method"}}{{/crossLink}} + * + * @method _connect + * @return {Promise} A promise resolving once connection has been stablished + * with the worker and communication is possible. + * @private + */ + _connect: function () { + if (!('serviceWorker' in navigator)) { + return Promise.reject(new Error('serviceworkers-not-supported')); + } + + var installMessageHandlers = this._installMessageHandlers.bind(this); + var checkForActivationPending = this._checkForActivationPending.bind(this); + return new Promise(function (fulfill, reject) { + navigator.serviceWorker.getRegistration(root).then(function (registration) { + if (registration.active) { + installMessageHandlers(); + checkForActivationPending(); + return fulfill(); + } + + var installingWorker = registration.installing; + if (!installingWorker) { + return reject(new Error('impossible-to-connect')); + } + + installingWorker.onstatechange = function () { + if (installingWorker.state === 'installed') { + installMessageHandlers(); + checkForActivationPending(); + fulfill(); + } + }; + }); + }); }, /** @@ -117,7 +174,7 @@ * @method update * @return {Promise} If the update process is successful, the promise will * resolve to a new version and an - * {{#crossLink OfflinerClient/activationPending:event}}{{/crossLink}} + * {{#crossLink "OfflinerClient/activationPending:event"}}{{/crossLink}} * will be triggered. If the update is not needed, the promise will be * rejected with `no-update-needed` reason. */ @@ -145,6 +202,7 @@ * @param type {String} The type of the events selecting the listeners to * be run. * @param evt {Object} The event contents. + * @private */ _runListeners: function (type, evt) { var listeners = this._eventListeners[type] || []; @@ -160,6 +218,7 @@ * and the client code. * * @method _installMessageHandlers + * @private */ _installMessageHandlers: function installMessageHandlers() { var that = this; @@ -185,7 +244,20 @@ }, /** - * Discriminates between {{#crossLink OfflinerClient/xpromise:event}}{{/crossLink}} + * Make offliner to check for pending activations and dispatch + * {{#crossLink "OfflinerClient/activationPending:event"}}{{/crossLink}} + * if so. + * + * @method _checkForActivationPending + * @private + */ + _checkForActivationPending: function () { + // TODO: should we add a prefix for offliner messages? + this._send({ type: 'checkForActivationPending' }); + }, + + /** + * Discriminates between {{#crossLink "OfflinerClient/xpromise:event"}}{{/crossLink}} * events which are treated in a special way and the rest of the events that * simply trigger the default dispatching algorithm. * @@ -193,6 +265,7 @@ * @param offlinerType {String} The type of the message without the * `offliner:` prefix. * @param msg {Any} The event. + * @private */ _handleMessage: function (offlinerType, msg) { var sw = navigator.serviceWorker; @@ -211,6 +284,7 @@ * @param willBeThis {Object} The context object `this` which the function * will be called with. * @return `true` if the listener registration already exists. + * @private */ _has: function (type, handler, willBeThis) { var listeners = this._eventListeners[type] || []; @@ -233,6 +307,7 @@ * the implementation to run. * @return {Promise} A promise delegating its implementation in some code * running in a worker. + * @private */ _xpromise: function (order) { return new Promise(function (accept, reject) { @@ -253,16 +328,18 @@ /** * Sends a message to the worker. + * * @method _send * @param msg {Any} The message to be sent. + * @private */ _send: function (msg) { - navigator.serviceWorker.getRegistration() + navigator.serviceWorker.getRegistration(root) .then(function (registration) { if (!registration || !registration.active) { // TODO: Wait for the service worker to be active and try to // resend. - warn('Not service worker active right now.'); + console.warn('Not service worker active right now.'); } else { return registration.active.postMessage(msg); @@ -276,6 +353,7 @@ * * @method _resolveCrossPromise * @param msg {Object} An object with the proper data to resolve a xpromise. + * @private */ _resolveCrossPromise: function (msg) { var implementation = this._xpromises[msg.id]; diff --git a/dist/offliner-client.min.js b/dist/offliner-client.min.js index 15fe5b8..9d78d1a 100644 --- a/dist/offliner-client.min.js +++ b/dist/offliner-client.min.js @@ -1 +1 @@ -!function(e){"use strict";var t=1,n=e.off,r=function(){var e=new URL(document.currentScript.dataset.root||"",window.location.origin).href;return e.endsWith("/")?e:e+"/"}(),s=r+(document.currentScript.dataset.worker||"offliner-worker.js");e.off={_eventListeners:{},_xpromises:{},restore:function(){return e.off=n,this},install:function(){return this._installMessageHandlers(),navigator.serviceWorker.register(s,{scope:r})},connect:function(){this._installMessageHandlers()},on:function(e,t,n){this._has(e,t,n)||(this._eventListeners[e]=this._eventListeners[e]||[],this._eventListeners[e].push([t,n]))},update:function(){return this._xpromise("update")},activate:function(){return this._xpromise("activate")},_runListeners:function(e,t){var n=this._eventListeners[e]||[];n.forEach(function(e){var n=e[0],r=e[1];n.call(r,t)})},_installMessageHandlers:function i(){function e(e){var n=e.data,r=n?n.type:"",s=r.split(":");"offliner"===s[0]&&t._handleMessage(s[1],n)}var t=this;if(!i.done){if("function"==typeof BroadcastChannel){var n=new BroadcastChannel("offliner-channel");n.onmessage=e}else navigator.serviceWorker.addEventListener("message",e);i.done=!0}},_handleMessage:function(e,t){navigator.serviceWorker;"xpromise"===e?this._resolveCrossPromise(t):this._runListeners(e,t)},_has:function(e,t,n){for(var r,s=this._eventListeners[e]||[],i=0;r=s[i];i++)if(r[0]===t&&r[1]===n)return!0;return!1},_xpromise:function(e){return new Promise(function(n,r){function s(e){r(new Error(e))}var i=t++,o={type:"xpromise",id:i,order:e};this._xpromises[i]=[n,s],this._send(o)}.bind(this))},_send:function(e){navigator.serviceWorker.getRegistration().then(function(t){return t&&t.active?t.active.postMessage(e):void warn("Not service worker active right now.")})},_resolveCrossPromise:function(e){var t=this._xpromises[e.id];t?t["rejected"===e.status?1:0](e.value):console.warn("Trying to resolve unexistent promise:",e.id)}}}(this.exports||this); \ No newline at end of file +!function(e){"use strict";var n=1,t=e.off,r=function(){var e=new URL(document.currentScript.dataset.root||"",window.location.origin).href;return e.endsWith("/")?e:e+"/"}(),i=r+(document.currentScript.dataset.worker||"offliner-worker.js");e.off={_eventListeners:{},_xpromises:{},restore:function(){return e.off=t,this},install:function(){return"serviceWorker"in navigator?navigator.serviceWorker.register(i,{scope:r}).then(function(e){return this.connect().then(function(){return e})}.bind(this)):Promise.reject(new Error("serviceworkers-not-supported"))},_connecting:null,connect:function(){return this._connecting||(this._connecting=this._connect()),this._connecting},_connect:function(){if(!("serviceWorker"in navigator))return Promise.reject(new Error("serviceworkers-not-supported"));var e=this._installMessageHandlers.bind(this),n=this._checkForActivationPending.bind(this);return new Promise(function(t,i){navigator.serviceWorker.getRegistration(r).then(function(r){if(r.active)return e(),n(),t();var s=r.installing;return s?void(s.onstatechange=function(){"installed"===s.state&&(e(),n(),t())}):i(new Error("impossible-to-connect"))})})},on:function(e,n,t){this._has(e,n,t)||(this._eventListeners[e]=this._eventListeners[e]||[],this._eventListeners[e].push([n,t]))},update:function(){return this._xpromise("update")},activate:function(){return this._xpromise("activate")},_runListeners:function(e,n){var t=this._eventListeners[e]||[];t.forEach(function(e){var t=e[0],r=e[1];t.call(r,n)})},_installMessageHandlers:function s(){function e(e){var t=e.data,r=t?t.type:"",i=r.split(":");"offliner"===i[0]&&n._handleMessage(i[1],t)}var n=this;if(!s.done){if("function"==typeof BroadcastChannel){var t=new BroadcastChannel("offliner-channel");t.onmessage=e}else navigator.serviceWorker.addEventListener("message",e);s.done=!0}},_checkForActivationPending:function(){this._send({type:"checkForActivationPending"})},_handleMessage:function(e,n){navigator.serviceWorker;"xpromise"===e?this._resolveCrossPromise(n):this._runListeners(e,n)},_has:function(e,n,t){for(var r,i=this._eventListeners[e]||[],s=0;r=i[s];s++)if(r[0]===n&&r[1]===t)return!0;return!1},_xpromise:function(e){return new Promise(function(t,r){function i(e){r(new Error(e))}var s=n++,o={type:"xpromise",id:s,order:e};this._xpromises[s]=[t,i],this._send(o)}.bind(this))},_send:function(e){navigator.serviceWorker.getRegistration(r).then(function(n){return n&&n.active?n.active.postMessage(e):void console.warn("Not service worker active right now.")})},_resolveCrossPromise:function(e){var n=this._xpromises[e.id];n?n["rejected"===e.status?1:0](e.value):console.warn("Trying to resolve unexistent promise:",e.id)}}}(this.exports||this); \ No newline at end of file diff --git a/dist/offliner.js b/dist/offliner.js index 3e2fe40..939a4eb 100644 --- a/dist/offliner.js +++ b/dist/offliner.js @@ -130,7 +130,7 @@ * @method standalone * @throws {Error} offliner throws when trying to install it in standalone * mode if it was already used as middleware by calling - * {{#crossLink Offliner/asMiddleware:method}}{{/crossLink}}. + * {{#crossLink "Offliner/asMiddleware:method"}}{{/crossLink}}. */ Offliner.prototype.standalone = function () { if (this._isMiddleware) { @@ -185,7 +185,7 @@ * @method asMiddleware * @return {Object} A serviceworkerware middleware. * @throws {Error} offliner will throw if you try to use it as middleware - * after calling {{#crossLink Offliner/standalone:method}}{{/crossLink}}. + * after calling {{#crossLink "Offliner/standalone:method"}}{{/crossLink}}. */ Offliner.prototype.asMiddleware = function () { if (this._isStarted) { @@ -228,6 +228,9 @@ case 'xpromise': this._receiveCrossPromise(msg.id, msg.order); break; + case 'checkForActivationPending': + this._checkForActivationPending(); + break; default: warn('Message not recognized:', msg); break; @@ -263,6 +266,21 @@ } }; + /** + * Check if there is an activation pending. If so, offliner dispatches an + * activation pending request. + * + * @method _checkForActivationPending + * @private + */ + Offliner.prototype._checkForActivationPending = function () { + this.get('activation-pending').then(function (isActivationPending) { + if (isActivationPending) { + this._sendActivationPending(); + } + }.bind(this)); + }; + /** * Resolves a cross promise. * @@ -461,6 +479,7 @@ * * @method _sendActivationDone * @private + * @for Offliner */ Offliner.prototype._sendActivationDone = function () { this._broadcastMessage({ type: 'activationDone' }); @@ -480,7 +499,7 @@ /** * Broadcast a message in the clients. The method will add the `offliner:` * prefix to the type of the events but this is stripped out automatically by - * the {{#crossLink OfflinerClient/_installMessageHandlers:method}}{{/crossLink}} + * the {{#crossLink "OfflinerClient/_installMessageHandlers:method"}}{{/crossLink}} * client side. * * @method _broadcastMessage @@ -768,7 +787,7 @@ */ /** - * Normalizes a resource not following the {{#crossLink Resource}} + * Normalizes a resource not following the {{#crossLink "Resource"}} * {{/crossLink}} convention. * * @method normalize @@ -798,7 +817,7 @@ } /** - * Register a {{#crossLink Fetcher}}{{/crossLink}}. The fetcher will be used + * Register a {{#crossLink "Fetcher"}}{{/crossLink}}. The fetcher will be used * to retrieve the resources of the fetcher's type. * * @method use diff --git a/dist/offliner.min.js b/dist/offliner.min.js index 74b803b..4a56221 100644 --- a/dist/offliner.min.js +++ b/dist/offliner.min.js @@ -1 +1 @@ -!function(e){"use strict";function t(e){Object.defineProperty(this,"_uniquename",{get:function(){return e?e+":":""}}),this._isStarted=!1,this._isMiddleware=!1,this._middleware=null,Object.defineProperty(this,"_updateControl",{value:{scheduled:!1,alreadyRunOnce:!1,intervalId:null,inProgressProcess:null}}),Object.defineProperty(this,"fetch",{value:new r}),Object.defineProperty(this,"prefetch",{value:new n}),Object.defineProperty(this,"update",{value:new i})}function n(){this._resourceFetchers={},this._resources=[]}function i(){this._options={}}function r(){this._pipeline=[]}["log","warn","error"].forEach(function(t){e[t]=console[t].bind(console)});var o="-offliner:v0",s="__offliner-config";t.prototype.standalone=function(){if(this._isMiddleware)throw new Error("offliner has been already started as a middleware.");this._isStarted||(e.addEventListener("install",function(t){t.waitUntil(this._install().then(function(){return log("Offliner installed"),"function"==typeof e.skipWaiting?e.skipWaiting():Promise.resolve()}))}.bind(this)),e.addEventListener("activate",function(t){var n=function(){return log("Offliner activated!"),"function"==typeof e.clients.claim?e.clients.claim():Promise.resolve()};t.waitUntil(this._activate().then(n,n))}.bind(this)),e.addEventListener("fetch",function(e){e.respondWith("GET"!==e.request.method?fetch(e.request):this._fetch(e.request))}.bind(this)),e.addEventListener("message",function(e){this._processMessage(e.data)}.bind(this)),this._isStarted=!0)},t.prototype.asMiddleware=function(){if(this._isStarted)throw new Error("offliner has been already installed in standalone mode");return this._middleware||(this._middleware={onInstall:this._install.bind(this),onActivate:this._activate.bind(this),onFetch:function(e,t){return t||"GET"!==e.method?Promise.resolve(t):void this._fetch(e)}.bind(this),onMessage:function(e){this._processMessage(e.data)}.bind(this)}),this._isMiddleware=!0,this._middleware},t.prototype._activate=function(){return this.get("activation-pending").then(function(e){e&&this._sendActivationPending()}.bind(this))},t.prototype._processMessage=function(e){switch(e.type){case"xpromise":this._receiveCrossPromise(e.id,e.order);break;default:warn("Message not recognized:",e)}},t.prototype._receiveCrossPromise=function(e,t){switch(t){case"update":;this._update().then(this._resolve.bind(this,e),this._reject.bind(this,e));break;case"activate":this._activateNextCache().then(this._resolve.bind(this,e),this._reject.bind(this,e));break;default:warn("Cross Promise implementation not recognized:",t)}},t.prototype._resolve=function(e,t){this._resolvePromise(e,"resolved",t)},t.prototype._reject=function(e,t){this._resolvePromise(e,"rejected",t)},t.prototype._resolvePromise=function(e,t,n){this._broadcastMessage({type:"xpromise",id:e,status:t,value:n})},t.prototype.get=function(e){var t=this._getConfigURL(e);return caches.open(s).then(function(e){return e.match(t).then(function(e){return e?e.json():Promise.resolve(null)})})},t.prototype.set=function(e,t){var n=this._getConfigURL(e),i=new Response(JSON.stringify(t));return caches.open(s).then(function(e){return e.put(n,i)})},t.prototype._getConfigURL=function(e){return"http://config/"+this._uniquename+e},t.prototype._install=function(){var e=!0;return this.get("current-version").then(function(t){var n=this.update.option("enabled");return t?n?this._update(e):Promise.resolve():this._initialize().then(this._prefetch.bind(this))}.bind(this),error)},t.prototype._initialize=function(){return this._getCacheNameForVersion(o).then(this.set.bind(this,"active-cache")).then(this.set.bind(this,"current-version",o)).then(this.set.bind(this,"activation-pending",!1))},t.prototype._update=function(e){function t(e){return e?i._getCacheNameForVersion(e).then(caches.open.bind(caches)).then(i._evolveCache.bind(i)).then(i.set.bind(i,"activation-pending",!0)).then(i._sendActivationPending.bind(i)).then(function(){return n(),Promise.resolve(e)}):(n(),Promise.reject("no-update-needed"))}function n(){i._updateControl.alreadyRunOnce=!0,i._updateControl.inProgressProcess=null}var i=this;return this._updateControl.inProgressProcess||(this._updateControl.inProgressProcess=this.get("current-version").then(function(t){this.update.flags={isCalledFromInstall:e,isFirstUpdate:t===o}}.bind(this)).then(this._getLatestVersion.bind(this)).then(this._checkIfNewVersion.bind(this)).then(t)),this._updateControl.inProgressProcess},t.prototype._sendActivationPending=function(){this._broadcastMessage({type:"activationPending"})},t.prototype._sendActivationDone=function(){this._broadcastMessage({type:"activationDone"})},t.prototype._sendActivationFailed=function(){this._broadcastMessage({type:"activationFailed"})},t.prototype._broadcastMessage=function(e){if(e.type="offliner:"+e.type,this._isMiddleware)this.asMiddleware().broadcastMessage(e,"offliner-channel");else if("function"==typeof BroadcastChannel){var t=new BroadcastChannel("offliner-channel");t.postMessage(e),t.close()}else clients.matchAll().then(function(t){t.forEach(function(t){t.postMessage(e)})})},t.prototype._getCacheNameForVersion=function(e){return Promise.resolve(this._uniquename+"cache-"+e)},t.prototype._prefetch=function(){return this._openActiveCache().then(this._doPrefetch.bind(this))},t.prototype._doPrefetch=function(e){function t(e,t){var n=e.reduce(function(e,t){return e[t.type]=[],e},{});return t.forEach(function(e){var t=n[e.type];t&&t.push(e)}),n}var n=this.prefetch.resources(),i=this.prefetch.fetchers(),r=t(i,n);return i.reduce(function(t,n){return t.then(function(){var t=r[n.type];return n.prefetch(t,e)})},Promise.resolve())},t.prototype._getLatestVersion=function(){return this.update.check()},t.prototype._checkIfNewVersion=function(e){return this.get("current-version").then(function(t){var n=this.update.isNewVersion(t,e);return n?(log("New version "+e+" found!"),log(t?"Updating from version "+t:"First update"),this.set("next-version",e).then(function(){return e})):(log("No update needed"),null)}.bind(this))},t.prototype._evolveCache=function(e){return this._openActiveCache().then(function(t){var n=this._doPrefetch.bind(this,e);return this.update.evolve(t,e,n)}.bind(this))},t.prototype._openActiveCache=function(){return this.get("active-cache").then(caches.open.bind(caches))},t.prototype._activateNextCache=function(){return this.get("activation-pending").then(function(e){return e?this._swapCaches().then(this._updateCurrentVersion.bind(this)):Promise.reject("no-activation-pending")}.bind(this))},t.prototype._swapCaches=function(){function e(){return r.get("active-cache")}function t(){return r.get("next-version").then(r._getCacheNameForVersion.bind(r))}function n(e){var t=(e[0],e[1]);return r.set("active-cache",t).then(i([t,s]))}function i(e){return function(){return caches.keys().then(function(t){return Promise.all(t.filter(function(t){return e.indexOf(t)<0}).map(function(e){return caches["delete"](e)}))})}}var r=this;return Promise.all([e(),t()]).then(n)},t.prototype._updateCurrentVersion=function(){var e=this.get("next-version");return e.then(this.set.bind(this,"current-version")).then(this.set.bind(this,"activation-pending",!1)).then(function(){return e})},t.prototype._fetch=function(e){return new Promise(function(t,n){this._openActiveCache().then(function(i){function r(o,s){s=s||0;o.length;s===o.length?n():o[s](e,i).then(t,function(){r(o,s+1)})}var o=this.fetch.pipeline();r(o)}.bind(this))}.bind(this))},n.prototype.use=function(e){return this._resourceFetchers[e.type]=e,this._activeFetcher=e,this},n.prototype.resources=function(e){if(0===arguments.length)return this._resources;Array.isArray(e)||(e=[e]);for(var t,n=0;t=e[n];n++){var i;if("object"!=typeof t||!t||!t.type)try{i=this._activeFetcher.normalize(t)}catch(r){}i?this._resources.push(i):warn(t,"can not be normalized by",this._activeFetcher.type)}return this},n.prototype.fetchers=function(){return Object.keys(this._resourceFetchers).map(function(e){return this._resourceFetchers[e]}.bind(this))},i.prototype.option=function(e,t){return 2===arguments.length?(this._options[e]=t,this):1===arguments.length?this._options[e]:void 0},i.prototype.use=function(e){return this.option("enabled",!0),this._impl=e,this},Object.defineProperty(i.prototype,"flags",{set:function(e){this._impl.flags=e},get:function(){return this._impl.flags}}),i.prototype.check=function(){return this._impl&&this._impl.check()},i.prototype.isNewVersion=function(e,t){return this._impl.isNewVersion(e,t)},i.prototype.evolve=function(e,t,n){return this._impl.evolve(e,t,n)},r.prototype.use=function(e){return this._pipeline.push(e),this},r.prototype.pipeline=function(){return this._pipeline},r.prototype.orFail=function(){this.use(function(){return Promise.reject(new Error("End of fetch pipeline!"))})},e.off={},e.off.Offliner=t,e.off.sources={},e.off.fetchers={},e.off.updaters={}}("undefined"==typeof self?this:self); \ No newline at end of file +!function(e){"use strict";function t(e){Object.defineProperty(this,"_uniquename",{get:function(){return e?e+":":""}}),this._isStarted=!1,this._isMiddleware=!1,this._middleware=null,Object.defineProperty(this,"_updateControl",{value:{scheduled:!1,alreadyRunOnce:!1,intervalId:null,inProgressProcess:null}}),Object.defineProperty(this,"fetch",{value:new r}),Object.defineProperty(this,"prefetch",{value:new n}),Object.defineProperty(this,"update",{value:new i})}function n(){this._resourceFetchers={},this._resources=[]}function i(){this._options={}}function r(){this._pipeline=[]}["log","warn","error"].forEach(function(t){e[t]=console[t].bind(console)});var o="-offliner:v0",s="__offliner-config";t.prototype.standalone=function(){if(this._isMiddleware)throw new Error("offliner has been already started as a middleware.");this._isStarted||(e.addEventListener("install",function(t){t.waitUntil(this._install().then(function(){return log("Offliner installed"),"function"==typeof e.skipWaiting?e.skipWaiting():Promise.resolve()}))}.bind(this)),e.addEventListener("activate",function(t){var n=function(){return log("Offliner activated!"),"function"==typeof e.clients.claim?e.clients.claim():Promise.resolve()};t.waitUntil(this._activate().then(n,n))}.bind(this)),e.addEventListener("fetch",function(e){"GET"!==e.request.method?e.respondWith(fetch(e.request)):e.respondWith(this._fetch(e.request))}.bind(this)),e.addEventListener("message",function(e){this._processMessage(e.data)}.bind(this)),this._isStarted=!0)},t.prototype.asMiddleware=function(){if(this._isStarted)throw new Error("offliner has been already installed in standalone mode");return this._middleware||(this._middleware={onInstall:this._install.bind(this),onActivate:this._activate.bind(this),onFetch:function(e,t){return t||"GET"!==e.method?Promise.resolve(t):void this._fetch(e)}.bind(this),onMessage:function(e){this._processMessage(e.data)}.bind(this)}),this._isMiddleware=!0,this._middleware},t.prototype._activate=function(){return this.get("activation-pending").then(function(e){e&&this._sendActivationPending()}.bind(this))},t.prototype._processMessage=function(e){switch(e.type){case"xpromise":this._receiveCrossPromise(e.id,e.order);break;case"checkForActivationPending":this._checkForActivationPending();break;default:warn("Message not recognized:",e)}},t.prototype._receiveCrossPromise=function(e,t){switch(t){case"update":;this._update().then(this._resolve.bind(this,e),this._reject.bind(this,e));break;case"activate":this._activateNextCache().then(this._resolve.bind(this,e),this._reject.bind(this,e));break;default:warn("Cross Promise implementation not recognized:",t)}},t.prototype._checkForActivationPending=function(){this.get("activation-pending").then(function(e){e&&this._sendActivationPending()}.bind(this))},t.prototype._resolve=function(e,t){this._resolvePromise(e,"resolved",t)},t.prototype._reject=function(e,t){this._resolvePromise(e,"rejected",t)},t.prototype._resolvePromise=function(e,t,n){this._broadcastMessage({type:"xpromise",id:e,status:t,value:n})},t.prototype.get=function(e){var t=this._getConfigURL(e);return caches.open(s).then(function(e){return e.match(t).then(function(e){return e?e.json():Promise.resolve(null)})})},t.prototype.set=function(e,t){var n=this._getConfigURL(e),i=new Response(JSON.stringify(t));return caches.open(s).then(function(e){return e.put(n,i)})},t.prototype._getConfigURL=function(e){return"http://config/"+this._uniquename+e},t.prototype._install=function(){var e=!0;return this.get("current-version").then(function(t){var n=this.update.option("enabled");return t?n?this._update(e):Promise.resolve():this._initialize().then(this._prefetch.bind(this))}.bind(this),error)},t.prototype._initialize=function(){return this._getCacheNameForVersion(o).then(this.set.bind(this,"active-cache")).then(this.set.bind(this,"current-version",o)).then(this.set.bind(this,"activation-pending",!1))},t.prototype._update=function(e){function t(e){return e?i._getCacheNameForVersion(e).then(caches.open.bind(caches)).then(i._evolveCache.bind(i)).then(i.set.bind(i,"activation-pending",!0)).then(i._sendActivationPending.bind(i)).then(function(){return n(),Promise.resolve(e)}):(n(),Promise.reject("no-update-needed"))}function n(){i._updateControl.alreadyRunOnce=!0,i._updateControl.inProgressProcess=null}var i=this;return this._updateControl.inProgressProcess||(this._updateControl.inProgressProcess=this.get("current-version").then(function(t){this.update.flags={isCalledFromInstall:e,isFirstUpdate:t===o}}.bind(this)).then(this._getLatestVersion.bind(this)).then(this._checkIfNewVersion.bind(this)).then(t)),this._updateControl.inProgressProcess},t.prototype._sendActivationPending=function(){this._broadcastMessage({type:"activationPending"})},t.prototype._sendActivationDone=function(){this._broadcastMessage({type:"activationDone"})},t.prototype._sendActivationFailed=function(){this._broadcastMessage({type:"activationFailed"})},t.prototype._broadcastMessage=function(e){if(e.type="offliner:"+e.type,this._isMiddleware)this.asMiddleware().broadcastMessage(e,"offliner-channel");else if("function"==typeof BroadcastChannel){var t=new BroadcastChannel("offliner-channel");t.postMessage(e),t.close()}else clients.matchAll().then(function(t){t.forEach(function(t){t.postMessage(e)})})},t.prototype._getCacheNameForVersion=function(e){return Promise.resolve(this._uniquename+"cache-"+e)},t.prototype._prefetch=function(){return this._openActiveCache().then(this._doPrefetch.bind(this))},t.prototype._doPrefetch=function(e){function t(e,t){var n=e.reduce(function(e,t){return e[t.type]=[],e},{});return t.forEach(function(e){var t=n[e.type];t&&t.push(e)}),n}var n=this.prefetch.resources(),i=this.prefetch.fetchers(),r=t(i,n);return i.reduce(function(t,n){return t.then(function(){var t=r[n.type];return n.prefetch(t,e)})},Promise.resolve())},t.prototype._getLatestVersion=function(){return this.update.check()},t.prototype._checkIfNewVersion=function(e){return this.get("current-version").then(function(t){var n=this.update.isNewVersion(t,e);return n?(log("New version "+e+" found!"),t?log("Updating from version "+t):log("First update"),this.set("next-version",e).then(function(){return e})):(log("No update needed"),null)}.bind(this))},t.prototype._evolveCache=function(e){return this._openActiveCache().then(function(t){var n=this._doPrefetch.bind(this,e);return this.update.evolve(t,e,n)}.bind(this))},t.prototype._openActiveCache=function(){return this.get("active-cache").then(caches.open.bind(caches))},t.prototype._activateNextCache=function(){return this.get("activation-pending").then(function(e){return e?this._swapCaches().then(this._updateCurrentVersion.bind(this)):Promise.reject("no-activation-pending")}.bind(this))},t.prototype._swapCaches=function(){function e(){return r.get("active-cache")}function t(){return r.get("next-version").then(r._getCacheNameForVersion.bind(r))}function n(e){var t=(e[0],e[1]);return r.set("active-cache",t).then(i([t,s]))}function i(e){return function(){return caches.keys().then(function(t){return Promise.all(t.filter(function(t){return e.indexOf(t)<0}).map(function(e){return caches["delete"](e)}))})}}var r=this;return Promise.all([e(),t()]).then(n)},t.prototype._updateCurrentVersion=function(){var e=this.get("next-version");return e.then(this.set.bind(this,"current-version")).then(this.set.bind(this,"activation-pending",!1)).then(function(){return e})},t.prototype._fetch=function(e){return new Promise(function(t,n){this._openActiveCache().then(function(i){function r(o,s){s=s||0;o.length;s===o.length?n():o[s](e,i).then(t,function(){r(o,s+1)})}var o=this.fetch.pipeline();r(o)}.bind(this))}.bind(this))},n.prototype.use=function(e){return this._resourceFetchers[e.type]=e,this._activeFetcher=e,this},n.prototype.resources=function(e){if(0===arguments.length)return this._resources;Array.isArray(e)||(e=[e]);for(var t,n=0;t=e[n];n++){var i;if("object"!=typeof t||!t||!t.type)try{i=this._activeFetcher.normalize(t)}catch(r){}i?this._resources.push(i):warn(t,"can not be normalized by",this._activeFetcher.type)}return this},n.prototype.fetchers=function(){return Object.keys(this._resourceFetchers).map(function(e){return this._resourceFetchers[e]}.bind(this))},i.prototype.option=function(e,t){return 2===arguments.length?(this._options[e]=t,this):1===arguments.length?this._options[e]:void 0},i.prototype.use=function(e){return this.option("enabled",!0),this._impl=e,this},Object.defineProperty(i.prototype,"flags",{set:function(e){this._impl.flags=e},get:function(){return this._impl.flags}}),i.prototype.check=function(){return this._impl&&this._impl.check()},i.prototype.isNewVersion=function(e,t){return this._impl.isNewVersion(e,t)},i.prototype.evolve=function(e,t,n){return this._impl.evolve(e,t,n)},r.prototype.use=function(e){return this._pipeline.push(e),this},r.prototype.pipeline=function(){return this._pipeline},r.prototype.orFail=function(){this.use(function(){return Promise.reject(new Error("End of fetch pipeline!"))})},e.off={},e.off.Offliner=t,e.off.sources={},e.off.fetchers={},e.off.updaters={}}("undefined"==typeof self?this:self); \ No newline at end of file diff --git a/docs/classes/FetchConfig.html b/docs/classes/FetchConfig.html index f828501..930eb78 100644 --- a/docs/classes/FetchConfig.html +++ b/docs/classes/FetchConfig.html @@ -116,7 +116,7 @@

FetchConfig Class

- Defined in: /home/salva/workspace/offliner/src/offliner.js:996 + Defined in: /Users/salva/workspace/offliner/src/offliner.js:1015
@@ -231,7 +231,7 @@

orFail

- /home/salva/workspace/offliner/src/offliner.js:1030 + /Users/salva/workspace/offliner/src/offliner.js:1049

@@ -290,7 +290,7 @@

pipeline

- /home/salva/workspace/offliner/src/offliner.js:1020 + /Users/salva/workspace/offliner/src/offliner.js:1039

@@ -371,7 +371,7 @@

use

- /home/salva/workspace/offliner/src/offliner.js:1007 + /Users/salva/workspace/offliner/src/offliner.js:1026

diff --git a/docs/classes/Fetcher.html b/docs/classes/Fetcher.html index 4451984..62dd9b1 100644 --- a/docs/classes/Fetcher.html +++ b/docs/classes/Fetcher.html @@ -116,7 +116,7 @@

Fetcher Class

- Defined in: /home/salva/workspace/offliner/src/offliner.js:752 + Defined in: /Users/salva/workspace/offliner/src/offliner.js:771
@@ -251,7 +251,7 @@

normalize

- /home/salva/workspace/offliner/src/offliner.js:770 + /Users/salva/workspace/offliner/src/offliner.js:789

@@ -262,7 +262,7 @@

normalize

-

Normalizes a resource not following the convention.

+

Normalizes a resource not following the Resource convention.

@@ -346,7 +346,7 @@

prefetch

- /home/salva/workspace/offliner/src/offliner.js:778 + /Users/salva/workspace/offliner/src/offliner.js:797

@@ -440,7 +440,7 @@

type

- /home/salva/workspace/offliner/src/offliner.js:761 + /Users/salva/workspace/offliner/src/offliner.js:780

diff --git a/docs/classes/Offliner.html b/docs/classes/Offliner.html index 35bd741..e931221 100644 --- a/docs/classes/Offliner.html +++ b/docs/classes/Offliner.html @@ -116,7 +116,7 @@

Offliner Class

- Defined in: /home/salva/workspace/offliner/src/offliner.js:41 + Defined in: /Users/salva/workspace/offliner/src/offliner.js:41
@@ -162,11 +162,74 @@

Methods

+ + + + + + + + + +
+

_checkForActivationPending

+ + + () + + + + + + + + private + + + + + + + + + + +
+ + + +

+ + Defined in + + + + + /Users/salva/workspace/offliner/src/offliner.js:269 + +

+ + + + + +
+ +
+

Check if there is an activation pending. If so, offliner dispatches an +activation pending request.

+ +
+ + + + + + +
+ + +
+

_checkIfNewVersion

+ + + () + + + + + Promise + + + + + + + private + + + + + + + + + + +
+ + + +

+ + Defined in + + + + + /Users/salva/workspace/offliner/src/offliner.js:596 + +

+ + + + + +
+ +
+

Determine if there is a new version based on the latest version and the +current one by using the update middleware.

+ +
+ + + + +
+

Returns:

+ +
+ + + Promise: + +

latestVersion The new version tag is returned +if there is a new version or null otherwise.

+ + +
+
+ + + +
+ + +
+

_doPrefetch

+ + +
+ () +
+ + + + + + + + private + + + + + + + + + + +
+ + + +

+ + Defined in + + + + + /Users/salva/workspace/offliner/src/offliner.js:553 + +

+ + + + + +
+ +
+

Processes prefetch declared resources using the registered middlewares.

+ +
+ + +
+

Parameters:

+ + +
+ + + + + +
+ + +
+

_evolveCache

+ + +
+ () +
+ + + + + + + + private + + + + + + + + + + +
+ + + +

+ + Defined in + + + + + /Users/salva/workspace/offliner/src/offliner.js:625 + +

+ + + + + +
+ +
+

Evolves the current cache to the new cache by using the update middleware.

+ +
+ + +
+

Parameters:

+ + +
+ + + + + +
+ + +
+

_fetch

+ + +
+ () +
+ + + + + + + + private + + + + + + + + + + +
+ + + +

+ + Defined in + + + + + /Users/salva/workspace/offliner/src/offliner.js:729 + +

+ + + + + +
+ +
+

Use configured middlewares to perform the fetch process.

+ +
+ + +
+

Parameters:

+ + +
+ + + + + +
+ + +
+

_getCacheNameForVersion

+ + +
+ () +
+ + + + + Promise + + + + + + + private + + + + + + + + + + +
+ + + +

+ + Defined in + + + + + /Users/salva/workspace/offliner/src/offliner.js:530 + +

+ + + + + +
+ +
+

Return the CACHE name for a version given.

+ +
+ + +
+

Parameters:

+ + +
+ + + +
+

Returns:

+ +
+ + + Promise: + +

A promise resolving with the name for the +version.

+ + +
+
+ + + +
+ + +
+

_getConfigURL

+ + +
+ () +
+ + + + + + + + + + + + private + + + + + + + + + + +
+ + + +

+ + Defined in + + + + + /Users/salva/workspace/offliner/src/offliner.js:361 + +

+ + + + + +
+ +
+

Return a fake URL scheme for a setting.

+ +
+ + +
+

Parameters:

+ + +
+ + + +
+

Returns:

+ +
+ + +

a fake URL scheme for the setting.

+ + +
+
+ + + +
+ + +
+

_getLatestVersion

+ + + () + + + + + Promise + + + + + + + private + + + + + + + + + + +
+ + + +

+ + Defined in + + + + + /Users/salva/workspace/offliner/src/offliner.js:584 + +

+ + + + + +
+ +
+

Obtains the latest version using the update middleware.

+ +
+ + + + +
+

Returns:

+ +
+ + + Promise: + +

Tag representing the latest version. The tag will +be used as suffix for the new cache.

+ + +
+
+ + + +
+ + +
+

_initialize

+ + + () + + + + + + + + private + + + + + + + + + + +
+ + + +

+ + Defined in + + + + + /Users/salva/workspace/offliner/src/offliner.js:391 + +

+ + + + + +
+ +
+

Initializes the current version and active cache for the first time.

+ +
+ + + + + + +
+ + +
+

_install

+ + + () + + + + + + + + private + + + + + + + + + + +
+ + + +

+ + Defined in + + + + + /Users/salva/workspace/offliner/src/offliner.js:373 + +

+ + + + + +
-
+
+

Determine if the worker should prefetch or update after (re)installing the +service worker.

- - - - -
- + -
-

Returns:

- -
- - -

a fake URL scheme for the setting.

- -
-
-
-

_initialize

+
+

_openActiveCache

() + + Promise + + @@ -474,7 +1463,7 @@

_initialize

- /home/salva/workspace/offliner/src/offliner.js:373 + /Users/salva/workspace/offliner/src/offliner.js:639

@@ -485,20 +1474,34 @@

_initialize

-

Initializes the current version and active cache for the first time.

+

Uses dynamic information to open the active CACHE.

+
+

Returns:

+ +
+ + + Promise: + +

A promise resolving to the active cache.

+ + +
+
+
-
-

_install

+
+

_prefetch

() @@ -531,7 +1534,7 @@

_install

- /home/salva/workspace/offliner/src/offliner.js:355 + /Users/salva/workspace/offliner/src/offliner.js:543

@@ -542,8 +1545,7 @@

_install

-

Determine if the worker should prefetch or update after (re)installing the -service worker.

+

Opens current active cache and starts prefetch.

@@ -589,7 +1591,7 @@

_processMessage

- /home/salva/workspace/offliner/src/offliner.js:220 + /Users/salva/workspace/offliner/src/offliner.js:220

@@ -662,7 +1664,7 @@

_receiveCrossPromise

- /home/salva/workspace/offliner/src/offliner.js:237 + /Users/salva/workspace/offliner/src/offliner.js:240

@@ -775,7 +1777,7 @@

_reject

- /home/salva/workspace/offliner/src/offliner.js:278 + /Users/salva/workspace/offliner/src/offliner.js:296

@@ -888,7 +1890,7 @@

_resolve

- /home/salva/workspace/offliner/src/offliner.js:266 + /Users/salva/workspace/offliner/src/offliner.js:284

@@ -1007,7 +2009,7 @@

_resolvePromise

- /home/salva/workspace/offliner/src/offliner.js:290 + /Users/salva/workspace/offliner/src/offliner.js:308

@@ -1085,6 +2087,122 @@

Parameters:

+
+ + +
+

_sendActivationDone

+ + + () + + + + + + + + private + + + + + + + + + + +
+ + + +

+ + Defined in + + + + + /Users/salva/workspace/offliner/src/offliner.js:476 + +

+ + + + + +
+ +
+

Broadcast a message to all clients to indicate the activation of the +new version ended properly.

+ +
+ + + + + + +
+ + +
+

_sendActivationFailed

+ + + () + + + + + + + + private + + + + + + + + + + +
+ + + +

+ + Defined in + + + + + /Users/salva/workspace/offliner/src/offliner.js:488 + +

+ + + + + +
+ +
+

Broadcast a message to all clients to indicate there was a failure while +activating the update.

+ +
+ + + + + +
@@ -1122,7 +2240,7 @@

_sendActivationPending

- /home/salva/workspace/offliner/src/offliner.js:440 + /Users/salva/workspace/offliner/src/offliner.js:458

@@ -1143,6 +2261,64 @@

_sendActivationPending

+ + + +
+

_swapCaches

+ + + () + + + + + + + + private + + + + + + + + + + +
+ + + +

+ + Defined in + + + + + /Users/salva/workspace/offliner/src/offliner.js:669 + +

+ + + + + +
+ +
+

Makes active cache to be the next-version cache populated during a past +update process. After swapping, the previous cache is lost.

+ +
+ + + + + +
@@ -1194,7 +2370,7 @@

_update

- /home/salva/workspace/offliner/src/offliner.js:386 + /Users/salva/workspace/offliner/src/offliner.js:404

@@ -1260,6 +2436,63 @@

Returns:

+ + + +
+

_updateCurrentVersion

+ + + () + + + + + + + + private + + + + + + + + + + +
+ + + +

+ + Defined in + + + + + /Users/salva/workspace/offliner/src/offliner.js:715 + +

+ + + + + +
+ +
+

Updates the current version.

+ +
+ + + + + +
@@ -1299,7 +2532,7 @@

asMiddleware

- /home/salva/workspace/offliner/src/offliner.js:180 + /Users/salva/workspace/offliner/src/offliner.js:180

@@ -1382,7 +2615,7 @@

get

- /home/salva/workspace/offliner/src/offliner.js:310 + /Users/salva/workspace/offliner/src/offliner.js:328

@@ -1479,7 +2712,7 @@

set

- /home/salva/workspace/offliner/src/offliner.js:327 + /Users/salva/workspace/offliner/src/offliner.js:345

@@ -1574,7 +2807,7 @@

standalone

- /home/salva/workspace/offliner/src/offliner.js:128 + /Users/salva/workspace/offliner/src/offliner.js:128

@@ -1630,7 +2863,7 @@

_isMiddleware

- /home/salva/workspace/offliner/src/offliner.js:65 + /Users/salva/workspace/offliner/src/offliner.js:65

@@ -1679,7 +2912,7 @@

_isStarted

- /home/salva/workspace/offliner/src/offliner.js:55 + /Users/salva/workspace/offliner/src/offliner.js:55

@@ -1728,7 +2961,7 @@

_middleware;

- /home/salva/workspace/offliner/src/offliner.js:75 + /Users/salva/workspace/offliner/src/offliner.js:75

@@ -1777,7 +3010,7 @@

_updateControl

- /home/salva/workspace/offliner/src/offliner.js:85 + /Users/salva/workspace/offliner/src/offliner.js:85

@@ -1822,7 +3055,7 @@

fetch

- /home/salva/workspace/offliner/src/offliner.js:100 + /Users/salva/workspace/offliner/src/offliner.js:100

@@ -1867,7 +3100,7 @@

prefetch

- /home/salva/workspace/offliner/src/offliner.js:109 + /Users/salva/workspace/offliner/src/offliner.js:109

@@ -1912,7 +3145,7 @@

update

- /home/salva/workspace/offliner/src/offliner.js:118 + /Users/salva/workspace/offliner/src/offliner.js:118

diff --git a/docs/classes/OfflinerClient.html b/docs/classes/OfflinerClient.html index 31dc6e0..e4ea6a8 100644 --- a/docs/classes/OfflinerClient.html +++ b/docs/classes/OfflinerClient.html @@ -116,7 +116,7 @@

OfflinerClient Class

- Defined in: /home/salva/workspace/offliner/src/offliner-client.js:19 + Defined in: /Users/salva/workspace/offliner/src/offliner-client.js:19
@@ -166,146 +166,62 @@

Methods