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