12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121 |
- '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', 'dashboard-analytics/apiHelpers/SlotAPIHelper', 'underscore', './AnimationDirector', './TimeQueue', './util/WidgetHelper'], function (Events, SlotAPIHelper, _, AnimationDirector, TimeQueue, WidgetHelper) {
- var Controller = Events.extend({
- authoring: false,
- duration: 10000,
- defaultWidgetDuration: 5000,
- animationDuration: 500,
- cursorTime: 0,
- suggestionTimelineCheckingInterval: 100,
- suggestionTimeRange: [],
- lastEstablishedEventQueue: [],
- init: function init(options) {
- Controller.inherited('init', this, arguments);
- this.canvasController = options.canvasController;
- this.dashboardApi = options.dashboardApi;
- this.timeline = options.model.timeline;
- this.layout = options.model.layout;
- this.widgetInstances = options.model.widgetInstances;
- this.model = options.model;
- this.eventRouter = options.eventRouter;
- this.timeQueues = {};
- this.selectedWidgetIds = {};
- this.authoring = options.authoring;
- this.services = options.services;
- this.stringResources = this.services.getSvcSync('.StringResources');
- this.widgetHelper = new WidgetHelper({ dashboardApi: this.dashboardApi });
- if (this.model.pageContext) {
- this._animationDirector = new AnimationDirector({
- widgetHelper: this.widgetHelper,
- pageContextAPI: this.model.pageContext.getAPI()
- });
- }
- this.canvas = this.dashboardApi.getCanvas();
- //Listen for events
- this.eventRouter.on('widget:maximize', this._onMaximizeWidget, this);
- this.eventRouter.on('widget:restore', this._onRestoreWidget, this);
- this.eventRouter.on('widget:selected', this.onSelectWidget, this);
- this.eventRouter.on('widget:deselected', this.onDeselectWidget, this);
- this.eventRouter.on('timequeue:tick', this.onTimeQueueTick, this);
- this.eventRouter.on('timequeue:stateChanged', this.onTimeQueueStateChanged, this);
- this.eventRouter.on('timequeue:durationChanged', this.onDurationChanged, this);
- this.eventRouter.on('navigation:complete', this.onNavigationComplete, this);
- this.eventRouter.on('scene:reorder', this.onSceneReorder, this);
- this.eventRouter.on('scene:swap', this.onSceneSwap, this);
- this.eventRouter.on('mode:change', this.onModeChanged, this);
- this.eventRouter.on('rendered', this.onWidgetRendered, this);
- this.eventRouter.on('widget:animate', this.onAnimate, this);
- this.eventRouter.on('layoutType:changed', this._onLayoutTypeChanged, this);
- this.eventRouter.on('timeline:end', this.onTimelinePlaybackEnd, this);
- this.timeline.on('change:kioskMode', this.onKioskModeChange, this);
- this.timeline.on('change:navigateMarkers', this.onNavigateMarkersChange, this);
- this.timeline.on('change:episodes', this.onModelEpisodeChange, this);
- this.timeline.on('timeline:episodeFragmentAdded', this.onEpisodeFragmentAdded, this);
- this.timeline.episodes.on('add', this.onEpisodeAdded, this);
- this.timeline.episodes.on('remove', this.onEpisodeRemoved, this);
- this.model.on('widget:change', this.onChangeWidget, this);
- },
- destroy: function destroy() {
- // Please keep the same order as registering events for easier maintenance.
- this.eventRouter.off('widget:maximize', this._onMaximizeWidget, this);
- this.eventRouter.off('widget:restore', this._onRestoreWidget, this);
- this.eventRouter.off('widget:selected', this.onSelectWidget, this);
- this.eventRouter.off('widget:deselected', this.onDeselectWidget, this);
- this.eventRouter.off('timequeue:tick', this.onTimeQueueTick, this);
- this.eventRouter.off('timequeue:stateChanged', this.onTimeQueueStateChanged, this);
- this.eventRouter.off('timequeue:durationChanged', this.onDurationChanged, this);
- this.eventRouter.off('navigation:complete', this.onNavigationComplete, this);
- this.eventRouter.off('scene:reorder', this.onSceneReorder, this);
- this.eventRouter.off('scene:swap', this.onSceneSwap, this);
- this.eventRouter.off('mode:change', this.onModeChanged, this);
- this.eventRouter.off('rendered', this.onWidgetRendered, this);
- this.eventRouter.off('widget:animate', this.onAnimate, this);
- this.eventRouter.off('layoutType:changed', this._onLayoutTypeChanged, this);
- this.eventRouter.off('timeline:end', this.onTimelinePlaybackEnd, this);
- this.timeline.off('change:kioskMode', this.onKioskModeChange, this);
- this.timeline.off('change:navigateMarkers', this.onNavigateMarkersChange, this);
- this.timeline.off('change:episodes', this.onModelEpisodeChange, this);
- this.timeline.off('timeline:episodeFragmentAdded', this.onEpisodeFragmentAdded, this);
- this.timeline.episodes.off('add', this.onEpisodeAdded, this);
- this.timeline.episodes.off('remove', this.onEpisodeRemoved, this);
- this.model.off('widget:change', this.onChangeWidget, this);
- var that = this;
- _.each(this.widgetInstances, function (widgetInstance) {
- var widget = that.widgetHelper.getWidget(widgetInstance.getId());
- if (widget && widget.getVisApi && widget.getVisApi() && widget.getVisApi().ownerWidget) {
- widget.getVisApi().ownerWidget.off('dwChange:visTransaction', that.onWidgetChange, that);
- widget.getVisApi().ownerWidget.off('dwChange:visId', that.onWidgetChange, that);
- }
- });
- },
- _onLayoutTypeChanged: function _onLayoutTypeChanged() {
- this.timeQueues = {};
- this.scene = null;
- },
- onWidgetChange: function onWidgetChange(event) {
- // in the case of undo/redo we have already did have an entry on the undo/redo stack so we don't need to do anything
- // (also for undo/redo of dwChange:visId the events are in the wrong order so best to just ignore)
- if (!(event && event.model && event.model.id && event.sender !== 'UndoRedoController')) {
- return;
- }
- var options = event.options || {};
- options.payloadData = event.data;
- options.sender = event.sender;
- options = this._validateModelOptions(options);
- var episode = this.getTimelineEpisodeById(event.model.id);
- if (episode) {
- //grab the default "empty" and send it off the the model
- var blankPayloadExemplar = this._getTimelineHighlightsForWidget(event.model.id);
- episode.acts.updateHighlightPayloadColumns(blankPayloadExemplar, options);
- }
- },
- setScene: function setScene(scene) {
- if (scene) {
- if (this.scene !== scene) {
- this.scene = scene;
- this.setCursorTime(0);
- }
- } else {
- this.scene = null;
- this.trigger('time:update', { currentTime: 0 });
- }
- },
- onWidgetRendered: function onWidgetRendered(api) {
- if (api) {
- this.trigger('slider:change', {
- 'name': 'rendered',
- 'modelId': api.getId()
- });
- }
- var widget = this.widgetHelper.getWidget(api.getId());
- if (widget && widget.getVisApi && widget.getVisApi() && widget.getVisApi().ownerWidget) {
- // deregister first and re-register
- widget.getVisApi().ownerWidget.off('dwChange:visTransaction', this.onWidgetChange, this);
- widget.getVisApi().ownerWidget.on('dwChange:visTransaction', this.onWidgetChange, this);
- widget.getVisApi().ownerWidget.off('dwChange:visId', this.onWidgetChange, this);
- widget.getVisApi().ownerWidget.on('dwChange:visId', this.onWidgetChange, this);
- }
- },
- onRemoveLayout: function onRemoveLayout(evt) {
- _.each(evt.value.parameter, function (sceneId) {
- if (this.timeQueues[sceneId]) {
- delete this.timeQueues[sceneId];
- }
- }.bind(this));
- },
- _seekStory: function _seekStory(sceneIndex) {
- if (this.layout.type === 'slideshow') {
- // scene preparation is limited to three scenes in slideshow
- var timeQueue = this._currentTimeQueue();
- if (timeQueue) {
- timeQueue.pause();
- }
- var sceneCount = this.model.layout.items.length;
- this._maxScene(sceneIndex === 0 ? sceneCount - 1 : sceneIndex - 1);
- this._zeroScene(sceneIndex);
- this._zeroScene(sceneIndex === sceneCount - 1 ? 0 : sceneIndex + 1);
- } else {
- this.maxPreviousScenes(sceneIndex);
- this.zeroFutureScenes(sceneIndex);
- }
- },
- onNavigationComplete: function onNavigationComplete(evt) {
- var play;
- if (evt) {
- play = evt.play;
- this._seekStory(evt.index);
- }
- // In consumption mode we auto play when we switch scenes unless an explicit play instruction is received
- if (play !== false) {
- if (!this.isAuthoring() && !this.isPlaying() && !this.isNavigateMarkers() || play) {
- this.play();
- }
- }
- },
- isAuthoring: function isAuthoring() {
- return this.authoring;
- },
- onModeChanged: function onModeChanged(event) {
- this.authoring = event.authoring;
- },
- onSceneReorder: function onSceneReorder(event) {
- this.trigger('scene:reorder', event);
- },
- maxPreviousScenes: function maxPreviousScenes(idx) {
- var seekPromises = [];
- if (idx === -2) {
- // Special case - end of story!
- idx = this.model.layout.items.length;
- }
- for (var i = 0; i < idx; i++) {
- seekPromises.push(this._maxScene(i));
- }
- return Promise.all(seekPromises);
- },
- zeroFutureScenes: function zeroFutureScenes(idx) {
- var seekPromises = [];
- if (idx !== -2) {
- var sceneCount = this.model.layout.items.length;
- var i = idx;
- if (i < 0) {
- i = 0;
- }
- for (; i < sceneCount; i++) {
- seekPromises.push(this._zeroScene(i));
- }
- }
- return Promise.all(seekPromises);
- },
- _zeroScene: function _zeroScene(index) {
- var scene = this.model.layout.items[index];
- var timeQueue = this._getTimeQueue(scene.id);
- if (timeQueue.isPlaying()) {
- timeQueue.stop();
- }
- return timeQueue.reset();
- },
- _maxScene: function _maxScene(index) {
- var scene = this.model.layout.items[index];
- var timeQueue = this._getTimeQueue(scene.id);
- if (timeQueue.isPlaying()) {
- timeQueue.stop();
- }
- return timeQueue.endScene();
- },
- getCurrentScene: function getCurrentScene() {
- return this.scene;
- },
- getTimelineEpisodeCount: function getTimelineEpisodeCount() {
- return this._getWidgetIds().length;
- },
- getTimelineEpisodeById: function getTimelineEpisodeById(id) {
- return this._isWidgetInCurrentScene(id) ? this._getEpisodeModel(id) : undefined;
- },
- /* this method must return the episodes in the order they are in the collection*/
- getTimelineEpisodes: function getTimelineEpisodes() {
- var widgets = {};
- // create an 'index' of the widget ids for faster lookup
- this._getWidgetIds().forEach(function (id) {
- widgets[id] = id;
- });
- return _.filter(this.model.timeline.episodes.models, function (episode) {
- return episode.id in widgets;
- }.bind(this));
- },
- getTimelineEpisodeIDs: function getTimelineEpisodeIDs() {
- var episodeIDs = [];
- var episodeObjs = this.getTimelineEpisodes();
- Object.keys(episodeObjs).forEach(function (key) {
- return episodeIDs.push(episodeObjs[key].id);
- });
- return episodeIDs;
- },
- getMarkers: function getMarkers() {
- var timeQueue = this._currentTimeQueue();
- if (timeQueue) {
- return timeQueue.getMarkers();
- }
- return [];
- },
- isHighlightSupported: function isHighlightSupported(widgetId) {
- var highlights = this._getTimelineHighlightsForWidget(widgetId);
- return Boolean(highlights && highlights.length);
- },
- updateLastEstablishedEventQueue: function updateLastEstablishedEventQueue() {
- //get all the start times in timeQueue._eventQueue
- this.lastEstablishedEventQueue = [];
- var timeQueue = this._currentTimeQueue();
- if (timeQueue) {
- this._updateLastEstablishedEventQueue(timeQueue);
- }
- return this.lastEstablishedEventQueue;
- },
- _updateLastEstablishedEventQueue: function _updateLastEstablishedEventQueue(timeQueue) {
- for (var key in timeQueue._eventQueue) {
- if (timeQueue._eventQueue.hasOwnProperty(key)) {
- for (var selectedWidgetId in this.selectedWidgetIds) {
- if (timeQueue._eventQueue[key][0].item !== selectedWidgetId || timeQueue._eventQueue[key].length > 1) {
- this.lastEstablishedEventQueue.push(key);
- }
- }
- }
- }
- },
- moveEpisodeBefore: function moveEpisodeBefore(id, beforeId, options) {
- options = this._validateModelOptions(options);
- this.timeline.episodes.reorder(id, beforeId, options);
- },
- updateTimelineDuration: function updateTimelineDuration(id, start, end, options) {
- options = this._validateModelOptions(options);
- if (this.suggestionTimeRange.length > 0) {
- start = this.suggestionTimeRange[0];
- end = this.suggestionTimeRange[1];
- }
- var episode = this.timeline.episodes.get(id);
- var previousEpisodeActs = _.map(episode.acts.toArray(), _.clone);
- var previousEpisodeStart = episode.getEntranceAct().timer;
- var previousEpisodeEnd = episode.getExitAct().timer;
- var previousDuration = previousEpisodeEnd - previousEpisodeStart;
- var newDuration = end - start;
- // +- 2 ms is close... this should handle all rounding cases in a way that is not noticeable visually.
- // What is important is that we correctly detect all moves. If we detect a resize of 2 ms as a move that is not an issue.
- options.isMove = newDuration <= previousDuration + 2 && previousDuration <= newDuration + 2;
- // if we are reordering - let the reorder model change handle undo-redo
- var isReorder = options.isMove && Math.abs(previousEpisodeStart - start) <= 5;
- var notifyUpdateDurationListeners = function (isUndoRedo) {
- this.trigger('modelEpisode:changed', {
- id: id,
- value: [episode.getEntranceAct().timer, episode.getExitAct().timer]
- });
- this.model.trigger('change', isUndoRedo || isReorder ? {} : {
- value: _.map(episode.acts.toArray(), _.clone),
- prevValue: previousEpisodeActs,
- sender: options.sender,
- data: options.payloadData,
- senderContext: {
- applyFn: function (value) {
- episode.acts.set(value, { 'silent': true });
- notifyUpdateDurationListeners(true);
- }.bind(this)
- }
- });
- this._refreshTimeQueue();
- }.bind(this);
- episode.updateDuration(start, end, options);
- notifyUpdateDurationListeners();
- this.suggestionTimeRange = [];
- },
- _getTimelineHighlightsForWidget: function _getTimelineHighlightsForWidget(contentId) {
- var content = this.widgetHelper.getContent(contentId);
- var visualization = content.getFeature('Visualization');
- // this returns only the mapped data slots
- var slots = visualization && visualization.getSlots().getMappedSlotList();
- var slotEligible = function slotEligible(slot, dataItem, index) {
- // True if type is attribute and slot is not a multi measure series
- return !SlotAPIHelper.isMultiMeasuresSeriesOrValue(slot, index) && dataItem.getType() === 'attribute';
- };
- var highlightPayloads = [];
- _.each(slots, function (slot) {
- _.each(slot.getDataItemList(), function (dataItem, index) {
- var columnId = dataItem.getColumnId();
- var label = dataItem.getLabel();
- if (slotEligible(slot, dataItem, index) && !_.findWhere(highlightPayloads, {
- columnId: columnId
- })) {
- // using dataItem.getUniqueId() for the ID is problematic since the dataitem
- // that gets mapped to each payload entry can change when columns are dragged around, added, or deleted.
- highlightPayloads.push({
- columnId: columnId,
- columnLabel: label,
- id: columnId,
- values: []
- });
- }
- });
- });
- return highlightPayloads;
- },
- addTimelineHighlight: function addTimelineHighlight(widgetId, timer) {
- var episodeModel = this._getEpisodeModel(widgetId);
- var highlights = this._getTimelineHighlightsForWidget(widgetId);
- // default to current time if no timer is passed in
- timer = timer || timer === 0 ? timer : this.getCurrentTime();
- // we might want that in the timeline models... we need to make sure that the current time is inside the widget time.
- // we make ensure that it's at a least 1 ms inside the entrance and exit time.
- timer = Math.max(timer, episodeModel.getEntranceAct().timer + 1);
- timer = Math.min(timer, episodeModel.getExitAct().timer - 1);
- var includesAllAct = {
- timer: timer,
- action: 'highlight',
- payload: highlights
- };
- var options = {
- payloadData: {
- undoRedoTransactionId: _.uniqueId('highlight')
- }
- };
- var newAct = episodeModel.acts.add(includesAllAct, options);
- this.trigger('slider:showHighlightSummary', {
- episodeModel: episodeModel,
- actModel: newAct
- });
- return newAct;
- },
- updateTimelineHighlight: function updateTimelineHighlight(widgetId, actId, attributes) {
- var episodeModel = this._getEpisodeModel(widgetId);
- var act = episodeModel.acts.get(actId);
- if (act) {
- act.set(attributes);
- }
- return act;
- },
- deleteTimelineHighlight: function deleteTimelineHighlight(widgetId, actId) {
- var episodeModel = this._getEpisodeModel(widgetId);
- if (episodeModel && actId) {
- var actModel = episodeModel.acts.get(actId);
- if (actModel && actModel.action === 'highlight') {
- episodeModel.acts.remove(actModel);
- }
- }
- },
- getSnapIndicatorTime: function getSnapIndicatorTime(timeRange, draggingElement) {
- var timeLength = timeRange[1] - timeRange[0],
- newTimeRange = [],
- changed = null,
- i;
- for (i = timeRange.length - 1; i >= 0; i--) {
- newTimeRange.unshift(timeRange[i]);
- }
- var checkTimeQueueResult = this._checkTimeQueueForSnapIndicator(timeRange, draggingElement, newTimeRange, timeLength);
- if (checkTimeQueueResult.changed) {
- newTimeRange = checkTimeQueueResult.timeRange;
- changed = checkTimeQueueResult.changed || changed;
- }
- var checkStartAndEndResult = this._checkStartAndEndForSnapIndicator(timeRange, draggingElement, newTimeRange);
- if (checkStartAndEndResult.changed) {
- newTimeRange = checkStartAndEndResult.timeRange;
- changed = checkStartAndEndResult.changed || changed;
- }
- if (draggingElement === 'content') {
- if (changed === 'start') {
- newTimeRange[1] = newTimeRange[0] + timeLength;
- } else if (changed === 'end') {
- newTimeRange[0] = newTimeRange[1] - timeLength;
- }
- }
- this.suggestionTimeRange = newTimeRange;
- return {
- changed: changed,
- timeRange: newTimeRange
- };
- },
- selectWidgetAndSlider: function selectWidgetAndSlider(widgetId) {
- this.deselectAllWidgetsAndSliders();
- this.canvas.selectContent([widgetId], { hideContextBar: true });
- return Promise.resolve();
- },
- deselectWidgetAndSlider: function deselectWidgetAndSlider(widgetId) {
- this.deselectAllWidgetsAndSliders();
- this.canvas.deselectContent([widgetId]);
- return Promise.resolve();
- },
- deselectAllWidgetsAndSliders: function deselectAllWidgetsAndSliders() {
- var selectedContentList = this.canvas.getSelectedContentList();
- var selectedContentIdList = selectedContentList.map(function (content) {
- return content.getId();
- });
- this.canvas.deselectContent(selectedContentIdList);
- },
- getDefaultWidgetDuration: function getDefaultWidgetDuration() {
- return this.defaultWidgetDuration;
- },
- getDuration: function getDuration() {
- var timeQueue = this._currentTimeQueue();
- return timeQueue ? timeQueue.getDuration() : 0;
- },
- isAtEndOfScene: function isAtEndOfScene() {
- return this.getCurrentTime() === this.getDuration();
- },
- isAtStartOfScene: function isAtStartOfScene() {
- return this.getCurrentTime() === 0;
- },
- togglePlayThrough: function togglePlayThrough() {
- this.timeline.set({
- playThrough: !this.isPlayThrough()
- }, {
- payloadData: {
- undoRedoTransactionId: _.uniqueId('togglePlayThrough')
- }
- });
- },
- toggleKioskMode: function toggleKioskMode() {
- this.timeline.set({
- kioskMode: !this.isKioskMode()
- }, {
- payloadData: {
- undoRedoTransactionId: _.uniqueId('toggleKioskMode')
- }
- });
- },
- toggleNavigateMarkers: function toggleNavigateMarkers() {
- this.timeline.set({
- navigateMarkers: !this.isNavigateMarkers()
- }, {
- payloadData: {
- undoRedoTransactionId: _.uniqueId('toggleNavigateMarkers')
- }
- });
- },
- toggleRefreshData: function toggleRefreshData() {
- this.timeline.set({
- refreshData: !this.isRefreshData()
- }, {
- payloadData: {
- undoRedoTransactionId: _.uniqueId('toggleRefreshData')
- }
- });
- },
- isPlayThrough: function isPlayThrough() {
- return this.timeline.get('playThrough');
- },
- isKioskMode: function isKioskMode() {
- return this.timeline.get('kioskMode');
- },
- isNavigateMarkers: function isNavigateMarkers() {
- return this.timeline.get('navigateMarkers');
- },
- isRefreshData: function isRefreshData() {
- return this.timeline.get('refreshData');
- },
- jumpToNextMarker: function jumpToNextMarker() {
- var timeQueue = this._currentTimeQueue();
- return timeQueue.jumpToNextMarker();
- },
- jumpToPreviousMarker: function jumpToPreviousMarker() {
- var timeQueue = this._currentTimeQueue();
- return timeQueue.jumpToPreviousMarker();
- },
- setCurrentTime: function setCurrentTime(time) {
- var _this = this;
- var ret = false;
- var timeQueue = this._currentTimeQueue();
- if (timeQueue) {
- return timeQueue.seek(time).then(function (result) {
- _this._animationDirector.finishWidgetsAnimation();
- return result;
- });
- }
- // TO DO: Need to look into to see finishWidgetsAnimation() needs promise chaining.
- this._animationDirector.finishWidgetsAnimation();
- return Promise.resolve(ret);
- },
- getCurrentTime: function getCurrentTime() {
- var timeQueue = this._currentTimeQueue();
- return timeQueue ? timeQueue.getState().currentTime : 0;
- },
- setCursorTime: function setCursorTime(time) {
- this.cursorTime = time;
- },
- getCursorTime: function getCursorTime() {
- return this.cursorTime;
- },
- getTickDuration: function getTickDuration() {
- var timeQueue = this._currentTimeQueue();
- return timeQueue ? timeQueue.getTickLength() : 0;
- },
- isPlaying: function isPlaying() {
- var timeQueue = this._currentTimeQueue();
- return timeQueue ? timeQueue.isPlaying() : false;
- },
- isStopped: function isStopped() {
- var timeQueue = this._currentTimeQueue();
- return timeQueue ? timeQueue.isStopped() : true;
- },
- pause: function pause() {
- var ret = false;
- var timeQueue = this._currentTimeQueue();
- if (timeQueue) {
- ret = timeQueue.pause();
- }
- this._animationDirector.pauseWidgetsAnimation();
- return ret;
- },
- play: function play() {
- var ret = false;
- var timeQueue = this._currentTimeQueue();
- if (timeQueue) {
- this.deselectAllWidgetsAndSliders();
- ret = timeQueue.play();
- }
- this._animationDirector.resumeWidgetsAnimation();
- return ret;
- },
- stop: function stop() {
- var ret = false;
- var timeQueue = this._currentTimeQueue();
- if (timeQueue) {
- ret = timeQueue.stop();
- }
- this._animationDirector.finishWidgetsAnimation();
- return ret;
- },
- isWidgetSelected: function isWidgetSelected(id) {
- return this.selectedWidgetIds[id] ? true : false;
- },
- getSelectedWidgetMap: function getSelectedWidgetMap() {
- return this.selectedWidgetIds;
- },
- getTimeComponents: function getTimeComponents(milliseconds) {
- var remainingTime = milliseconds || 0;
- var hours = Math.floor(remainingTime / (60 * 60 * 1000));
- remainingTime -= hours * 60 * 60 * 1000;
- var minutes = Math.floor(remainingTime / (60 * 1000));
- remainingTime -= minutes * 60 * 1000;
- var seconds = Math.floor(remainingTime / 1000);
- remainingTime -= seconds * 1000;
- return {
- hours: hours,
- minutes: minutes,
- seconds: seconds,
- milliseconds: remainingTime
- };
- },
- getTimeLabel: function getTimeLabel(milliseconds, precision) {
- var components = this.getTimeComponents(milliseconds);
- // Truncate to 1 decimal place.
- var tenthSeconds = Math.round(components.milliseconds / 100);
- if (tenthSeconds >= 10) {
- // There was rounding to 10; we only want 1 decimal place, so round up to a second.
- tenthSeconds = 0;
- components.seconds++;
- }
- var label = '';
- var hoursLabel = components.hours > 0 ? components.hours + ':' : '';
- var minutesLabel = components.minutes + ':';
- var secondsLabel = components.seconds;
- if (components.seconds < 10) {
- secondsLabel = '0' + secondsLabel;
- }
- label = label.concat(hoursLabel);
- label = label.concat(minutesLabel);
- label = label.concat(secondsLabel);
- if (precision) {
- label = label.concat('.' + tenthSeconds);
- }
- return label;
- },
- /*
- * Event handlers.
- */
- onSelectWidget: function onSelectWidget(event) {
- this.selectedWidgetIds[event.sender] = true;
- this._selectTimelineSlider(event.sender);
- },
- onDeselectWidget: function onDeselectWidget(event) {
- delete this.selectedWidgetIds[event.sender];
- this._deselectTimelineSlider(event.sender);
- },
- onChangeWidget: function onChangeWidget(event) {
- this.trigger('slider:change', event);
- },
- onDurationChanged: function onDurationChanged(event) {
- if (!this.scene || event.sceneId === this.scene.id) {
- this.trigger('duration:changed', event);
- }
- },
- onTimeQueueTick: function onTimeQueueTick(event) {
- if (!this.scene || event.sceneId === this.scene.id) {
- this._triggerUpdateTime();
- }
- },
- onTimeQueueStateChanged: function onTimeQueueStateChanged(event) {
- if (!this.scene || event.sceneId === this.scene.id) {
- this._triggerUpdateTime();
- this.trigger('playState:change', event);
- }
- },
- onEpisodeAdded: function onEpisodeAdded(event) {
- // TODO: the views should probably be listening to the collection directly.
- this.trigger('modelEpisode:added', event.model.id);
- },
- onEpisodeRemoved: function onEpisodeRemoved(event) {
- var widget = this.widgetHelper.getWidget(event.model.id);
- if (widget && widget.getVisApi && widget.getVisApi() && widget.getVisApi().ownerWidget) {
- widget.getVisApi().ownerWidget.off('dwChange:visTransaction', this.onWidgetChange, this);
- widget.getVisApi().ownerWidget.off('dwChange:visId', this.onWidgetChange, this);
- }
- //TODO: the views should probably listen on the collection directly.
- this.trigger('modelEpisode:removed', event.model.id);
- this._refreshTimeQueue();
- },
- onSceneSwap: function onSceneSwap(event) {
- var _this2 = this;
- _.each(event.scenes, function (scene) {
- _this2._getTimeQueue(scene.id).refresh();
- });
- },
- onKioskModeChange: function onKioskModeChange(event) {
- this.trigger('kioskMode:change', event);
- },
- onNavigateMarkersChange: function onNavigateMarkersChange(event) {
- this.trigger('navigateMarkers:change', event);
- },
- onModelEpisodeChange: function onModelEpisodeChange(event) {
- // we need to rethink this method... all the cases should be different events really. This might mean we need to move some logic to the model.
- var timelineEvent = event;
- var episodeEvent = event.origCollectionEvent;
- var actEvent = event.origCollectionEvent ? event.origCollectionEvent.origCollectionEvent : null;
- if (actEvent && actEvent.name === 'add') {
- if (actEvent.model && actEvent.model.action === 'highlight') {
- this.trigger('slider:addHighlight', {
- actModel: actEvent.model,
- episodeModel: episodeEvent.model
- });
- }
- } else if (actEvent && actEvent.name === 'remove') {
- if (actEvent.model && actEvent.model.action === 'highlight') {
- this.trigger('slider:removeHighlight', {
- actModel: actEvent.model,
- episodeModel: episodeEvent.model
- });
- }
- } else if (episodeEvent && episodeEvent.model) {
- var episode = this._getEpisodeModel(episodeEvent.model.id);
- if (episode) {
- var timeRange = [episode.getEntranceAct().timer, episode.getExitAct().timer];
- this.trigger('modelEpisode:changed', {
- id: event.origCollectionEvent.model.id,
- value: timeRange
- });
- }
- if (actEvent && event.name === 'action') {
- this._previewAnimation(event.value, episodeEvent.model, actEvent.model);
- }
- } else if (timelineEvent.name === 'reorder') {
- // order changed, tell everyone about that
- this.trigger('modelEpisodes:reorder');
- }
- this._refreshTimeQueue();
- },
- onTimelinePlaybackEnd: function onTimelinePlaybackEnd() {
- this._animationDirector.finishWidgetsAnimation();
- },
- _previewAnimation: function _previewAnimation(animation, episode, act) {
- var _this3 = this;
- var currentTime = this.getCurrentTime();
- var entranceTime = episode.getEntranceAct().timer;
- var exitTime = episode.getExitAct().timer;
- var isVisible = currentTime >= entranceTime && currentTime < exitTime;
- var isEntrance = act.timer === entranceTime;
- if (isVisible) {
- if (isEntrance) {
- this._animationDirector.animate({
- target: episode.id,
- animation: 'hide',
- duration: 0
- }).then(function () {
- _this3._animationDirector.animate({
- target: episode.id,
- animation: animation,
- duration: _this3.animationDuration
- });
- });
- } else {
- this._animationDirector.animate({
- target: episode.id,
- animation: animation,
- duration: this.animationDuration
- }).then(function () {
- _this3._animationDirector.animate({
- target: episode.id,
- animation: 'show',
- duration: 0
- });
- });
- }
- } else {
- if (isEntrance) {
- this._animationDirector.animate({
- target: episode.id,
- animation: animation,
- duration: this.animationDuration
- }).then(function () {
- _this3._animationDirector.animate({
- target: episode.id,
- animation: 'hide',
- duration: _this3.animationDuration / 2
- });
- });
- } else {
- this._animationDirector.animate({
- target: episode.id,
- animation: 'show',
- duration: this.animationDuration / 2
- }).then(function () {
- _this3._animationDirector.animate({
- target: episode.id,
- animation: animation,
- duration: _this3.animationDuration
- });
- });
- }
- }
- },
- /*
- * Helpers.
- */
- _validateModelOptions: function _validateModelOptions(options) {
- options = options || {};
- options.sender = options.sender || this;
- options.payloadData = options.payloadData || {};
- options.payloadData.undoRedoTransactionId = options.payloadData.undoRedoTransactionId || _.uniqueId('TimelineController');
- return options;
- },
- _selectTimelineSlider: function _selectTimelineSlider(widgetId) {
- this.trigger('slider:select', {
- widgetId: widgetId
- });
- },
- _deselectTimelineSlider: function _deselectTimelineSlider(widgetId) {
- this.trigger('slider:deselect', {
- widgetId: widgetId
- });
- },
- _refreshTimeQueue: function _refreshTimeQueue() {
- var timeQueue = this._currentTimeQueue();
- if (timeQueue) {
- timeQueue.refresh();
- }
- },
- _triggerUpdateTime: function _triggerUpdateTime() {
- var timeQueue = this._currentTimeQueue();
- if (timeQueue) {
- this.trigger('time:update', timeQueue.getState());
- }
- },
- _getWidgetIds: function _getWidgetIds() {
- if (this.scene) {
- return this.model.layout.listWidgets([this.scene.id]);
- }
- return [];
- },
- _isWidgetInCurrentScene: function _isWidgetInCurrentScene(id) {
- return this._getWidgetIds().indexOf(id) !== -1;
- },
- _getEpisodeModel: function _getEpisodeModel(id) {
- return this.model.timeline.episodes.get(id);
- },
- _getNewWidgetEpisodeInfo: function _getNewWidgetEpisodeInfo() {
- var currentTime = this.getCurrentTime();
- var duration = this.getDuration();
- var widgetDuration = duration - currentTime;
- // if we are at the end we use the full duration
- if (widgetDuration === 0) {
- widgetDuration = this.defaultWidgetDuration;
- }
- return {
- start: currentTime,
- end: currentTime + widgetDuration
- };
- },
- /*
- * called when an episode fragment is added,
- * It's the controller's job to adjust the timers to match the current 'scrubber' position.
- */
- onEpisodeFragmentAdded: function onEpisodeFragmentAdded(event) {
- var options = this._validateModelOptions(event.options || {});
- var startingDuration = this.getDuration();
- // update the episode with the new duration.
- // this is needed since the episode is added in the board model with potentially the default values.
- // once we get here we know how to patch those default to take into account the 'scrubber' position.
- var info = this._getNewWidgetEpisodeInfo();
- this.updateTimelineDuration(event.id, info.start, info.end, options);
- var currentDuration = this.getDuration();
- // currently the duration is only extended when a widget is added at the end.
- // so we use it as a flag
- if (currentDuration > startingDuration) {
- this.timeline.stretchEndingEpisodes({
- currentEndTime: startingDuration,
- newEndTime: currentDuration,
- subset: this._getWidgetIds()
- }, this, options.payloadData);
- }
- },
- _checkTimeQueueForSnapIndicator: function _checkTimeQueueForSnapIndicator(timeRange, draggingElement, newTimeRange) {
- var eventQueueLength = this.lastEstablishedEventQueue.length;
- if (eventQueueLength === 0) {
- return {};
- }
- var checkingInterval = this.suggestionTimelineCheckingInterval,
- changed = null,
- isContent,
- i;
- var checkSnapRange = function checkSnapRange(index, value) {
- var timeDifference = Math.abs(timeRange[index] - value),
- ret = timeDifference < checkingInterval;
- if (ret) {
- // convert to string
- newTimeRange[index] = +value;
- }
- return ret;
- };
- //check the closest time in the event queue
- for (i = 0; i < eventQueueLength && !changed; i++) {
- isContent = draggingElement === 'content';
- if ((isContent || draggingElement === 'leftHandle') && checkSnapRange(0, this.lastEstablishedEventQueue[i])) {
- changed = 'start';
- }
- if ((isContent || draggingElement === 'rightHandle') && checkSnapRange(1, this.lastEstablishedEventQueue[i])) {
- changed = 'end';
- }
- }
- return {
- timeRange: newTimeRange,
- changed: changed
- };
- },
- _checkStartAndEndForSnapIndicator: function _checkStartAndEndForSnapIndicator(timeRange, draggingElement, newTimeRange) {
- var changed = null,
- start = timeRange[0],
- end = timeRange[1],
- duration = this.getDuration(),
- isContent = draggingElement === 'content';
- if ((isContent || draggingElement === 'leftHandle') && start > 0 && start < this.suggestionTimelineCheckingInterval) {
- newTimeRange[0] = 0;
- changed = 'start';
- }
- if ((isContent || draggingElement === 'rightHandle') && end < duration && end > duration - this.suggestionTimelineCheckingInterval) {
- newTimeRange[1] = duration;
- changed = 'end';
- }
- return {
- timeRange: newTimeRange,
- changed: changed
- };
- },
- _getTimeQueue: function _getTimeQueue(id) {
- var timeQueue = this.timeQueues[id];
- if (!timeQueue) {
- timeQueue = new TimeQueue({
- sceneId: id,
- boardModel: this.model,
- eventRouter: this.eventRouter,
- widgetHelper: this.widgetHelper
- });
- this.timeQueues[id] = timeQueue;
- timeQueue.reset();
- }
- return timeQueue;
- },
- _currentTimeQueue: function _currentTimeQueue() {
- var ret = null;
- if (this.scene && this.scene.id) {
- ret = this._getTimeQueue(this.scene.id);
- }
- return ret;
- },
- _onMaximizeWidget: function _onMaximizeWidget() {
- if (this.isPlaying()) {
- this.pause();
- this.pausedForMaximize = true;
- } else {
- this.pausedForMaximize = false;
- }
- },
- _onRestoreWidget: function _onRestoreWidget() {
- if (this.pausedForMaximize) {
- this.play();
- }
- },
- onAnimate: function onAnimate(event) {
- if (this._animationDirector) {
- this._animationDirector.animate(event);
- }
- }
- });
- return Controller;
- });
- //# sourceMappingURL=TimelineController.js.map
|