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