~didrocks/+junk/face-detection-15.04

« back to all changes in this revision

Viewing changes to facedetection/www/bower_components/platinum-push-messaging/platinum-push-messaging.html

  • Committer: Didier Roche
  • Date: 2016-05-10 23:09:11 UTC
  • Revision ID: didier.roche@canonical.com-20160510230911-c7xr490zrj3yrzxd
New version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<!--
 
2
@license
 
3
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
 
4
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
 
5
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
 
6
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
 
7
Code distributed by Google as part of the polymer project is also
 
8
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
 
9
-->
 
10
<link rel="import" href="../polymer/polymer.html">
 
11
<link rel="import" href="../promise-polyfill/promise-polyfill.html">
 
12
 
 
13
<script>
 
14
  (function() {
 
15
    'use strict';
 
16
    // TODO: Doesn't work for IE or Safari, and the usual
 
17
    // document.getElementsByTagName('script') workaround seems to be broken by
 
18
    // HTML imports. Not important for now as neither of those browsers support
 
19
    // service worker yet.
 
20
    var currentScript = (document.currentScript || {}).baseURI;
 
21
    var SCOPE = new URL('./$$platinum-push-messaging$$/', currentScript).href;
 
22
    var BASE_URL = new URL('./', document.location.href).href;
 
23
    var SUPPORTED = 'serviceWorker' in navigator &&
 
24
        'PushManager' in window  &&
 
25
        'Notification' in window;
 
26
 
 
27
    /**
 
28
     * @const {Number} The desired version of the service worker to use. This is
 
29
     * not strictly tied to anything except that it should be changed whenever
 
30
     * a breaking change is made to the service worker code.
 
31
     */
 
32
    var VERSION = 1;
 
33
 
 
34
    // This allows us to use the PushSubscription attribute type in browsers
 
35
    // where it is not defined.
 
36
    if (!('PushSubscription' in window)) {
 
37
      window.PushSubscription = {};
 
38
    }
 
39
 
 
40
    /**
 
41
     * `<platinum-push-messaging>` sets up a [push messaging][1] subscription
 
42
     * and allows you to define what happens when a push message is received.
 
43
     *
 
44
     * The element can be placed anywhere, but should only be used once in a
 
45
     * page. If there are multiple occurrences, only one will be active.
 
46
     *
 
47
     * # Sample
 
48
     *
 
49
     * For a complete sample that uses the element, see the [Cat Push
 
50
     * Notifications][3] project.
 
51
     *
 
52
     * # Requirements
 
53
     * Push messaging is currently only available in Google Chrome, which
 
54
     * requires you to configure Google Cloud Messaging. Chrome will check that
 
55
     * your page links to a manifest file that contains a `gcm_sender_id` field.
 
56
     * You can find full details of how to set all of this up in the [HTML5
 
57
     * Rocks guide to push notifications][1].
 
58
     *
 
59
     * # Notification details
 
60
     * The data for how a notification should be displayed can come from one of
 
61
     * three places.
 
62
     *
 
63
     * Firstly, you can specify a URL from which to fetch the message data.
 
64
     * ```
 
65
     * <platinum-push-messaging
 
66
     *   message-url="notification-data.json">
 
67
     * </platinum-push-messaging>
 
68
     * ```
 
69
     *
 
70
     * The second way is to send the message data in the body of
 
71
     * the push message from your server. In this case you do not need to
 
72
     * configure anything in your page:
 
73
     * ```
 
74
     * <platinum-push-messaging></platinum-push-messaging>
 
75
     * ```
 
76
     * **Note that this method is not currently supported by any browser**. It
 
77
     * is, however, defined in the
 
78
     * [draft W3C specification](http://w3c.github.io/push-api/#the-push-event)
 
79
     * and this element should use that data when it is implemented in the
 
80
     * future.
 
81
     *
 
82
     * If a message-url is provided then the message body will be ignored in
 
83
     * favor of the first method.
 
84
     *
 
85
     * Thirdly, you can manually define the attributes on the element:
 
86
     * ```
 
87
     * <platinum-push-messaging
 
88
     *   title="Application updated"
 
89
     *   message="The application was updated in the background"
 
90
     *   icon-url="icon.png"
 
91
     *   click-url="notification.html">
 
92
     * </platinum-push-messaging>
 
93
     * ```
 
94
     * These values will also be used as defaults if one of the other methods
 
95
     * does not provide a value for that property.
 
96
     *
 
97
     * # Testing
 
98
     * If you have set up Google Cloud Messaging then you can send push messages
 
99
     * to your browser by following the guide in the [GCM documentation][2].
 
100
     *
 
101
     * However, for quick client testing there are two options. You can use the
 
102
     * `testPush` method, which allows you to simulate a push message that
 
103
     * includes a payload.
 
104
     *
 
105
     * Or, at a lower level, you can open up chrome://serviceworker-internals in
 
106
     * Chrome and use the 'Push' button for the service worker corresponding to
 
107
     * your app.
 
108
     *
 
109
     * [1]: http://updates.html5rocks.com/2015/03/push-notificatons-on-the-open-web
 
110
     * [2]: https://developer.android.com/google/gcm/http.html
 
111
     * [3]: https://github.com/notwaldorf/caturday-post
 
112
     *
 
113
     * @demo demo/
 
114
     */
 
115
    Polymer({
 
116
      is: 'platinum-push-messaging',
 
117
 
 
118
      properties: {
 
119
 
 
120
        /**
 
121
         * Indicates whether the Push and Notification APIs are supported by
 
122
         * this browser.
 
123
         */
 
124
        supported: {
 
125
          readOnly: true,
 
126
          type: Boolean,
 
127
          value: function() { return SUPPORTED; }
 
128
        },
 
129
 
 
130
        /**
 
131
         * The details of the current push subscription, if any.
 
132
         */
 
133
        subscription: {
 
134
          readOnly: true,
 
135
          type: PushSubscription,
 
136
          notify: true,
 
137
        },
 
138
 
 
139
        /**
 
140
         * Indicates the status of the element. If true, push messages will be
 
141
         * received.
 
142
         */
 
143
        enabled: {
 
144
          readOnly: true,
 
145
          type: Boolean,
 
146
          notify: true,
 
147
          value: false
 
148
        },
 
149
 
 
150
        /**
 
151
         * The location of the service worker script required by the element.
 
152
         * The script is distributed alongside the main HTML import file for the
 
153
         * element, so the location can normally be determined automatically.
 
154
         * However, if you vulcanize your project you will need to include the
 
155
         * script in your built project manually and use this property to let
 
156
         * the element know how to load it.
 
157
         */
 
158
        workerUrl: {
 
159
          type: String,
 
160
          value: function() {
 
161
            return new URL('./service-worker.js', currentScript).href;
 
162
          }
 
163
        },
 
164
 
 
165
        /**
 
166
         * A URL from which message information can be retrieved.
 
167
         *
 
168
         * When a push event happens that does not contain a message body this
 
169
         * URL will be fetched. The document will be parsed as JSON, and should
 
170
         * result in an object.
 
171
         *
 
172
         * The valid keys for the object are `title`, `message`, `url`, `icon`,
 
173
         * `tag`, `dir`, `lang`, `noscreen`, `renotify`, `silent`, `sound`,
 
174
         * `sticky` and `vibrate`. For documentation of these values see the
 
175
         * attributes of the same names, except that these values override the
 
176
         * element attributes.
 
177
         */
 
178
        messageUrl: String,
 
179
 
 
180
        /**
 
181
         * The default notification title.
 
182
         */
 
183
        title: String,
 
184
 
 
185
        /**
 
186
         * The default notification message.
 
187
         */
 
188
        message: String,
 
189
 
 
190
        /**
 
191
         * A default tag for the notifications that will be generated by
 
192
         * this element. Notifications with the same tag will overwrite one
 
193
         * another, so that only one will be shown at once.
 
194
         */
 
195
        tag: String,
 
196
 
 
197
        /**
 
198
         * The URL of a default icon for notifications.
 
199
         */
 
200
        iconUrl: String,
 
201
 
 
202
        /**
 
203
         * The default text direction for the title and body of the
 
204
         * notification. Can be `auto`, `ltr` or `rtl`.
 
205
         */
 
206
        dir: {
 
207
          type: String,
 
208
          value: 'auto'
 
209
        },
 
210
 
 
211
        /**
 
212
         * The default language to assume for the title and body of the
 
213
         * notification. If set this must be a valid
 
214
         * [BCP 47](https://tools.ietf.org/html/bcp47) language tag.
 
215
         */
 
216
        lang: String,
 
217
 
 
218
        /**
 
219
         * If true then displaying the notification should not turn the device's
 
220
         * screen on.
 
221
         */
 
222
        noscreen: {
 
223
          type: Boolean,
 
224
          value: false
 
225
        },
 
226
 
 
227
        /**
 
228
         * When a notification is displayed that has the same `tag` as an
 
229
         * existing notification, the existing one will be replaced. If this
 
230
         * flag is true then such a replacement will cause the user to be
 
231
         * alerted as though it were a new notification, by vibration or sound
 
232
         * as appropriate.
 
233
         */
 
234
        renotify: {
 
235
          type: Boolean,
 
236
          value: false
 
237
        },
 
238
 
 
239
        /**
 
240
         * If true then displaying the notification should not cause any
 
241
         * vibration or sound to be played.
 
242
         */
 
243
        silent: {
 
244
          type: Boolean,
 
245
          value: false
 
246
        },
 
247
 
 
248
        /**
 
249
         * If true then the notification should be sticky, meaning that it is
 
250
         * not directly dismissable.
 
251
         */
 
252
        sticky: {
 
253
          type: Boolean,
 
254
          value: false
 
255
        },
 
256
 
 
257
        /**
 
258
         * The pattern of vibration that should be used by default when a
 
259
         * notification is displayed. See
 
260
         */
 
261
        vibrate: Array,
 
262
 
 
263
        /**
 
264
         * The URL of a default sound file to play when a notification is shown.
 
265
         */
 
266
        sound: String,
 
267
 
 
268
        /**
 
269
         * The default URL to display when a notification is clicked.
 
270
         */
 
271
        clickUrl: {
 
272
          type: String,
 
273
          value: document.location.href
 
274
        },
 
275
      },
 
276
 
 
277
      /**
 
278
       * Fired when a notification is clicked that had the current page as the
 
279
       * click URL.
 
280
       *
 
281
       * @event platinum-push-messaging-click
 
282
       * @param {Object} The push message data used to create the notification
 
283
       */
 
284
 
 
285
      /**
 
286
       * Fired when a push message is received but no notification is shown.
 
287
       * This happens when the click URL is for this page and the page is
 
288
       * visible to the user on the screen.
 
289
       *
 
290
       * @event platinum-push-messaging-push
 
291
       * @param {Object} The push message data that was received
 
292
       */
 
293
 
 
294
      /**
 
295
       * Fired when an error occurs while enabling or disabling notifications
 
296
       *
 
297
       * @event platinum-push-messaging-error
 
298
       * @param {String} The error message
 
299
       */
 
300
 
 
301
      /**
 
302
       * Returns a promise which will resolve to the registration object
 
303
       * associated with our current service worker.
 
304
       *
 
305
       * @return {Promise<ServiceWorkerRegistration>}
 
306
       */
 
307
      _getRegistration: function() {
 
308
        return navigator.serviceWorker.getRegistration(SCOPE).then(function(registration) {
 
309
          // If we have a service worker whose scope is a superset of the push
 
310
          // scope then getRegistration will return that if our own worker is
 
311
          // not registered. Check that the registration we have is really ours
 
312
          if (registration && registration.scope === SCOPE) {
 
313
            return registration;
 
314
          }
 
315
 
 
316
          return;
 
317
        });
 
318
      },
 
319
 
 
320
      /**
 
321
       * Returns a promise that will resolve when the given registration becomes
 
322
       * active.
 
323
       *
 
324
       * @param registration {ServiceWorkerRegistration}
 
325
       * @return {Promise<undefined>}
 
326
       */
 
327
      _registrationReady: function(registration) {
 
328
        if (registration.active) {
 
329
          return Promise.resolve();
 
330
        }
 
331
 
 
332
        var serviceWorker = registration.installing || registration.waiting;
 
333
 
 
334
        return new Promise(function(resolve, reject) {
 
335
          // Because the Promise function is called on next tick there is a
 
336
          // small chance that the worker became active already.
 
337
          if (serviceWorker.state === 'activated') {
 
338
            resolve();
 
339
          }
 
340
          var listener = function(event) {
 
341
            if (serviceWorker.state === 'activated') {
 
342
              resolve();
 
343
            } else if (serviceWorker.state === 'redundant') {
 
344
              reject(new Error('Worker became redundant'));
 
345
            } else {
 
346
              return;
 
347
            }
 
348
            serviceWorker.removeEventListener('statechange', listener);
 
349
          };
 
350
          serviceWorker.addEventListener('statechange', listener);
 
351
        });
 
352
      },
 
353
 
 
354
      /**
 
355
       * Event handler for the `message` event.
 
356
       *
 
357
       * @param event {MessageEvent}
 
358
       */
 
359
      _messageHandler: function(event) {
 
360
        if (event.data && event.data.source === SCOPE) {
 
361
          switch(event.data.type) {
 
362
          case 'push':
 
363
            this.fire('platinum-push-messaging-push', event.data);
 
364
            break;
 
365
          case 'click':
 
366
            this.fire('platinum-push-messaging-click', event.data);
 
367
            break;
 
368
          }
 
369
        }
 
370
      },
 
371
 
 
372
      /**
 
373
       * Takes an options object and creates a stable JSON serialization of it.
 
374
       * This naive algorithm will only work if the object contains only
 
375
       * non-nested properties.
 
376
       *
 
377
       * @param options {Object.<String, ?(String|Number|Boolean)>}
 
378
       * @return String
 
379
       */
 
380
      _serializeOptions: function(options) {
 
381
        var props = Object.keys(options);
 
382
        props.sort();
 
383
        var parts = props.filter(function(propName) {
 
384
          return !!options[propName];
 
385
        }).map(function(propName) {
 
386
          return JSON.stringify(propName) + ':' + JSON.stringify(options[propName]);
 
387
        });
 
388
        return encodeURIComponent('{' + parts.join(',') + '}');
 
389
      },
 
390
 
 
391
      /**
 
392
       * Determine the URL of the worker based on the currently set parameters
 
393
       *
 
394
       * @return String the URL
 
395
       */
 
396
      _getWorkerURL: function() {
 
397
        var options = this._serializeOptions({
 
398
          tag: this.tag,
 
399
          messageUrl: this.messageUrl,
 
400
          title: this.title,
 
401
          message: this.message,
 
402
          iconUrl: this.iconUrl,
 
403
          clickUrl: this.clickUrl,
 
404
          dir: this.dir,
 
405
          lang: this.lang,
 
406
          noscreen: this.noscreen,
 
407
          renotify: this.renotify,
 
408
          silent: this.silent,
 
409
          sound: this.sound,
 
410
          sticky: this.sticky,
 
411
          vibrate: this.vibrate,
 
412
          version: VERSION,
 
413
          baseUrl: BASE_URL
 
414
        });
 
415
 
 
416
        return this.workerUrl + '?' + options;
 
417
      },
 
418
 
 
419
      /**
 
420
       * Update the subscription property, but only if the value has changed.
 
421
       * This prevents triggering the subscription-changed event twice on page
 
422
       * load.
 
423
       */
 
424
      _updateSubscription: function(subscription) {
 
425
        if (JSON.stringify(subscription) !== JSON.stringify(this.subscription)) {
 
426
          this._setSubscription(subscription);
 
427
        }
 
428
      },
 
429
 
 
430
      /**
 
431
       * Programmatically trigger a push message
 
432
       *
 
433
       * @param message {Object} the message payload
 
434
       */
 
435
      testPush: function(message) {
 
436
        this._getRegistration().then(function(registration) {
 
437
          registration.active.postMessage({
 
438
            type: 'test-push',
 
439
            message: message
 
440
          });
 
441
        });
 
442
      },
 
443
 
 
444
      /**
 
445
       * Request push messaging to be enabled.
 
446
       *
 
447
       * @return {Promise<undefined>}
 
448
       */
 
449
      enable: function() {
 
450
        if (!this.supported) {
 
451
          this.fire('platinum-push-messaging-error', 'Your browser does not support push notifications');
 
452
          return Promise.resolve();
 
453
        }
 
454
 
 
455
        return navigator.serviceWorker.register(this._getWorkerURL(), {scope: SCOPE}).then(function(registration) {
 
456
          return this._registrationReady(registration).then(function() {
 
457
            return registration.pushManager.subscribe({userVisibleOnly: true});
 
458
          });
 
459
        }.bind(this)).then(function(subscription) {
 
460
          this._updateSubscription(subscription);
 
461
          this._setEnabled(true);
 
462
        }.bind(this)).catch(function(error) {
 
463
          this.fire('platinum-push-messaging-error', error.message || error);
 
464
        }.bind(this));
 
465
      },
 
466
 
 
467
      /**
 
468
       * Request push messaging to be disabled.
 
469
       *
 
470
       * @return {Promise<undefined>}
 
471
       */
 
472
      disable: function() {
 
473
        if (!this.supported) {
 
474
          return Promise.resolve();
 
475
        }
 
476
 
 
477
        return this._getRegistration().then(function(registration) {
 
478
          if (!registration) {
 
479
            return;
 
480
          }
 
481
          return registration.pushManager.getSubscription().then(function(subscription) {
 
482
            if (subscription) {
 
483
              return subscription.unsubscribe();
 
484
            }
 
485
          }).then(function() {
 
486
            return registration.unregister();
 
487
          }).then(function() {
 
488
            this._updateSubscription();
 
489
            this._setEnabled(false);
 
490
          }.bind(this)).catch(function(error) {
 
491
            this.fire('platinum-push-messaging-error', error.message || error);
 
492
          }.bind(this));
 
493
        }.bind(this));
 
494
      },
 
495
 
 
496
      ready: function() {
 
497
        if (this.supported) {
 
498
          var handler = this._messageHandler.bind(this);
 
499
          // NOTE: We add the event listener twice because the specced and
 
500
          // implemented behaviors do not match. In Chrome 42, messages are
 
501
          // received on window. In the current spec they are supposed to be
 
502
          // received on navigator.serviceWorker.
 
503
          // TODO: Remove the non-spec code in the future.
 
504
          window.addEventListener('message', handler);
 
505
          navigator.serviceWorker.addEventListener('message', handler);
 
506
 
 
507
          this._getRegistration().then(function(registration) {
 
508
            if (!registration) {
 
509
              return;
 
510
            }
 
511
            if (registration.active && registration.active.scriptURL !== this._getWorkerURL()) {
 
512
              // We have an existing worker in this scope, but it is out of date
 
513
              return this.enable();
 
514
            }
 
515
            return registration.pushManager.getSubscription().then(function(subscription) {
 
516
              this._updateSubscription(subscription);
 
517
              this._setEnabled(true);
 
518
            }.bind(this));
 
519
          }.bind(this));
 
520
        }
 
521
      }
 
522
    });
 
523
  })();
 
524
</script>