shaka-player 2.5.1 → 2.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/.csslintrc +13 -0
  2. package/AUTHORS +3 -0
  3. package/CHANGELOG.md +156 -0
  4. package/CONTRIBUTORS +3 -0
  5. package/README.md +3 -0
  6. package/build/README.md +5 -5
  7. package/build/apps.py +3 -1
  8. package/build/build.py +5 -2
  9. package/build/check.py +17 -0
  10. package/build/compiler.py +49 -0
  11. package/build/conformance.textproto +1 -0
  12. package/build/docs.py +0 -6
  13. package/build/generateExterns.js +72 -20
  14. package/build/install-linux-prereqs.sh +4 -4
  15. package/build/shakaBuildHelpers.py +7 -8
  16. package/build/test.py +21 -9
  17. package/build/types/core +1 -0
  18. package/build/types/ui +6 -1
  19. package/demo/asset_card.js +129 -41
  20. package/demo/cast_receiver/receiver_app.css +27 -13
  21. package/demo/close_button.js +10 -15
  22. package/demo/common/asset.js +6 -6
  23. package/demo/common/assets.js +72 -14
  24. package/demo/config.js +28 -25
  25. package/demo/custom.js +20 -16
  26. package/demo/demo.less +107 -16
  27. package/demo/demo_uncompiled.js +33 -0
  28. package/demo/demo_utils.js +4 -3
  29. package/demo/front.js +12 -8
  30. package/demo/index.html +14 -17
  31. package/demo/input.js +30 -23
  32. package/demo/input_container.js +15 -11
  33. package/demo/load.js +5 -7
  34. package/demo/main.js +138 -49
  35. package/demo/search.js +40 -36
  36. package/demo/service_worker.js +1 -1
  37. package/demo/tooltip.js +16 -24
  38. package/dist/.csslintstamp +0 -0
  39. package/dist/controls.css +1 -1
  40. package/dist/controls.css.map +1 -1
  41. package/dist/demo.compiled.debug.js +165 -152
  42. package/dist/demo.compiled.debug.map +4 -4
  43. package/dist/demo.compiled.js +165 -152
  44. package/dist/demo.compiled.map +4 -4
  45. package/dist/demo.css +1 -1
  46. package/dist/demo.css.map +1 -1
  47. package/dist/deps.js +28 -11
  48. package/dist/receiver.compiled.debug.js +11 -11
  49. package/dist/receiver.compiled.debug.map +3 -3
  50. package/dist/receiver.compiled.js +11 -11
  51. package/dist/receiver.compiled.map +3 -3
  52. package/dist/shaka-player.compiled.debug.externs.js +232 -102
  53. package/dist/shaka-player.compiled.debug.js +189 -165
  54. package/dist/shaka-player.compiled.debug.map +4 -4
  55. package/dist/shaka-player.compiled.externs.js +232 -102
  56. package/dist/shaka-player.compiled.js +567 -553
  57. package/dist/shaka-player.compiled.map +4 -4
  58. package/dist/shaka-player.ui.debug.externs.js +338 -113
  59. package/dist/shaka-player.ui.debug.js +296 -276
  60. package/dist/shaka-player.ui.debug.map +4 -4
  61. package/dist/shaka-player.ui.externs.js +338 -113
  62. package/dist/shaka-player.ui.js +685 -671
  63. package/dist/shaka-player.ui.map +4 -4
  64. package/docs/tutorials/drm-config.md +1 -0
  65. package/docs/tutorials/fairplay.md +77 -0
  66. package/docs/tutorials/index.json +1 -0
  67. package/docs/tutorials/manifest-parser.md +1 -1
  68. package/docs/tutorials/ui-customization.md +5 -2
  69. package/docs/tutorials/ui.md +10 -4
  70. package/docs/tutorials/welcome.md +2 -2
  71. package/externs/mediasession.js +16 -1
  72. package/externs/shaka/net.js +14 -4
  73. package/externs/shaka/player.js +136 -90
  74. package/externs/tippy.js +30 -0
  75. package/externs/webkitmediakeys.js +4 -0
  76. package/karma.conf.js +30 -24
  77. package/lib/cast/cast_proxy.js +94 -23
  78. package/lib/cast/cast_receiver.js +8 -1
  79. package/lib/dash/content_protection.js +33 -1
  80. package/lib/dash/dash_parser.js +13 -5
  81. package/lib/hls/hls_parser.js +20 -4
  82. package/lib/media/drm_engine.js +29 -2
  83. package/lib/media/media_source_engine.js +9 -6
  84. package/lib/media/play_rate_controller.js +14 -3
  85. package/lib/media/playhead.js +8 -3
  86. package/lib/media/streaming_engine.js +68 -20
  87. package/lib/media/transmuxer.js +2 -2
  88. package/lib/media/video_wrapper.js +8 -3
  89. package/lib/net/data_uri_plugin.js +5 -2
  90. package/lib/net/http_fetch_plugin.js +3 -3
  91. package/lib/net/http_plugin_utils.js +2 -4
  92. package/lib/net/http_xhr_plugin.js +2 -2
  93. package/lib/net/networking_engine.js +1 -0
  94. package/lib/offline/offline_scheme.js +7 -2
  95. package/lib/player.js +18 -8
  96. package/lib/polyfill/input_event.js +7 -11
  97. package/lib/polyfill/mediasource.js +1 -1
  98. package/lib/polyfill/patchedmediakeys_apple.js +2 -72
  99. package/lib/text/cue.js +1 -1
  100. package/lib/text/ttml_text_parser.js +1 -1
  101. package/lib/util/config_utils.js +1 -0
  102. package/lib/util/error.js +45 -11
  103. package/lib/util/event_manager.js +211 -100
  104. package/lib/util/fairplay_utils.js +122 -0
  105. package/lib/util/i_releasable.js +1 -0
  106. package/lib/util/player_configuration.js +7 -0
  107. package/lib/util/pssh.js +41 -0
  108. package/lib/util/string_utils.js +42 -6
  109. package/lib/util/timer.js +1 -1
  110. package/package.json +33 -33
  111. package/roadmap.md +96 -0
  112. package/shaka-player.uncompiled.js +5 -1
  113. package/test/dash/dash_parser_content_protection_unit.js +35 -1
  114. package/test/dash/dash_parser_manifest_unit.js +60 -0
  115. package/test/hls/hls_live_unit.js +7 -1
  116. package/test/hls/hls_parser_unit.js +50 -0
  117. package/test/media/drm_engine_unit.js +10 -4
  118. package/test/media/media_source_engine_integration.js +12 -0
  119. package/test/media/media_source_engine_unit.js +3 -1
  120. package/test/media/playhead_unit.js +7 -5
  121. package/test/media/streaming_engine_unit.js +46 -1
  122. package/test/media/transmuxer_integration.js +13 -0
  123. package/test/net/data_uri_plugin_unit.js +13 -2
  124. package/test/net/http_plugin_unit.js +17 -12
  125. package/test/net/networking_engine_unit.js +17 -8
  126. package/test/offline/offline_scheme_unit.js +18 -8
  127. package/test/player_external.js +3 -2
  128. package/test/player_integration.js +94 -3
  129. package/test/player_src_equals_integration.js +2 -2
  130. package/test/player_unit.js +21 -8
  131. package/test/test/boot.js +20 -5
  132. package/test/test/externs/jasmine.js +48 -0
  133. package/test/test/util/expect_async_polyfill.js +145 -0
  134. package/test/test/util/fake_networking_engine.js +1 -0
  135. package/test/test/util/manifest_generator.js +2 -1
  136. package/test/test/util/test_scheme.js +34 -2
  137. package/test/test/util/ui_utils.js +41 -0
  138. package/test/test/util/util.js +16 -8
  139. package/test/ui/ui_unit.js +130 -4
  140. package/ui/audio_language_selection.js +17 -96
  141. package/ui/big_play_button.js +61 -0
  142. package/ui/controls.js +226 -335
  143. package/ui/controls.less +3 -5
  144. package/ui/element.js +24 -6
  145. package/ui/externs/ui.js +62 -9
  146. package/ui/fullscreen_button.js +2 -52
  147. package/ui/less/buttons.less +10 -16
  148. package/ui/less/containers.less +75 -20
  149. package/ui/less/general.less +18 -2
  150. package/ui/less/other_elements.less +2 -1
  151. package/ui/less/overflow_menu.less +11 -10
  152. package/ui/less/range_elements.less +12 -10
  153. package/ui/less/spinner.less +2 -0
  154. package/ui/overflow_menu.js +1 -1
  155. package/ui/pip_button.js +1 -1
  156. package/ui/{play_pause_button.js → play_button.js} +17 -47
  157. package/ui/presentation_time.js +17 -12
  158. package/ui/range_element.js +176 -0
  159. package/ui/resolution_selection.js +43 -106
  160. package/ui/seek_bar.js +204 -0
  161. package/ui/settings_menu.js +113 -0
  162. package/ui/small_play_button.js +73 -0
  163. package/ui/text_displayer.js +17 -14
  164. package/ui/text_selection.js +42 -107
  165. package/ui/ui.js +17 -40
  166. package/ui/volume_bar.js +40 -68
package/build/test.py CHANGED
@@ -191,8 +191,8 @@ class Launcher:
191
191
  action='store_true')
192
192
  running_commands.add_argument(
193
193
  '--runs',
194
- help='Set the number of times each test should run. This '
195
- 'defaults to 1.',
194
+ help='Set the number of times each test should run. (default '
195
+ '%(default)s) ',
196
196
  type=_IntGreaterThanZero,
197
197
  default=1,
198
198
  dest='runs')
@@ -215,16 +215,16 @@ class Launcher:
215
215
  running_commands.add_argument(
216
216
  '--capture-timeout',
217
217
  help='Kill the browser if it does not capture in the given time [ms]. '
218
- 'This defaults to one minute.',
218
+ '(default %(default)s)',
219
219
  type=int,
220
220
  default=60000)
221
221
  running_commands.add_argument(
222
222
  '--delay-tests',
223
- help='Insert an artifical delay between tests [s]. '
223
+ help='Insert an artificial delay between tests, in seconds. '
224
224
  'This can be helpful when tracking down asynchronous test '
225
225
  'pollution, in which an async process belonging to one test may '
226
226
  'trigger a failure after other tests have begun. '
227
- 'If no delay is specified, defaults to 2 seconds.',
227
+ '(default %(const)s)',
228
228
  type=int,
229
229
  default=None,
230
230
  const=2,
@@ -242,6 +242,12 @@ class Launcher:
242
242
  type=str,
243
243
  metavar='KEY_SYSTEM_ID=LICENSE_SERVER_URI',
244
244
  action=_HandleKeyValuePairs)
245
+ running_commands.add_argument(
246
+ '--test-timeout',
247
+ help='Sets the test timeout value [ms] (default %(default)s)',
248
+ dest='test_timeout',
249
+ default=120000,
250
+ type=int)
245
251
 
246
252
 
247
253
  logging_commands.add_argument(
@@ -267,9 +273,9 @@ class Launcher:
267
273
  action='store_true')
268
274
  logging_commands.add_argument(
269
275
  '--enable-logging',
270
- help='Print log messages from tests and limits the type of log messages '
271
- 'printed. If no value is given "info" will be used. If '
272
- '--enable-logging is not used, logging will default to "none".',
276
+ help='Print log messages from tests and limits the type of log '
277
+ 'messages printed. If --enable-logging is not given, no logs '
278
+ 'will be printed. (default %(const)s)',
273
279
  choices=['none', 'error', 'warning', 'info', 'debug', 'v1', 'v2'],
274
280
  default='none',
275
281
  const='info',
@@ -348,6 +354,7 @@ class Launcher:
348
354
  'delay_tests',
349
355
  'test_custom_asset',
350
356
  'test_custom_license_server',
357
+ 'test_timeout',
351
358
  ]
352
359
 
353
360
  # Check each value before setting it to avoid passing null values.
@@ -379,6 +386,10 @@ class Launcher:
379
386
  if self.parsed_args.exclude_browsers and 'browsers' in self.karma_config:
380
387
  all_browsers = set(self.karma_config['browsers'])
381
388
  bad_browsers = set(self.parsed_args.exclude_browsers)
389
+ if bad_browsers - all_browsers:
390
+ raise RuntimeError('Attempting to exclude unselected browsers: %s' %
391
+ ','.join(bad_browsers - all_browsers))
392
+
382
393
  good_browsers = all_browsers - bad_browsers
383
394
  self.karma_config['browsers'] = list(good_browsers)
384
395
 
@@ -421,7 +432,8 @@ class Launcher:
421
432
  # Run the command.
422
433
  results = []
423
434
  for run in range(self.parsed_args.runs):
424
- logging.info('Running test (%d / %d)...', run + 1, self.parsed_args.runs)
435
+ logging.info('Running test (%d / %d, %d failed so far)...',
436
+ run + 1, self.parsed_args.runs, len(results) - results.count(0))
425
437
  results.append(shakaBuildHelpers.execute_get_code(cmd))
426
438
 
427
439
  # Print a summary of the results.
package/build/types/core CHANGED
@@ -63,6 +63,7 @@
63
63
  +../../lib/util/ebml_parser.js
64
64
  +../../lib/util/error.js
65
65
  +../../lib/util/event_manager.js
66
+ +../../lib/util/fairplay_utils.js
66
67
  +../../lib/util/fake_event.js
67
68
  +../../lib/util/fake_event_target.js
68
69
  +../../lib/util/functional.js
package/build/types/ui CHANGED
@@ -3,6 +3,8 @@
3
3
  +../../third_party/language-mapping-list/language-mapping-list.js
4
4
  +../../ui/audio_language_selection.js
5
5
  +../../ui/externs/ui.js
6
+ +../../ui/play_button.js
7
+ +../../ui/big_play_button.js
6
8
  +../../ui/cast_button.js
7
9
  +../../ui/controls.js
8
10
  +../../ui/constants.js
@@ -15,10 +17,13 @@
15
17
  +../../ui/mute_button.js
16
18
  +../../ui/overflow_menu.js
17
19
  +../../ui/pip_button.js
18
- +../../ui/play_pause_button.js
20
+ +../../ui/small_play_button.js
19
21
  +../../ui/presentation_time.js
22
+ +../../ui/range_element.js
20
23
  +../../ui/resolution_selection.js
21
24
  +../../ui/rewind_button.js
25
+ +../../ui/seek_bar.js
26
+ +../../ui/settings_menu.js
22
27
  +../../ui/spacer.js
23
28
  +../../ui/text_displayer.js
24
29
  +../../ui/text_selection.js
@@ -16,17 +16,20 @@
16
16
  */
17
17
 
18
18
 
19
+ goog.provide('shakaDemo.AssetCard');
20
+
21
+
19
22
  /**
20
23
  * Creates and contains an MDL card that presents info about the given asset.
21
24
  * @final
22
25
  */
23
- class AssetCard {
26
+ shakaDemo.AssetCard = class {
24
27
  /**
25
28
  * @param {!Element} parentDiv
26
29
  * @param {!ShakaDemoAssetInfo} asset
27
30
  * @param {boolean} isFeatured True if this card should use the "featured"
28
31
  * style, which use the asset's short name and have descriptions.
29
- * @param {function(!AssetCard)} remakeButtonsFn
32
+ * @param {function(!shakaDemo.AssetCard)} remakeButtonsFn
30
33
  */
31
34
  constructor(parentDiv, asset, isFeatured, remakeButtonsFn) {
32
35
  /** @private {!Element} */
@@ -38,8 +41,15 @@ class AssetCard {
38
41
  /** @private {!Element} */
39
42
  this.featureIconsContainer_ = document.createElement('div');
40
43
  /** @private {!Element} */
41
- this.progressBar_ = document.createElement('progress');
42
- /** @private {function(!AssetCard)} */
44
+ this.progressCircle_ = document.createElement('div');
45
+ const svgns = 'http://www.w3.org/2000/svg';
46
+ /** @private {!Element} */
47
+ this.progressCircleSvg_ = document.createElementNS(svgns, 'svg');
48
+ /** @private {!Element} */
49
+ this.progressCircleBack_ = document.createElementNS(svgns, 'circle');
50
+ /** @private {!Element} */
51
+ this.progressCircleBar_ = document.createElementNS(svgns, 'circle');
52
+ /** @private {function(!shakaDemo.AssetCard)} */
43
53
  this.remakeButtonsFn_ = remakeButtonsFn;
44
54
 
45
55
  // Lay out the card.
@@ -53,7 +63,8 @@ class AssetCard {
53
63
  this.card_.appendChild(titleDiv);
54
64
  const titleText = document.createElement('h2');
55
65
  titleText.classList.add('mdl-card__title-text');
56
- titleText.textContent = asset.shortName || asset.name;
66
+ titleText.textContent =
67
+ isFeatured ? (asset.shortName || asset.name) : asset.name;
57
68
  titleDiv.appendChild(titleText);
58
69
 
59
70
  if (asset.iconUri) {
@@ -92,12 +103,17 @@ class AssetCard {
92
103
  this.actions_.classList.add('mdl-card--border');
93
104
  this.card_.appendChild(this.actions_);
94
105
 
95
- const progressContainer = document.createElement('div');
96
- this.progressBar_.classList.add('hidden');
97
- this.progressBar_.setAttribute('max', 1);
98
- this.progressBar_.setAttribute('value', asset.storedProgress);
99
- progressContainer.appendChild(this.progressBar_);
100
- this.card_.appendChild(progressContainer);
106
+ this.progressCircle_.classList.add('hidden');
107
+ this.progressCircle_.classList.add('progress-circle');
108
+ this.card_.appendChild(this.progressCircle_);
109
+ this.progressCircleSvg_.appendChild(this.progressCircleBack_);
110
+ this.progressCircleSvg_.appendChild(this.progressCircleBar_);
111
+ this.progressCircle_.appendChild(this.progressCircleSvg_);
112
+ // You can't use access the classList of an svg on IE, so set the class
113
+ // attribute instead.
114
+ this.progressCircleSvg_.setAttribute('class', 'progress-circle-svg');
115
+ this.progressCircleBack_.setAttribute('class', 'progress-circle-back');
116
+ this.progressCircleBar_.setAttribute('class', 'progress-circle-bar');
101
117
 
102
118
  parentDiv.appendChild(this.card_);
103
119
  // Remake buttons AFTER appending to parent div, so that any tooltips can
@@ -105,6 +121,23 @@ class AssetCard {
105
121
  this.remakeButtons();
106
122
  }
107
123
 
124
+ /**
125
+ * @param {number} progress
126
+ * @private
127
+ */
128
+ styleProgressCircle_(progress) {
129
+ const svg = this.progressCircleSvg_;
130
+ const bar = this.progressCircleBar_;
131
+ const circleSize = 45;
132
+ const circleThickness = 5;
133
+ const circleRadius = (circleSize - circleThickness) / 2;
134
+ const circumference = 2 * Math.PI * circleRadius;
135
+
136
+ svg.setAttribute('viewBox', '0 0 ' + circleSize + ' ' + circleSize);
137
+ bar.setAttribute('stroke-dasharray', circumference);
138
+ bar.setAttribute('stroke-dashoffset', (circumference * (1 - progress)));
139
+ }
140
+
108
141
  /**
109
142
  * @param {string} icon
110
143
  * @param {string} title
@@ -116,7 +149,7 @@ class AssetCard {
116
149
  iconDiv.setAttribute('icon', icon);
117
150
  this.featureIconsContainer_.appendChild(iconDiv);
118
151
 
119
- ShakaDemoTooltips.make(this.featureIconsContainer_, iconDiv, title);
152
+ shakaDemo.Tooltips.make(iconDiv, title);
120
153
  }
121
154
 
122
155
  /**
@@ -162,7 +195,7 @@ class AssetCard {
162
195
 
163
196
  /**
164
197
  * Modify an asset to make it clear that it is unsupported.
165
- * @param {?string} unsupportedReason
198
+ * @param {string} unsupportedReason
166
199
  */
167
200
  markAsUnsupported(unsupportedReason) {
168
201
  this.card_.classList.add('asset-card-unsupported');
@@ -172,7 +205,7 @@ class AssetCard {
172
205
  /**
173
206
  * Make a button that represents the lack of a working button.
174
207
  * @param {string} buttonName
175
- * @param {?string} unsupportedReason
208
+ * @param {string} unsupportedReason
176
209
  * @return {!Element}
177
210
  * @private
178
211
  */
@@ -180,22 +213,17 @@ class AssetCard {
180
213
  const button = this.addButton(buttonName, () => {});
181
214
  button.setAttribute('disabled', '');
182
215
 
183
- // Place the tooltip into the parent container of the card, so that the
184
- // tooltip won't be clipped by other asset cards.
185
- // Also, tooltips don't work on disabled buttons (on some platforms), so
216
+ // Tooltips don't work on disabled buttons (on some platforms), so
186
217
  // the button itself has to be "uprooted" and placed in a synthetic div
187
218
  // specifically to attach the tooltip to.
188
- const tooltipContainer = this.card_.parentElement;
189
- if (tooltipContainer && unsupportedReason) {
190
- const attachPoint = document.createElement('div');
191
- if (button.parentElement) {
192
- button.parentElement.removeChild(button);
193
- }
194
- attachPoint.classList.add('tooltip-attach-point');
195
- attachPoint.appendChild(button);
196
- this.actions_.appendChild(attachPoint);
197
- ShakaDemoTooltips.make(tooltipContainer, attachPoint, unsupportedReason);
219
+ const attachPoint = document.createElement('div');
220
+ if (button.parentElement) {
221
+ button.parentElement.removeChild(button);
198
222
  }
223
+ attachPoint.classList.add('tooltip-attach-point');
224
+ attachPoint.appendChild(button);
225
+ this.actions_.appendChild(attachPoint);
226
+ shakaDemo.Tooltips.make(attachPoint, unsupportedReason);
199
227
 
200
228
  return button;
201
229
  }
@@ -229,10 +257,12 @@ class AssetCard {
229
257
  * Makes the contents of the button into an MDL icon, and moves it into the
230
258
  * upper-righthand corner with CSS styles.
231
259
  * @param {!Element} button
260
+ * @param {!Element} attachPoint If there is no attach point, just pass the
261
+ * button in here.
232
262
  * @param {string} iconText
233
263
  */
234
- const styleAsDownloadButton = (button, iconText) => {
235
- button.classList.add('asset-card-corner-button');
264
+ const styleAsDownloadButton = (button, attachPoint, iconText) => {
265
+ attachPoint.classList.add('asset-card-corner-button');
236
266
  const icon = document.createElement('i');
237
267
  icon.textContent = iconText;
238
268
  icon.classList.add('material-icons');
@@ -241,27 +271,85 @@ class AssetCard {
241
271
 
242
272
  const unsupportedReason = shakaDemoMain.getAssetUnsupportedReason(
243
273
  this.asset_, /* needOffline= */ true);
244
- if (unsupportedReason || !this.asset_.storeCallback) {
274
+ if (!unsupportedReason) {
275
+ goog.asserts.assert(this.asset_.storeCallback,
276
+ 'A storage callback is expected for all supported assets!');
277
+ }
278
+ if (unsupportedReason) {
245
279
  // This can't be stored.
246
280
  const button = this.makeUnsupportedButton_('', unsupportedReason);
247
- styleAsDownloadButton(button, 'get_app');
281
+ // As this is a unsupported button, it is wrapped in an "attach point";
282
+ // that is the element this has to move with CSS, otherwise the tooltip
283
+ // will end up coming out of the wrong place.
284
+ const attachPoint = button.parentElement || button;
285
+ styleAsDownloadButton(button, attachPoint, 'get_app');
248
286
  return;
249
287
  }
250
288
  if (this.asset_.isStored()) {
251
- const deleteButton = this.addButton('', async () => {
252
- deleteButton.disabled = true;
253
- await this.asset_.unstoreCallback();
254
- this.remakeButtons();
289
+ const deleteButton = this.addButton('', () => {
290
+ this.makeDeleteDialog_(deleteButton);
255
291
  });
256
- styleAsDownloadButton(deleteButton, 'delete');
292
+ styleAsDownloadButton(deleteButton, deleteButton, 'offline_pin');
257
293
  } else {
258
294
  const downloadButton = this.addButton('', async () => {
259
295
  downloadButton.disabled = true;
260
296
  await this.asset_.storeCallback();
261
297
  this.remakeButtons();
262
298
  });
263
- styleAsDownloadButton(downloadButton, 'get_app');
299
+ styleAsDownloadButton(downloadButton, downloadButton, 'get_app');
300
+ }
301
+ }
302
+
303
+ /**
304
+ * @param {!Element} deleteButton
305
+ * @private
306
+ */
307
+ makeDeleteDialog_(deleteButton) {
308
+ const parentDiv = this.card_.parentElement;
309
+ if (!parentDiv) {
310
+ return;
311
+ }
312
+
313
+ const dialog = document.createElement('dialog');
314
+ dialog.classList.add('mdl-dialog');
315
+ parentDiv.appendChild(dialog);
316
+ if (!dialog.showModal) {
317
+ dialogPolyfill.registerDialog(dialog);
264
318
  }
319
+
320
+ const textElement = document.createElement('h2');
321
+ textElement.classList.add('mdl-typography--title');
322
+ // TODO: Localize these messages.
323
+ textElement.textContent = 'Delete the offline copy?';
324
+ dialog.appendChild(textElement);
325
+
326
+ const buttonsDiv = document.createElement('div');
327
+ dialog.appendChild(buttonsDiv);
328
+ const makeButton = (text, fn) => {
329
+ const button = document.createElement('button');
330
+ button.textContent = text;
331
+ button.classList.add('mdl-button');
332
+ button.classList.add('mdl-button--colored');
333
+ button.classList.add('mdl-js-button');
334
+ button.classList.add('mdl-js-ripple-effect');
335
+ button.addEventListener('click', () => {
336
+ fn();
337
+ });
338
+ buttonsDiv.appendChild(button);
339
+ button.blur();
340
+ };
341
+ // TODO: Localize these messages.
342
+ makeButton('Yes', async () => {
343
+ dialog.close();
344
+ deleteButton.disabled = true;
345
+ await this.asset_.unstoreCallback();
346
+ this.remakeButtons();
347
+ });
348
+ makeButton('No', () => {
349
+ dialog.close();
350
+ });
351
+
352
+ dialog.showModal();
265
353
  }
266
354
 
267
355
  /**
@@ -269,17 +357,17 @@ class AssetCard {
269
357
  */
270
358
  updateProgress() {
271
359
  if (this.asset_.storedProgress < 1) {
272
- this.progressBar_.classList.remove('hidden');
360
+ this.progressCircle_.classList.remove('hidden');
273
361
  for (const button of this.actions_.childNodes) {
274
362
  button.disabled = true;
275
363
  }
276
364
  } else {
277
- this.progressBar_.classList.add('hidden');
365
+ this.progressCircle_.classList.add('hidden');
278
366
  for (const button of this.actions_.childNodes) {
279
367
  button.disabled = false;
280
368
  }
281
369
  }
282
- this.progressBar_.setAttribute('value', this.asset_.storedProgress);
370
+ this.styleProgressCircle_(this.asset_.storedProgress);
283
371
  }
284
372
 
285
373
  /**
@@ -304,4 +392,4 @@ class AssetCard {
304
392
  this.actions_.appendChild(button);
305
393
  return button;
306
394
  }
307
- }
395
+ };
@@ -53,6 +53,14 @@ body {
53
53
  display: none;
54
54
  }
55
55
 
56
+ /* The UI default style for fullscreen is to make the font size relative to the
57
+ * window height. That is also appropriate for the cast receiver experience.
58
+ * Since playback here is not technically in fullscreen mode, we just duplicate
59
+ * the style. */
60
+ .shaka-text-container {
61
+ font-size: 4.4vh;
62
+ }
63
+
56
64
  #idle {
57
65
  position: absolute;
58
66
  top: 0;
@@ -64,7 +72,6 @@ body {
64
72
  * they will be visible on top until we decide to hide them and show the
65
73
  * player. */
66
74
  z-index: 99;
67
-
68
75
  padding-top: 60px;
69
76
  padding-left: 0;
70
77
  background-color: black;
@@ -97,18 +104,25 @@ body:after {
97
104
  }
98
105
 
99
106
  @keyframes bg-change {
100
- 0% { background-image: url('idle1.jpg'); padding-left: 0; }
101
- 32% { background-image: url('idle1.jpg'); padding-left: 0; }
102
-
103
- 34% { background-image: url('idle2.jpg'); padding-left: 0; }
104
- 49% { background-image: url('idle2.jpg'); padding-left: 0; }
105
- 50% { background-image: url('idle2.jpg'); padding-left: 400px; }
106
- 65% { background-image: url('idle2.jpg'); padding-left: 400px; }
107
-
108
- 67% { background-image: url('idle3.jpg'); padding-left: 400px; }
109
- 87% { background-image: url('idle3.jpg'); padding-left: 400px; }
110
- 88% { background-image: url('idle3.jpg'); padding-left: 0; }
111
- 98% { background-image: url('idle3.jpg'); padding-left: 0; }
107
+ 0% { background-image: url('idle1.jpg'); padding-left: 0; }
108
+
109
+ 32% { background-image: url('idle1.jpg'); padding-left: 0; }
110
+
111
+ 34% { background-image: url('idle2.jpg'); padding-left: 0; }
112
+
113
+ 49% { background-image: url('idle2.jpg'); padding-left: 0; }
114
+
115
+ 50% { background-image: url('idle2.jpg'); padding-left: 400px; }
116
+
117
+ 65% { background-image: url('idle2.jpg'); padding-left: 400px; }
118
+
119
+ 67% { background-image: url('idle3.jpg'); padding-left: 400px; }
120
+
121
+ 87% { background-image: url('idle3.jpg'); padding-left: 400px; }
122
+
123
+ 88% { background-image: url('idle3.jpg'); padding-left: 0; }
124
+
125
+ 98% { background-image: url('idle3.jpg'); padding-left: 0; }
112
126
 
113
127
  100% { background-image: url('idle1.jpg'); padding-left: 0; }
114
128
  }
@@ -16,21 +16,22 @@
16
16
  */
17
17
 
18
18
 
19
+ goog.provide('shakaDemo.CloseButton');
20
+
21
+
19
22
  /**
20
23
  * A custom UI button, to allow users to close the video element.
21
24
  * This cannot actually extend shaka.ui.Element, as that class does not exist
22
25
  * at load-time when in uncompiled mode.
23
- * @implements {shaka.extern.IUIElement}
26
+ * @extends {shaka.ui.Element}
24
27
  */
25
- class CloseButton {
28
+ shakaDemo.CloseButton = class extends shaka.ui.Element {
26
29
  /**
27
30
  * @param {!HTMLElement} parent
28
31
  * @param {!shaka.ui.Controls} controls
29
32
  */
30
33
  constructor(parent, controls) {
31
- /** @protected {!HTMLElement} */
32
- this.parent = parent;
33
-
34
+ super(parent, controls);
34
35
  this.button_ = document.createElement('button');
35
36
  this.button_.classList.add('material-icons');
36
37
  this.button_.classList.add('close-button');
@@ -38,12 +39,6 @@ class CloseButton {
38
39
  this.parent.appendChild(this.button_);
39
40
 
40
41
  this.button_.addEventListener('click', () => {
41
- if (document.fullscreenElement) {
42
- document.exitFullscreen();
43
- }
44
- if (document.pictureInPictureElement) {
45
- document.exitPictureInPicture();
46
- }
47
42
  shakaDemoMain.unload();
48
43
  });
49
44
 
@@ -55,19 +50,19 @@ class CloseButton {
55
50
  destroy() {
56
51
  return Promise.resolve();
57
52
  }
58
- }
53
+ };
59
54
 
60
55
  /**
61
56
  * @implements {shaka.extern.IUIElement.Factory}
62
57
  * @final
63
58
  */
64
- CloseButton.Factory = class {
59
+ shakaDemo.CloseButton.Factory = class {
65
60
  /** @override */
66
61
  create(rootElement, controls) {
67
- return new CloseButton(rootElement, controls);
62
+ return new shakaDemo.CloseButton(rootElement, controls);
68
63
  }
69
64
  };
70
65
 
71
- // This button is registered inside setup in ShakaDemoMain, rather than
66
+ // This button is registered inside setup in shakaDemo.Main, rather than
72
67
  // statically here, since shaka.ui.Controls does not exist in this stage of the
73
68
  // load process.
@@ -18,8 +18,6 @@
18
18
 
19
19
  goog.provide('ShakaDemoAssetInfo');
20
20
 
21
- goog.require('goog.asserts');
22
-
23
21
 
24
22
  /**
25
23
  * An object that contains information about an asset.
@@ -127,9 +125,6 @@ const ShakaDemoAssetInfo = class {
127
125
  * @return {!ShakaDemoAssetInfo}
128
126
  */
129
127
  addFeature(feature) {
130
- goog.asserts.assert(feature != shakaAssets.Feature.STORED,
131
- 'Assets should not be given the synthetic "STORED" ' +
132
- 'property!');
133
128
  this.features.push(feature);
134
129
  // Sort the features list, so that features are in a predictable order.
135
130
  this.features.sort(ShakaDemoAssetInfo.caseLessAlphaComparator_);
@@ -141,7 +136,7 @@ const ShakaDemoAssetInfo = class {
141
136
  * @return {!ShakaDemoAssetInfo}
142
137
  */
143
138
  addKeySystem(keySystem) {
144
- if (this.drm.length == 1 && this.drm[0] == shakaAssets.KeySystem.CLEAR) {
139
+ if (this.isClear()) {
145
140
  // Once an asset has an actual key system, it's no longer a CLEAR asset.
146
141
  this.drm = [];
147
142
  }
@@ -151,6 +146,11 @@ const ShakaDemoAssetInfo = class {
151
146
  return this;
152
147
  }
153
148
 
149
+ /** @return {boolean} */
150
+ isClear() {
151
+ return this.drm.length == 1 && this.drm[0] == shakaAssets.KeySystem.CLEAR;
152
+ }
153
+
154
154
  /**
155
155
  * @param {!Object} extraConfig
156
156
  * @return {!ShakaDemoAssetInfo}