1. 1 : /**
  2. 2 : * @file playback-rate-menu-button.js
  3. 3 : */
  4. 4 : import MenuButton from '../../menu/menu-button.js';
  5. 5 : import PlaybackRateMenuItem from './playback-rate-menu-item.js';
  6. 6 : import Component from '../../component.js';
  7. 7 : import * as Dom from '../../utils/dom.js';
  8. 8 :
  9. 9 : /**
  10. 10 : * @typedef { import('../../player').default } Player
  11. 11 : */
  12. 12 :
  13. 13 : /**
  14. 14 : * The component for controlling the playback rate.
  15. 15 : *
  16. 16 : * @extends MenuButton
  17. 17 : */
  18. 18 : class PlaybackRateMenuButton extends MenuButton {
  19. 19 :
  20. 20 : /**
  21. 21 : * Creates an instance of this class.
  22. 22 : *
  23. 23 : * @param {Player} player
  24. 24 : * The `Player` that this class should be attached to.
  25. 25 : *
  26. 26 : * @param {Object} [options]
  27. 27 : * The key/value store of player options.
  28. 28 : */
  29. 29 : constructor(player, options) {
  30. 30 : super(player, options);
  31. 31 :
  32. 32 : this.menuButton_.el_.setAttribute('aria-describedby', this.labelElId_);
  33. 33 :
  34. 34 : this.updateVisibility();
  35. 35 : this.updateLabel();
  36. 36 :
  37. 37 : this.on(player, 'loadstart', (e) => this.updateVisibility(e));
  38. 38 : this.on(player, 'ratechange', (e) => this.updateLabel(e));
  39. 39 : this.on(player, 'playbackrateschange', (e) => this.handlePlaybackRateschange(e));
  40. 40 : }
  41. 41 :
  42. 42 : /**
  43. 43 : * Create the `Component`'s DOM element
  44. 44 : *
  45. 45 : * @return {Element}
  46. 46 : * The element that was created.
  47. 47 : */
  48. 48 : createEl() {
  49. 49 : const el = super.createEl();
  50. 50 :
  51. 51 : this.labelElId_ = 'vjs-playback-rate-value-label-' + this.id_;
  52. 52 :
  53. 53 : this.labelEl_ = Dom.createEl('div', {
  54. 54 : className: 'vjs-playback-rate-value',
  55. 55 : id: this.labelElId_,
  56. 56 : textContent: '1x'
  57. 57 : });
  58. 58 :
  59. 59 : el.appendChild(this.labelEl_);
  60. 60 :
  61. 61 : return el;
  62. 62 : }
  63. 63 :
  64. 64 : dispose() {
  65. 65 : this.labelEl_ = null;
  66. 66 :
  67. 67 : super.dispose();
  68. 68 : }
  69. 69 :
  70. 70 : /**
  71. 71 : * Builds the default DOM `className`.
  72. 72 : *
  73. 73 : * @return {string}
  74. 74 : * The DOM `className` for this object.
  75. 75 : */
  76. 76 : buildCSSClass() {
  77. 77 : return `vjs-playback-rate ${super.buildCSSClass()}`;
  78. 78 : }
  79. 79 :
  80. 80 : buildWrapperCSSClass() {
  81. 81 : return `vjs-playback-rate ${super.buildWrapperCSSClass()}`;
  82. 82 : }
  83. 83 :
  84. 84 : /**
  85. 85 : * Create the list of menu items. Specific to each subclass.
  86. 86 : *
  87. 87 : */
  88. 88 : createItems() {
  89. 89 : const rates = this.playbackRates();
  90. 90 : const items = [];
  91. 91 :
  92. 92 : for (let i = rates.length - 1; i >= 0; i--) {
  93. 93 : items.push(new PlaybackRateMenuItem(this.player(), {rate: rates[i] + 'x'}));
  94. 94 : }
  95. 95 :
  96. 96 : return items;
  97. 97 : }
  98. 98 :
  99. 99 : /**
  100. 100 : * On playbackrateschange, update the menu to account for the new items.
  101. 101 : *
  102. 102 : * @listens Player#playbackrateschange
  103. 103 : */
  104. 104 : handlePlaybackRateschange(event) {
  105. 105 : this.update();
  106. 106 : }
  107. 107 :
  108. 108 : /**
  109. 109 : * Get possible playback rates
  110. 110 : *
  111. 111 : * @return {Array}
  112. 112 : * All possible playback rates
  113. 113 : */
  114. 114 : playbackRates() {
  115. 115 : const player = this.player();
  116. 116 :
  117. 117 : return (player.playbackRates && player.playbackRates()) || [];
  118. 118 : }
  119. 119 :
  120. 120 : /**
  121. 121 : * Get whether playback rates is supported by the tech
  122. 122 : * and an array of playback rates exists
  123. 123 : *
  124. 124 : * @return {boolean}
  125. 125 : * Whether changing playback rate is supported
  126. 126 : */
  127. 127 : playbackRateSupported() {
  128. 128 : return this.player().tech_ &&
  129. 129 : this.player().tech_.featuresPlaybackRate &&
  130. 130 : this.playbackRates() &&
  131. 131 : this.playbackRates().length > 0
  132. 132 : ;
  133. 133 : }
  134. 134 :
  135. 135 : /**
  136. 136 : * Hide playback rate controls when they're no playback rate options to select
  137. 137 : *
  138. 138 : * @param {Event} [event]
  139. 139 : * The event that caused this function to run.
  140. 140 : *
  141. 141 : * @listens Player#loadstart
  142. 142 : */
  143. 143 : updateVisibility(event) {
  144. 144 : if (this.playbackRateSupported()) {
  145. 145 : this.removeClass('vjs-hidden');
  146. 146 : } else {
  147. 147 : this.addClass('vjs-hidden');
  148. 148 : }
  149. 149 : }
  150. 150 :
  151. 151 : /**
  152. 152 : * Update button label when rate changed
  153. 153 : *
  154. 154 : * @param {Event} [event]
  155. 155 : * The event that caused this function to run.
  156. 156 : *
  157. 157 : * @listens Player#ratechange
  158. 158 : */
  159. 159 : updateLabel(event) {
  160. 160 : if (this.playbackRateSupported()) {
  161. 161 : this.labelEl_.textContent = this.player().playbackRate() + 'x';
  162. 162 : }
  163. 163 : }
  164. 164 :
  165. 165 : }
  166. 166 :
  167. 167 : /**
  168. 168 : * The text that should display over the `PlaybackRateMenuButton`s controls.
  169. 169 : *
  170. 170 : * Added for localization.
  171. 171 : *
  172. 172 : * @type {string}
  173. 173 : * @protected
  174. 174 : */
  175. 175 : PlaybackRateMenuButton.prototype.controlText_ = 'Playback Rate';
  176. 176 :
  177. 177 : Component.registerComponent('PlaybackRateMenuButton', PlaybackRateMenuButton);
  178. 178 : export default PlaybackRateMenuButton;