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