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. nextChapters = this.player.getChapters(nextLanguage);
  101. if (nextChapters.length) {
  102. break;
  103. }
  104. }
  105. if (!nextChapters.length) {
  106. nextLanguage = 'und';
  107. nextChapters = this.player.getChapters(nextLanguage);
  108. }
  109. const languageChanged = nextLanguage !== this.chaptersLanguage_;
  110. const chaptersChanged = this.chapters_.length !== nextChapters.length ||
  111. !this.chapters_.some((c, idx) => {
  112. const n = nextChapters.at(idx);
  113. return chaptersEqual(c, n) ||
  114. nextChapters.some((n) => chaptersEqual(c, n));
  115. });
  116. this.chaptersLanguage_ = nextLanguage;
  117. this.chapters_ = nextChapters;
  118. if (!nextChapters.length) {
  119. this.deletePreviousChapters_();
  120. } else if (languageChanged || chaptersChanged) {
  121. for (const chapter of this.chapters_) {
  122. const button = shaka.util.Dom.createButton();
  123. const span = shaka.util.Dom.createHTMLElement('span');
  124. span.classList.add('shaka-chapter');
  125. span.textContent = chapter.title;
  126. button.appendChild(span);
  127. this.eventManager.listen(button, 'click', () => {
  128. this.video.currentTime = chapter.startTime;
  129. });
  130. this.menu.appendChild(button);
  131. }
  132. shaka.ui.Utils.setDisplay(this.button, true);
  133. shaka.ui.Utils.focusOnTheChosenItem(this.menu);
  134. }
  135. }
  136. };
  137. /**
  138. * @implements {shaka.extern.IUIElement.Factory}
  139. * @final
  140. */
  141. shaka.ui.ChapterSelection.Factory = class {
  142. /** @override */
  143. create(rootElement, controls) {
  144. return new shaka.ui.ChapterSelection(rootElement, controls);
  145. }
  146. };
  147. shaka.ui.OverflowMenu.registerElement(
  148. 'chapter', new shaka.ui.ChapterSelection.Factory());
  149. shaka.ui.Controls.registerElement(
  150. 'chapter', new shaka.ui.ChapterSelection.Factory());