- /**
- StoryLineView module manages the Timeline > Channels UI while displaying the visual length over time for each block on the selected channel
- @class StorylineView
- @constructor
- @param {String}
- @return {Object} instantiated StorylineView
- **/
- define(['jquery', 'backbone', 'text', 'text!_templates/_storyboard.html'], function ($, Backbone, text, storylineTemplate) {
-
- /**
- Custom event fired when a block is selected on the storyline
- @event STORYLINE_BLOCK_SELECTED
- @param {This} caller
- @param {Self} context caller
- @param {Event}
- @static
- @final
- **/
- BB.EVENTS.STORYLINE_BLOCK_SELECTED = 'STORYLINE_BLOCK_SELECTED';
-
- BB.SERVICES.STORYLINE = 'StoryLine';
-
- var StorylineView = BB.View.extend({
-
- /**
- Constructor
- @method initialize
- **/
- initialize: function () {
- var self = this;
- self.m_storyWidth = 0;
- self.m_owner = self;
- self.m_selectedTimelineID = undefined;
- self.m_selectedBlockID = undefined;
- self.m_selectedChannel = undefined;
- self.m_blockZindex = 3; // future drag support
- BB.comBroker.setService(BB.SERVICES.STORYLINE, self);
- BB.comBroker.listen(BB.EVENTS.SIDE_PANEL_SIZED, $.proxy(self._updateWidth, self));
- self._listenReset();
- self._listenTimelineSelected();
- self._listenTimelineChanged();
- self._listenBlockSelection();
- self._listenTimelineBlockRemoved();
- self._listenStackViewSelected();
- self._listenToggleStorylineCollapsible();
- self._listenAppResized();
- self._listenContextMenu();
- self._listenExitPreview();
- self._updateWidth();
-
- //setTimeout(function(){
- // self.collapseStoryLine()
- //},8000)
- },
-
- /**
- Draw a fresh storyline for current timeline
- @method _render
- **/
- _render: function () {
- var self = this;
- if (_.isUndefined(self.m_render)) {
- self.m_render = _.debounce(function () {
- $(Elements.STORYLINE_ELEM).empty();
- self.m_storylineContainerSnippet = $(storylineTemplate).find(Elements.STORYLINE_CONTAINER).parent();
- self.m_TableSnippet = $(storylineTemplate).find('table').parent();
- self.m_ChannelSnippet = $(storylineTemplate).find(Elements.CLASS_STORYLINE_CHANNEL).parent();
- self._populateScala();
- self._populateChannels();
- self._listenSelections();
- self._addBlockSelection(self.m_selectedBlockID);
- self._addChannelSelection(self.m_selectedChannel);
- }, 100);
- }
- self.m_render();
- },
-
- /**
- Listen to reset of when switching to different campaign so we forget current state
- @method _listenReset
- **/
- _listenReset: function () {
- var self = this;
- BB.comBroker.listen(BB.EVENTS.CAMPAIGN_RESET, function () {
- self.m_storyWidth = 0;
- self.m_selectedTimelineID = undefined;
- self.m_selectedBlockID = undefined;
- self.m_selectedChannel = undefined;
- });
- },
-
- /**
- Anytime the containing StackView is selected, re-render the Storyline as resources or scenes could have been
- removed while we were gone
- @method _listenStackViewSelected
- **/
- _listenStackViewSelected: function () {
- var self = this;
- var appContentFaderView = BB.comBroker.getService(BB.SERVICES['APP_CONTENT_FADER_VIEW']);
- var campaignSliderStackView = BB.comBroker.getService(BB.SERVICES['CAMPAIGN_SLIDER_STACK_VIEW']);
-
- campaignSliderStackView.on(BB.EVENTS.SELECTED_STACK_VIEW, function (e) {
- if (e == BB.comBroker.getService(BB.SERVICES['CAMPAIGN_VIEW'])) {
- self._delayedRender();
- }
- });
- appContentFaderView.on(BB.EVENTS.SELECTED_STACK_VIEW, function (e) {
- if (e == BB.comBroker.getService(BB.SERVICES['CAMPAIGN_MANAGER_VIEW'])) {
- self._delayedRender();
- }
- });
- },
-
- /**
- Listen for block selection
- @method _listenBlockSelection
- **/
- _listenBlockSelection: function () {
- var self = this;
- BB.comBroker.listen(BB.EVENTS.BLOCK_SELECTED, function (e) {
- var blockID = e.edata;
- if (!_.isNumber(blockID)) // ignore scene blocks
- return;
- self._addBlockSelection(blockID);
- });
- },
-
- /**
- Listen to when the app is resized so we can re-render
- @method _listenAppResized
- **/
- _listenAppResized: function () {
- var self = this;
- BB.comBroker.listen(BB.EVENTS.APP_SIZED, function (e) {
- self._delayedRender();
- });
- },
-
- /**
- Add block selection by marking it on the storyline and remembering selection
- @method _addBlockSelection
- @param {Number} i_blockID
- **/
- _addBlockSelection: function (i_blockID) {
- var self = this;
- if (_.isUndefined(i_blockID))
- return;
- self._removeBlockSelection();
- self.m_selectedBlockID = i_blockID;
- var blockElem = $(Elements.STORYLINE_CONTAINER).find('[data-timeline_channel_block_id="' + i_blockID + '"]');
- $(blockElem).addClass(BB.lib.unclass(Elements.CLASS_TIMELINE_BLOCK_SELECTED));
- },
-
- /**
- Add channel selection by marking it on the storyline and remembering selection
- @method _addChannelSelection
- @param {Number} i_selectedChannel
- **/
- _addChannelSelection: function (i_selectedChannel) {
- var self = this;
- if (_.isUndefined(i_selectedChannel))
- return;
- self._removeChannelSelection();
- self.m_selectedChannel = i_selectedChannel;
- var blockElem = $(Elements.STORYLINE_CONTAINER).find('[data-timeline_channel_id="' + i_selectedChannel + '"]');
- blockElem = $(blockElem).filter('.channelHead');
- $(blockElem).addClass(BB.lib.unclass(Elements.CLASS_CHANNEL_HEAD_SELECTED));
- },
-
- /**
- Remove currently selected channel by removing selection as well forgetting it
- @method _removeChannelSelection
- **/
- _removeChannelSelection: function () {
- var self = this;
- self.m_selectedChannel = undefined;
- $(Elements.CLASS_CHANNEL_HEAD_SELECTED, Elements.STORYLINE_CONTAINER).removeClass(BB.lib.unclass(Elements.CLASS_CHANNEL_HEAD_SELECTED));
- },
-
- /**
- Remove currently selected block by removing selection as well forgetting it
- @method _removeBlockSelection
- **/
- _removeBlockSelection: function () {
- var self = this;
- self.m_selectedBlockID = undefined;
- $(Elements.CLASS_TIMELINE_BLOCK, Elements.STORYLINE_CONTAINER).removeClass(BB.lib.unclass(Elements.CLASS_TIMELINE_BLOCK_SELECTED));
- },
-
- /**
- Build the UI for the top seconds / minutes scala of the storyline
- @method _populateScala
- **/
- _populateScala: function () {
- var self = this, i;
- var ticks = [];
- var format = 's';
- var totalDuration = parseInt(pepper.getTimelineTotalDuration(self.m_selectedTimelineID));
- if (totalDuration > 420) {
- totalDuration = totalDuration / 60;
- format = 'm';
- }
-
- var tick = totalDuration / 4;
- for (i = 1; i < 5; i++) {
- tick = BB.lib.parseToFloatDouble(tick);
- ticks.push(tick * i);
- }
-
- ticks.unshift(0);
- ticks[ticks.length - 1] = totalDuration;
- var l = String((ticks[ticks.length - 1]).toFixed(2)).length;
- var lastTick = '';
- var scalaRuler = $(self.m_TableSnippet).find(Elements.CLASS_SCALA_RULER);
- for (i = 0; i < ticks.length; i++) {
- if (i == ticks.length - 1)
- lastTick = 'width="1%"'
- var value = BB.lib.padZeros(BB.lib.parseToFloatDouble(ticks[i]), l) + format; // log(value);
- $(scalaRuler).append('<td class="scalaNum"' + lastTick + ' >' + value + '</td>');
- }
- $(Elements.STORYLINE_ELEM).append(self.m_TableSnippet);
- },
-
- /**
- Populate UI channels
- @method _populateChannels
- **/
- _populateChannels: function () {
- var self = this;
- var channelsIDs = pepper.getChannelsOfTimeline(self.m_selectedTimelineID);
- for (var n = 0; n < channelsIDs.length; n++) {
- var channelID = channelsIDs[n];
- var channelSnippet = _.template(_.unescape(self.m_ChannelSnippet.html()), {value: n + 1});
- var viewerID = pepper.getAssignedViewerIdFromChannelId(channelID);
- $(self.m_storylineContainerSnippet).find('section').append(channelSnippet);
- var channelHead = $(self.m_storylineContainerSnippet).find(Elements.CLASS_CHANNEL_HEAD + ':last');
- var channelBody = $(self.m_storylineContainerSnippet).find(Elements.CLASS_CHANNEL_BODY + ':last');
- $(channelHead).attr('data-timeline_channel_id', channelID);
- $(channelBody).attr('data-timeline_channel_id', channelID);
- $(channelHead).attr('data-campaign_timeline_board_viewer_id', viewerID);
- $(channelBody).attr('data-campaign_timeline_board_viewer_id', viewerID);
- self._populateBlocks(channelID);
- }
- $(Elements.STORYLINE_ELEM).append(self.m_storylineContainerSnippet);
- self._updateWidth();
- setTimeout(function () {
- self._updateWidth();
- }, 5);
- },
-
- /**
- Populate UI blocks
- @method _populateBlocks
- @params {Number} i_campaign_timeline_chanel_id
- **/
- _populateBlocks: function (i_campaign_timeline_chanel_id) {
- var self = this;
- var timeline = BB.comBroker.getService(BB.SERVICES['CAMPAIGN_VIEW']).getTimelineInstance(self.m_selectedTimelineID);
- var channel = timeline.getChannelInstance(i_campaign_timeline_chanel_id);
- var blocks = channel.getBlocks();
- var snippet, totalPercent = 0;
- for (var block in blocks) {
- var blockData = blocks[block].getBlockData();
- var blockID = blockData.blockID;
- var fontAwesome = blocks[block].getBlockData().blockFontAwesome;
- var totalDuration = parseInt(pepper.getTimelineTotalDuration(self.m_selectedTimelineID));
- var blockDuration = pepper.getBlockTimelineChannelBlockLength(blockID).totalInSeconds;
- var percent = (parseFloat(blockDuration) / parseFloat(totalDuration) * 100);
- totalPercent += percent;
-
- var blockWidth = (self.m_storyWidth * percent) / 100;
- if (blockWidth < 1)
- continue;
- if (blockWidth < 25) {
- snippet = '<div class="timelineBlock" data-timeline_channel_block_id="' + blockID + '" style="width: ' + percent + '%;"></div>';
- } else {
- snippet = '<div class="timelineBlock" data-timeline_channel_block_id="' + blockID + '" style="width: ' + percent + '%;"><i style="font-size: 14px" class="fa ' + fontAwesome + '"></i></div>';
-
- /* future support draggable */
- // snippet = '<div class="draggable ui-widget-content ui-draggable ui-draggable-handle timelineBlock" data-timeline_channel_block_id="' + blockID + '" style="width: ' + percent + '%;"><i style="font-size: 14px" class="fa ' + fontAwesome + '"></i></div>';
-
- }
- /* future support draggable */
- // setTimeout(function(){
- // $(".timelineBlock").draggable({
- // axis: "x",
- // start: function(event, ui) { $(this).css("z-index", self.m_blockZindex++); }
- // });
- //},700);
-
- $(self.m_storylineContainerSnippet).find('.channelBody:last').append(snippet);
- }
- },
-
- /**
- Compute the storyline UI width total width
- @method _updateWidth
- **/
- _updateWidth: function () {
- var self = this;
- self.m_storyWidth = parseInt($(Elements.STORYLINE_CONTAINER).width()) - 25;
- $(Elements.CLASS_CHANNEL_BODY_CONTAINER).width(self.m_storyWidth);
- },
-
- /**
- Listen to changes in the timeline (channel, block length etc) so we can re-render the storyline
- @method _listenTimelineChanged
- **/
- _listenTimelineChanged: function () {
- var self = this;
- pepper.listen(Pepper.BLOCK_LENGTH_CHANGED, $.proxy(self._render, self));
- BB.comBroker.listen(BB.EVENTS.CAMPAIGN_TIMELINE_CHANGED, function () {
- self._render();
- })
- },
-
- /**
- Listen to a new timeline selection so we can re-render the storyline
- @method _listenTimelineSelected
- **/
- _listenTimelineSelected: function () {
- var self = this;
- BB.comBroker.listen(BB.EVENTS.CAMPAIGN_TIMELINE_SELECTED, function (e) {
- self._deselection();
- self.m_selectedTimelineID = e.edata;
- self._render();
- });
- },
-
- /**
- Forget all current selections
- @method _deselection
- **/
- _deselection: function () {
- var self = this;
- self.m_selectedTimelineID = undefined;
- self.m_selectedBlockID = undefined;
- self.m_selectedChannel = undefined;
- },
-
- /**
- Listen to channel selection so we can re-render storyline
- @method _listenSelections
- **/
- _listenSelections: function () {
- var self = this;
- $(Elements.CLASS_CHANNEL_HEAD).off('click');
- $(Elements.CLASS_CHANNEL_HEAD).on('click', function (e) {
- $.proxy(self._blockChannelSelected(e), self);
- BB.comBroker.fire(BB.EVENTS.CAMPAIGN_TIMELINE_CHANNEL_SELECTED, this, null, self.m_selectedChannel);
- });
- $(Elements.CLASS_STORYLINE_CHANNEL).off('click');
- $(Elements.CLASS_STORYLINE_CHANNEL).on('click', function (e) {
- $.proxy(self._blockChannelSelected(e), self);
- BB.comBroker.fire(BB.EVENTS.CAMPAIGN_TIMELINE_CHANNEL_SELECTED, this, null, self.m_selectedChannel);
- });
- $(Elements.CLASS_TIMELINE_BLOCK).off('click contextmenu');
- $(Elements.CLASS_TIMELINE_BLOCK).on('click contextmenu', function (e) {
- $.proxy(self._blockSelected(e), self);
-
- /* future support draggable */
- // $(this).addClass('top').removeClass('bottom');
- // $(this).siblings().removeClass('top').addClass('bottom');
- // $(this).css("z-index", self.m_blockZindex++);
- });
- },
-
- /**
- When a block is selected within a channel, get the resource element so we can select it and fire
- the BLOCK_SELECTED event
- @method _blockSelected
- @param {Event} e
- **/
- _blockChannelSelected: function (e) {
- var self = this;
- if (e.button == 0) {
- e.stopImmediatePropagation();
- $(Elements.STORYLINE_CONTEXT_MENU).hide();
- }
-
- var blockElem = $(e.target);
-
- if (_.isUndefined($(blockElem).attr('class')))
- return true;
-
- if ($(blockElem).hasClass(BB.lib.unclass(Elements.CLASS_STORYLINE_CHANNEL)))
- blockElem = $(blockElem).find(Elements.CLASS_CHANNEL_HEAD);
-
- if ($(blockElem).hasClass(BB.lib.unclass(Elements.CLASS_TIMELINE_BLOCK)))
- blockElem = $(blockElem).closest(Elements.CLASS_CHANNEL_BODY);
-
- var timeline_channel_id = $(blockElem).data('timeline_channel_id');
- var campaign_timeline_board_viewer_id = $(blockElem).data('campaign_timeline_board_viewer_id');
-
- //if (_.isUndefined(timeline_channel_id) || _.isUndefined(campaign_timeline_board_viewer_id))
- // return false;
-
- if (self.m_selectedChannel == timeline_channel_id)
- return true;
-
- self.m_selectedChannel = timeline_channel_id;
- var screenData = {
- m_owner: self,
- campaign_timeline_id: self.m_selectedTimelineID,
- campaign_timeline_board_viewer_id: campaign_timeline_board_viewer_id
- };
- self._removeBlockSelection();
- self._addChannelSelection(self.m_selectedChannel);
- var sequencer = BB.comBroker.getService(BB.SERVICES['SEQUENCER_VIEW']);
- sequencer.selectViewer(screenData.campaign_timeline_id, screenData.campaign_timeline_board_viewer_id);
- BB.comBroker.fire(BB.EVENTS.ON_VIEWER_SELECTED, this, screenData);
- return true;
- },
-
- /**
- When a block is selected within a channel, get the resource element so we can select it and fire
- the BLOCK_SELECTED event
- @method _blockSelected
- @param {Event} e
- **/
- _blockSelected: function (e) {
- var self = this;
- //e.stopImmediatePropagation();
- var blockElem = $(e.target);
- self.selected_block_id = $(blockElem).data('timeline_channel_block_id');
- // if label was selected
- if (_.isUndefined(self.selected_block_id)) {
- blockElem = $(e.target).parent();
- self.selected_block_id = $(blockElem).data('timeline_channel_block_id');
- }
- e['target'] = blockElem[0];
- self._blockChannelSelected(e);
- BB.comBroker.fire(BB.EVENTS.STORYLINE_BLOCK_SELECTED, this, null, self.selected_block_id);
- $(blockElem).addClass(BB.lib.unclass(Elements.CLASS_TIMELINE_BLOCK_SELECTED));
- //return false;
- },
-
- /**
- Toggle the arrow of the collapsible storyline UI widget
- @method _listenToggleStorylineCollapsible
- **/
- _listenToggleStorylineCollapsible: function () {
- var self = this;
- $(Elements.TOGGLE_STORYLINE_COLLAPSIBLE).on('click', function () {
- var toggle = $(this).find('span')[0];
- if ($(toggle).hasClass('glyphicon-chevron-down')) {
- $(toggle).removeClass('glyphicon-chevron-down').addClass('glyphicon-chevron-right')
- } else {
- $(toggle).removeClass('glyphicon-chevron-right').addClass('glyphicon-chevron-down')
- }
- $(Elements.STORYLINE_ELEM).fadeIn(500).queue(function () {
- self._render();
- }).dequeue().delay(500).queue(function () {
- self._render();
- }).dequeue();
- });
- },
-
- /**
- Listen to any canvas right click
- @method _listenContextMenu
- **/
- _listenContextMenu: function () {
- var self = this;
- $(Elements.STORYLINE_ELEM).contextmenu({
- target: Elements.STORYLINE_CONTEXT_MENU,
- before: function (e, element, target) {
- e.preventDefault();
- //self.m_mouseX = e.offsetX;
- //self.m_mouseY = e.offsetY;
- if (_.isUndefined(self.m_selectedBlockID))
- return false;
- return true;
- },
- onItem: function (context, e) {
- self._onContentMenuSelection($(e.target).attr('name'))
- }
- });
- },
-
- /**
- Re-render the storyboard upon live preview exit
- @method _listenExitPreview
- **/
- _listenExitPreview: function () {
- var self = this;
- BB.comBroker.listen(BB.EVENTS.PREVIEW_EXIT, function (e) {
- self._delayedRender();
- });
- },
-
- /**
- Delayed render of stoeyboard
- @method _delayedRender
- **/
- _delayedRender: function () {
- var self = this;
- setTimeout(function () {
- self._updateWidth();
- self._render();
- }, 500);
- },
-
- /**
- On Scene right click context menu selection command
- @method _onContentMenuSelection
- @param {String} i_command
- **/
- _onContentMenuSelection: function (i_command) {
- var self = this;
- var campaign_timeline_id = BB.comBroker.getService(BB.SERVICES.CAMPAIGN_VIEW).getSelectedTimeline();
- if (campaign_timeline_id == -1 || _.isUndefined(campaign_timeline_id))
- return;
-
- switch (i_command) {
- case 'nextChannel':
- {
- self.selectNextChannel();
- break;
- }
- case 'addContent':
- {
- $(Elements.ADD_BLOCK_BUTTON).trigger('click');
- break;
- }
- case 'removeContent':
- {
- $(Elements.REMOVE_BLOCK_BUTTON).trigger('click');
- break;
- }
- case 'first':
- {
- BB.comBroker.getService(BB.SERVICES.CHANNEL_LIST_VIEW).moveBlockFirst();
- break;
- }
- case 'last':
- {
- BB.comBroker.getService(BB.SERVICES.CHANNEL_LIST_VIEW).moveBlockLast();
- break;
- }
- }
- return true;
- },
-
- /**
- Listen to when a timeline block is removed
- @method _listenTimelineBlockRemoved
- @param {Object} e
- **/
- _listenTimelineBlockRemoved: function () {
- var self = this;
- pepper.listen(Pepper.REMOVE_TIMELINE_CHANNEL_BLOCK, function (e) {
- self.m_selectedBlockID = undefined;
- self._removeBlockSelection();
- });
- },
-
- /**
- Collapse the storyline bootstrap panel and title
- @method collapseStoryLine
- **/
- collapseStoryLine: function () {
- var self = this;
- if (!$(Elements.STORYLINE_CONTAINER_COLLAPSE).hasClass('in'))
- return;
- $('.panel-collapse', Elements.STORYLINE_COLLAPSIBLE).collapse('hide');
- $('.panel-title', Elements.STORYLINE_COLLAPSIBLE).attr('data-toggle', 'collapse');
- var coll = $(Elements.STORYLINE_COLLAPSIBLE);
- var toggle = $(coll).find('span')[0];
- $(toggle).removeClass('glyphicon-chevron-down').addClass('glyphicon-chevron-right')
- },
-
- /**
- Select next channel
- @method selectNextChannel
- **/
- selectNextChannel: function () {
- var self = this;
- var timeline_channel_id, campaign_timeline_board_viewer_id;
- var channelsIDs = pepper.getChannelsOfTimeline(self.m_selectedTimelineID);
- if (_.isUndefined(self.m_selectedChannel)) {
- timeline_channel_id = channelsIDs[0];
- } else {
- for (var ch in channelsIDs) {
- if (channelsIDs[ch] == self.m_selectedChannel) {
- if (_.isUndefined(channelsIDs[parseInt(ch) + 1])) {
- timeline_channel_id = channelsIDs[0];
- } else {
- timeline_channel_id = channelsIDs[parseInt(ch) + 1];
- }
- }
- }
- }
- campaign_timeline_board_viewer_id = pepper.getAssignedViewerIdFromChannelId(timeline_channel_id);
- // note: workaround for when viewer is unassigned, need to investigate
- if (_.isUndefined(campaign_timeline_board_viewer_id))
- return;
- var screenData = {
- m_owner: self,
- campaign_timeline_id: self.m_selectedTimelineID,
- campaign_timeline_board_viewer_id: campaign_timeline_board_viewer_id
- };
- self._removeBlockSelection();
- self._addChannelSelection(timeline_channel_id);
- BB.comBroker.getService(BB.SERVICES['SEQUENCER_VIEW']).selectViewer(screenData.campaign_timeline_id, screenData.campaign_timeline_board_viewer_id);
- BB.comBroker.fire(BB.EVENTS.ON_VIEWER_SELECTED, this, screenData);
- BB.comBroker.fire(BB.EVENTS.CAMPAIGN_TIMELINE_CHANNEL_SELECTED, this, null, self.m_selectedChannel);
- }
- });
-
- return StorylineView;
-
- });
-