Source: ui/loop_button.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.ui.LoopButton');
  7. goog.require('shaka.ui.ContextMenu');
  8. goog.require('shaka.ui.Controls');
  9. goog.require('shaka.ui.Element');
  10. goog.require('shaka.ui.Enums');
  11. goog.require('shaka.ui.Locales');
  12. goog.require('shaka.ui.Localization');
  13. goog.require('shaka.ui.OverflowMenu');
  14. goog.require('shaka.ui.Utils');
  15. goog.require('shaka.util.Dom');
  16. goog.require('shaka.util.Timer');
  17. goog.requireType('shaka.ui.Controls');
  18. /**
  19. * @extends {shaka.ui.Element}
  20. * @final
  21. * @export
  22. */
  23. shaka.ui.LoopButton = class extends shaka.ui.Element {
  24. /**
  25. * @param {!HTMLElement} parent
  26. * @param {!shaka.ui.Controls} controls
  27. */
  28. constructor(parent, controls) {
  29. super(parent, controls);
  30. const LocIds = shaka.ui.Locales.Ids;
  31. /** @private {!HTMLButtonElement} */
  32. this.button_ = shaka.util.Dom.createButton();
  33. this.button_.classList.add('shaka-loop-button');
  34. this.button_.classList.add('shaka-tooltip');
  35. /** @private {!HTMLElement} */
  36. this.icon_ = shaka.util.Dom.createHTMLElement('i');
  37. this.icon_.classList.add('material-icons-round');
  38. this.icon_.textContent = shaka.ui.Enums.MaterialDesignIcons.LOOP;
  39. this.button_.appendChild(this.icon_);
  40. const label = shaka.util.Dom.createHTMLElement('label');
  41. label.classList.add('shaka-overflow-button-label');
  42. label.classList.add('shaka-overflow-menu-only');
  43. label.classList.add('shaka-simple-overflow-button-label-inline');
  44. this.nameSpan_ = shaka.util.Dom.createHTMLElement('span');
  45. this.nameSpan_.textContent = this.localization.resolve(LocIds.LOOP);
  46. label.appendChild(this.nameSpan_);
  47. /** @private {!HTMLElement} */
  48. this.currentState_ = shaka.util.Dom.createHTMLElement('span');
  49. this.currentState_.classList.add('shaka-current-selection-span');
  50. label.appendChild(this.currentState_);
  51. this.button_.appendChild(label);
  52. this.updateLocalizedStrings_();
  53. this.parent.appendChild(this.button_);
  54. this.eventManager.listen(
  55. this.localization, shaka.ui.Localization.LOCALE_UPDATED, () => {
  56. this.updateLocalizedStrings_();
  57. });
  58. this.eventManager.listen(
  59. this.localization, shaka.ui.Localization.LOCALE_CHANGED, () => {
  60. this.updateLocalizedStrings_();
  61. });
  62. this.eventManager.listen(this.button_, 'click', () => {
  63. if (!this.controls.isOpaque()) {
  64. return;
  65. }
  66. this.onClick_();
  67. });
  68. /** @private {boolean} */
  69. this.loopEnabled_ = this.video.loop;
  70. // No event is fired when the video.loop property changes, so
  71. // in order to detect a manual change to the property, we have
  72. // two options:
  73. // 1) set an observer that gets triggered every time the video
  74. // object is mutated and check is the loop property was changed.
  75. // 2) create a timer that checks the state of the loop property
  76. // regularly.
  77. // I (ismena) opted to go for #2 as at least video.currentTime
  78. // will be changing constantly during playback, to say nothing
  79. // about other video properties. I expect the timer to be less
  80. // of a performance hit.
  81. /**
  82. * The timer that tracks down the ad progress.
  83. *
  84. * @private {shaka.util.Timer}
  85. */
  86. this.timer_ = new shaka.util.Timer(() => {
  87. this.onTimerTick_();
  88. });
  89. this.timer_.tickEvery(1);
  90. this.eventManager.listen(this.player, 'unloading', () => {
  91. this.checkAvailability_();
  92. });
  93. this.eventManager.listen(this.player, 'loaded', () => {
  94. this.checkAvailability_();
  95. });
  96. this.eventManager.listen(this.player, 'manifestupdated', () => {
  97. this.checkAvailability_();
  98. });
  99. this.eventManager.listen(this.video, 'durationchange', () => {
  100. this.checkAvailability_();
  101. });
  102. }
  103. /**
  104. * @override
  105. */
  106. release() {
  107. this.timer_.stop();
  108. this.timer_ = null;
  109. super.release();
  110. }
  111. /** @private */
  112. onClick_() {
  113. this.video.loop = !this.video.loop;
  114. this.timer_.tickNow();
  115. this.timer_.tickEvery(1);
  116. }
  117. /** @private */
  118. onTimerTick_() {
  119. if (this.loopEnabled_ == this.video.loop) {
  120. return;
  121. }
  122. this.updateLocalizedStrings_();
  123. this.loopEnabled_ = this.video.loop;
  124. }
  125. /**
  126. * @private
  127. */
  128. updateLocalizedStrings_() {
  129. const LocIds = shaka.ui.Locales.Ids;
  130. const Icons = shaka.ui.Enums.MaterialDesignIcons;
  131. this.nameSpan_.textContent =
  132. this.localization.resolve(LocIds.LOOP);
  133. const labelText = this.video.loop ? LocIds.ON : LocIds.OFF;
  134. this.currentState_.textContent = this.localization.resolve(labelText);
  135. const icon = this.video.loop ? Icons.UNLOOP : Icons.LOOP;
  136. this.icon_.textContent = icon;
  137. const ariaText = this.video.loop ?
  138. LocIds.EXIT_LOOP_MODE : LocIds.ENTER_LOOP_MODE;
  139. this.button_.ariaLabel = this.localization.resolve(ariaText);
  140. }
  141. /**
  142. * @private
  143. */
  144. checkAvailability_() {
  145. shaka.ui.Utils.setDisplay(this.button_, !this.player.isLive());
  146. }
  147. };
  148. /**
  149. * @implements {shaka.extern.IUIElement.Factory}
  150. * @final
  151. */
  152. shaka.ui.LoopButton.Factory = class {
  153. /** @override */
  154. create(rootElement, controls) {
  155. return new shaka.ui.LoopButton(rootElement, controls);
  156. }
  157. };
  158. shaka.ui.OverflowMenu.registerElement(
  159. 'loop', new shaka.ui.LoopButton.Factory());
  160. shaka.ui.Controls.registerElement(
  161. 'loop', new shaka.ui.LoopButton.Factory());
  162. shaka.ui.ContextMenu.registerElement(
  163. 'loop', new shaka.ui.LoopButton.Factory());