Source: ui/text_selection.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.ui.TextSelection');
  7. goog.require('shaka.ui.Controls');
  8. goog.require('shaka.ui.Enums');
  9. goog.require('shaka.ui.LanguageUtils');
  10. goog.require('shaka.ui.Locales');
  11. goog.require('shaka.ui.Localization');
  12. goog.require('shaka.ui.OverflowMenu');
  13. goog.require('shaka.ui.SettingsMenu');
  14. goog.require('shaka.ui.Utils');
  15. goog.require('shaka.util.Dom');
  16. goog.require('shaka.util.FakeEvent');
  17. goog.requireType('shaka.ui.Controls');
  18. /**
  19. * @extends {shaka.ui.SettingsMenu}
  20. * @final
  21. * @export
  22. */
  23. shaka.ui.TextSelection = class extends shaka.ui.SettingsMenu {
  24. /**
  25. * @param {!HTMLElement} parent
  26. * @param {!shaka.ui.Controls} controls
  27. */
  28. constructor(parent, controls) {
  29. super(parent,
  30. controls, shaka.ui.Enums.MaterialDesignIcons.CLOSED_CAPTIONS);
  31. this.button.classList.add('shaka-caption-button');
  32. this.button.classList.add('shaka-tooltip-status');
  33. this.menu.classList.add('shaka-text-languages');
  34. if (this.player && this.player.isTextTrackVisible()) {
  35. this.button.ariaPressed = 'true';
  36. } else {
  37. this.button.ariaPressed = 'false';
  38. }
  39. this.addOffOption_();
  40. this.eventManager.listen(
  41. this.localization, shaka.ui.Localization.LOCALE_UPDATED, () => {
  42. this.updateLocalizedStrings_();
  43. // If captions/subtitles are off, this string needs localization.
  44. // TODO: is there a more efficient way of updating just the strings
  45. // we need instead of running the whole language update?
  46. this.updateTextLanguages_();
  47. });
  48. this.eventManager.listen(
  49. this.localization, shaka.ui.Localization.LOCALE_CHANGED, () => {
  50. this.updateLocalizedStrings_();
  51. // If captions/subtitles are off, this string needs localization.
  52. // TODO: is there a more efficient way of updating just the strings
  53. // we need instead of running the whole language update?
  54. this.updateTextLanguages_();
  55. });
  56. this.eventManager.listen(this.player, 'loading', () => {
  57. this.onCaptionStateChange_();
  58. this.updateTextLanguages_();
  59. });
  60. this.eventManager.listen(this.player, 'loaded', () => {
  61. this.onCaptionStateChange_();
  62. this.updateTextLanguages_();
  63. });
  64. this.eventManager.listen(this.player, 'unloading', () => {
  65. this.onCaptionStateChange_();
  66. this.updateTextLanguages_();
  67. });
  68. this.eventManager.listen(this.player, 'texttrackvisibility', () => {
  69. this.onCaptionStateChange_();
  70. this.updateTextLanguages_();
  71. });
  72. this.eventManager.listen(this.player, 'textchanged', () => {
  73. this.updateTextLanguages_();
  74. });
  75. this.eventManager.listen(this.player, 'trackschanged', () => {
  76. this.updateTextLanguages_();
  77. });
  78. // Initialize caption state with a fake event.
  79. this.onCaptionStateChange_();
  80. // Set up all the strings in the user's preferred language.
  81. this.updateLocalizedStrings_();
  82. this.updateTextLanguages_();
  83. }
  84. /**
  85. * @private
  86. */
  87. addOffOption_() {
  88. const off = shaka.util.Dom.createButton();
  89. off.ariaSelected = 'true';
  90. this.menu.appendChild(off);
  91. off.appendChild(shaka.ui.Utils.checkmarkIcon());
  92. /** @private {!HTMLElement} */
  93. this.captionsOffSpan_ = shaka.util.Dom.createHTMLElement('span');
  94. off.appendChild(this.captionsOffSpan_);
  95. }
  96. /** @private */
  97. onCaptionStateChange_() {
  98. if (this.player.isTextTrackVisible()) {
  99. this.icon.textContent =
  100. shaka.ui.Enums.MaterialDesignIcons.CLOSED_CAPTIONS;
  101. this.button.ariaPressed = 'true';
  102. } else {
  103. this.icon.textContent =
  104. shaka.ui.Enums.MaterialDesignIcons.CLOSED_CAPTIONS_OFF;
  105. this.button.ariaPressed = 'false';
  106. }
  107. this.controls.dispatchEvent(
  108. new shaka.util.FakeEvent('captionselectionupdated'));
  109. }
  110. /** @private */
  111. updateTextLanguages_() {
  112. const tracks = this.player.getTextTracks() || [];
  113. shaka.ui.LanguageUtils.updateTextTracks(tracks, this.menu,
  114. (track) => this.onTextTrackSelected_(track),
  115. // Don't mark current text language as chosen unless captions are
  116. // enabled
  117. this.player.isTextTrackVisible(),
  118. this.currentSelection,
  119. this.localization,
  120. this.controls.getConfig().textTrackLabelFormat);
  121. // Add the Off button
  122. const offButton = shaka.util.Dom.createButton();
  123. offButton.classList.add('shaka-turn-captions-off-button');
  124. this.eventManager.listen(offButton, 'click', () => {
  125. this.player.setTextTrackVisibility(false);
  126. this.updateTextLanguages_();
  127. });
  128. offButton.appendChild(this.captionsOffSpan_);
  129. this.menu.appendChild(offButton);
  130. if (!this.player.isTextTrackVisible()) {
  131. offButton.ariaSelected = 'true';
  132. offButton.appendChild(shaka.ui.Utils.checkmarkIcon());
  133. this.captionsOffSpan_.classList.add('shaka-chosen-item');
  134. this.currentSelection.textContent =
  135. this.localization.resolve(shaka.ui.Locales.Ids.OFF);
  136. } else {
  137. this.captionsOffSpan_.classList.remove('shaka-chosen-item');
  138. }
  139. this.button.setAttribute('shaka-status', this.currentSelection.textContent);
  140. shaka.ui.Utils.focusOnTheChosenItem(this.menu);
  141. this.controls.dispatchEvent(
  142. new shaka.util.FakeEvent('captionselectionupdated'));
  143. shaka.ui.Utils.setDisplay(this.button, tracks.length > 0);
  144. }
  145. /**
  146. * @param {!shaka.extern.TextTrack} track
  147. * @return {!Promise}
  148. * @private
  149. */
  150. async onTextTrackSelected_(track) {
  151. // setTextTrackVisibility should be called after selectTextTrack.
  152. // selectTextTrack sets a text stream, and setTextTrackVisibility(true)
  153. // will set a text stream if it isn't already set. Consequently, reversing
  154. // the order of these calls makes two languages display simultaneously
  155. // if captions are turned off -> on in a different language.
  156. this.player.selectTextTrack(track);
  157. await this.player.setTextTrackVisibility(true);
  158. // Set text preference for when reloading the stream (e.g. casting), keep
  159. // this selection.
  160. this.player.configure('preferredTextLanguage', track.language);
  161. this.player.configure('preferForcedSubs', track.forced);
  162. }
  163. /**
  164. * @private
  165. */
  166. updateLocalizedStrings_() {
  167. const LocIds = shaka.ui.Locales.Ids;
  168. this.button.ariaLabel = this.localization.resolve(LocIds.CAPTIONS);
  169. this.backButton.ariaLabel = this.localization.resolve(LocIds.BACK);
  170. this.nameSpan.textContent =
  171. this.localization.resolve(LocIds.CAPTIONS);
  172. this.backSpan.textContent =
  173. this.localization.resolve(LocIds.CAPTIONS);
  174. this.captionsOffSpan_.textContent =
  175. this.localization.resolve(LocIds.OFF);
  176. }
  177. };
  178. /**
  179. * @implements {shaka.extern.IUIElement.Factory}
  180. * @final
  181. */
  182. shaka.ui.TextSelection.Factory = class {
  183. /** @override */
  184. create(rootElement, controls) {
  185. return new shaka.ui.TextSelection(rootElement, controls);
  186. }
  187. };
  188. shaka.ui.OverflowMenu.registerElement(
  189. 'captions', new shaka.ui.TextSelection.Factory());
  190. shaka.ui.Controls.registerElement(
  191. 'captions', new shaka.ui.TextSelection.Factory());