PlayerViewProgressBar.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. 'use strict';
  2. /**
  3. * Licensed Materials - Property of IBM
  4. * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2017, 2018
  5. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  6. */
  7. define(['jquery', 'baglass/core-client/js/core-client/ui/core/View', './TimelineRulerView', './TimelineTimeIndicatorView', '../NoScaleManager', 'text!./templates/PlayerViewProgressBar.html', 'underscore'], function ($, View, TimelineRulerView, TimelineTimeIndicatorView, NoScaleManager, ProgressBarTemplate, _) {
  8. var PlayerViewProgressBar = View.extend({
  9. templateString: ProgressBarTemplate,
  10. events: {
  11. 'primaryaction div.marker': 'onMarkerClick',
  12. 'primaryaction': 'onProgressBarClick'
  13. },
  14. init: function init(options) {
  15. PlayerViewProgressBar.inherited('init', this, arguments);
  16. this.controller = options.controller;
  17. this.timelineController = options.timelineController;
  18. this.glassContext = options.glassContext;
  19. this.services = options.services;
  20. this.stringResources = this.services.getSvcSync('.StringResources');
  21. this.noScaleManager = new NoScaleManager();
  22. this._refreshTimer = null;
  23. /*
  24. * This Event Listener helps with when the user changes the length of a scene
  25. * in the timeline view. Changing the length triggers a resize event which causes
  26. * the progress bar to resize down to 5px (the size of the element with the slash)
  27. * so when we close the timeline and the progress bar comes back we need to
  28. * recalculate size for the progress bar.
  29. */
  30. this._progressBarTransition = function (event) {
  31. if (event.target === this.$el[0] && event.propertyName.indexOf('flex-grow') > -1) {
  32. this._recalculateProgressBar();
  33. }
  34. }.bind(this);
  35. this.$el[0].addEventListener('transitionend', this._progressBarTransition);
  36. /*
  37. * Grab the navbar home button as it will be visible in all perspectives all the time.
  38. * We need it because it gets animated when switching between authoring/consumption modes or
  39. * when a browser window is resized to collapse the navbar.
  40. */
  41. var homeBtn = this.glassContext.appController.findPlugin('com.ibm.bi.glass.common.home');
  42. if (homeBtn && homeBtn.$el && homeBtn.$el.length === 1) {
  43. this._navbarItem = homeBtn.$el[0];
  44. this._navbarTransitionCheck = function (event) {
  45. if (event.propertyName === 'width') {
  46. this._recalculateProgressBar();
  47. }
  48. }.bind(this);
  49. this._navbarItem.addEventListener('transitionend', this._navbarTransitionCheck);
  50. }
  51. /*
  52. * We ALSO listen for window resize to recalculate the progress bar size
  53. */
  54. this._resizeHandler = this._recalculateProgressBar.bind(this);
  55. $(window).on('resize', this._resizeHandler);
  56. this.timelineController.eventRouter.on('navigation:complete', this.refreshMarkers.bind(this));
  57. this.markers = [];
  58. },
  59. /**
  60. * Renders the ProgressBar.
  61. *
  62. * @returns
  63. */
  64. render: function render() {
  65. var sHtml = this.dotTemplate({});
  66. this.$el.html(sHtml);
  67. this._scaleToFit();
  68. this.timeRulerView = new TimelineRulerView({
  69. el: this.$el.find('.progressBarControl'),
  70. controller: this.timelineController,
  71. scaleManager: this.noScaleManager,
  72. useDiv: true
  73. });
  74. this.timeRulerView.render();
  75. this.timeIndicatorView = new TimelineTimeIndicatorView({
  76. el: this.$el.find('.progressBarPosition'),
  77. controller: this.timelineController,
  78. scaleManager: this.noScaleManager,
  79. indicatorOnly: true,
  80. services: this.services,
  81. progressBarCallback: this._completionBarUpdate.bind(this)
  82. });
  83. this.timeIndicatorView.render();
  84. this._completionBarUpdate();
  85. },
  86. remove: function remove() {
  87. clearTimeout(this._refreshTimer);
  88. // Adding this removeEventListener line first to make sure that this.$el is still a valid object
  89. this.$el[0].removeEventListener('transitionend', this._progressBarTransition);
  90. this._removeMarkers();
  91. PlayerViewProgressBar.inherited('remove', this, arguments);
  92. $(window).off('resize', this._resizeHandler);
  93. if (this._navBarItem) {
  94. this._navbarItem.removeEventListener('transitionend', this._navbarTransitionCheck);
  95. }
  96. if (this.timeIndicatorView) {
  97. this.timeIndicatorView.remove();
  98. this.timeIndicatorView = null;
  99. }
  100. if (this.timeRulerView) {
  101. this.timeRulerView.remove();
  102. this.timeRulerView = null;
  103. }
  104. },
  105. refreshMarkers: function refreshMarkers() {
  106. if (this.timelineController.isNavigateMarkers() && !this.controller.isOverview()) {
  107. if (this.markers.length === 0) {
  108. this._renderMarkers();
  109. } else {
  110. var updatedMarkers = this.timelineController.getMarkers();
  111. var existingMarker;
  112. _.each(updatedMarkers, function (timer, index) {
  113. existingMarker = this.markers[index];
  114. if (existingMarker) {
  115. // re-use existing pause points to avoid creating new elements
  116. this._updateMarkerTimer(existingMarker, timer);
  117. } else {
  118. this._renderMarker(timer);
  119. }
  120. }.bind(this));
  121. // remove any unused pause points
  122. this._removeMarkers(updatedMarkers.length);
  123. }
  124. } else {
  125. this._removeMarkers();
  126. }
  127. },
  128. refresh: function refresh() {
  129. if (this.timeRulerView.$el.is(':visible')) {
  130. this._scaleToFit();
  131. this.refreshMarkers();
  132. this.timeRulerView.refresh();
  133. this.timeIndicatorView.refresh();
  134. }
  135. },
  136. onMarkerClick: function onMarkerClick(event) {
  137. var marker = this._getMarker(event.currentTarget);
  138. if (marker) {
  139. this.timelineController.setCurrentTime(marker.timer);
  140. this.timeIndicatorView.setFocus();
  141. }
  142. },
  143. onProgressBarClick: function onProgressBarClick() {
  144. this.timeIndicatorView.setFocus();
  145. },
  146. _removeMarkers: function _removeMarkers(startIndex) {
  147. if (startIndex) {
  148. for (var i = startIndex; i < this.markers.length; i++) {
  149. this.markers[i].$el.remove();
  150. this.markers[i] = null;
  151. }
  152. this.markers = _.compact(this.markers);
  153. } else if (this.markers.length > 0) {
  154. this.$el.find('.marker').remove();
  155. this.markers = [];
  156. }
  157. },
  158. _renderMarkers: function _renderMarkers() {
  159. var markers = this.timelineController.getMarkers();
  160. _.each(markers, this._renderMarker.bind(this));
  161. },
  162. _renderMarker: function _renderMarker(timer) {
  163. var $marker = $("<div class='marker' tab-index='0'/>");
  164. $marker.attr('aria-label', this.stringResources.get('navigationMarker'));
  165. var marker = {
  166. $el: $marker,
  167. timer: timer
  168. };
  169. this.$el.find('.markersContainer').append($marker);
  170. this.markers.push(marker);
  171. this._positionMarker(marker);
  172. return $marker;
  173. },
  174. _updateMarkerTimer: function _updateMarkerTimer(marker, newTimer) {
  175. marker.timer = newTimer;
  176. this._positionMarker(marker);
  177. },
  178. _positionMarker: function _positionMarker(marker) {
  179. var percent = marker.timer / this.timelineController.getDuration() * 100;
  180. var leftVal = percent + '%';
  181. var halfIconWidth = marker.$el.outerWidth() / 2;
  182. marker.$el.css('left', 'calc(' + leftVal + ' - ' + halfIconWidth + 'px)');
  183. },
  184. _getMarker: function _getMarker(el) {
  185. var marker = _.find(this.markers, function (marker) {
  186. return marker.$el.is(el);
  187. });
  188. return marker;
  189. },
  190. _recalculateProgressBar: function _recalculateProgressBar() {
  191. if (this.timeRulerView && this.timeIndicatorView) {
  192. /*
  193. * !! WARNING!! setTimeout
  194. * There are two reasons for this setTimeout.
  195. * 1) When switching between authoring and consumption modes the width of the timeline player change
  196. * because the navbar shrinks/expands. When it changes it is animated so we need to wait longer than 200ms.
  197. * 200ms comes from the toolbars.css file in the glass-foundations component under the '.navbar button' style.
  198. * 2) We need to wait for a length of time when transition from the timeline view to film strip view.
  199. * The animation here takes a while and through trial and error 1000ms (1 second) worked consistently.
  200. * This is also for switching to fullscreen mode and also effects when switching from it.
  201. */
  202. clearTimeout(this._refreshTimer);
  203. this._refreshTimer = setTimeout(function () {
  204. this.refresh();
  205. }.bind(this), 1000);
  206. }
  207. },
  208. _completionBarUpdate: function _completionBarUpdate(percent) {
  209. if (this.timeIndicatorView) {
  210. if (!percent) {
  211. percent = 0;
  212. }
  213. this.$el.find('.completedBar').css('width', percent + '%');
  214. }
  215. },
  216. _scaleToFit: function _scaleToFit() {
  217. var duration = this.timelineController.getDuration();
  218. this.noScaleManager.updateDuration(duration, this.$el.outerWidth(false));
  219. this.noScaleManager.scaleToFit();
  220. }
  221. });
  222. return PlayerViewProgressBar;
  223. });
  224. //# sourceMappingURL=PlayerViewProgressBar.js.map