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