APIs

Show:
  1. /**
  2. The sequencer module is responsible for management and order of playback of each timeline within each campaign
  3. @class SequencerView
  4. @constructor
  5. @param {String} i_container element that CompCampaignNavigator inserts itself into
  6. @return {Object} instantiated CompCampaignNavigator
  7. **/
  8. define(['jquery', 'backbone', 'ScreenTemplateFactory', 'contextmenu'], function ($, Backbone, ScreenTemplateFactory, contextmenu) {
  9.  
  10. BB.SERVICES.SEQUENCER_VIEW = 'SequencerView';
  11.  
  12. var SequencerView = BB.View.extend({
  13.  
  14. /**
  15. Constructor
  16. Init the instance and enable drag and drop operation.
  17. We also wire the open properties UI so we can populate a selected timeline through the properties panel.
  18. @method initialize
  19. **/
  20. initialize: function () {
  21. var self = this;
  22. this.m_thumbsContainer = this.$el;
  23. this.m_timelines = {};
  24. this.m_screenTemplates = {};
  25.  
  26. self._listenContextMenu();
  27. self._listenReset();
  28. pepper.listen(Pepper.TIMELINE_DELETED, $.proxy(self._deleteSequencedTimeline, self));
  29. },
  30.  
  31. /**
  32. Listen to any canvas right click
  33. @method _listenContextMenu
  34. **/
  35. _listenContextMenu: function () {
  36. var self = this;
  37. $(Elements.SCREEN_SELECTOR_CONTAINER).contextmenu({
  38. target: Elements.SEQUENCER_CONTEXT_MENU,
  39. before: function (e, element, target) {
  40. e.preventDefault();
  41. //self.m_mouseX = e.offsetX;
  42. //self.m_mouseY = e.offsetY;
  43. return true;
  44. },
  45. onItem: function (context, e) {
  46. self._onContentMenuSelection($(e.target).attr('name'))
  47. }
  48. });
  49. },
  50.  
  51. /**
  52. On Scene right click context menu selection command
  53. @method _onContentMenuSelection
  54. @param {String} i_command
  55. **/
  56. _onContentMenuSelection: function (i_command) {
  57. var self = this;
  58. var campaign_timeline_id = BB.comBroker.getService(BB.SERVICES.CAMPAIGN_VIEW).getSelectedTimeline();
  59. if (campaign_timeline_id == -1 || _.isUndefined(campaign_timeline_id))
  60. return;
  61.  
  62. switch (i_command) {
  63. case 'firstChannel':
  64. {
  65. $(Elements.SELECT_NEXT_CHANNEL).trigger('click');
  66. break;
  67. }
  68. case 'editLayout':
  69. {
  70. $(Elements.EDIT_SCREEN_LAYOUT).trigger('click');
  71. break;
  72. }
  73. case 'duplicate':
  74. {
  75. BB.comBroker.getService(BB.SERVICES.CAMPAIGN_VIEW).duplicateTimeline(campaign_timeline_id, {});
  76. break;
  77. }
  78. case 'remove':
  79. {
  80. $(Elements.REMOVE_TIMELINE_BUTTON).trigger('click');
  81. break;
  82. }
  83. case 'first':
  84. {
  85. var elem = $(self.m_thumbsContainer).find('[data-campaign_timeline_id="' + campaign_timeline_id + '"]').eq(0).closest('svg');
  86. $(self.m_thumbsContainer).prepend(elem);
  87. self.reSequenceTimelines();
  88. break;
  89. }
  90. case 'last':
  91. {
  92. var elem = $(self.m_thumbsContainer).find('[data-campaign_timeline_id="' + campaign_timeline_id + '"]').eq(0).closest('svg');
  93. $(self.m_thumbsContainer).append(elem);
  94. self.reSequenceTimelines();
  95. break;
  96. }
  97. }
  98. return true;
  99. },
  100.  
  101. /**
  102. Delete a timeline from the Sequencer UI, as well as from the local member m_timelines.
  103. @method _deleteSequencedTimeline
  104. @param {Number} i_campaign_timeline_id
  105. @return none
  106. **/
  107. _deleteSequencedTimeline: function (e) {
  108. var self = this;
  109. var campaign_timeline_id = e.edata;
  110. self._deleteTimelineThumbUI(campaign_timeline_id);
  111. delete self.m_timelines[campaign_timeline_id];
  112. pepper.removeTimelineFromSequences(campaign_timeline_id);
  113. self.reSequenceTimelines();
  114. },
  115.  
  116. /**
  117. Remove the element's UI thumb of a template layout
  118. @method _deleteTimelineThumbUI
  119. @param {Number} i_campaign_timeline_id
  120. **/
  121. _deleteTimelineThumbUI: function (i_campaign_timeline_id) {
  122. var self = this;
  123. var elementID = self.m_timelines[i_campaign_timeline_id];
  124. $('#' + elementID).remove();
  125. if (self.m_screenTemplates[i_campaign_timeline_id])
  126. self.m_screenTemplates[i_campaign_timeline_id].destroy();
  127. },
  128.  
  129. /**
  130. Listen to reset of when switching to different campaign so we forget current state
  131. @method _listenReset
  132. **/
  133. _listenReset: function () {
  134. var self = this;
  135. BB.comBroker.listen(BB.EVENTS.CAMPAIGN_RESET, function () {
  136. self.m_timelines = {};
  137. self.m_screenTemplates = {};
  138. $(self.m_thumbsContainer).empty();
  139. });
  140. },
  141.  
  142. /**
  143. Create a sortable channel list
  144. @method _createSortable
  145. @param {Element} i_selector
  146. **/
  147. _createSortable: function (i_selector) {
  148. var self = this;
  149. if ($(i_selector).children().length == 0) return;
  150. var sortable = document.querySelector(i_selector);
  151. self.m_draggables = Draggable.create(sortable.children, {
  152. type: "x",
  153. bounds: sortable,
  154. edgeResistance: 1,
  155. dragResistance: 0,
  156. onPress: self._sortablePress,
  157. onDragStart: self._sortableDragStart,
  158. onDrag: self._sortableDrag,
  159. liveSnap: self._sortableSnap,
  160. zIndexBoost: true,
  161. onDragEnd: function () {
  162. var t = this.target,
  163. max = t.kids.length - 1,
  164. //newIndex = Math.round(this.x / t.currentWidth);
  165. newIndex = Math.ceil(this.x / t.currentWidth);
  166. newIndex += (newIndex < 0 ? -1 : 0) + t.originalIndex;
  167. if (newIndex === max) {
  168. t.parentNode.appendChild(t);
  169. } else {
  170. t.parentNode.insertBefore(t, t.kids[newIndex + 1]);
  171. }
  172. TweenLite.set(t.kids, { xPercent: 0, overwrite: "all" });
  173. TweenLite.set(t, { y: 0, color: "" });
  174. var orderedTimelines = self.reSequenceTimelines();
  175. $(self.m_thumbsContainer).empty();
  176. BB.comBroker.getService(BB.SERVICES.CAMPAIGN_VIEW).populateTimelines(orderedTimelines);
  177. var campaign_timeline_id = BB.comBroker.getService(BB.SERVICES.CAMPAIGN_VIEW).getSelectedTimeline();
  178. self.selectTimeline(campaign_timeline_id);
  179.  
  180. }
  181. });
  182. },
  183.  
  184. /**
  185. Sortable channel list on press
  186. @method _sortablePress
  187. **/
  188. _sortablePress: function () {
  189. var t = this.target,
  190. i = 0,
  191. child = t;
  192. while (child = child.previousSibling)
  193. if (child.nodeType === 1) i++;
  194. t.originalIndex = i;
  195. t.currentWidth = $(t).outerWidth();
  196. t.kids = [].slice.call(t.parentNode.children); // convert to array
  197. },
  198.  
  199. /**
  200. Sortable drag channel list on press
  201. @method _sortableDragStart
  202. **/
  203. _sortableDragStart: function () {
  204. TweenLite.set(this.target, { color: "#88CE02" });
  205. },
  206.  
  207. /**
  208. Sortable drag channel list
  209. @method _sortableDrag
  210. **/
  211. _sortableDrag: function () {
  212. var t = this.target,
  213. elements = t.kids.slice(), // clone
  214. // indexChange = Math.round(this.x / t.currentWidth), // round flawed on large values
  215. indexChange = Math.ceil(this.x / t.currentWidth),
  216. srcIndex = t.originalIndex,
  217. dstIndex = srcIndex + indexChange;
  218.  
  219. // console.log('k ' + t.kids.length + ' s:' + srcIndex + ' d:' + indexChange + ' t:' + (dstIndex - srcIndex));
  220.  
  221. if (srcIndex < dstIndex) { // moved right
  222. TweenLite.to(elements.splice(srcIndex + 1, dstIndex - srcIndex), 0.15, { xPercent: -140 }); // 140 = width of screen layout widget
  223. TweenLite.to(elements, 0.15, { xPercent: 0 });
  224. } else if (srcIndex === dstIndex) {
  225. elements.splice(srcIndex, 1);
  226. TweenLite.to(elements, 0.15, { xPercent: 0 });
  227. } else { // moved left
  228. // ignore if destination > source index
  229. if ( (indexChange < 0 ? indexChange * -1 : indexChange) > srcIndex)
  230. return;
  231. TweenLite.to(elements.splice(dstIndex, srcIndex - dstIndex), 0.15, { xPercent: 140 }); // 140 = width of screen layout widget
  232. TweenLite.to(elements, 0.15, { xPercent: -10 });
  233. }
  234. },
  235.  
  236. /**
  237. snap channels to set rounder values
  238. @method _sortableSnap
  239. **/
  240. _sortableSnap: function (y) {
  241. return y;
  242. /* enable code below to use live drag snapping */
  243. // var h = this.target.currentHeight;
  244. // return Math.round(y / h) * h;
  245. },
  246.  
  247. /**
  248. Select the first timeline in the Sequencer
  249. @method selectFirstTimeline
  250. **/
  251. selectFirstTimeline: function () {
  252. var self = this;
  253. var timeline;
  254. for (timeline in self.m_timelines) {
  255. self.selectTimeline(timeline);
  256. break;
  257. }
  258. },
  259.  
  260. /**
  261. Select a viewer
  262. @method selectViewer
  263. @param {Number} i_timeline_id
  264. @param {Number} i_viewer_id
  265. **/
  266. selectViewer: function (i_timeline_id, i_viewer_id) {
  267. var self = this;
  268. self.m_screenTemplates[i_timeline_id].selectDivison(i_viewer_id);
  269. },
  270.  
  271. /**
  272. Create the timeline template (a.k.a timeline thumbnail) via the ScreenTemplateFactory
  273. and insert it into the sequencer UI. We proceed by activating the newly created timeline thumbnail
  274. via the ScreenTemplateFactory public methods.
  275. @method createTimelineThumbnailUI
  276. @param {Object} i_screenProps
  277. **/
  278. createTimelineThumbnailUI: function (i_screenProps) {
  279. var self = this;
  280. var index = -1;
  281. var elem = undefined;
  282.  
  283. // Get the timeline id for current timeline creating
  284. for (var screenProp in i_screenProps) {
  285. var campaign_timeline_id = i_screenProps[screenProp]['campaign_timeline_id']
  286. break;
  287. }
  288.  
  289. // if timeline_id already exists, it means this is an update from ScreenLayoutEditorView so we
  290. // must first delete previous UI as well as it's matching instance
  291. if (self.m_timelines[campaign_timeline_id] != undefined) {
  292. var elementID = '#' + self.m_timelines[campaign_timeline_id];
  293. index = $(elementID).index();
  294. self._deleteTimelineThumbUI(campaign_timeline_id);
  295. }
  296.  
  297. var screenTemplateData = {
  298. orientation: BB.comBroker.getService(BB.SERVICES.ORIENTATION_SELECTOR_VIEW).getOrientation(),
  299. resolution: BB.comBroker.getService(BB.SERVICES.RESOLUTION_SELECTOR_VIEW).getResolution(),
  300. screenProps: i_screenProps,
  301. scale: '14'
  302. };
  303.  
  304. var screenTemplate = new ScreenTemplateFactory({
  305. i_screenTemplateData: screenTemplateData,
  306. i_selfDestruct: false,
  307. i_owner: this
  308. });
  309.  
  310. var snippet = screenTemplate.create();
  311. var elementID = $(snippet).attr('id');
  312.  
  313. self.m_timelines[campaign_timeline_id] = elementID;
  314. self.m_screenTemplates[campaign_timeline_id] = screenTemplate;
  315.  
  316. //screenTemplate.selectablelDivision();
  317. //screenTemplate.activate();
  318.  
  319. switch (index) {
  320. case -1:
  321. {
  322. // position thumbnail in beginning (first creation)
  323. self.m_thumbsContainer.append(snippet);
  324. screenTemplate.selectableFrame();
  325. break;
  326. }
  327.  
  328. case 0:
  329. {
  330. // position thumbnail index in beginning (append if no other thumbnails)
  331. elem = self.m_thumbsContainer.children().eq(0);
  332. if (elem.length > 0) {
  333. $(snippet).insertBefore(elem)
  334. } else {
  335. self.m_thumbsContainer.append(snippet);
  336. }
  337. screenTemplate.selectableFrame();
  338. break;
  339. }
  340.  
  341. default:
  342. {
  343. // position thumbnail as previous index position
  344. elem = self.m_thumbsContainer.children().eq(index - 1);
  345. $(snippet).insertAfter(elem)
  346. screenTemplate.selectableFrame();
  347. break;
  348. }
  349. }
  350.  
  351. self._createSortable(Elements.SCREEN_LAYOUTS_UL);
  352. },
  353.  
  354. /**
  355. Reorder the timeline in the local msdb to match the UI order of the timeline thumbnails in the Sequencer
  356. @method reSequenceTimelines
  357. @return {Array} order of timelines ids
  358. **/
  359. reSequenceTimelines: function () {
  360. var self = this;
  361. var order = [];
  362. var timelines = $(self.m_thumbsContainer).children().each(function (sequenceIndex) {
  363. var element = $(this).find('[data-campaign_timeline_id]').eq(0);
  364. var campaign_timeline_id = $(element).data('campaign_timeline_id');
  365. order.push(campaign_timeline_id);
  366. var selectedCampaign = BB.comBroker.getService(BB.SERVICES.CAMPAIGN_VIEW).getSelectedCampaign();
  367. pepper.setCampaignTimelineSequencerIndex(selectedCampaign, campaign_timeline_id, sequenceIndex);
  368. });
  369. return order;
  370. },
  371.  
  372. /**
  373. Return this instance.
  374. @method getOwner
  375. @return {Object} this
  376. **/
  377. getOwner: function () {
  378. return this;
  379. },
  380.  
  381. /**
  382. Find the campaign_timeline_id within the Sequencer and trigger a click event on it so it gets selected.
  383. @method selectTimeline
  384. @param {Number} i_campaign_timeline_id
  385. @return {Number} i_campaign_timeline_id or -1
  386. **/
  387. selectTimeline: function (i_campaign_timeline_id) {
  388. var self = this;
  389. BB.comBroker.fire(BB.EVENTS.CAMPAIGN_TIMELINE_SELECTED, this, null, i_campaign_timeline_id);
  390. var total = $(self.m_thumbsContainer).find('[data-campaign_timeline_id="' + i_campaign_timeline_id + '"]').eq(0).mouseup();;
  391. if (total.length == 0)
  392. return -1;
  393. return i_campaign_timeline_id;
  394. }
  395. });
  396.  
  397. return SequencerView;
  398.  
  399. });