Source: ui/ad_statistics_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.AdStatisticsButton');
  7. goog.require('shaka.log');
  8. goog.require('shaka.ads.Utils');
  9. goog.require('shaka.ui.ContextMenu');
  10. goog.require('shaka.ui.Controls');
  11. goog.require('shaka.ui.Element');
  12. goog.require('shaka.ui.Enums');
  13. goog.require('shaka.ui.Locales');
  14. goog.require('shaka.ui.Localization');
  15. goog.require('shaka.ui.OverflowMenu');
  16. goog.require('shaka.ui.Utils');
  17. goog.require('shaka.util.Dom');
  18. goog.require('shaka.util.Timer');
  19. goog.requireType('shaka.ui.Controls');
  20. /**
  21. * @extends {shaka.ui.Element}
  22. * @final
  23. * @export
  24. */
  25. shaka.ui.AdStatisticsButton = class extends shaka.ui.Element {
  26. /**
  27. * @param {!HTMLElement} parent
  28. * @param {!shaka.ui.Controls} controls
  29. */
  30. constructor(parent, controls) {
  31. super(parent, controls);
  32. /** @private {!HTMLButtonElement} */
  33. this.button_ = shaka.util.Dom.createButton();
  34. this.button_.classList.add('shaka-ad-statistics-button');
  35. /** @private {!HTMLElement} */
  36. this.icon_ = shaka.util.Dom.createHTMLElement('i');
  37. this.icon_.classList.add('material-icons-round');
  38. this.icon_.textContent =
  39. shaka.ui.Enums.MaterialDesignIcons.STATISTICS_ON;
  40. this.button_.appendChild(this.icon_);
  41. const label = shaka.util.Dom.createHTMLElement('label');
  42. label.classList.add('shaka-overflow-button-label');
  43. label.classList.add('shaka-simple-overflow-button-label-inline');
  44. /** @private {!HTMLElement} */
  45. this.nameSpan_ = shaka.util.Dom.createHTMLElement('span');
  46. label.appendChild(this.nameSpan_);
  47. /** @private {!HTMLElement} */
  48. this.stateSpan_ = shaka.util.Dom.createHTMLElement('span');
  49. this.stateSpan_.classList.add('shaka-current-selection-span');
  50. label.appendChild(this.stateSpan_);
  51. this.button_.appendChild(label);
  52. this.parent.appendChild(this.button_);
  53. /** @private {!HTMLElement} */
  54. this.container_ = shaka.util.Dom.createHTMLElement('div');
  55. this.container_.classList.add('shaka-no-propagation');
  56. this.container_.classList.add('shaka-show-controls-on-mouse-over');
  57. this.container_.classList.add('shaka-ad-statistics-container');
  58. this.container_.classList.add('shaka-hidden');
  59. const controlsContainer = this.controls.getControlsContainer();
  60. controlsContainer.appendChild(this.container_);
  61. /** @private {!Array} */
  62. this.statisticsList_ = [];
  63. /** @private {!shaka.extern.AdsStats} */
  64. this.currentStats_ = this.adManager.getStats();
  65. shaka.ui.Utils.setDisplay(this.button_, this.currentStats_.started > 0);
  66. /** @private {!Map<string, HTMLElement>} */
  67. this.displayedElements_ = new Map();
  68. const parseLoadTimes = (name) => {
  69. let totalTime = 0;
  70. const loadTimes =
  71. /** @type {!Array<number>} */ (this.currentStats_[name]);
  72. for (const loadTime of loadTimes) {
  73. totalTime += parseFloat(loadTime);
  74. }
  75. return totalTime;
  76. };
  77. const showNumber = (name) => {
  78. return this.currentStats_[name];
  79. };
  80. /** @private {!Map<string, function(string): string>} */
  81. this.parseFrom_ = new Map()
  82. .set('loadTimes', parseLoadTimes)
  83. .set('averageLoadTime', showNumber)
  84. .set('started', showNumber)
  85. .set('overlayAds', showNumber)
  86. .set('playedCompletely', showNumber)
  87. .set('skipped', showNumber)
  88. .set('errors', showNumber);
  89. /** @private {shaka.util.Timer} */
  90. this.timer_ = new shaka.util.Timer(() => {
  91. this.onTimerTick_();
  92. });
  93. this.updateLocalizedStrings_();
  94. this.loadContainer_();
  95. this.eventManager.listen(
  96. this.localization, shaka.ui.Localization.LOCALE_UPDATED, () => {
  97. this.updateLocalizedStrings_();
  98. });
  99. this.eventManager.listen(
  100. this.localization, shaka.ui.Localization.LOCALE_CHANGED, () => {
  101. this.updateLocalizedStrings_();
  102. });
  103. this.eventManager.listen(this.button_, 'click', () => {
  104. if (!this.controls.isOpaque()) {
  105. return;
  106. }
  107. this.onClick_();
  108. this.updateLocalizedStrings_();
  109. });
  110. this.eventManager.listen(this.player, 'loading', () => {
  111. shaka.ui.Utils.setDisplay(this.button_, false);
  112. });
  113. this.eventManager.listen(
  114. this.adManager, shaka.ads.Utils.AD_STARTED, () => {
  115. shaka.ui.Utils.setDisplay(this.button_, true);
  116. });
  117. }
  118. /** @private */
  119. onClick_() {
  120. if (this.container_.classList.contains('shaka-hidden')) {
  121. this.icon_.textContent =
  122. shaka.ui.Enums.MaterialDesignIcons.STATISTICS_OFF;
  123. this.timer_.tickEvery(0.1);
  124. shaka.ui.Utils.setDisplay(this.container_, true);
  125. } else {
  126. this.icon_.textContent =
  127. shaka.ui.Enums.MaterialDesignIcons.STATISTICS_ON;
  128. this.timer_.stop();
  129. shaka.ui.Utils.setDisplay(this.container_, false);
  130. }
  131. }
  132. /** @private */
  133. updateLocalizedStrings_() {
  134. const LocIds = shaka.ui.Locales.Ids;
  135. this.nameSpan_.textContent =
  136. this.localization.resolve(LocIds.AD_STATISTICS);
  137. this.button_.ariaLabel = this.localization.resolve(LocIds.AD_STATISTICS);
  138. const labelText = this.container_.classList.contains('shaka-hidden') ?
  139. LocIds.OFF : LocIds.ON;
  140. this.stateSpan_.textContent = this.localization.resolve(labelText);
  141. }
  142. /**
  143. * @param {string} name
  144. * @return {!HTMLElement}
  145. * @private
  146. */
  147. generateComponent_(name) {
  148. const section = shaka.util.Dom.createHTMLElement('div');
  149. const label = shaka.util.Dom.createHTMLElement('label');
  150. label.textContent = name + ':';
  151. section.appendChild(label);
  152. const value = shaka.util.Dom.createHTMLElement('span');
  153. value.textContent = this.parseFrom_.get(name)(name);
  154. section.appendChild(value);
  155. this.displayedElements_.set(name, value);
  156. return section;
  157. }
  158. /** @private */
  159. loadContainer_() {
  160. const closeElement = shaka.util.Dom.createHTMLElement('div');
  161. closeElement.classList.add('shaka-no-propagation');
  162. closeElement.classList.add('shaka-statistics-close');
  163. const icon = shaka.util.Dom.createHTMLElement('i');
  164. icon.classList.add('material-icons');
  165. icon.classList.add('notranslate');
  166. icon.classList.add('material-icons-round');
  167. icon.textContent =
  168. shaka.ui.Enums.MaterialDesignIcons.CLOSE;
  169. closeElement.appendChild(icon);
  170. this.container_.appendChild(closeElement);
  171. this.eventManager.listen(icon, 'click', () => {
  172. this.onClick_();
  173. });
  174. for (const name of this.controls.getConfig().adStatisticsList) {
  175. if (name in this.currentStats_) {
  176. this.container_.appendChild(this.generateComponent_(name));
  177. this.statisticsList_.push(name);
  178. } else {
  179. shaka.log.alwaysWarn('Unrecognized ad statistic element:', name);
  180. }
  181. }
  182. }
  183. /** @private */
  184. onTimerTick_() {
  185. this.currentStats_ = this.adManager.getStats();
  186. for (const name of this.statisticsList_) {
  187. this.displayedElements_.get(name).textContent =
  188. this.parseFrom_.get(name)(name);
  189. }
  190. }
  191. /** @override */
  192. release() {
  193. this.timer_.stop();
  194. this.timer_ = null;
  195. super.release();
  196. }
  197. };
  198. /**
  199. * @implements {shaka.extern.IUIElement.Factory}
  200. * @final
  201. */
  202. shaka.ui.AdStatisticsButton.Factory = class {
  203. /** @override */
  204. create(rootElement, controls) {
  205. return new shaka.ui.AdStatisticsButton(rootElement, controls);
  206. }
  207. };
  208. shaka.ui.OverflowMenu.registerElement(
  209. 'ad_statistics', new shaka.ui.AdStatisticsButton.Factory());
  210. shaka.ui.ContextMenu.registerElement(
  211. 'ad_statistics', new shaka.ui.AdStatisticsButton.Factory());