Source: ui/chapter_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.ChapterSelection');
  7. goog.require('shaka.ui.Controls');
  8. goog.require('shaka.ui.Enums');
  9. goog.require('shaka.ui.Locales');
  10. goog.require('shaka.ui.Localization');
  11. goog.require('shaka.ui.OverflowMenu');
  12. goog.require('shaka.ui.SettingsMenu');
  13. goog.require('shaka.ui.Utils');
  14. goog.require('shaka.util.Dom');
  15. goog.requireType('shaka.ui.Controls');
  16. /**
  17. * @extends {shaka.ui.SettingsMenu}
  18. * @final
  19. * @export
  20. */
  21. shaka.ui.ChapterSelection = class extends shaka.ui.SettingsMenu {
  22. /**
  23. * @param {!HTMLElement} parent
  24. * @param {!shaka.ui.Controls} controls
  25. */
  26. constructor(parent, controls) {
  27. super(parent, controls, shaka.ui.Enums.MaterialDesignIcons.CHAPTER);
  28. this.button.classList.add('shaka-chapter-button');
  29. this.menu.classList.add('shaka-chapters');
  30. this.button.classList.add('shaka-tooltip-status');
  31. /** @type {!Array<shaka.extern.Chapter>} */
  32. this.chapters_ = [];
  33. this.chaptersLanguage_ = 'und';
  34. this.eventManager.listen(
  35. this.localization, shaka.ui.Localization.LOCALE_UPDATED, () => {
  36. this.updateLocalizedStrings_();
  37. this.updateChapters_();
  38. });
  39. this.eventManager.listen(
  40. this.localization, shaka.ui.Localization.LOCALE_CHANGED, () => {
  41. this.updateLocalizedStrings_();
  42. this.updateChapters_();
  43. });
  44. this.eventManager.listen(this.player, 'unloading', () => {
  45. this.deletePreviousChapters_();
  46. this.chaptersLanguage_ = 'und';
  47. this.chapters_ = [];
  48. });
  49. this.eventManager.listen(this.player, 'trackschanged', () => {
  50. this.updateChapters_();
  51. });
  52. // Set up all the strings in the user's preferred language.
  53. this.updateLocalizedStrings_();
  54. this.updateChapters_();
  55. }
  56. /**
  57. * @private
  58. */
  59. updateLocalizedStrings_() {
  60. const LocIds = shaka.ui.Locales.Ids;
  61. this.backButton.ariaLabel = this.localization.resolve(LocIds.BACK);
  62. this.button.ariaLabel = this.localization.resolve(LocIds.CHAPTERS);
  63. this.nameSpan.textContent = this.localization.resolve(LocIds.CHAPTERS);
  64. this.backSpan.textContent = this.localization.resolve(LocIds.CHAPTERS);
  65. }
  66. /**
  67. * @private
  68. */
  69. deletePreviousChapters_() {
  70. // 1. Save the back to menu button
  71. const backButton = shaka.ui.Utils.getFirstDescendantWithClassName(
  72. this.menu, 'shaka-back-to-overflow-button');
  73. // 2. Remove everything
  74. shaka.util.Dom.removeAllChildren(this.menu);
  75. // 3. Add the backTo Menu button back
  76. this.menu.appendChild(backButton);
  77. // 4. Hidden button
  78. shaka.ui.Utils.setDisplay(this.button, false);
  79. }
  80. /**
  81. * @private
  82. */
  83. updateChapters_() {
  84. /**
  85. * Does a value compare on chapters.
  86. * @param {shaka.extern.Chapter} a
  87. * @param {shaka.extern.Chapter} b
  88. * @return {boolean}
  89. */
  90. const chaptersEqual = (a, b) => {
  91. return (!a && !b) || (a.id === b.id && a.title === b.title &&
  92. a.startTime === b.startTime && a.endTime === b.endTime);
  93. };
  94. let nextLanguage = 'und';
  95. /** @type {!Array<shaka.extern.Chapter>} */
  96. let nextChapters = [];
  97. const currentLocales = this.localization.getCurrentLocales();
  98. for (const locale of Array.from(currentLocales)) {
  99. nextLanguage = locale;
  100. // If player is a proxy, and the cast receiver doesn't support this
  101. // method, you get back undefined.
  102. nextChapters = this.player.getChapters(nextLanguage) || [];
  103. if (nextChapters.length) {
  104. break;
  105. }
  106. }
  107. if (!nextChapters.length) {
  108. nextLanguage = 'und';
  109. // If player is a proxy, and the cast receiver doesn't support this
  110. // method, you get back undefined.
  111. nextChapters = this.player.getChapters(nextLanguage) || [];
  112. }
  113. const languageChanged = nextLanguage !== this.chaptersLanguage_;
  114. const chaptersChanged = this.chapters_.length !== nextChapters.length ||
  115. !this.chapters_.some((c, idx) => {
  116. const n = nextChapters.at(idx);
  117. return chaptersEqual(c, n) ||
  118. nextChapters.some((n) => chaptersEqual(c, n));
  119. });
  120. this.chaptersLanguage_ = nextLanguage;
  121. this.chapters_ = nextChapters;
  122. if (!nextChapters.length) {
  123. this.deletePreviousChapters_();
  124. } else if (languageChanged || chaptersChanged) {
  125. for (const chapter of this.chapters_) {
  126. const button = shaka.util.Dom.createButton();
  127. const span = shaka.util.Dom.createHTMLElement('span');
  128. span.classList.add('shaka-chapter');
  129. span.textContent = chapter.title;
  130. button.appendChild(span);
  131. this.eventManager.listen(button, 'click', () => {
  132. this.video.currentTime = chapter.startTime;
  133. });
  134. this.menu.appendChild(button);
  135. }
  136. shaka.ui.Utils.setDisplay(this.button, true);
  137. shaka.ui.Utils.focusOnTheChosenItem(this.menu);
  138. }
  139. }
  140. };
  141. /**
  142. * @implements {shaka.extern.IUIElement.Factory}
  143. * @final
  144. */
  145. shaka.ui.ChapterSelection.Factory = class {
  146. /** @override */
  147. create(rootElement, controls) {
  148. return new shaka.ui.ChapterSelection(rootElement, controls);
  149. }
  150. };
  151. shaka.ui.OverflowMenu.registerElement(
  152. 'chapter', new shaka.ui.ChapterSelection.Factory());
  153. shaka.ui.Controls.registerElement(
  154. 'chapter', new shaka.ui.ChapterSelection.Factory());