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