OLD | NEW |
(Empty) | |
| 1 <!-- |
| 2 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. |
| 3 This code may only be used under the BSD style license found at http://polymer.g
ithub.io/LICENSE.txt |
| 4 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt |
| 5 The complete set of contributors may be found at http://polymer.github.io/CONTRI
BUTORS.txt |
| 6 Code distributed by Google as part of the polymer project is also |
| 7 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS.txt |
| 8 --> |
| 9 <link rel="import" href="../polymer/polymer.html"> |
| 10 |
| 11 <script> |
| 12 (function() { |
| 13 // Grab the URI of this file to use as a base when resolving relative paths. |
| 14 // Fallback to './' as a default, though current browsers that don't support |
| 15 // document.currentScript also don't support service workers. |
| 16 var baseURI = document.currentScript ? document.currentScript.baseURI : './'
; |
| 17 |
| 18 /** |
| 19 * The `<platinum-sw-register>` element handles |
| 20 * [service worker](http://www.html5rocks.com/en/tutorials/service-worker/in
troduction/) |
| 21 * registration, reflects the overall service worker state, and coordinates
the configuration |
| 22 * provided by other Service Worker Elements. |
| 23 * `<platinum-sw-register>` is used as a parent element for child elements i
n the |
| 24 * `<platinum-sw-*>` group. |
| 25 * |
| 26 * <platinum-sw-register skip-waiting |
| 27 * clients-claim |
| 28 * auto-register |
| 29 * state="{{state}}" |
| 30 * on-service-worker-error="handleSWError" |
| 31 * on-service-worker-updated="handleSWUpdated" |
| 32 * on-service-worker-installed="handleSWInstalled"
> |
| 33 * ...one or more <platinum-sw-*> children which share the service wor
ker registration... |
| 34 * </platinum-sw-register> |
| 35 * |
| 36 * Please see https://github.com/PolymerElements/platinum-sw#top-level-sw-im
portjs for a |
| 37 * *crucial* prerequisite file you must create before `<platinum-sw-register
>` can be used! |
| 38 * |
| 39 * @demo demo/index.html An offline-capable eReader demo. |
| 40 */ |
| 41 Polymer({ |
| 42 is: 'platinum-sw-register', |
| 43 |
| 44 // Used as an "emergency" switch if we make breaking changes in the way <p
latinum-sw-register> |
| 45 // talks to service-worker.js. Otherwise, it shouldn't need to change, and
isn't meant to be |
| 46 // kept in sync with the element's release number. |
| 47 _version: '1.0', |
| 48 |
| 49 /** |
| 50 * Fired when the initial service worker installation completes successful
ly. |
| 51 * The service worker will normally only be installed once, the first time
a page with a |
| 52 * `<platinum-sw-register>` element is visited in a given browser. If the
same page is visited |
| 53 * again, the existing service worker will be reused, and there won't be a
nother |
| 54 * `service-worker-installed` fired. |
| 55 * |
| 56 * @event service-worker-installed |
| 57 * @param {String} A message indicating that the installation succeeded. |
| 58 */ |
| 59 |
| 60 /** |
| 61 * Fired when the service worker update flow completes successfully. |
| 62 * If you make changes to your `<platinum-sw-register>` configuration (i.e
. by adding in new |
| 63 * `<platinum-sw-*>` child elements, or changing their attributes), users
who had the old |
| 64 * service worker installed will get the update installed when they see th
e modified elements. |
| 65 * |
| 66 * @event service-worker-updated |
| 67 * @param {String} A message indicating that the update succeeded. |
| 68 */ |
| 69 |
| 70 /** |
| 71 * Fired when an error prevents the service worker installation from compl
eting. |
| 72 * |
| 73 * @event service-worker-error |
| 74 * @param {String} A message indicating what went wrong. |
| 75 */ |
| 76 |
| 77 properties: { |
| 78 /** |
| 79 * Whether this element should automatically register the corresponding
service worker as |
| 80 * soon as its added to a page. |
| 81 * |
| 82 * If set to `false`, then the service worker won't be automatically reg
istered, and you |
| 83 * must call this element's `register()` method if you want service work
er functionality. |
| 84 * This is useful if, for example, the service worker needs to be config
ured using |
| 85 * information that isn't immediately available at the time the page loa
ds. |
| 86 * |
| 87 * If set to `true`, the service worker will be automatically registered
without having to |
| 88 * call any methods. |
| 89 */ |
| 90 autoRegister: { |
| 91 type: Boolean, |
| 92 value: false |
| 93 }, |
| 94 |
| 95 /** |
| 96 * Whether the activated service worker should [take immediate control](
https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#clients-claim-m
ethod) |
| 97 * of any pages under its scope. |
| 98 * |
| 99 * If this is `false`, the service worker won't have any effect until th
e next time the page |
| 100 * is visited/reloaded. |
| 101 * If this is `true`, it will take control and start handling events for
the current page |
| 102 * (and any pages under the same scope open in other tabs/windows) as so
on it's active. |
| 103 * @see {@link https://slightlyoff.github.io/ServiceWorker/spec/service_
worker/#clients-claim-method} |
| 104 */ |
| 105 clientsClaim: { |
| 106 type: Boolean, |
| 107 value: false |
| 108 }, |
| 109 |
| 110 /** |
| 111 * The service worker script that is [registered](https://slightlyoff.gi
thub.io/ServiceWorker/spec/service_worker/#navigator-service-worker-register). |
| 112 * The script *should* be located at the top level of your site, to ensu
re that it is able |
| 113 * to control all the pages on your site. |
| 114 * |
| 115 * It's *strongly* recommended that you create a top-level file named `s
w-import.js` |
| 116 * containing only: |
| 117 * |
| 118 * `importScripts('bower_components/platinum-sw/service-worker.js');` |
| 119 * |
| 120 * (adjust to match the path where your `platinum-sw` element directory
can be found). |
| 121 * |
| 122 * This will ensure that your service worker script contains everything
needed to play |
| 123 * nicely with the Service Worker Elements group. |
| 124 * |
| 125 * @see {@link https://slightlyoff.github.io/ServiceWorker/spec/service_
worker/#navigator-service-worker-register} |
| 126 */ |
| 127 href: { |
| 128 type: String, |
| 129 value: 'sw-import.js' |
| 130 }, |
| 131 |
| 132 /** |
| 133 * Whether the page should be automatically reloaded (via `window.locati
on.reload()`) when |
| 134 * the service worker is successfully installed. |
| 135 * |
| 136 * While it's perfectly valid to continue using a page with a freshly in
stalled service |
| 137 * worker, it's a common pattern to want to reload it immediately follow
ing the install. |
| 138 * This ensures that, for example, if you're using a `<platinum-sw-cache
>` with an on the |
| 139 * fly caching strategy, it will get a chance to intercept all the reque
sts needed to render |
| 140 * your page and store them in the cache. |
| 141 * |
| 142 * If you don't immediately reload your page, then any resources that we
re loaded before the |
| 143 * service worker was installed (e.g. this `platinum-sw-register.html` f
ile) won't be present |
| 144 * in the cache until the next time the page is loaded. |
| 145 * |
| 146 * Note that this reload will only happen when a service worker is insta
lled for the first |
| 147 * time. If the service worker is subsequently updated, it won't trigger
another reload. |
| 148 */ |
| 149 reloadOnInstall: { |
| 150 type: Boolean, |
| 151 value: false |
| 152 }, |
| 153 |
| 154 /** |
| 155 * The scope of the service worker, relative to the registered service w
orker script. |
| 156 * All pages that fall under this scope will be controlled by the regist
ered service worker. |
| 157 * |
| 158 * Normally, this would not need to be changed, unless you want the serv
ice worker to only |
| 159 * apply to a subset of your site. |
| 160 * |
| 161 * @see {@link https://slightlyoff.github.io/ServiceWorker/spec/service_
worker/#navigator-service-worker-register} |
| 162 */ |
| 163 scope: { |
| 164 type: String, |
| 165 value: './' |
| 166 }, |
| 167 |
| 168 /** |
| 169 * Whether an updated service worker should [bypass the `waiting` state]
(https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker
-global-scope-skipwaiting) |
| 170 * and immediately become `active`. |
| 171 * |
| 172 * Normally, during an update, the new service worker stays in the |
| 173 * `waiting` state until the current page and any other tabs/windows tha
t are using the old |
| 174 * service worker are unloaded. |
| 175 * |
| 176 * If this is `false`, an updated service worker won't be activated unti
l all instances of |
| 177 * the old server worker have been unloaded. |
| 178 * |
| 179 * If this is `true`, an updated service worker will become `active` imm
ediately. |
| 180 * @see {@link https://slightlyoff.github.io/ServiceWorker/spec/service_
worker/#service-worker-global-scope-skipwaiting} |
| 181 */ |
| 182 skipWaiting: { |
| 183 type: Boolean, |
| 184 value: false |
| 185 }, |
| 186 |
| 187 /** |
| 188 * The current state of the service worker registered by this element. |
| 189 * |
| 190 * One of: |
| 191 * - 'installed' |
| 192 * - 'updated' |
| 193 * - 'error' |
| 194 * - 'unsupported' |
| 195 */ |
| 196 state: { |
| 197 notify: true, |
| 198 readOnly: true, |
| 199 type: String |
| 200 } |
| 201 }, |
| 202 |
| 203 /** |
| 204 * Registers the service worker based on the configuration options in this
element and any |
| 205 * child elements. |
| 206 * |
| 207 * If you set the `autoRegister` property to `true`, then this method is c
alled automatically |
| 208 * at page load. |
| 209 * It can be useful to set `autoRegister` to `false` and then explicitly c
all this method if |
| 210 * there are options that are only configured after the page is loaded. |
| 211 */ |
| 212 register: function() { |
| 213 if ('serviceWorker' in navigator) { |
| 214 this._constructServiceWorkerUrl().then(function(serviceWorkerUrl) { |
| 215 this._registerServiceWorker(serviceWorkerUrl); |
| 216 }.bind(this)); |
| 217 } else { |
| 218 this._setState('unsupported'); |
| 219 this.fire('service-worker-error', 'Service workers are not available i
n the current browser.'); |
| 220 } |
| 221 }, |
| 222 |
| 223 _constructServiceWorkerUrl: function() { |
| 224 var paramsPromises = []; |
| 225 var children = Polymer.dom(this).childNodes; |
| 226 for (var i = 0; i < children.length; i++) { |
| 227 if (typeof children[i]._getParameters === 'function') { |
| 228 paramsPromises.push(children[i]._getParameters(baseURI)); |
| 229 } |
| 230 } |
| 231 |
| 232 return Promise.all(paramsPromises).then(function(paramsResolutions) { |
| 233 var params = { |
| 234 baseURI: baseURI, |
| 235 version: this._version |
| 236 }; |
| 237 |
| 238 paramsResolutions.forEach(function(childParams) { |
| 239 Object.keys(childParams).forEach(function(key) { |
| 240 if (Array.isArray(params[key])) { |
| 241 params[key].push(childParams[key]); |
| 242 } else { |
| 243 params[key] = [childParams[key]]; |
| 244 } |
| 245 }); |
| 246 }); |
| 247 |
| 248 return params; |
| 249 }.bind(this)).then(function(params) { |
| 250 if (params.importscriptLate) { |
| 251 if (params.importscript) { |
| 252 params.importscript = params.importscript.concat(params.importscri
ptLate); |
| 253 } else { |
| 254 params.importscript = params.importscriptLate; |
| 255 } |
| 256 } |
| 257 |
| 258 if (params.importscript) { |
| 259 params.importscript = this._unique(params.importscript); |
| 260 } |
| 261 |
| 262 params.clientsClaim = this.clientsClaim; |
| 263 params.skipWaiting = this.skipWaiting; |
| 264 |
| 265 var serviceWorkerUrl = new URL(this.href, window.location); |
| 266 // It's very important to ensure that the serialization is stable. |
| 267 // Serializing the same settings should always produce the same URL. |
| 268 // Serializing different settings should always produce a different UR
L. |
| 269 // This ensures that the service worker upgrade flow is triggered when
settings change. |
| 270 serviceWorkerUrl.search = this._serializeUrlParams(params); |
| 271 |
| 272 return serviceWorkerUrl; |
| 273 }.bind(this)); |
| 274 }, |
| 275 |
| 276 _unique: function(arr) { |
| 277 return arr.filter(function(item, index) { |
| 278 return arr.indexOf(item) === index; |
| 279 }); |
| 280 }, |
| 281 |
| 282 _serializeUrlParams: function(params) { |
| 283 return Object.keys(params).sort().map(function(key) { |
| 284 // encodeURIComponent(['a', 'b']) => 'a%2Cb', |
| 285 // so this will still work when the values are Arrays. |
| 286 // TODO: It won't work if the values in the Arrays have ',' characters
in them. |
| 287 return encodeURIComponent(key) + "=" + encodeURIComponent(params[key])
; |
| 288 }).join('&'); |
| 289 }, |
| 290 |
| 291 _registerServiceWorker: function(serviceWorkerUrl) { |
| 292 navigator.serviceWorker.register(serviceWorkerUrl, {scope: this.scope}).
then(function(registration) { |
| 293 if (registration.active) { |
| 294 this._setState('installed'); |
| 295 } |
| 296 |
| 297 registration.onupdatefound = function() { |
| 298 var installingWorker = registration.installing; |
| 299 installingWorker.onstatechange = function() { |
| 300 switch (installingWorker.state) { |
| 301 case 'installed': |
| 302 if (navigator.serviceWorker.controller) { |
| 303 this._setState('updated'); |
| 304 this.fire('service-worker-updated', |
| 305 'A new service worker was installed, replacing the old ser
vice worker.'); |
| 306 } else { |
| 307 if (this.reloadOnInstall) { |
| 308 window.location.reload(); |
| 309 } else { |
| 310 this._setState('installed'); |
| 311 this.fire('service-worker-installed', 'A new service worke
r was installed.'); |
| 312 } |
| 313 } |
| 314 break; |
| 315 |
| 316 case 'redundant': |
| 317 this._setState('error'); |
| 318 this.fire('service-worker-error', 'The installing service work
er became redundant.'); |
| 319 break; |
| 320 } |
| 321 }.bind(this); |
| 322 }.bind(this); |
| 323 }.bind(this)).catch(function(error) { |
| 324 this._setState('error'); |
| 325 this.fire('service-worker-error', error.toString()); |
| 326 if (error.name === 'NetworkError') { |
| 327 var location = serviceWorkerUrl.origin + serviceWorkerUrl.pathname; |
| 328 console.error('A valid service worker script was not found at ' + lo
cation + '\n' + |
| 329 'To learn how to fix this, please see\n' + |
| 330 'https://github.com/PolymerElements/platinum-sw#top-level-sw-impor
tjs'); |
| 331 } |
| 332 }.bind(this)); |
| 333 }, |
| 334 |
| 335 attached: function() { |
| 336 if (this.autoRegister) { |
| 337 this.async(this.register); |
| 338 } |
| 339 } |
| 340 }); |
| 341 })(); |
| 342 </script> |
OLD | NEW |