TimelinePlayerView.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. 'use strict';
  2. /**
  3. * Licensed Materials - Property of IBM
  4. * IBM Cognos Products: Storytelling
  5. * (C) Copyright IBM Corp. 2014, 2020
  6. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  7. */
  8. define(['baglass/core-client/js/core-client/ui/core/View', 'baglass/core-client/js/core-client/utils/Utils', './PlayerViewProgressBar', 'text!./templates/TimelinePlayerView.html', 'gemini/app/util/ScreenReaderUtil', 'storytelling-ui/storytelling-ui.min', '../lib/@ba-ui-toolkit/ba-graphics/dist/icons-js/chevron-left_16', '../lib/@ba-ui-toolkit/ba-graphics/dist/icons-js/chevron-right_16', '../lib/@ba-ui-toolkit/ba-graphics/dist/icons-js/settings_16', '../lib/@ba-ui-toolkit/ba-graphics/dist/icons-js/play_16', '../lib/@ba-ui-toolkit/ba-graphics/dist/icons-js/pause_16', '../lib/@ba-ui-toolkit/ba-graphics/dist/icons-js/minimize_16', '../lib/@ba-ui-toolkit/ba-graphics/dist/icons-js/skip-back_32', '../lib/@ba-ui-toolkit/ba-graphics/dist/icons-js/skip-forward_32', 'jquery', 'react', 'react-dom'], function (View, Utils, PlayerProgressBar, Template, ScreenReaderUtil, StorytellingUI, chevronLeftIcon, chevronRightIcon, manageIcon, playIcon, pauseIcon, exitFullscreenIcon, skipBack, skipForward, $, React, ReactDOM) {
  9. var TimelinePlayerView = View.extend({
  10. templateString: Template,
  11. controller: null,
  12. currentPlayState: null,
  13. events: {
  14. 'primaryaction button.playPause': 'onPlayPauseClick',
  15. 'primaryaction button.sceneStart': 'onSceneStartClick',
  16. 'primaryaction button.sceneEnd': 'onSceneEndClick',
  17. 'primaryaction button.fullscreen': 'onExitFullscreenClick',
  18. 'primaryaction button.timelineSettings': 'onTimelineSettingsClick',
  19. 'primaryaction button.prevScene': 'onPreviousSceneClick',
  20. 'primaryaction button.nextScene': 'onNextSceneClick'
  21. },
  22. init: function init(options) {
  23. TimelinePlayerView.inherited('init', this, arguments);
  24. this.controller = options.controller; // StoryPaneController
  25. this.glassContext = options.glassContext;
  26. this.services = options.services;
  27. this._ScreenReader = new ScreenReaderUtil();
  28. this.stringResources = this.services.getSvcSync('.StringResources');
  29. this.flyoutHolder = document.createElement('div');
  30. this.flyoutHolder.classList.add('flyout-holder');
  31. },
  32. /**
  33. * Renders the TimelinePlayerView.
  34. *
  35. * @returns
  36. */
  37. render: function render() {
  38. this.$el.addClass('timelinePlayer').attr('role', 'application');
  39. // we want to be blocked when widgets are expanded
  40. this.$el.addClass('blockOnExpandView');
  41. this.currentPlayState = {};
  42. var sHtml = this.dotTemplate({
  43. stringSceneCount: this._getSceneCountString(),
  44. sceneStartLabel: this.stringResources.get('sceneStart', { position: '0:00.0' }),
  45. playLabel: this.stringResources.get('timelinePlay'),
  46. prevSceneLabel: this.stringResources.get('navPrevScene'),
  47. prevSceneAbbreviatedLabel: this.stringResources.get('navPrevSceneAbbreviated'),
  48. nextSceneLabel: this.stringResources.get('navNextScene'),
  49. navModelLabel: this.stringResources.get('sceneNavigation'),
  50. showOverview: this.controller.hasOverview(),
  51. toggleOverviewLabel: this.stringResources.get('navToggleOverview'), // TODO: delete me?
  52. fullscreenLabel: this.stringResources.get('navExitFullScreen'),
  53. timelineSettingsLabel: this.stringResources.get('timelineSettingsButton')
  54. });
  55. this.$el.html(sHtml);
  56. this.$sceneCountLabel = this.$el.find('.numScenes');
  57. this.progressBar = new PlayerProgressBar({
  58. el: this.$el.find('.progressInfo > .progressBarView'),
  59. timelineController: this.controller.getTimelineController(),
  60. controller: this.controller,
  61. glassContext: this.glassContext,
  62. services: this.services
  63. });
  64. this.progressBar.render();
  65. this.timeLabel = this.$el.find('.progressInfo > .timeLabel');
  66. this.durationLabel = this.$el.find('.progressInfo > .durationLabel');
  67. this.$previousSceneButton = this.$el.find('button.prevScene');
  68. this.$sceneStartButton = this.$el.find('button.sceneStart');
  69. this.$playPauseButton = this.$el.find('button.playPause');
  70. this.$sceneEndButton = this.$el.find('button.sceneEnd');
  71. this.$nextSceneButton = this.$el.find('button.nextScene');
  72. this.$timelineSettingsButton = this.$el.find('button.timelineSettings');
  73. this.$fullscreenButton = this.$el.find('button.fullscreen');
  74. this.$fullscreenButtonContainer = this.$el.find('.storyContainer.fullscreen');
  75. this.$fullscreenButtonContainer.hide();
  76. // Set/insert SVG icons
  77. Utils.setIcon(this.$previousSceneButton, chevronLeftIcon.default.id);
  78. Utils.setIcon(this.$sceneStartButton, skipBack.default.id);
  79. Utils.setIcon(this.$sceneEndButton, skipForward.default.id);
  80. Utils.setIcon(this.$nextSceneButton, chevronRightIcon.default.id, null, null, true);
  81. Utils.setIcon(this.$timelineSettingsButton, manageIcon.default.id);
  82. Utils.setIcon(this.$fullscreenButton, exitFullscreenIcon.default.id);
  83. // register events
  84. $(window).on('resize.privateViewEvents' + this.viewId, this.onSmallScreenUpdate.bind(this));
  85. this.controller.on('scene:add', this.onSceneAdded, this);
  86. this.controller.on('time:update', this.onTimeUpdated, this);
  87. this.controller.on('playState:change', this.onPlayStateChanged, this);
  88. this.controller.on('scene:select', this.onSceneSelected, this);
  89. this.controller.on('scene:expand', this.onSceneExpand, this);
  90. this.controller.on('scene:collapse', this.onSceneCollapse, this);
  91. this.controller.on('navigation:complete', this.onNavigationComplete, this);
  92. this.controller.on('change:showOverviews', this.onShowOverviewsChanged, this);
  93. this.controller.on('mode:change', this.onModeChange, this);
  94. this.controller.on('kioskMode:change', this._updateLabels, this);
  95. this.controller.on('navigateMarkers:change', this._onNavigateMarkersChange, this);
  96. this.controller.getTimelineController().on('duration:changed', this.onDurationChanged, this);
  97. this._updateTime(this.controller.getStoryCurrentTimeLabel());
  98. this._updateDuration();
  99. this._updatePlayState();
  100. this._updateLabels();
  101. this._updateLabelsVisibility();
  102. return this;
  103. },
  104. onSmallScreenUpdate: function onSmallScreenUpdate() {
  105. if (this._flyout) {
  106. this._closePlayThroughFlyout();
  107. }
  108. this._updateLabelsVisibility();
  109. },
  110. _updateLabelsVisibility: function _updateLabelsVisibility() {
  111. var width = this.$el.width();
  112. var $prevLabel = this.$previousSceneButton.find('.navLabel');
  113. var $nextLabel = this.$nextSceneButton.find('.navLabel');
  114. if (width < 1050) {
  115. $prevLabel.addClass('hidden');
  116. $nextLabel.addClass('hidden');
  117. } else {
  118. $prevLabel.removeClass('hidden');
  119. $nextLabel.removeClass('hidden');
  120. }
  121. },
  122. remove: function remove() {
  123. this.timeLabel = null;
  124. $(window).off('resize.privateViewEvents' + this.viewId);
  125. this.controller.off('scene:add', this.onSceneAdded, this);
  126. this.controller.off('time:update', this.onTimeUpdated, this);
  127. this.controller.off('playState:change', this.onPlayStateChanged, this);
  128. this.controller.off('scene:select', this.onSceneSelected, this);
  129. this.controller.off('scene:expand', this.onSceneExpand, this);
  130. this.controller.off('scene:collapse', this.onSceneCollapse, this);
  131. this.controller.off('navigation:complete', this.onNavigationComplete, this);
  132. this.controller.off('change:showOverviews', this.onShowOverviewsChanged, this);
  133. this.controller.off('mode:change', this.onModeChange, this);
  134. this.controller.off('kioskMode:change', this._updateLabels, this);
  135. this.controller.off('navigateMarkers:change', this._onNavigateMarkersChange, this);
  136. this.controller.getTimelineController().off('duration:changed', this.onDurationChanged, this);
  137. if (this.progressBar) {
  138. this.progressBar.remove();
  139. }
  140. TimelinePlayerView.inherited('remove', this, arguments);
  141. },
  142. /*
  143. * View events.
  144. */
  145. onPlayPauseClick: function onPlayPauseClick() {
  146. this.controller.togglePlayPause();
  147. },
  148. onSceneStartClick: function onSceneStartClick() {
  149. this.controller.pause();
  150. this.controller.setCurrentTime(0);
  151. },
  152. onSceneEndClick: function onSceneEndClick() {
  153. this.controller.pause();
  154. this.controller.setCurrentTime(this.controller.getDuration());
  155. },
  156. /*
  157. * Controller events.
  158. */
  159. onSceneAdded: function onSceneAdded() {
  160. this._updateLabels();
  161. },
  162. onModeChange: function onModeChange() {
  163. this.updateProgressBar();
  164. this._updateLabels();
  165. },
  166. onDurationChanged: function onDurationChanged() {
  167. this._updateDuration();
  168. this.updateProgressBar();
  169. },
  170. onTimeUpdated: function onTimeUpdated(evt) {
  171. var isPlaying = this.controller.isPlaying();
  172. if (!isPlaying) {
  173. this._updateLabels();
  174. }
  175. this._updateTime(evt.label);
  176. this._updatePlayState({
  177. time: evt.time,
  178. playing: isPlaying
  179. });
  180. },
  181. onPlayStateChanged: function onPlayStateChanged(evt) {
  182. var isPlaying = this.controller.isPlaying();
  183. if (!isPlaying && evt.currentTime !== 0) {
  184. this.progressBar.timeIndicatorView.handleTimelinePositionIndicatorMove();
  185. }
  186. this._updatePlayState({
  187. time: evt.currentTime,
  188. playing: isPlaying
  189. });
  190. },
  191. onSceneSelected: function onSceneSelected() {
  192. this._updateLabels();
  193. this._updateTime(this.controller.getStoryCurrentTimeLabel());
  194. this._updateDuration();
  195. },
  196. onSceneExpand: function onSceneExpand() {
  197. this.$el.addClass('viewingScene');
  198. },
  199. onSceneCollapse: function onSceneCollapse() {
  200. this.$el.removeClass('viewingScene');
  201. },
  202. onNavigationComplete: function onNavigationComplete() {
  203. this._updatePlayState();
  204. },
  205. toggleFullscreenControls: function toggleFullscreenControls(toggleFullscreen) {
  206. // Show the exit fullscreen button when in fullscreen
  207. if (toggleFullscreen) {
  208. this.$fullscreenButtonContainer.show();
  209. if (this.controller.isAuthoring()) {
  210. // Reset only when going to full screen
  211. this.controller.setCurrentTime(0);
  212. }
  213. } else {
  214. this.$fullscreenButtonContainer.hide();
  215. }
  216. if (toggleFullscreen) {
  217. this.$el.hide();
  218. }
  219. },
  220. onTimelineSettingsClick: function onTimelineSettingsClick(event) {
  221. if (this._flyout) {
  222. this._closePlayThroughFlyout();
  223. } else {
  224. this._createPlayThroughFlyout(event);
  225. }
  226. },
  227. onExitFullscreenClick: function onExitFullscreenClick() {
  228. // Exit fullscreen.
  229. this.controller.eventRouter.trigger('fullscreen:exit');
  230. },
  231. onPreviousSceneClick: function onPreviousSceneClick() {
  232. this.controller.previousScene();
  233. this.progressBar.timeIndicatorView.handlePositionMoveLeft();
  234. },
  235. onNextSceneClick: function onNextSceneClick() {
  236. this.controller.nextScene();
  237. this.progressBar.timeIndicatorView.handlePositionMoveRight();
  238. },
  239. onShowOverviewsChanged: function onShowOverviewsChanged() {
  240. this._updateLabels();
  241. },
  242. /*
  243. * Helpers.
  244. */
  245. _createPlayThroughFlyout: function _createPlayThroughFlyout(event) {
  246. this.$el.append(this.flyoutHolder);
  247. this._flyout = React.createElement(StorytellingUI.PlayThroughFlyout, {
  248. anchorElement: event.currentTarget,
  249. closeFlyout: this._closePlayThroughFlyout.bind(this),
  250. controller: this.controller
  251. });
  252. ReactDOM.render(this._flyout, this.flyoutHolder);
  253. this.$timelineSettingsButton.addClass('selected');
  254. },
  255. _closePlayThroughFlyout: function _closePlayThroughFlyout() {
  256. ReactDOM.unmountComponentAtNode(this.flyoutHolder);
  257. this.$el.find('.flyout-holder').remove();
  258. this._flyout = null;
  259. this.$timelineSettingsButton.removeClass('selected');
  260. },
  261. _getSceneCountString: function _getSceneCountString() {
  262. var count = this.controller.getSceneCount();
  263. var text;
  264. var currentSceneIndex = this.controller.getSelectedSceneIndex();
  265. if (currentSceneIndex >= 0) {
  266. text = this.stringResources.get('storySceneRangeCountLabel', {
  267. index: currentSceneIndex + 1,
  268. count: count
  269. });
  270. } else if (count === 1) {
  271. text = this.stringResources.get('storySceneSingleCountLabel');
  272. } else {
  273. text = this.stringResources.get('storySceneCountLabel', {
  274. count: count
  275. });
  276. }
  277. return text;
  278. },
  279. _onNavigateMarkersChange: function _onNavigateMarkersChange() {
  280. this._updateLabels();
  281. this.updateProgressBar();
  282. },
  283. _updateLabels: function _updateLabels() {
  284. var canShowPrevious;
  285. var canShowNext;
  286. // update button states
  287. if (this.controller.isKioskMode()) {
  288. canShowNext = true;
  289. canShowPrevious = true;
  290. } else {
  291. canShowPrevious = !this.controller.isOnFirstScene();
  292. canShowNext = !this.controller.isOnLastScene();
  293. if (this.controller.isNavigateMarkers()) {
  294. if (!canShowPrevious) {
  295. canShowPrevious = !this.controller.isAtStartOfScene();
  296. }
  297. if (!canShowNext) {
  298. canShowNext = !this.controller.isAtEndOfScene();
  299. }
  300. }
  301. }
  302. this.$previousSceneButton.prop('disabled', !canShowPrevious);
  303. this.$nextSceneButton.prop('disabled', !canShowNext);
  304. this.$sceneCountLabel.html(this._getSceneCountString());
  305. // update button labels
  306. var previousSceneLabels = {
  307. abbreviatedString: this.stringResources.get('navPrevSceneAbbreviated'),
  308. string: this.stringResources.get('navPrevScene')
  309. };
  310. var nextSceneLabels = {
  311. string: this.stringResources.get('navNextScene')
  312. };
  313. if (this.controller.isNavigateMarkers()) {
  314. var previousMarkerLabels = {
  315. abbreviatedString: this.stringResources.get('navPrevMarkerAbbreviated'),
  316. string: this.stringResources.get('navPrevMarker')
  317. };
  318. var nextMarkerLabels = {
  319. string: this.stringResources.get('navNextMarker')
  320. };
  321. this._updateLabel(this.$previousSceneButton, this.controller.isAtStartOfScene() ? previousSceneLabels : previousMarkerLabels);
  322. this._updateLabel(this.$nextSceneButton, this.controller.isAtEndOfScene() ? nextSceneLabels : nextMarkerLabels);
  323. } else {
  324. this._updateLabel(this.$previousSceneButton, previousSceneLabels);
  325. this._updateLabel(this.$nextSceneButton, nextSceneLabels);
  326. }
  327. },
  328. _updateLabel: function _updateLabel($button, labels) {
  329. $button.attr('aria-label', labels.string);
  330. $button.attr('title', labels.string);
  331. $button.find('.navLabel').text(labels.abbreviatedString ? labels.abbreviatedString : labels.string);
  332. },
  333. updateProgressBar: function updateProgressBar() {
  334. if (this.progressBar) {
  335. this.progressBar.refresh();
  336. }
  337. },
  338. _updateDuration: function _updateDuration() {
  339. if (this.durationLabel) {
  340. var _label = this.controller.getStoryDurationLabel();
  341. this.durationLabel.text(_label);
  342. }
  343. var $button = this.$el.find('.sceneEnd');
  344. var label = this.stringResources.get('sceneEnd', { position: this.progressBar.timeIndicatorView.getEndTime() });
  345. $button.attr('aria-label', label);
  346. $button.attr('title', label);
  347. },
  348. _updateTime: function _updateTime(time) {
  349. if (this.timeLabel) {
  350. this.timeLabel.text(time);
  351. }
  352. },
  353. _getCurrentTime: function _getCurrentTime() {
  354. return this.progressBar.timeIndicatorView.getCurrentTime();
  355. },
  356. _updatePlayState: function _updatePlayState(options) {
  357. options = options || {
  358. time: this.controller.getCurrentTime(),
  359. playing: this.controller.isPlaying()
  360. };
  361. var duration = this.controller.getDuration(),
  362. playing = options.playing;
  363. var canPlayPause = duration > 0 || this.controller.isStartOverview(),
  364. canGoToStart = options.time > 0,
  365. canGoToEnd = options.time < duration;
  366. if (playing !== this.currentPlayState.playing) {
  367. var buttonLabel = playing ? this.stringResources.get('timelinePause') : this.stringResources.get('timelinePlay');
  368. this.$playPauseButton.attr('title', buttonLabel).attr('aria-label', buttonLabel).html('');
  369. this._ScreenReader.callOut(buttonLabel);
  370. if (playing) {
  371. Utils.setIcon(this.$playPauseButton, pauseIcon.default.id);
  372. } else {
  373. Utils.setIcon(this.$playPauseButton, playIcon.default.id);
  374. }
  375. this.currentPlayState.playing = playing;
  376. }
  377. if (canPlayPause !== this.currentPlayState.canPlayPause) {
  378. this.$playPauseButton.prop('disabled', !canPlayPause);
  379. this.currentPlayState.canPlayPause = canPlayPause;
  380. }
  381. if (canGoToStart !== this.currentPlayState.canGoToStart) {
  382. this.$sceneStartButton.prop('disabled', !canGoToStart);
  383. this.currentPlayState.canGoToStart = canGoToStart;
  384. }
  385. if (canGoToEnd !== this.currentPlayState.canGoToEnd) {
  386. this.$sceneEndButton.prop('disabled', !canGoToEnd);
  387. this.currentPlayState.canGoToEnd = canGoToEnd;
  388. }
  389. }
  390. });
  391. return TimelinePlayerView;
  392. });
  393. //# sourceMappingURL=TimelinePlayerView.js.map