- /**
- This class manages the UI of all blocks within a channel as well as the ability to sort and reorder the content of a channel
- @class ChannelListView
- @constructor
- @return {Object} instantiated CompCampaignNavigator
- **/
- define(['jquery', 'backbone', 'jqueryui', 'TouchPunch', 'Timeline', 'SequencerView', 'StorylineView', 'Draggable'], function ($, Backbone, jqueryui, TouchPunch, Timeline, SequencerView, StorylineView, Draggable) {
-
- BB.SERVICES.CHANNEL_LIST_VIEW = 'ChannelListView';
-
- var ChannelListView = BB.View.extend({
-
- /**
- Init the ChannelList component and enable sortable channels UI via drag and drop operations.
- @method initialize
- **/
- initialize: function () {
- var self = this;
-
- self.m_property = BB.comBroker.getService(BB.SERVICES.PROPERTIES_VIEW);
- self.selected_block_id = undefined;
- self.selected_campaign_timeline_chanel_id = undefined;
- self.selected_campaign_timeline_id = undefined;
- self.selected_campaign_timeline_board_viewer_id = undefined;
-
- self._listenAddRemoveBlocks();
- self._listenTimelineSelected();
- self._listenResourceRemoved();
- self._listenSceneRemoved();
- self._listenBlockLengthChanged();
- self._listenStorylineBlockSelected();
- self._listenStorylineChannelSelected();
- self._listenReset();
- self._listenContextMenu();
- pepper.listen(Pepper.TIMELINE_DELETED, $.proxy(self._onTimelineDeleted, self));
-
- },
-
- /**
- 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.selected_block_id = undefined;
- self.selected_campaign_timeline_chanel_id = undefined;
- self.selected_campaign_timeline_id = undefined;
- self.selected_campaign_timeline_board_viewer_id = undefined;
- });
- },
-
- /**
- Wire the UI and listen to channel remove and channel add button events.
- @method _listenAddRemoveBlocks
- @return none
- **/
- _listenAddRemoveBlocks: function () {
- var self = this;
- $(Elements.REMOVE_BLOCK_BUTTON).on('click', function (e) {
- if (_.isUndefined(self.selected_block_id)) {
- bootbox.alert($(Elements.MSG_BOOTBOX_SELECT_RESOURCE).text());
- return;
- }
- self._deleteChannelBlock(self.selected_block_id);
- });
- $(Elements.ADD_BLOCK_BUTTON).on('click', function (e) {
- if (_.isUndefined(self.selected_campaign_timeline_id)) {
- bootbox.alert($(Elements.MSG_BOOTBOX_SELECT_CHANNEL).text());
- return;
- }
- self._openAddBlockWizard(e);
- });
- },
-
- /**
- Wire the UI for timeline deletion.
- @method _onTimelineDeleted
- @return none
- **/
- _onTimelineDeleted: function () {
- var self = this;
- $(Elements.ADD_BLOCK_BUTTON).fadeOut();
- $(Elements.REMOVE_BLOCK_BUTTON).fadeOut();
- $(Elements.TIMELIME_PREVIEW).fadeOut();
- self._resetChannel();
- },
-
- /**
- Update the blocks offset times according to current order of LI elements and reorder accordingly in msdb.
- @method _reOrderChannelBlocks
- @return none
- **/
- _reOrderChannelBlocks: function () {
- var self = this
- var blocks = $(Elements.SORTABLE).children();
- var playerOffsetTime = 0;
- $(blocks).each(function (i) {
- var block_id = $(this).data('block_id');
- var recBlock = pepper.getBlockRecord(block_id);
- var playerDuration = recBlock['player_duration']
- pepper.setBlockRecord(block_id, 'player_offset_time', playerOffsetTime);
- log('player ' + block_id + ' offset ' + playerOffsetTime + ' playerDuration ' + playerDuration);
- playerOffsetTime = parseFloat(playerOffsetTime) + parseFloat(playerDuration);
- });
- pepper.calcTimelineTotalDuration(this.selected_campaign_timeline_id);
- BB.comBroker.fire(BB.EVENTS.CAMPAIGN_TIMELINE_CHANGED, self);
- BB.comBroker.fire(BB.EVENTS.BLOCK_SELECTED, this, null, self.selected_block_id);
- },
-
- /**
- Get the total duration in seconds of the channel
- @method _getTotalDurationChannel
- @return {Number} totalChannelLength
- **/
- _getTotalDurationChannel: function () {
- var self = this
- var blocks = $(Elements.SORTABLE).children();
- var blocksIDs = [];
- $(blocks).each(function (i) {
- var block_id = $(this).data('block_id');
- blocksIDs.push(block_id);
- });
- var totalChannelLength = pepper.getTotalDurationOfBlocks(blocksIDs);
- return totalChannelLength;
- },
-
- /**
- Launch the add new block wizard UI component.
- @method _openAddBlockWizard
- @return none
- **/
- _openAddBlockWizard: function (e) {
- var self = this;
- var addBlockView = BB.comBroker.getService(BB.SERVICES.ADD_BLOCK_VIEW);
- addBlockView.setPlacement(BB.CONSTS.PLACEMENT_CHANNEL);
- addBlockView.selectView();
- BB.comBroker.listenOnce(BB.EVENTS.ADD_NEW_BLOCK_CHANNEL, function (e) {
- self._createNewChannelBlock(e.edata.blockCode, e.edata.resourceID, e.edata.sceneID);
- e.stopImmediatePropagation();
- e.preventDefault();
- });
- },
-
- /**
- Create a new block (player) on the current channel and refresh UI bindings such as properties open events.
- @method _createNewChannelBlock
- @param {Number} i_blockID
- @param {Number} i_resourceID optional param used when creating a block with embedded resource (i.e.: video / image / swf)
- @param {Number} i_sceneID optional param used when creating a block with embedded scene
- @return {Boolean} false
- **/
- _createNewChannelBlock: function (i_blockID, i_resourceID, i_sceneID) {
- var self = this;
- var totalChannelLength = self._getTotalDurationChannel();
- var jData = pepper.createNewChannelPlayer(self.selected_campaign_timeline_chanel_id, i_blockID, totalChannelLength, i_resourceID, i_sceneID);
- var campaign_timeline_chanel_player_id = jData['campaign_timeline_chanel_player_id'];
- var campaign_timeline_chanel_player_data = jData['campaign_timeline_chanel_player_data'];
-
- var timeline = BB.comBroker.getService(BB.SERVICES.CAMPAIGN_VIEW).getTimelineInstance(self.selected_campaign_timeline_id);
- var channel = timeline.getChannelInstance(self.selected_campaign_timeline_chanel_id);
- channel.createChannelBlock(campaign_timeline_chanel_player_id, campaign_timeline_chanel_player_data);
-
- var campaign_timeline_board_viewer_id = self.selected_campaign_timeline_board_viewer_id;
- var campaign_timeline_id = self.selected_campaign_timeline_id;
- var campaign_timeline_chanel_id = self.selected_campaign_timeline_chanel_id;
-
- // self._resetChannel();
- $(Elements.SORTABLE).empty();
- self._loadChannelBlocks(campaign_timeline_id, campaign_timeline_chanel_id);
- self._listenBlockSelected();
- // self._deselectBlocksFromChannel();
- self._selectLastBlockOnChannel();
- self._reOrderChannelBlocks();
- return false;
- },
-
- /**
- Listen to when a resource has been deleted so we can delete the associated block and re calc channel length
- @method _listenResourceRemoved
- @return none
- **/
- _listenResourceRemoved: function () {
- var self = this;
- BB.comBroker.listen(BB.EVENTS.REMOVED_RESOURCE, function (e) {
- if (self.selected_campaign_timeline_id != undefined && self.selected_campaign_timeline_chanel_id != undefined) {
- $(Elements.SORTABLE).empty();
- self._loadChannelBlocks(self.selected_campaign_timeline_id, self.selected_campaign_timeline_chanel_id);
- self._reOrderChannelBlocks();
- }
- });
- },
-
- /**
- Listen to when a resource has been deleted so we can delete the associated block and re calc channel length
- @method _listenSceneRemoved
- @return none
- **/
- _listenSceneRemoved: function () {
- var self = this;
- BB.comBroker.listen(BB.EVENTS.REMOVED_SCENE, function () {
- if (self.selected_campaign_timeline_id != undefined && self.selected_campaign_timeline_chanel_id != undefined) {
- $(Elements.SORTABLE).empty();
- self._loadChannelBlocks(self.selected_campaign_timeline_id, self.selected_campaign_timeline_chanel_id);
- self._reOrderChannelBlocks();
- }
- });
- },
-
- /**
- Listen to the BB.EVENTS.ON_VIEWER_SELECTED so we know when a timeline has been selected.
- Once a timeline selection was done we check if the event if one of a timeline owner or other; if of timeline
- we populate channel list, if latter reset list.
- @method _listenTimelineSelected
- @return none
- **/
- _listenTimelineSelected: function () {
- var self = this;
-
- BB.comBroker.listen(BB.EVENTS.ON_VIEWER_SELECTED, function (e) {
-
- self._resetChannel();
- self.selected_campaign_timeline_board_viewer_id = e.caller.campaign_timeline_board_viewer_id;
- self.selected_campaign_timeline_id = e.caller.campaign_timeline_id;
-
- if (e.context.m_owner instanceof Timeline || e.context.m_owner instanceof StorylineView) {
-
- var recCampaignTimelineViewerChanels = pepper.getChannelIdFromCampaignTimelineBoardViewer(self.selected_campaign_timeline_board_viewer_id, self.selected_campaign_timeline_id);
- self._loadChannelBlocks(self.selected_campaign_timeline_id, recCampaignTimelineViewerChanels['campaign_timeline_chanel_id']);
- $(Elements.ADD_BLOCK_BUTTON).fadeIn();
- $(Elements.REMOVE_BLOCK_BUTTON).fadeIn();
- $(Elements.TIMELIME_PREVIEW).fadeIn();
- }
-
- if (e.context.m_owner instanceof SequencerView) {
- self._resetChannel();
- $(Elements.ADD_BLOCK_BUTTON).fadeOut();
- $(Elements.REMOVE_BLOCK_BUTTON).fadeOut();
- $(Elements.TIMELIME_PREVIEW).fadeOut();
- }
- });
- },
-
- /**
- Load the channel list with its own blocks and refresh the UI.
- @method _loadChannelBlocks
- @param {Number} i_campaign_timeline_id
- @param {Number} i_campaign_timeline_chanel_id
- @return none
- **/
- _loadChannelBlocks: function (i_campaign_timeline_id, i_campaign_timeline_chanel_id) {
- var self = this;
-
- self.selected_campaign_timeline_chanel_id = i_campaign_timeline_chanel_id;
-
- var timeline = BB.comBroker.getService(BB.SERVICES['CAMPAIGN_VIEW']).getTimelineInstance(i_campaign_timeline_id);
- var channel = timeline.getChannelInstance(i_campaign_timeline_chanel_id);
- var blocks = channel.getBlocks();
- var xdate = BB.comBroker.getService('XDATE');
-
- for (var block in blocks) {
- var blockData = blocks[block].getBlockData();
- var duration = pepper.getBlockTimelineChannelBlockLength(blockData.blockID).totalInSeconds;
- var durationFormatted = xdate.clearTime().addSeconds(duration).toString('HH:mm:ss');
- $(Elements.SORTABLE).append($('<li class="' + BB.lib.unclass(Elements.CLASS_CHANNEL_LIST_ITEMS) + ' list-group-item" data-block_id="' + blockData.blockID + '">' +
- '<a href="#">' +
- //'<img class="img-responsive" src="' + blockData.blockIcon + '"/>' +
- '<i class="fa ' + blockData.blockFontAwesome + '"></i>' +
- '<span>' + blockData.blockName + '</span>' +
- '<i style="padding: 0; margin: 0" class="dragch fa fa-arrows-v"></i>' +
- '<span class="' + BB.lib.unclass(Elements.CLASS_BLOCK_LENGTH_TIMER) + ' hidden-xs">' + durationFormatted + '</span>' +
- '</a>' +
- '</li>'));
- }
- self._listenBlockSelected();
- self._createSortable(Elements.SORTABLE);
- },
-
- /**
- Listen when a block is selected, if its properties need to be open than open panel.
- Also, reference the selected block internally and fire event announcing it was selected.
- We also load required AMD moduels if this is the first time a block was selected (i.e.: modules
- were never loaded yet) and when they finish loaded we continue with thread execution.
- @method _listenBlockSelected
- @return none
- **/
- _listenBlockSelected: function () {
- var self = this;
- // clear previous listeners
- $(Elements.CLASS_CHANNEL_LIST_ITEMS).off('mousedown');
- $(Elements.CLASS_CHANNEL_LIST_ITEMS).on('mousedown contextmenu', function (e) {
- $.proxy(self._listenChannelBlockSelected(e), self);
- });
-
- },
-
- /**
- When block is selected within a channel, get the resource element so we can select it and fire
- the BLOCK_SELECTED event
- @method _listenChannelBlockSelected
- @param {Event} e
- **/
- _listenChannelBlockSelected: function (e) {
- var self = this;
- var resourceElem = $(e.target).closest('li');
- self.selected_block_id = $(resourceElem).data('block_id');
- BB.comBroker.fire(BB.EVENTS.BLOCK_SELECTED, this, null, self.selected_block_id);
- $(Elements.CLASS_CHANNEL_LIST_ITEMS).removeClass('activated').find('a').removeClass('whiteFont');
- $(resourceElem).addClass('activated').find('a').addClass('whiteFont');
- return false;
- },
-
- /**
- Listen to when a channel is selected, but through the storyline so we can re-select appropriate block in channel list
- @method _listenStorylineChannelSelected
- **/
- _listenStorylineChannelSelected: function () {
- var self = this;
- BB.comBroker.listen(BB.EVENTS['STORYLINE_CHANNEL_SELECTED'], function (e) {
- self.selected_block_id = e.edata;
- var resourceElem = $(Elements.CHANNEL_LIST_ELEM_VIEW).find('[data-block_id="' + self.selected_block_id + '"]');
- BB.comBroker.fire(BB.EVENTS.BLOCK_SELECTED, this, null, self.selected_block_id);
- $(Elements.CLASS_CHANNEL_LIST_ITEMS).removeClass('activated').find('a').removeClass('whiteFont');
- $(resourceElem).addClass('activated').find('a').addClass('whiteFont');
- return false;
- });
- },
-
- /**
- When a block is selected within a storyline get the resource element so we can select it and fire global block selection event
- @method _listenStorylineBlockSelected
- @param {Event} e
- **/
- _listenStorylineBlockSelected: function (e) {
- var self = this;
- BB.comBroker.listen(BB.EVENTS['STORYLINE_BLOCK_SELECTED'], function (e) {
- self.selected_block_id = e.edata;
- var resourceElem = $(Elements.CHANNEL_LIST_ELEM_VIEW).find('[data-block_id="' + self.selected_block_id + '"]');
- BB.comBroker.fire(BB.EVENTS.BLOCK_SELECTED, this, null, self.selected_block_id);
- $(Elements.CLASS_CHANNEL_LIST_ITEMS).removeClass('activated').find('a').removeClass('whiteFont');
- $(resourceElem).addClass('activated').find('a').addClass('whiteFont');
- return false;
- });
- },
-
- /**
- Forget the selected channel and reset channel member references
- @method _resetChannel
- **/
- _resetChannel: function () {
- var self = this;
- $(Elements.SORTABLE).empty();
- self.selected_block_id = undefined;
- self.selected_campaign_timeline_board_viewer_id = undefined;
- self.selected_campaign_timeline_id = undefined;
- self.selected_campaign_timeline_chanel_id = undefined;
- },
-
- /**
- Reset the UI when no block on channel is selected.
- @method _deselectBlocksFromChannel
- **/
- _deselectBlocksFromChannel: function () {
- var self = this;
- self.selected_block_id = undefined;
- self.m_property.resetPropertiesView();
- },
-
- /**
- Listen to when a block length has changed so we can update all other blocks offset respectively
- @method _listenBlockLengthChanged
- @return none
- **/
- _listenBlockLengthChanged: function () {
- var self = this;
- pepper.listen(Pepper.BLOCK_LENGTH_CHANGED, $.proxy(self._onBlockLengthChanged, self));
- },
-
- /**
- Listen to when a block on channel is modified with respect to its length
- @method _onBlockLengthChanged
- @param {Event} e block changed data
- **/
- _onBlockLengthChanged: function (e) {
- var self = this;
- var block_id = e.edata.campaignTimelineChanelPlayerID;
- var duration = e.edata.totalSeconds;
- var selectedLI = $(Elements.SORTABLE).find('[data-block_id="' + block_id + '"]');
- self.m_xdate = BB.comBroker.getService('XDATE');
- var durationFormated = self.m_xdate.clearTime().addSeconds(duration).toString('HH:mm:ss');
- $(Elements.CLASS_BLOCK_LENGTH_TIMER, selectedLI).text(durationFormated);
- self._reOrderChannelBlocks();
- },
-
- /**
- Select the last block on the channel (last LI element) and fire a click event on it.
- @method _selectLastBlockOnChannel
- @return none
- **/
- _selectLastBlockOnChannel: function () {
- var self = this
- var blocks = $(Elements.SORTABLE).children();
- var block = undefined;
- $(blocks).each(function (i) {
- block = this;
- });
- if (block)
- $(block).click();
- },
-
- /**
- Delete the selected block from the channel
- @method _deleteChannelBlock
- @return none
- **/
- _deleteChannelBlock: function (i_block_id) {
- var self = this;
- var selectedLI = $(Elements.SORTABLE).find('[data-block_id="' + i_block_id + '"]');
- selectedLI.remove();
- var timeline = BB.comBroker.getService(BB.SERVICES.CAMPAIGN_VIEW).getTimelineInstance(self.selected_campaign_timeline_id);
- var channel = timeline.getChannelInstance(self.selected_campaign_timeline_chanel_id);
- channel.deleteBlock(i_block_id);
- self._deselectBlocksFromChannel();
- self._reOrderChannelBlocks();
- },
-
- /**
- Listen to any canvas right click
- @method _listenContextMenu
- **/
- _listenContextMenu: function () {
- var self = this;
- $(Elements.SORTABLE).contextmenu({
- target: Elements.CHANNEL_LIST_CONTEXT_MENU,
- before: function (e, element, target) {
- e.preventDefault();
- //self.m_mouseX = e.offsetX;
- //self.m_mouseY = e.offsetY;
- if (_.isUndefined(self.selected_block_id))
- return false;
- return true;
- },
- onItem: function (context, e) {
- self._onContentMenuSelection($(e.target).attr('name'))
- }
- });
- },
-
- /**
- 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 'remove':
- {
- $(Elements.REMOVE_BLOCK_BUTTON).trigger('click');
- break;
- }
- case 'first':
- {
- self.moveBlockFirst();
- break;
- }
- case 'last':
- {
- self.moveBlockLast();
- break;
- }
- }
- return true;
- },
-
- /**
- Create a draggable sortable channel list
- @method _createSortable
- @param {String} i_selector
- **/
- _createSortable: function (i_selector) {
- var self = this;
- if ($(i_selector).children().length==0) return;
- var sortable = document.querySelector(i_selector);
- self.m_draggables = Draggable.create(sortable.children, {
- type: "y",
- bounds: sortable,
- dragClickables: true,
- edgeResistance: 1,
- onPress: self._sortablePress,
- onDragStart: self._sortableDragStart,
- onDrag: self._sortableDrag,
- liveSnap: self._sortableSnap,
- onDragEnd: function () {
- var t = this.target,
- max = t.kids.length - 1,
- newIndex = Math.round(this.y / t.currentHeight);
- newIndex += (newIndex < 0 ? -1 : 0) + t.currentIndex;
- if (newIndex === max) {
- t.parentNode.appendChild(t);
- } else {
- t.parentNode.insertBefore(t, t.kids[newIndex + 1]);
- }
- TweenLite.set(t.kids, { yPercent: 0, overwrite: "all" });
- TweenLite.set(t, { y: 0, color: "" });
- self._reOrderChannelBlocks();
-
- //_.each(self.m_draggables, function(i){
- // this.enabled(false);
- //});
- }
- });
- },
-
- /**
- Sortable channel list on press
- @method _sortablePress
- **/
- _sortablePress: function () {
- var t = this.target,
- i = 0,
- child = t;
- while (child = child.previousSibling)
- if (child.nodeType === 1) i++;
- t.currentIndex = i;
- t.currentHeight = t.offsetHeight;
- t.kids = [].slice.call(t.parentNode.children); // convert to array
- },
-
- /**
- Sortable drag channel list on press
- @method _sortableDragStart
- **/
- _sortableDragStart: function () {
- TweenLite.set(this.target, { color: "#88CE02" });
- },
-
- /**
- Sortable drag channel list
- @method _sortableDrag
- **/
- _sortableDrag: function () {
- var t = this.target,
- elements = t.kids.slice(), // clone
- indexChange = Math.round(this.y / t.currentHeight),
- bound1 = t.currentIndex,
- bound2 = bound1 + indexChange;
- if (bound1 < bound2) { // moved down
- TweenLite.to(elements.splice(bound1 + 1, bound2 - bound1), 0.15, { yPercent: -100 });
- TweenLite.to(elements, 0.15, { yPercent: 0 });
- } else if (bound1 === bound2) {
- elements.splice(bound1, 1);
- TweenLite.to(elements, 0.15, { yPercent: 0 });
- } else { // moved up
- TweenLite.to(elements.splice(bound2, bound1 - bound2), 0.15, { yPercent: 100 });
- TweenLite.to(elements, 0.15, { yPercent: 0 });
- }
- },
-
- /**
- snap channels to set rounder values
- @method _sortableSnap
- **/
- _sortableSnap: function (y) {
- return y;
- // enable code below to enable snapinnes on dragging
- // var h = this.target.currentHeight;
- // return Math.round(y / h) * h;
- },
-
- /**
- Move current selected block to be the first to play within the channel
- @method moveBlockFirst
- **/
- moveBlockFirst: function () {
- var self = this;
- var resourceElem = $('.activated', self.$el);
- $(Elements.SORTABLE).prepend($(resourceElem));
- self._reOrderChannelBlocks();
- },
-
- /**
- Move current selected block to be the last to play within the channel
- @method moveBlockLast
- **/
- moveBlockLast: function () {
- var self = this;
- var resourceElem = $('.activated', self.$el);
- $(Elements.SORTABLE).append($(resourceElem));
- self._reOrderChannelBlocks();
- }
- });
-
- return ChannelListView;
-
- });
-