'use strict'; /** * Licensed Materials - Property of IBM * IBM Cognos Products: Storytelling * (C) Copyright IBM Corp. 2014, 2019 * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ define(['baglass/core-client/js/core-client/ui/core/Events', 'underscore', 'baglass/core-client/js/core-client/utils/ClassFactory'], function (Events, _, ClassFactory) { var Controller = Events.extend({ authoring: false, currentSceneIndex: 0, timelineController: null, sceneCount: 0, fullscreen: false, // Map 'layout type' to 'listing' values layoutToListing: { slideshow: 'slideShow', panAndZoom1: 'panAndZoom1', panAndZoom2: 'panAndZoom2', panAndZoom3: 'panAndZoom3', panAndZoom4: 'panAndZoom4', panAndZoom5: 'panAndZoom5', panAndZoom6: 'panAndZoom6' }, init: function init(options) { Controller.inherited('init', this, arguments); this.eventRouter = options.eventRouter; this.model = options.model; // dashboard-core.BoardModel this.timelineController = options.timelineController; this.authoring = options.authoring; this.dashboardApi = options.dashboardApi; // Listen for events. this.eventRouter.on('layoutType:changed', this._onLayoutTypeChanged, this); this.eventRouter.on('mode:change', this.onModeChanged, this); this.eventRouter.on('navigation:started', this.onNavigationStarted, this); this.eventRouter.on('navigation:complete', this.onNavigationComplete, this); this.eventRouter.on('playback:prev', this.previousScene, this); this.eventRouter.on('playback:next', this.nextScene, this); this.eventRouter.on('playback:togglePlayPause', this.togglePlayPause, this); this.eventRouter.on('timeline:end', this.playThrough, this); this.model.layout.on('change:showOverviews', this.onShowOverviewsChanged, this); this.model.layout.on('change:pageSize', this.onPageSizeChange, this); this.timelineController.on('time:update', this.onTimeUpdated, this); this.timelineController.on('playState:change', this.onTimeStateChanged, this); this.timelineController.on('navigateMarkers:change', this.onNavigateMarkersChange, this); this.timelineController.on('kioskMode:change', this.onKioskModeChange, this); // Start listening for layouts being added. this.model.on('addLayouts', this.onAddLayouts, this); this.model.on('removeLayouts', this.onRemoveLayout, this); // Keep track of the scene count. this.sceneCount = this.getSceneCount(); this.playThroughTimer = null; this.overviewPauseLength = 5000; // ms this.startOverviewIndex = -1; this.endOverviewIndex = -2; // we want to display scene 0 in authoring mode even if we have overviews this.currentSceneIndex = this.showStartOverview() && !this.isAuthoring() ? this.startOverviewIndex : 0; // the start/end overview id relate to the 'id' property from the consumeView PanAndZoomLayout._overviews array this.startOverviewId = 'start'; this.endOverviewId = 'end'; this.currentSceneId = this.showStartOverview() && !this.isAuthoring() ? this.startOverviewId : this.model.layout.items[0].id; // track sharing this.shareState = null; }, onStoryLayoutReady: function onStoryLayoutReady(sceneId, sceneTime) { // set the current index to match the id from the shareable URL if (sceneId) { // the indices -1 and -2 are used as scene IDs for start overview and end overview var idx = parseInt(sceneId, 10); this.currentSceneIndex = idx < 0 ? idx : this.getSceneIndex(sceneId); } // Update the time queue with the current scene. this._updateSelectedScene(); // Make the state of the story match this.timelineController.maxPreviousScenes(this.currentSceneIndex); this.timelineController.zeroFutureScenes(this.currentSceneIndex); // set the current time to match the time from the shareable URL if (sceneTime >= 0) { this.setCurrentTime(sceneTime); } }, destroy: function destroy() { this.eventRouter.off('layoutType:changed', this._onLayoutTypeChanged, this); this.eventRouter.off('mode:change', this.onModeChanged, this); this.eventRouter.off('navigation:started', this.onNavigationStarted, this); this.eventRouter.off('navigation:complete', this.onNavigationComplete, this); this.eventRouter.off('playback:prev', this.previousScene, this); this.eventRouter.off('playback:next', this.nextScene, this); this.eventRouter.off('playback:togglePlayPause', this.togglePlayPause, this); this.eventRouter.off('timeline:end', this.playThrough, this); this.model.layout.off('change:showOverviews', this.onShowOverviewsChanged, this); this.model.layout.off('change:pageSize', this.onPageSizeChange, this); this.timelineController.off('time:update', this.onTimeUpdated, this); this.timelineController.off('playState:change', this.onTimeStateChanged, this); this.timelineController.off('navigateMarkers:change', this.onNavigateMarkersChange, this); this.timelineController.off('kioskMode:change', this.onKioskModeChange, this); // Stop listening for layouts being added. this.model.off('addLayouts', this.onAddLayouts, this); this.model.off('removeLayouts', this.onRemoveLayout, this); }, /** * Callback when share panel shows. * @param slideout {Object} The share panel slideout */ enterShareState: function enterShareState(slideout) { if (!this.shareState) { var playing = this.isPlaying(); if (playing) { this.pause(); } this.shareState = { wasPlaying: playing, slideout: slideout }; } }, /** * Callback when share panel hides. */ leaveShareState: function leaveShareState() { if (this.shareState) { if (this.shareState.wasPlaying && !this.isPlaying()) { this.play(); } this.shareState = null; } }, enterPrintMode: function enterPrintMode() { return this.timelineController.maxPreviousScenes(-2); }, leavePrintMode: function leavePrintMode(sceneTime) { var _this = this; return this.timelineController.zeroFutureScenes(-1).then(function () { if (sceneTime >= 0) { _this.setCurrentTime(sceneTime); } }); }, getTimelineController: function getTimelineController() { return this.timelineController; }, pause: function pause() { this.cancelPlaythroughTimer(); this.timelineController.pause(); }, cancelPlaythroughTimer: function cancelPlaythroughTimer() { if (this.playThroughTimer) { clearTimeout(this.playThroughTimer); this.playThroughTimer = null; } }, selectNextSceneAfterPause: function selectNextSceneAfterPause(options) { if (!this.playThroughTimer) { this.playThroughTimer = setTimeout(function () { if (!this.isAuthoring()) { this.nextScene(options); } }.bind(this), this.overviewPauseLength); } }, playThrough: function playThrough(event) { if (this.isPlayThrough() && !this.isAuthoring() && !this.isPlaying() && event) { if (event.play !== false) { if (this.isOverview()) { this.selectNextSceneAfterPause({ play: true }); } else { this.nextScene({ play: event.endState === 'playing' }); } } } }, play: function play() { this.cancelPlaythroughTimer(); if (this.isStartOverview()) { this.selectScene({ index: 0, play: true }); } else { this.timelineController.play(); } }, stop: function stop() { this.cancelPlaythroughTimer(); this.timelineController.stop(); }, isScenePopulated: function isScenePopulated(sceneId) { return this.model.layout.listWidgets([sceneId]).length > 0; }, isPlaying: function isPlaying() { return this.timelineController.isPlaying(); }, togglePlayThrough: function togglePlayThrough() { this.cancelPlaythroughTimer(); this.timelineController.togglePlayThrough(); }, toggleKioskMode: function toggleKioskMode() { this.timelineController.toggleKioskMode(); // update Next/Prev scene labels this.trigger('kioskMode:change', { isKioskMode: this.isKioskMode() }); }, toggleNavigateMarkers: function toggleNavigateMarkers() { this.timelineController.toggleNavigateMarkers(); }, toggleRefreshData: function toggleRefreshData() { this.timelineController.toggleRefreshData(); }, togglePlayPause: function togglePlayPause() { if (this.isPlaying()) { this.pause(); } else { this.play(); } }, getCurrentTime: function getCurrentTime() { return this.timelineController.getCurrentTime(); }, setCurrentTime: function setCurrentTime(time) { return this.timelineController.setCurrentTime(time); }, getDuration: function getDuration() { return this.timelineController.getDuration(); }, widgetAtStartOfCurrentScene: function widgetAtStartOfCurrentScene(widgetId) { var episode = this.timelineController.getTimelineEpisodeById(widgetId); if (episode) { return episode.touchesStart(); } return false; }, nextScene: function nextScene(options) { if (this.isNavigateMarkers() && !this.timelineController.isAtEndOfScene()) { return this._nextMarker(); } else { return this._nextScene(options); } }, _nextScene: function _nextScene(options) { this.dashboardApi.deselectAllWidgets(); // Trigger dashboard. if (this.isKioskMode() && this.isOnLastScene()) { var refreshData = this.isRefreshData() && !this.isAuthoring(); if (this.getSceneCount() > 1) { return this._invokeLifeCycleHandlers('scene:next', _.extend({}, options, { to: this.getFirstSceneIndex(), refreshData: refreshData })); } else { return this.selectScene({ index: this.getFirstSceneIndex(), forceSelect: true, refreshData: refreshData }); } } else if (!this.isOnLastScene()) { return this._invokeLifeCycleHandlers('scene:next', options); } }, _nextMarker: function _nextMarker() { return this.timelineController.jumpToNextMarker(); }, previousScene: function previousScene() { if (this.isNavigateMarkers() && !this.timelineController.isAtStartOfScene()) { return this._previousMarker(); } else { return this._previousScene(); } }, _previousScene: function _previousScene() { this.dashboardApi.deselectAllWidgets(); // Trigger dashboard. if (this.isKioskMode() && this.isOnFirstScene()) { if (this.getSceneCount() > 1) { return this._invokeLifeCycleHandlers('scene:previous', { to: this.getLastSceneIndex() }); } else { return this.selectScene({ index: this.getLastSceneIndex(), forceSelect: true }); } } else if (!this.isOnFirstScene()) { var refreshData = this.isOnSecondScene() && this.isRefreshData() && !this.isAuthoring(); return this._invokeLifeCycleHandlers('scene:previous', { refreshData: refreshData }); } }, _previousMarker: function _previousMarker() { return this.timelineController.jumpToPreviousMarker(); }, _invokeLifeCycleHandlers: function _invokeLifeCycleHandlers(name, payload) { return this.dashboardApi.getDashboardCoreSvc('.LifeCycleManager').invokeLifeCycleHandlers(name, payload); }, getStoryDurationLabel: function getStoryDurationLabel() { // TODO: calculate total story duration and return either story duration or scene duration depending on selection. return this.timelineController.getTimeLabel(this.timelineController.getDuration(), true); }, getStoryCurrentTimeLabel: function getStoryCurrentTimeLabel() { // TODO: calculate total story duration and return either story current time or scene current time depending on selection. return this.timelineController.getTimeLabel(this.timelineController.getCurrentTime(), true); }, getSelectedSceneIndex: function getSelectedSceneIndex() { return this.currentSceneIndex; }, getFirstSceneIndex: function getFirstSceneIndex() { return this.showAuthoringOverview() ? this.startOverviewIndex : 0; }, getLastSceneIndex: function getLastSceneIndex() { return this.showEndOverview() ? this.endOverviewIndex : this.model.layout.items.length - 1; }, getSceneCount: function getSceneCount() { return this.model.layout.items.length; }, getScenes: function getScenes() { return this.model.layout.items; }, getScene: function getScene(index) { return this.model.layout.items[index]; }, getSceneById: function getSceneById(id) { return this.model.layout.findModel(id); }, getSceneId: function getSceneId(index) { return this.model.layout.items[index].id; }, getSceneIndex: function getSceneIndex(id) { var items = this.model.layout.items || []; for (var i = 0; i < items.length; i++) { if (items[i].id === id) { return i; } } return -1; }, // FIXME: SceneLayout.js (and the two authoring classes SlideShow.js and PanAndZoomLayout.js) // is the only place we should be doing the conversion of our scene model IDs to index values. // The start and end overview's IDs are now "start" and "end" (for the most part) // but because StoryPaneController also does sceneId to model item index value it still works. getCurrentSceneInfo: function getCurrentSceneInfo() { var info = {}; // the indices -1 and -2 are used as scene IDs for start overview and end overview if (this.currentSceneIndex < 0) { info.id = this.currentSceneIndex.toString(); } else { var scene = this.getScene(this.currentSceneIndex); info.id = scene.id; info.title = scene.title; } return info; }, addScene: function addScene() { var _this2 = this; var sceneType = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'SceneLayout1'; var title = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; this.stop(); this.dashboardApi.deselectAllWidgets(); return ClassFactory.loadModule('storytelling/layouts/' + sceneType).then(function (sceneLayout) { var sceneSpec = sceneLayout.get(); sceneSpec.layout.title = title; var options = { addLayoutArray: [{ model: sceneSpec.layout, parentId: _this2.model.layout.id, modelIdsValid: true }], widgetSpecMap: sceneSpec.widgets }; return _this2.model.addLayouts(options); }); }, /** * Updates the 'currentSceneIndex' and triggers a global "scene:select" event * @param {Object} options * @param {Number} options.index a value that represents what scene will become selected * @param {String} options.modelId the model Id of the scene to be selected */ selectScene: function selectScene(options) { this.dashboardApi.deselectAllWidgets(); if (this.currentSceneIndex !== options.index || options.forceSelect) { var refreshData = options.refreshData || options.index === this.getFirstSceneIndex() && this.isRefreshData() && !this.isAuthoring(); this.currentSceneIndex = options.index; // Send event to dashboard (and this controller). return this._invokeLifeCycleHandlers('scene:select', _.extend({}, options, { refreshData: refreshData })); } return Promise.resolve(); }, getNavModel: function getNavModel() { return this.layoutToListing[this.model.layout.type] || ''; }, changeNavModel: function changeNavModel(navModel) { this.eventRouter.trigger('navigation:change', { type: navModel }); }, deleteScene: function deleteScene(id) { this.model.removeLayouts(id); }, duplicateScene: function duplicateScene(id) { this.model.duplicateLayout(id); }, renameScene: function renameScene(id, title) { var pageModel = this.model.layout.findModel(id); var options = this.model.getLanguageModelOptions(); options.silent = false; if (pageModel) { pageModel.set({ title: title }, options); } }, moveViewBefore: function moveViewBefore(pageModel, beforeId) { if (pageModel.id !== beforeId && pageModel.getNextSiblingId() !== beforeId) { pageModel.updateModel({ updateArray: [{ id: pageModel.id, parentId: pageModel.getParent().id, insertBefore: beforeId }] }); } }, moveSceneLeft: function moveSceneLeft(id) { var sceneModel = this.model.layout.findModel(id); var prevId = sceneModel.getPreviousSiblingId(); if (prevId) { this.moveViewBefore(sceneModel, prevId); } }, moveSceneRight: function moveSceneRight(id) { var sceneModel = this.model.layout.findModel(id); var nextModel = this.model.layout.findModel(sceneModel.getNextSiblingId()); if (nextModel) { this.moveViewBefore(sceneModel, nextModel.getNextSiblingId()); } }, isPlayThrough: function isPlayThrough() { return this.timelineController.isPlayThrough(); }, isKioskMode: function isKioskMode() { return this.timelineController.isKioskMode(); }, isNavigateMarkers: function isNavigateMarkers() { return this.timelineController.isNavigateMarkers(); }, isRefreshData: function isRefreshData() { return this.timelineController.isRefreshData(); }, isOnFirstScene: function isOnFirstScene() { return this.currentSceneIndex === this.getFirstSceneIndex(); }, isOnSecondScene: function isOnSecondScene() { return this.currentSceneIndex === this.getFirstSceneIndex() + 1; }, isOnLastScene: function isOnLastScene() { return this.currentSceneIndex === this.getLastSceneIndex(); }, isOverview: function isOverview() { return this.currentSceneIndex < 0; }, isStartOverview: function isStartOverview() { return this.currentSceneIndex === this.startOverviewIndex; }, isEndOverview: function isEndOverview() { return this.currentSceneIndex === this.endOverviewIndex; }, isAuthoring: function isAuthoring() { return this.authoring; }, isAtEndOfScene: function isAtEndOfScene() { return this.timelineController.isAtEndOfScene(); }, isAtStartOfScene: function isAtStartOfScene() { return this.timelineController.isAtStartOfScene(); }, isFullscreen: function isFullscreen() { return this.fullscreen; }, expandScene: function expandScene() { var options = { index: this.currentSceneIndex }; this.trigger('scene:expand', options); }, collapseScene: function collapseScene() { this.trigger('scene:collapse', { index: this.currentSceneIndex }); }, onShowOverviewsChanged: function onShowOverviewsChanged() { if (!this.showEndOverview() && this.currentSceneIndex === this.endOverviewIndex) { this.selectScene({ index: this.getLastSceneIndex() }); } this.trigger('change:showOverviews'); }, hasOverview: function hasOverview() { return this.model.layout.hasOverview; }, /** Returns true if the start overview frame should be shown, false otherwise. */ showStartOverview: function showStartOverview() { return this.model.layout.hasOverview && this.model.layout.showOverviews.showStart; }, /** Returns true if the start overview frame should be shown, false otherwise. * This one is used when we're in authoring mode and show the overview for editing * purposes but it may not be shown in consumption mode. */ showAuthoringOverview: function showAuthoringOverview() { return this.model.layout.hasOverview && (this.model.layout.showOverviews.showStart || this.authoring); }, /** Returns true if the end overview frame should be shown, false otherwise. */ showEndOverview: function showEndOverview() { return this.model.layout.hasOverview && this.model.layout.showOverviews.showEnd; }, didEnterFullScreen: function didEnterFullScreen() { this.fullscreen = true; }, didExitFullScreen: function didExitFullScreen() { this.fullscreen = false; }, /* * Event handlers. */ onModeChanged: function onModeChanged(event) { this.authoring = event.authoring; if (!this.showAuthoringOverview() && this.currentSceneIndex === this.startOverviewIndex) { this.selectScene({ index: 0, play: false }); } if (!this.authoring && this.isRefreshData()) { this.selectScene({ index: this.currentSceneIndex, play: false, forceSelect: true, refreshData: true }); } this.trigger('mode:change', { authoring: this.authoring }); }, onAddLayouts: function onAddLayouts(event) { var addLayoutArray = event.value.parameter.addLayoutArray; _.each(addLayoutArray, this.onAddLayout.bind(this)); }, onAddLayout: function onAddLayout(addLayout) { var layoutModel = this.model.layout.findModel(addLayout.model.id); var sceneCount = this.getSceneCount(); if (this.sceneCount !== sceneCount) { this.sceneCount = sceneCount; var insertBefore = addLayout.insertBefore ? addLayout.insertBefore : null; var sceneIndex = this.getSceneIndex(layoutModel.id); this.trigger('scene:add', { scene: layoutModel, index: sceneIndex, insertBefore: insertBefore }); } }, onRemoveLayout: function onRemoveLayout(event) { var sceneCount = this.getSceneCount(); if (this.sceneCount !== sceneCount) { this.sceneCount = sceneCount; _.each(event.value.parameter, function (sceneId) { this.trigger('scene:remove', { id: sceneId }); }.bind(this)); this.timelineController.onRemoveLayout(event); // When removing a scene the currentSceneIndex value will point to the removed scene's sibling. // If there is no n+1 sibling (meaning the last scene was removed) choose the value for the previous sibling var selectedIndex = this.currentSceneIndex; if (selectedIndex >= sceneCount) { selectedIndex = sceneCount - 1; } this.selectScene({ index: selectedIndex }); } }, onPageSizeChange: function onPageSizeChange(payload) { this.trigger('change:pageSize', payload.value); }, onTimeUpdated: function onTimeUpdated(evt) { this._triggerTimeUpdated(evt.currentTime, evt.sceneId); }, _hideSharePanel: function _hideSharePanel() { if (this.shareState) { if (this.shareState.slideout) { this.shareState.slideout.hide({ hideOnly: true }); } this.shareState = null; } }, onTimeStateChanged: function onTimeStateChanged(evt) { this._hideSharePanel(); this._triggerPlayStateUpdated(evt); }, onNavigateMarkersChange: function onNavigateMarkersChange(evt) { this.trigger('navigateMarkers:change', evt); }, onKioskModeChange: function onKioskModeChange(evt) { this.trigger('kioskMode:change', evt); }, onNavigationStarted: function onNavigationStarted(evt) { this.cancelPlaythroughTimer(); this.currentSceneIndex = evt.index; this.currentSceneId = evt.scene.id; this._updateSelectedScene(); this._triggerSceneChanged(); this.trigger('scene:select', evt); // hide share panel whenever a scene is about to play this._hideSharePanel(); // hide the filter dock if navigating to an overview scene // also trigger playthrough since overviews have no duration if (evt.overview || evt.index < 0) { this.playThrough(evt); this.eventRouter.trigger('filterDock:disable'); } else if (!this.isFullscreen() && evt.scene) { this.eventRouter.trigger('filterDock:enable'); } }, onNavigationComplete: function onNavigationComplete(evt) { this.trigger('navigation:complete', evt); }, _onLayoutTypeChanged: function _onLayoutTypeChanged(evt) { this.eventRouter.trigger('properties:refreshPane', {}); this.trigger('layoutType:changed', evt); }, /* * Helpers. */ _updateSelectedScene: function _updateSelectedScene() { var scene = this.getScene(this.currentSceneIndex); this.timelineController.setScene(scene); }, _triggerTimeUpdated: function _triggerTimeUpdated(time, sceneId) { var timeLabel = this.timelineController.getTimeLabel(time, true); this.trigger('time:update', { time: time, label: timeLabel, sceneId: sceneId }); }, _triggerPlayStateUpdated: function _triggerPlayStateUpdated(event) { this.trigger('playState:change', event); }, _triggerSceneChanged: function _triggerSceneChanged() { if (this.currentSceneIndex >= 0) { var modelId = this.getScene(this.currentSceneIndex).id; this.eventRouter.trigger('tab:tabChanged', { modelId: modelId }); } }, getTimelineAPI: function getTimelineAPI() { return this.timelineController.getAPI(); } }); return Controller; }); //# sourceMappingURL=StoryPaneController.js.map