123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601 |
- 'use strict';
- /**
- * Licensed Materials - Property of IBM
- * IBM Cognos Products: Storytelling
- * (C) Copyright IBM Corp. 2014, 2020
- * 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/View', 'jquery', 'underscore', 'text!./templates/TimelineView.html', './TimelineRulerView', './TimelineTimeIndicatorView', './TimelineSliderView', './SnapIndicatorView', 'storytelling-ui/storytelling-ui.min', 'react', 'react-dom', '../ScaleManager', 'baglass/core-client/js/core-client/utils/Utils', '../lib/@ba-ui-toolkit/ba-graphics/dist/icons-js/zoom-in_16', '../lib/@ba-ui-toolkit/ba-graphics/dist/icons-js/zoom-out_16', '../lib/@ba-ui-toolkit/ba-graphics/dist/icons-js/zoom-fit_16', '../lib/@ba-ui-toolkit/ba-graphics/dist/icons-js/minimize_16'], function (View, $, _, Template, TimelineRulerView, TimelineTimeIndicatorView, TimelineSliderView, SnapIndicatorView, StorytellingUI, React, ReactDOM, ScaleManager, Utils, zoonInIcon, zoomOutIcon, zoomFitIcon, minimizeIcon) {
- var TimelineView = View.extend({
- templateString: Template,
- events: {
- 'primaryaction .zoomIn': 'onZoomInClick',
- 'primaryaction .zoomOut': 'onZoomOutClick',
- 'primaryaction .zoomFit': 'onZoomToFitClick',
- 'primaryaction .timelineRuler': 'onTimelineRulerClick'
- },
- init: function init(options) {
- TimelineView.inherited('init', this, arguments);
- this.storyController = options.storyController;
- this.controller = options.controller;
- this.dashboardApi = options.dashboardApi;
- this.dndManager = options.dndManager;
- this.glassContext = options.glassContext;
- this.services = options.services;
- this.stringResources = this.dashboardApi.getDashboardCoreSvc('.StringResources');
- this.zoom = 1;
- this.rangeSliders = null;
- this.rangeSlidersMap = null;
- this.scaleManager = new ScaleManager();
- },
- render: function render() {
- this.controller.on('duration:changed', this.onDurationChanged, this);
- this.controller.on('modelEpisode:added', this.onWidgetAdded, this);
- this.controller.on('modelEpisode:removed', this.onWidgetRemoved, this);
- this.controller.on('modelEpisode:changed', this.onModelEpisodeChanged, this);
- this.controller.on('modelEpisodes:reorder', this.onModelEpisodesReorder, this);
- this.controller.on('slider:change', this.onSliderTitleChange, this);
- this.controller.on('timeline:change', this.onSliderChange, this);
- this.controller.on('timeline:willChange', this.onSliderWillChange, this);
- this.controller.on('timeline:select', this.onSliderSelect, this);
- this.controller.on('timeline:movingUp', this.onSliderMoveUp, this);
- this.controller.on('timeline:movingDown', this.onSliderMoveDown, this);
- this.controller.on('timeline:doneMoving', this.onSliderDrop, this);
- this.controller.on('timeline:sliderDragStarted', this.onDragStarted, this);
- this.controller.on('timeline:highlightIndicatorDragStarted', this.highlightIndicatorDragStarted, this);
- this.controller.on('timeline:highlightIndicatorDragEnded', this.highlightIndicatorDragEnded, this);
- this.scaleManager.on('scale:change', this.onScaleChanged, this);
- $(window).on('resize.privateViewEvents' + this.viewId, this.onResize.bind(this));
- var sHtml = this.dotTemplate({
- timelineRightContainerLabel: this.stringResources.get('timelineRightContainerLabel'),
- collapseScene: this.stringResources.get('close'),
- zoomToFit: this.stringResources.get('timelineZoomFit'),
- zoomIn: this.stringResources.get('timelineZoomIn'),
- zoomOut: this.stringResources.get('timelineZoomOut')
- });
- this.$el.addClass('timeline').attr('role', 'region').attr('aria-label', this.stringResources.get('timelineViewLabel')).html(sHtml);
- this.$scrollContainer = this.$('.timelineContent');
- this.$timelineSliders = this.$scrollContainer.find('.timelineSliders');
- this.$blanket = this.$el.find('.timelineBlanket');
- this.$leftContainer = this.$el.find('.leftContainer');
- var $rightContainer = this.$el.find('.rightContainer');
- this.$zoomOutButton = $rightContainer.find('.zoomOut');
- this.$zoomInButton = $rightContainer.find('.zoomIn');
- this.$zoomFitButton = $rightContainer.find('.zoomFit');
- this.$collapseButton = $rightContainer.find('.collapseScene');
- Utils.setIcon(this.$zoomInButton, zoonInIcon.default.id);
- Utils.setIcon(this.$zoomOutButton, zoomOutIcon.default.id);
- Utils.setIcon(this.$zoomFitButton, zoomFitIcon.default.id);
- Utils.setIcon(this.$collapseButton, minimizeIcon.default.id);
- this.renderTimelineRowTracks();
- this.renderTimelines();
- this.renderRulerView();
- this.scaleToFit();
- this.registerHandlers();
- this._updateSelectedSliders();
- this.setFocus();
- },
- /**
- * @param Object fadeInOptions an equivalent object to jquery fadeIn options
- * @param Function fadeInOptions.complete see jquery fadeIn
- * @param Number fadeInOptions.duration see jquery fadeIn
- */
- renderFadeIn: function renderFadeIn(fadeInOptions) {
- this.render();
- this.hide();
- this.$el.fadeIn(fadeInOptions);
- },
- /**
- * @param Object fadeOutOptions an equivalent object to jquery fadeOut options
- * @param Function fadeOutOptions.complete see jquery fadeOut
- * @param Number fadeOptionOptions.duration see jquery fadeOut
- */
- fadeOut: function fadeOut(fadeOutOptions) {
- this.$el.fadeOut(fadeOutOptions);
- },
- renderRulerView: function renderRulerView() {
- this.timeRulerView = new TimelineRulerView({
- el: this.$el.find('.timelineRuler'),
- controller: this.controller,
- scaleManager: this.scaleManager
- });
- this.timeRulerView.render();
- this.timeIndicatorView = new TimelineTimeIndicatorView({
- el: this.$el.find('.timelinePosition'),
- controller: this.controller,
- scaleManager: this.scaleManager,
- services: this.services
- });
- this.timeIndicatorView.render();
- // Fix the ruler position.
- this._fixRulerPosition();
- },
- renderSnapIndicatorView: function renderSnapIndicatorView() {
- this.snapIndicatorView = new SnapIndicatorView({
- $slidersHolder: this.$el.find('.timelineSliders'),
- controller: this.controller,
- scaleManager: this.scaleManager
- });
- this.snapIndicatorView.render();
- },
- registerHandlers: function registerHandlers() {
- this.$collapseButton.on('primaryaction', this.storyController.collapseScene.bind(this.storyController));
- this.$scrollContainer.on('scroll', this.onScroll.bind(this));
- },
- onSliderTitleChange: function onSliderTitleChange() /*event*/{
- if (this.rangeSlidersMap) {
- _.each(this.rangeSlidersMap, function (view) {
- var label = this._getWidgetTitle(view.id);
- if (label !== view.getLabel()) {
- view.setLabel(label);
- }
- }.bind(this));
- }
- },
- renderTimelineRowTracks: function renderTimelineRowTracks() {
- var marginTop = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
- var timelineRowTrack = React.createElement(StorytellingUI.RowTrackView, {
- timelineEpisodes: this.controller.getTimelineEpisodes(),
- marginTop: marginTop
- });
- ReactDOM.render(timelineRowTrack, this.$leftContainer[0]);
- },
- renderTimelines: function renderTimelines() {
- // Reset the slider list.
- this.rangeSlidersId = [];
- if (this.rangeSlidersMap) {
- _.each(this.rangeSlidersMap, function (view) {
- view.remove();
- });
- }
- this.rangeSlidersMap = {};
- // Use an object in order to treat it as a hashset (rather than an array, which allows duplicates)
- var timelineInfos = this._createTimelineInfos();
- _.each(timelineInfos, function (info) {
- this.renderTimeline(info);
- }.bind(this));
- },
- renderTimeline: function renderTimeline(info) {
- var el = $('<div/>').appendTo(this.$timelineSliders);
- var view = new TimelineSliderView({
- el: el,
- id: info.id,
- model: info.model,
- label: this._getWidgetTitle(info.id),
- scaleManager: this.scaleManager,
- timelineController: this.controller,
- glassContext: this.glassContext,
- dndManager: this.dndManager,
- services: this.services,
- dashboardApi: this.dashboardApi
- });
- view.render();
- this.rangeSlidersId.push(info.id);
- this.rangeSlidersMap[info.id] = view;
- },
- setFocus: function setFocus() {
- this.$el.find('.timelinePosition .handle').focus();
- },
- remove: function remove() {
- $(window).off('resize.privateViewEvents' + this.viewId);
- this.controller.off('duration:changed', this.onDurationChanged, this);
- this.controller.off('modelEpisode:added', this.onWidgetAdded, this);
- this.controller.off('modelEpisode:removed', this.onWidgetRemoved, this);
- this.controller.off('modelEpisode:changed', this.onModelEpisodeChanged, this);
- this.controller.off('modelEpisodes:reorder', this.onModelEpisodesReorder, this);
- this.controller.off('slider:change', this.onSliderTitleChange, this);
- this.controller.off('timeline:change', this.onSliderChange, this);
- this.controller.off('timeline:willChange', this.onSliderWillChange, this);
- this.controller.off('timeline:select', this.onSliderSelect, this);
- this.controller.off('timeline:movingUp', this.onSliderMoveUp, this);
- this.controller.off('timeline:movingDown', this.onSliderMoveDown, this);
- this.controller.off('timeline:doneMoving', this.onSliderDrop, this);
- this.controller.off('timeline:sliderDragStarted', this.onDragStarted, this);
- this.controller.off('timeline:highlightIndicatorDragStarted', this.highlightIndicatorDragStarted, this);
- this.controller.off('timeline:highlightIndicatorDragEnded', this.highlightIndicatorDragEnded, this);
- this.scaleManager.off('scale:change', this.onScaleChanged, this);
- this.timeRulerView = null;
- if (this.timeIndicatorView) {
- this.timeIndicatorView.remove();
- this.timeIndicatorView = null;
- }
- if (this.$leftContainer && this.$leftContainer.length) {
- ReactDOM.unmountComponentAtNode(this.$leftContainer[0]);
- }
- this.rangeSlidersId = null;
- if (this.rangeSlidersMap) {
- _.each(this.rangeSlidersMap, function (view) {
- view.remove();
- });
- }
- this.rangeSlidersMap = null;
- TimelineView.inherited('remove', this, arguments);
- },
- scaleToFit: function scaleToFit() {
- var duration = this.controller.getDuration();
- if (duration === 0) {
- duration = this.controller.getDefaultWidgetDuration();
- }
- this.scaleManager.updateDuration(duration, this.$scrollContainer.outerWidth(false));
- this.scaleManager.scaleToFit();
- },
- /* View events.*/
- onResize: function onResize() {
- if (this.$el.is(':visible')) {
- // We only resize if the view is visible.
- this.scaleToFit();
- this.scaleManager.stepScale(this.zoom);
- this._updateTimelineWidth();
- }
- },
- onScroll: function onScroll() {
- this._fixRulerPosition();
- this._updateSelectedSliders();
- },
- onZoomInClick: function onZoomInClick() {
- var step = 2;
- this.scaleManager.stepScale(step);
- this.zoom = this.zoom * step;
- this._updateZoomButtons();
- },
- onZoomOutClick: function onZoomOutClick() {
- var step = 0.5;
- this.scaleManager.stepScale(step);
- this.zoom = this.zoom * step;
- this._updateZoomButtons();
- },
- onZoomToFitClick: function onZoomToFitClick() {
- this.scaleToFit();
- this.zoom = 1;
- this._updateZoomButtons();
- },
- onTimelineRulerClick: function onTimelineRulerClick() {
- this.timeIndicatorView.setFocus();
- },
- onSliderWillChange: function onSliderWillChange(event) {
- if (this.controller.isPlaying()) {
- this.controller.pause();
- }
- if (event.getDragValue) {
- this.controller.updateLastEstablishedEventQueue();
- if (!this.snapIndicatorView) {
- this.renderSnapIndicatorView();
- }
- this.snapIndicatorView.showIndicator(event.getDragValue, event.getDraggingElement);
- }
- },
- onSliderChange: function onSliderChange() {
- // Update the scale since the width might have changed.
- this._updateTimelineWidth();
- if (this.lastStatePlaying && this.controller.getCursorTime() < this.controller.getDuration()) {
- this.controller.play();
- }
- if (this.snapIndicatorView) {
- this.snapIndicatorView.removeIndicator();
- }
- },
- onSliderSelect: function onSliderSelect() {
- this.lastStatePlaying = this.controller.isPlaying();
- },
- _closeFlyouts: function _closeFlyouts(widgetId) {
- this.controller.trigger('slider:closeFlyouts', { widgetId: widgetId });
- },
- /* Controller events.*/
- onScaleChanged: function onScaleChanged(event) {
- this._updateTimelinesScale(event.scale, event.previousScale);
- this._updateZoomButtons();
- },
- onDurationChanged: function onDurationChanged() {
- this._updateBlanket();
- },
- onWidgetAdded: function onWidgetAdded(id) {
- // Get the newly added widget timeline.
- var episode = this.controller.getTimelineEpisodeById(id);
- if (episode) {
- var info = this._createTimelineInfo(episode);
- // Render the timeline.
- this.renderTimeline(info);
- this._onWidgetAddRemove();
- }
- },
- onWidgetRemoved: function onWidgetRemoved(id) {
- this._closeFlyouts(id);
- var slider = this._getSliderByModelId(id);
- if (slider) {
- slider.remove();
- // Remove the reference to the range slider.
- var index = _.indexOf(this.rangeSlidersId, id);
- if (index >= 0) {
- this.rangeSlidersId.splice(index, 1);
- this.rangeSlidersMap[id].remove();
- delete this.rangeSlidersMap[id];
- }
- this._onWidgetAddRemove();
- }
- },
- _getWidgetTitle: function _getWidgetTitle(id) {
- var content = this.dashboardApi.getCanvas().getContent(id);
- var spec = content.getFeature('Serializer').toJSON();
- var hasGraphic = !!content.getPropertyValue('value.graphic.content');
- var contentData = Object.assign({}, spec.features.Models_internal, { hasGraphic: hasGraphic });
- return this.dashboardApi.getFeature('.SmartNamingSvc').getWidgetName(contentData);
- },
- onModelEpisodeChanged: function onModelEpisodeChanged(options) {
- if (this.rangeSlidersMap) {
- var slider = this.rangeSlidersMap[options.id];
- if (slider) {
- var newValue = this._createTimelineValue(options.value);
- slider.setValue(newValue, true);
- }
- }
- },
- onModelEpisodesReorder: function onModelEpisodesReorder() {
- var $previousFocus = $(document.activeElement);
- var timelineInfos = this._createTimelineInfos();
- this.rangeSlidersId = [];
- // this code assumes that the existing timeline entries were reordered
- // I.e no new timeline entries where added.
- var $previous = null;
- _.each(timelineInfos, function (info) {
- this.rangeSlidersId.push(info.id);
- var $slider = this._getSliderByModelId(info.id);
- if (!$previous) {
- this.$timelineSliders.prepend($slider);
- } else {
- $previous.after($slider);
- }
- $previous = $slider;
- }.bind(this));
- $previousFocus.focus();
- },
- onShowSliderSelection: function onShowSliderSelection(evt) {
- var selectedSlider = this.rangeSlidersMap[evt.widgetId];
- //when you select widget outside of focused scene, don't select timeline
- if (selectedSlider) {
- selectedSlider.toggleSelected(true);
- this._updateSelectedSlider(evt.widgetId);
- }
- },
- onSliderDrop: function onSliderDrop(event) {
- var index = _.indexOf(this.rangeSlidersId, event.id);
- this.controller.moveEpisodeBefore(event.id, this.rangeSlidersId[index + 1], { payloadData: event.payloadData });
- },
- onDragStarted: function onDragStarted(id) {
- this._closeFlyouts(id);
- },
- highlightIndicatorDragStarted: function highlightIndicatorDragStarted() {
- // Disable timeline scrolling when a highlight is dragged
- this.$scrollContainer.css({ overflow: 'hidden' });
- },
- highlightIndicatorDragEnded: function highlightIndicatorDragEnded() {
- // Re-enable timeline scrolling when highlight dragging stops
- this.$scrollContainer.css({ overflow: 'auto' });
- },
- onSliderMoveUp: function onSliderMoveUp(event) {
- var $previousFocus = $(document.activeElement);
- var index = _.indexOf(this.rangeSlidersId, event.id);
- var toIndex = event.to ? _.indexOf(this.rangeSlidersId, event.to) : index - 1;
- if (index === -1 || toIndex < 0) {
- return;
- }
- //move the slider up until we get to where we should be.
- while (index > toIndex) {
- var $slider = this._getSliderByModelId(this.rangeSlidersId[index]);
- var $previousSlider = this._getSliderByModelId(this.rangeSlidersId[index - 1]);
- $slider.insertBefore($previousSlider);
- var t = this.rangeSlidersId[index - 1];
- this.rangeSlidersId[index - 1] = this.rangeSlidersId[index];
- this.rangeSlidersId[index] = t;
- index--;
- }
- // fix ie 11 svg icons disappear problem
- // no need to check browser as embedSVGIcon does the check in the Utils
- Utils.embedSVGIcon(this.$el);
- $previousFocus.focus();
- },
- onSliderMoveDown: function onSliderMoveDown(event) {
- var $previousFocus = $(document.activeElement);
- var index = _.indexOf(this.rangeSlidersId, event.id);
- var toIndex = event.to ? _.indexOf(this.rangeSlidersId, event.to) : index + 1;
- if (index === -1 || toIndex === -1 || toIndex >= this.rangeSlidersId.length) {
- return;
- }
- //move the slider down until we get to where we should be.
- while (index < toIndex) {
- var $slider = this._getSliderByModelId(this.rangeSlidersId[index]);
- var $nextSlider = this._getSliderByModelId(this.rangeSlidersId[index + 1]);
- $slider.insertAfter($nextSlider);
- var t = this.rangeSlidersId[index + 1];
- this.rangeSlidersId[index + 1] = this.rangeSlidersId[index];
- this.rangeSlidersId[index] = t;
- index++;
- }
- // fix ie 11 svg icons disappear problem
- Utils.embedSVGIcon(this.$el);
- $previousFocus.focus();
- },
- _getSliderByModelId: function _getSliderByModelId(widgetId) {
- return this.$timelineSliders.find('#timelineWidgetSlider' + widgetId);
- },
- _updateBlanket: function _updateBlanket() {
- this.$blanket.css('left', this.scaleManager.convertTimeToPosition(this.controller.getDuration()) + 'px');
- },
- _updateSelectedSliders: function _updateSelectedSliders() {
- var map = this.controller.getSelectedWidgetMap();
- _.each(Object.keys(map), function (key) {
- if (map[key]) {
- this.onShowSliderSelection({ widgetId: key });
- // Close any flyouts on container scrolling
- this._closeFlyouts(key);
- }
- }.bind(this));
- },
- _updateSelectedSlider: function _updateSelectedSlider(id) {
- this.rangeSlidersMap[id].$el.toggleClass('bringToFront', this.$scrollContainer.scrollTop() === 0);
- },
- _clearHoverTimeout: function _clearHoverTimeout() {
- if (this.hoverTimeout) {
- clearTimeout(this.hoverTimeout);
- this.hoverTimeout = null;
- }
- },
- _onWidgetAddRemove: function _onWidgetAddRemove() {
- // Update the timeline scales.
- this._updateTimelineWidth();
- // Ensure the indicator is updated.
- this.timeIndicatorView.refresh();
- // Re-render the timeline row track and blanket position
- this._fixRulerPosition();
- },
- _updateTimelineWidth: function _updateTimelineWidth() {
- var durationWidth = 500 + this.scaleManager.convertTimeToPosition(this.controller.getDuration());
- var pageWidth = this.$scrollContainer.outerWidth(false);
- var width = Math.max(durationWidth, pageWidth);
- this.$timelineSliders.css('width', width + 'px');
- this.$blanket.css('width', width - parseInt(this.$blanket.css('left'), 10));
- this.timeRulerView.refresh();
- },
- _updateTimelinesScale: function _updateTimelinesScale(scale) {
- this._updateBlanket();
- this._updateTimelineWidth();
- var map = this.rangeSlidersMap;
- _.each(this.rangeSlidersId, function (id) {
- var item = map[id];
- item.setScale(scale);
- });
- },
- _createTimelineValue: function _createTimelineValue(value) {
- var defaultPixelsPerSecond = this.scaleManager.getScaleToPixelRatio();
- return [value[0] / 1000 * defaultPixelsPerSecond, value[1] / 1000 * defaultPixelsPerSecond];
- },
- _createTimelineInfo: function _createTimelineInfo(episode) {
- var start = episode.getEntranceAct().timer;
- var end = episode.getExitAct().timer;
- var timeValue = this._createTimelineValue([start, end]);
- return {
- model: {
- value: timeValue,
- scale: this.scaleManager.getScale()
- },
- id: episode.id
- };
- },
- _createTimelineInfos: function _createTimelineInfos() {
- // Create a timeline info object for each timeline.
- return _.map(this.controller.getTimelineEpisodes(), function (timeline) {
- return this._createTimelineInfo(timeline);
- }.bind(this));
- },
- _fixRulerPosition: function _fixRulerPosition() {
- // Ensure the ruler stays at the top when the timeline is scrolled vertically.
- var scrollTop = this.$scrollContainer.scrollTop();
- var scrollLeft = this.$scrollContainer.scrollLeft();
- this.$blanket.css('top', scrollTop + 'px');
- this.timeIndicatorView.onScroll({
- scrollTop: scrollTop,
- scrollLeft: scrollLeft
- });
- this.timeRulerView.setOffsetLeft(scrollLeft);
- this.renderTimelineRowTracks(-scrollTop);
- },
- _updateZoomButtons: function _updateZoomButtons() {
- var scale = this.scaleManager.getScale();
- var canZoomIn = scale < this.scaleManager.getMaxScale();
- var canZoomOut = scale > this.scaleManager.getMinScale();
- this.$zoomOutButton.toggleClass('disabled', !canZoomOut);
- this.$zoomInButton.toggleClass('disabled', !canZoomIn);
- this.$zoomInButton.attr('aria-disabled', !canZoomIn);
- this.$zoomOutButton.attr('aria-disabled', !canZoomOut);
- }
- });
- return TimelineView;
- });
- //# sourceMappingURL=TimelineView.js.map
|