API Docs for:
Show:

File: view\TimelineView.js

  1. /*
  2. * BGPlay.js #9660
  3. * A web-based service for the visualization of the Internet routing
  4. *
  5. * Copyright (c) 2012 Roma Tre University and RIPE NCC
  6. *
  7. * See the file LICENSE.txt for copying permission.
  8. */
  9.  
  10. /**
  11. * Template: timeline.html
  12. * @class TimelineView
  13. * @module modules
  14. */
  15. var TimelineView=Backbone.View.extend({
  16. events:function(){
  17. return {
  18. "click .bgplayTimelineControlCanvas":"updateCursorPosition",
  19. "click .bgplayTimelineSelectionCanvas":"updateSelectedEvent",
  20. "click .bgplayTimelineSelectionNext":"nextSelectionChart",
  21. "click .bgplayTimelineSelectionPrev":"prevSelectionChart"
  22. }
  23. },
  24.  
  25. /**
  26. * The initialization method of this object.
  27. * @method initialize
  28. * @param {Map} A map of parameters
  29. */
  30. initialize:function(){
  31. this.environment=this.options.environment;
  32. this.bgplay=this.environment.bgplay;
  33. this.fileRoot=this.environment.fileRoot;
  34. this.eventAggregator=this.environment.eventAggregator;
  35.  
  36. this.eventAggregator.trigger("moduleLoading", true);
  37. this.eventAggregator=this.environment.eventAggregator;
  38. this.allEvents=this.bgplay.get("allEvents");
  39. this.selectionChartPage=0;
  40. this.stopTriggerEvents=false;
  41. this.offsetOfVisibilityOnSelectionTimeline = this.environment.config.timeline.offsetOfVisibilityOnSelectionTimeline;
  42. this.eventOnSelectionChart=new net.webrobotics.TreeMap(comparator,{allowDuplicateKeys:false,suppressDuplicateKeyAlerts:true});
  43.  
  44. this.selectionChartWidth=this.environment.config.timeline.minSelectionChartWidth;
  45. this.controlChartHeight=this.environment.config.timeline.controlChartHeight;
  46. this.selectionChartHeight=this.environment.config.timeline.selectionChartHeight;
  47. this.cursorsWidth=this.environment.config.timeline.controlChartCursorsWidth;
  48. this.halfCursorWidth=this.environment.config.timeline.controlChartCursorsWidth/2;
  49. this.halfWarpWidth=this.environment.config.timeline.timeWarpWidth/2;
  50.  
  51. this.seekTimers=[];
  52. this.selectedIntervalCursorColor=this.environment.config.timeline.selectedIntervalCursorColor;
  53. this.animation=false; //No animation at initialization
  54. this.selectionStart=new Instant({id:0,timestamp:this.bgplay.get("starttimestamp")});
  55. this.selectionEnd=new Instant({id:0,timestamp:this.bgplay.get("endtimestamp")});
  56. this.selectionFirstStart=this.selectionStart;
  57. this.selectionFirstEnd=this.selectionEnd;
  58. this.globalTimeOffset=this.bgplay.get("endtimestamp")-this.bgplay.get("starttimestamp");
  59.  
  60. this.graphAnimationsOngoing=0;
  61.  
  62. this.eventAggregator.on("destroyAll", function(){
  63. this.destroyMe();
  64. },this);
  65.  
  66. this.eventAggregator.on("animate",function(start){
  67. this.stopAnimation();
  68. this.animation=start;
  69. this.animate();
  70. },this);
  71.  
  72. this.bgplay.on("change:cur_instant",function(){
  73. this.updateControlCanvas();
  74. this.scrollSelectionCanvas(this.bgplay.get("cur_instant"));
  75. this.updateSelectionCanvas();
  76. },this);
  77.  
  78. this.eventAggregator.on("allAnimationsCompleted",function(parameters){
  79. this.controlCanvasDom.css("cursor","pointer");
  80. this.selectionCanvasDom.css("cursor","pointer");
  81. this.stopTriggerEvents=false;
  82. this.animate();
  83. },this);
  84.  
  85. this.eventAggregator.on("graphAnimationComplete",function(value){
  86. if (value==false){
  87. this.controlCanvasDom.css("cursor","wait");
  88. this.selectionCanvasDom.css("cursor","wait");
  89. this.stopTriggerEvents=true;
  90. }
  91. },this);
  92.  
  93. this.render();
  94. log("Timelines loaded.");
  95. },
  96.  
  97. /*
  98. * This method creates the pointers to the DOM elements.
  99. */
  100. getDomElements:function(){
  101. this.timelineControlCanvasDate = this.dom.find('.bgplayTimelineControlCanvasDate');
  102. this.timelineControlCanvasText = this.dom.find('.bgplayTimelineControlCanvasText');
  103. this.controlCanvasDom = this.dom.find('.bgplayTimelineControlCanvas');
  104. this.selectionCanvasDom = this.dom.find('.bgplayTimelineSelectionCanvas');
  105. this.timelineSelectionDiv = this.dom.find('.bgplayTimelineSelectionDiv');
  106. this.timelineControlCanvasSlider = this.dom.find(".bgplayTimelineControlCanvasSlider");
  107. this.timelineSelectionNext = this.dom.find('.bgplayTimelineSelectionNext');
  108. this.timelineSelectionPrev = this.dom.find('.bgplayTimelineSelectionPrev');
  109. this.timelineSelectionWaitIco = this.dom.find('.bgplayTimelineSelectionWaitIco');
  110. this.timelineControlCanvasSliderLeft = this.dom.find(".bgplayTimelineControlCanvasSliderLeft");
  111. this.timelineControlCanvasSliderRight = this.dom.find(".bgplayTimelineControlCanvasSliderRight");
  112. this.timelineSelectionSliderLeft=this.dom.find('.bgplayTimelineSelectionSliderLeft');
  113. this.timelineSelectionSliderRight=this.dom.find('.bgplayTimelineSelectionSliderRight');
  114. },
  115.  
  116. /**
  117. * This method draws this module (eg. inject the DOM and elements).
  118. * @method render
  119. */
  120. render:function(){
  121. this.dom=$(this.el);
  122. this.selectionChartContainerWidth=this.dom.width()-90;
  123. this.selectionChartWidth=this.selectionChartContainerWidth;
  124. this.controlChartWidth=this.dom.width();
  125. this.sliderLeftPosition=0;
  126. this.sliderRightPosition=this.controlChartWidth;
  127.  
  128. parseTemplate('timeline.html',this,this.el);
  129. this.getDomElements();
  130.  
  131. this.drawControlChart();
  132. this.drawSelectionChart();
  133. this.updateControlCanvas();
  134. this.timelineSelectionDiv.width(this.selectionChartContainerWidth);
  135.  
  136. var $this=this; //The stop function needs only 2 parameters (event and ui), inside this function 'this' refers to the DOM element dragged
  137. this.timelineControlCanvasSlider.draggable({
  138. axis:"x",
  139. stop:function(event,ui){
  140. $this.confirmSlidersSelection($this);
  141. },
  142. drag:function(event,ui){
  143. return $this.checkSliderSelection(event,$this);
  144. }
  145. });
  146.  
  147. this.selectionCanvasDom.draggable({
  148. axis:"x",
  149. stop:function(){
  150. $this.miniArrowsManager($this);
  151. },
  152. drag:function(event,ui){
  153. var element=$(event.target);
  154. var container=element.parent();
  155. var elementLeft=element.position().left;
  156. var elementRight=element.position().left+element.width();
  157. if (elementLeft>0){
  158. element.css('left','0');
  159. return false;
  160. }
  161. if (elementRight<container.width()){
  162. element.css('left',container.width()-element.width());
  163. return false;
  164. }
  165. }
  166. });
  167. return this;
  168. },
  169.  
  170. /**
  171. * This method returns the next event in the timeline.
  172. * @method nextEvent
  173. * @param {Object} An instance of Instant
  174. * @param {Boolean} If this optional parameter is true then the next event will be the first of the next block
  175. * @return {Object} An instance of Event
  176. */
  177. nextEvent:function(instant,forceNotCumulative){
  178. var nextInstant, event, forceNotCumulative;
  179. forceNotCumulative=forceNotCumulative||false;
  180.  
  181. if (this.environment.config.cumulativeAnimations && forceNotCumulative==false){
  182. event=this.nextEvent(instant,true); //The real next event
  183.  
  184. if (event!=null){
  185. nextInstant=new Instant({id:0,timestamp:event.get("instant").get("timestamp")+1});
  186. event=this.allEvents.nearest(nextInstant,true,true); //The next event of this group of events
  187.  
  188. if (event!=null){
  189. nextInstant=new Instant({id:event.get("instant")-1,timestamp:event.get("instant").get("timestamp")});
  190. }else{
  191. nextInstant=new Instant({id:0,timestamp:this.bgplay.get("endtimestamp")});
  192. }
  193. event=this.allEvents.nearest(nextInstant,false,true); //The last event of this group
  194. }
  195. }else{
  196. event=this.allEvents.nearest(instant,true,false);
  197. }
  198.  
  199. return event;
  200. },
  201.  
  202. /**
  203. * This method manages the animation of the time cursor along the timeline.
  204. * @method seek
  205. * @param {Object} An instance of Instant which will be the final position of the time cursor
  206. * @param {Float} The duration of the animation
  207. */
  208. seek:function(instant,delay){
  209. var fps=this.environment.config.timeline.timelineCursorFps;
  210. var $this=this; //A local copy of this
  211.  
  212. var curTimestamp=this.bgplay.get("cur_instant").get("timestamp");
  213. var timeOffset=instant.get("timestamp")-curTimestamp;
  214.  
  215. if (timeOffset<=0){ //backward, fast jump
  216. $this.bgplay.setCurInstant(instant,false);
  217. return null;
  218. }
  219.  
  220. delay=(delay*1000); //Seconds to milliseconds
  221.  
  222. var interval=Math.ceil(1000/fps);
  223. var totalFrames=Math.ceil(delay/interval);
  224. if (totalFrames<1)
  225. totalFrames=1;
  226.  
  227. var fixedStep = timeOffset/totalFrames;
  228.  
  229. var i;
  230. for(i=1;i <= totalFrames;i++) {
  231. (function() {
  232. var n = i;
  233. function seekInTime(){ //Remember: this function is "an instance" of a function, all vars are local clones
  234. var newTimestamp= curTimestamp+(fixedStep*n);
  235. if (totalFrames==n){ //When totalFrames==n the cursor has reached the final seek position
  236. /*
  237. * We can't check $this.animation in order to trigger the event because $this.animation is a static
  238. * clone (doesn't change after its allocation) of this.animation.
  239. */
  240. if ($this.allEvents.compare(instant, $this.selectionEnd)>=0){
  241.  
  242. //No more events in the timeline, the animation stops
  243. $this.animation=false;
  244. $this.eventAggregator.trigger("animationEnd");
  245. if ($this.environment.config.timeline.reloadAnimationWhenItEnds){
  246. $this.eventAggregator.trigger("animationReload");
  247. }else{
  248. $this.bgplay.setCurInstant($this.selectionEnd,false);
  249. }
  250. }else{
  251. $this.bgplay.setCurInstant(instant,false);
  252. }
  253. }else{
  254. /*
  255. * When totalFrames!=n the cursor has reached a intermediate position calculated
  256. * by the seek function itself to emulate a fluid animation.
  257. */
  258. $this.bgplay.setCurInstant(new Instant({id:0,timestamp:newTimestamp}),true);//silent=true prevents the propagation of events, there are no BGP updates to be applied
  259. $this.updateSelectionCanvas();
  260. $this.updateControlCanvas();//We changed the current instant, now draw the cursor at the new position.
  261. }
  262. }
  263.  
  264. /*
  265. * Important: if we don't store the pointers to the timers then we can't stop animation in any way
  266. */
  267. $this.seekTimers.push(setTimeout(seekInTime,interval*n));
  268. })();
  269. }
  270. },
  271.  
  272. /**
  273. * This method is useful to calculate the duration for a seek operation with a logarithm approach.
  274. * @method logarithmicSeekTime
  275. * @param {Float} A time interval
  276. * @return {Float} A time interval
  277. */
  278. logarithmicSeekTime:function(offset){
  279. var base=this.environment.config.timeline.controlChartLogBaseWarp; //Tuning it, for 10 use common constant
  280. return Math.log(Math.sqrt(Math.sqrt(offset)))/Math.log(base);
  281. },
  282.  
  283. finishAnimation:function(){
  284. var seekTime;
  285. //No more events in the timeline, the animation stops
  286. this.animation=false;
  287.  
  288. // Ok, the animation stops, but the cursor must reach the full scale or rather the endtimestamp
  289. var timestampOffsetToEnd=this.selectionEnd.get("timestamp")- this.bgplay.get("cur_instant").get("timestamp"); //In seconds
  290. seekTime=Math.ceil(this.logarithmicSeekTime(timestampOffsetToEnd));
  291. this.seek(this.selectionEnd,seekTime);
  292. },
  293.  
  294. /**
  295. * This method starts the animation of the timeline.
  296. * The animation of the timeline is a set of consecutive seek invocation.
  297. * @method animate
  298. */
  299. animate:function(){
  300. if(this.animation!=true){
  301. this.stopAnimation();
  302. return;
  303. }
  304. var seekTime;
  305. var tmpEvent=this.nextEvent(this.bgplay.get("cur_instant"),false);
  306.  
  307. if (tmpEvent==null){
  308. this.finishAnimation();
  309. return;
  310. }
  311.  
  312. //Seconds between the current event and the next event on the treeMap
  313. var timestampOffsetToNextEvent=tmpEvent.get("instant").get("timestamp") - this.bgplay.get("cur_instant").get("timestamp"); //In seconds
  314.  
  315. if (timestampOffsetToNextEvent<2){ //this can be also <5 (equal for humans)
  316. this.seek(tmpEvent.get("instant"),0); //Move the pointer to the new timestamp in 0 sec
  317. }else{
  318. seekTime=Math.ceil(this.logarithmicSeekTime(timestampOffsetToNextEvent)); //Calculate the log of the offset for a faster seek
  319. this.seek(tmpEvent.get("instant"),seekTime); //Move the pointer to the new timestamp in seekTime sec
  320. }
  321. },
  322.  
  323. /**
  324. * This method returns the number of events occurring between two unix timestamps.
  325. * @method getNumberOfEventsBetween
  326. * @param {Integer} A unix timestamp
  327. * @param {Integer} A unix timestamp
  328. * @return {Integer} The number of events
  329. */
  330. getNumberOfEventsBetween:function(start,end){
  331. var subTreeMap=this.allEvents.getSubTreeMap(new Instant({id:1,timestamp:start}),new Instant({id:0,timestamp:(end+1)}));
  332. return (subTreeMap)?subTreeMap.size():0;
  333. },
  334.  
  335. /**
  336. * This method prints information about the timeline.
  337. * @method writeTimelineInfo
  338. * @param {String} Information
  339. */
  340. writeTimelineInfo: function(text,fontColor,fontStyle){
  341. this.timelineControlCanvasText.html(text);
  342. /*
  343. var ctx=this.contextControlCanvas;
  344. ctx.fillStyle = fontColor;
  345. ctx.font = fontStyle;
  346. ctx.textBaseline = 'top';
  347. ctx.fillText(text, 5, 0);
  348. */
  349. },
  350.  
  351. /**
  352. * This method prints information about the position of the time cursor.
  353. * @method writeTimelineDate
  354. */
  355. writeTimelineDate: function($this){
  356. $this.timelineControlCanvasDate.html("Current instant: "+dateToString(this.bgplay.get("cur_instant").get("timestamp")));
  357. /*
  358. var ctx=this.contextControlCanvas;
  359. ctx.fillStyle = this.environment.config.timeline.timelineDateColor;
  360. ctx.font = this.environment.config.timeline.timelineDateFont;
  361. ctx.textBaseline = 'top';
  362. ctx.fillText(dateToString(this.bgplay.get("cur_instant").get("timestamp")), 480, 0);
  363. */
  364. },
  365.  
  366. /**
  367. * This method draws the Control Timeline
  368. * @method drawControlChart
  369. */
  370. drawControlChart:function(){
  371. this.unit2pixel=this.environment.config.timeline.controlChartRulerUnitWidth; //1 unit on the ruler takes N pixels
  372. this.cursorColor=this.environment.config.timeline.controlChartCursorsColor;
  373. var fontColor="#000000";
  374. var peakColor="#FFFFFF";//for now
  375. var rulerColor="#000000";
  376. var fontStyle="bold 11px Arial";
  377. var lineColor="blue";
  378. var lineWidth=0.6;
  379. var rulerNotchWidth=0.8;
  380. var timeOffset=this.globalTimeOffset;
  381. this.globalCursorTimeOffset=this.controlChartWidth/this.globalTimeOffset;
  382. var unit2time=timeOffset/(this.controlChartWidth/this.unit2pixel); //Number of seconds represented by a unit on the ruler
  383. var numberOfIntervals=Math.ceil(timeOffset/unit2time); //Number of intervals needed to represent the timeOffset
  384. var canvas = this.controlCanvasDom[0];
  385. var ctx = canvas.getContext('2d');
  386. this.contextControlCanvas = ctx;
  387. var rulerUnitHeight=this.environment.config.timeline.controlChartRulerUnitHeight;
  388. var infoCanvasHeight=13;
  389. var peak=0;
  390. var eventHeight;
  391. var graph2YZero=this.environment.config.timeline.controlChartHeight-rulerUnitHeight-2; //The 0 in the Y axis of the graph2
  392.  
  393. //draw the ruler
  394. var rulerPosition=this.controlChartHeight-rulerUnitHeight;
  395. var amount=getAmountOfTime(timeOffset);
  396. var amountStr="";
  397. if (amount.days>0)amountStr=amountStr+amount.days+" days ";
  398. if (amount.hours>0)amountStr=amountStr+amount.hours+" hours ";
  399. if (amount.minutes>0)amountStr=amountStr+amount.minutes+" minutes ";
  400. amountStr=amountStr+amount.seconds+" seconds";
  401. //Unit: ~"+Math.round(unit2time)+" seconds.
  402. //this.writeTimelineInfo("From "+dateToString(this.bgplay.get("starttimestamp"))+" to "+dateToString(this.bgplay.get("endtimestamp"))+" ("+amountStr+") ["+(this.allEvents.size()-1)+" events]",fontColor,fontStyle); //-1 because the initial state is an event
  403. this.writeTimelineInfo("Period: "+amountStr+" ["+(this.allEvents.size()-1)+" events]",fontColor,fontStyle); //-1 because the initial state is an event
  404. ctx.fillStyle = "black";
  405. var ntime2pixel;
  406. var npixel2time;
  407. var numEvent;
  408. var maxEventHeight=this.controlChartHeight+infoCanvasHeight-rulerUnitHeight;
  409.  
  410. //Draw the first ruler's notch
  411. ctx.fillRect(0,rulerPosition,rulerNotchWidth,rulerUnitHeight);
  412.  
  413. for(n=0;n<numberOfIntervals;n++){
  414. ntime2pixel=n*this.unit2pixel;
  415. npixel2time=n*unit2time;
  416. //In the same for we can calculate the number event peak
  417. numEvent=this.getNumberOfEventsBetween(
  418. this.bgplay.get("starttimestamp")+npixel2time,
  419. this.bgplay.get("starttimestamp")+npixel2time+unit2time
  420. );
  421. if (numEvent>peak){
  422. peak=numEvent;
  423. }
  424.  
  425. //Draw the ruler units
  426. ctx.fillStyle=rulerColor;
  427. ctx.fillRect(ntime2pixel,rulerPosition,rulerNotchWidth,rulerUnitHeight);
  428. }
  429.  
  430. eventHeight=maxEventHeight/peak;
  431. ctx.beginPath();
  432. ctx.moveTo(0, graph2YZero);
  433. for(var n=0;n<numberOfIntervals;n++){
  434. ntime2pixel=n*this.unit2pixel;
  435. npixel2time=n*unit2time;
  436. var pointX=ntime2pixel;
  437. var numOfEvents=this.getNumberOfEventsBetween(
  438. this.bgplay.get("starttimestamp")+npixel2time,
  439. this.bgplay.get("starttimestamp")+npixel2time+unit2time
  440. );
  441. var pointY=Math.abs((eventHeight*numOfEvents)-(this.controlChartHeight-rulerUnitHeight-2));
  442. ctx.lineTo(pointX, pointY);
  443.  
  444. /*
  445. //Draw a background for the peak
  446. if (numOfEvents==peak){
  447. ctx.fillStyle = peakColor;
  448. ctx.fillRect(Math.abs(n-1)*this.unit2pixel+1,infoCanvasHeight,(this.unit2pixel*2)-1,this.controlChartHeight-infoCanvasHeight);
  449. ctx.fillStyle=rulerColor;
  450. ctx.fillRect(ntime2pixel,rulerPosition,rulerNotchWidth,rulerUnitHeight);
  451. }
  452. */
  453. }
  454. ctx.lineWidth = lineWidth;
  455. ctx.strokeStyle = lineColor;
  456. ctx.stroke();
  457. this.controlCanvasCache=ctx.getImageData(0,0, canvas.width,canvas.height);
  458. },
  459.  
  460. /**
  461. * This method draws the Selection Timeline
  462. * @method drawSelectionChart
  463. */
  464. drawSelectionChart:function(nextFirstStep){
  465. var canvas,ctx,prevEvent;
  466. var fontStyle="bold 11px Arial";
  467. var second2pixel=this.environment.config.timeline.selectionChartSecondToPixels; //how many pixels for a second?
  468. var lineHeight=this.selectionChartHeight-14;
  469. var timeWarpWidth=this.environment.config.timeline.timeWarpWidth;
  470. var legendPositionX=100;
  471. var uniqueEventTypeLegend=new Array();
  472. var selectionChartHeight=this.selectionChartHeight;
  473. var nextEvent=nextFirstStep||this.allEvents.at(0);
  474. var position=0;
  475. var drawnEvents=this.environment.config.timeline.maxSelectionChartEvents;
  476. var sameTimestampEvent=new Array(); //An array of events with the same timestamp (1 second)
  477.  
  478. this.manageSelectionChartArrows(); //Draw arrows
  479.  
  480. this.eventOnSelectionChart.empty(); //Empty the TreeMap containing the events shown in the chart
  481.  
  482. this.selectionCanvasCache=null;
  483.  
  484. canvas = this.selectionCanvasDom[0];
  485. if (this.contextSelectionCanvas==null){
  486. ctx = canvas.getContext('2d');
  487. this.contextSelectionCanvas=ctx;
  488. }else{
  489. ctx=this.contextSelectionCanvas;
  490. ctx.save();
  491. ctx.setTransform(1, 0, 0, 1, 0, 0);
  492. ctx.clearRect(0, 0, this.selectionChartWidth, this.selectionChartHeight);
  493. ctx.restore();
  494. }
  495. this.selectionCanvasDom.css("left","0"); //Reset canvas position
  496.  
  497. while (nextEvent!=null && drawnEvents>0){
  498.  
  499. if (sameTimestampEvent[0]!=null && sameTimestampEvent[0].get("instant").get("timestamp")!=nextEvent.get("instant").get("timestamp")){ //If the next event isn't in the same second
  500. position=drawSameTimestampEvents(this,position); //Draw this set of events with the same timestamp
  501. drawWarp(position,timeWarpWidth,this,nextEvent.get("instant").get("timestamp")-sameTimestampEvent[0].get("instant").get("timestamp")); //Draw the temporal warp (arrows)
  502. position+=timeWarpWidth; //The position of the next event
  503. sameTimestampEvent=new Array(); //Empty this set
  504. drawnEvents--;
  505. }
  506.  
  507. legendPositionX=drawLegend(nextEvent.get("subType"),second2pixel,legendPositionX,this); // Draw the legend if there is a new type of event
  508. sameTimestampEvent.push(nextEvent);
  509.  
  510. prevEvent=nextEvent;//If the next iteration fails then this is the last treated event
  511. nextEvent=this.nextEvent(nextEvent.get("instant"),true);
  512.  
  513. if (position+second2pixel+timeWarpWidth>this.selectionChartWidth){ //Enlarge the canvas or the next event could not fit into it
  514. updateCanvasWidth(canvas,this,second2pixel+timeWarpWidth);
  515. }
  516. }
  517.  
  518. if (drawnEvents>0)
  519. position=drawSameTimestampEvents(this,position);
  520.  
  521. if (nextEvent==null){//prevEvent is the last one event of the whole analyzed period
  522. var finalInterval=this.bgplay.get("endtimestamp")-prevEvent.get("instant").get("timestamp");
  523. if (finalInterval>0){
  524. drawWarp(position,timeWarpWidth,this,finalInterval);
  525. }
  526. }
  527.  
  528. this.drawIntervalOnSelectionCanvas();
  529.  
  530. ctx.fillStyle="black";
  531. ctx.fillRect(0,selectionChartHeight-1,this.selectionChartWidth,1);
  532. ctx.fillRect(1,5,second2pixel,1);
  533. ctx.fillRect(1,3,1,4);
  534. ctx.fillRect(second2pixel,3,1,4);
  535. ctx.font = fontStyle;
  536. ctx.textBaseline = 'top';
  537. ctx.fillText("1 sec",second2pixel+5, 0);
  538.  
  539. function updateCanvasWidth(canvas,env,width){
  540. env.selectionCanvasCache=ctx.getImageData(0,0,env.selectionChartWidth,env.selectionChartHeight);
  541. env.selectionChartWidth+=width;
  542. canvas.width=env.selectionChartWidth;
  543. ctx.save();
  544. ctx.setTransform(1, 0, 0, 1, 0, 0);
  545. ctx.clearRect(0, 0, env.selectionChartWidth, env.selectionChartHeight);
  546. ctx.restore();
  547. ctx.putImageData(env.selectionCanvasCache, 0, 0);
  548. env.selectionCanvasCache=null;
  549. }
  550.  
  551. function drawLegend(eventType,second2pixel,position,env){
  552. if(uniqueEventTypeLegend.contains(eventType)){
  553. return position;
  554. }
  555. uniqueEventTypeLegend.push(eventType);
  556. ctx=env.contextSelectionCanvas;
  557. ctx.beginPath();
  558. ctx.rect(position,1,10,10);
  559. ctx.fillStyle = env.getEventColor(eventType);
  560. ctx.fill();
  561. ctx.lineWidth = 0.5;
  562. ctx.strokeStyle = 'black';
  563. ctx.stroke();
  564. position += 13;
  565. ctx.fillStyle="black";
  566. ctx.font = fontStyle;
  567. ctx.textBaseline = 'top';
  568. ctx.fillText(env.getEventVerboseType(eventType),position, 0);
  569. return position+90;
  570. }
  571.  
  572. function drawSameTimestampEvents(env,position){
  573. var internalWidth=((second2pixel-(sameTimestampEvent.length+1))/sameTimestampEvent.length); //pixels for each event in this second - 1px of margin between each event
  574.  
  575. //Draw a group of events
  576. for(var internalPosition=0;internalPosition<sameTimestampEvent.length;internalPosition++){
  577. var eventTmp=sameTimestampEvent[internalPosition];
  578. var eventPositionXTmp=(internalWidth*internalPosition)+position+internalPosition+1;
  579. ctx.fillStyle=env.getEventColor(eventTmp.get("subType"));
  580. eventTmp.drawEventOnselectionCanvasX=eventPositionXTmp;
  581. eventTmp.drawEventOnselectionCanvasY=selectionChartHeight-lineHeight;
  582. eventTmp.drawEventOnselectionCanvasWidth=internalWidth;
  583. eventTmp.drawEventOnselectionCanvasHeight=lineHeight;
  584. env.eventOnSelectionChart.put(eventTmp.drawEventOnselectionCanvasX,eventTmp);
  585. ctx.fillRect(eventTmp.drawEventOnselectionCanvasX,eventTmp.drawEventOnselectionCanvasY,eventTmp.drawEventOnselectionCanvasWidth,eventTmp.drawEventOnselectionCanvasHeight);
  586. }
  587. position+=second2pixel;
  588. return position;
  589. }
  590.  
  591. function drawWarp(position,width,env,offset){ //width can be useful with a more complex warp representation
  592. var image=this.fileRoot+"view/html/img/warp.gif";
  593. var posY;
  594. var ctx=env.contextSelectionCanvas;
  595.  
  596. if (env.warpImageCache==null){
  597. var img = new Image();
  598. img.onload = function() {
  599. env.warpImageCache=img;
  600. draw(position,env,width,offset);
  601. };
  602. img.src = image;
  603. }else{
  604. return draw(position,env,width,offset);
  605. }
  606.  
  607. function draw(position,env,width,offset){
  608. ctx.fillStyle = "#000000";
  609. ctx.font = "bold 10px Arial";
  610. ctx.textBaseline = 'top';
  611. var textPositionY=lineHeight/5;
  612. var amount=getAmountOfTime(offset);
  613. var halfHeightWarpImage=env.warpImageCache.height/2;
  614. posY=Math.round(env.selectionChartHeight/3)-halfHeightWarpImage;
  615.  
  616. position+=2;//margin
  617. //ctx.drawImage(env.warpImageCache,position,posY);
  618. ctx.drawImage(env.warpImageCache,position,posY*2);
  619. //ctx.drawImage(env.warpImageCache,position,posY*3);
  620.  
  621. position+=13; //17
  622. if (amount.days>0) ctx.fillText(amount.days+" d", position, textPositionY*2);
  623. if (amount.hours>0) ctx.fillText(amount.hours+" h", position, textPositionY*3);
  624. if (amount.minutes>0) ctx.fillText(amount.minutes+" m", position, textPositionY*4);
  625. if (amount.seconds>0) ctx.fillText(amount.seconds+" s", position, textPositionY*5);
  626.  
  627. position+=24;//32
  628. //ctx.drawImage(env.warpImageCache,position,posY);
  629. ctx.drawImage(env.warpImageCache,position,posY*2);
  630. //ctx.drawImage(env.warpImageCache,position,posY*3);
  631. }
  632. }
  633. },
  634.  
  635. /**
  636. * This method draws the next page of the Selection Timeline
  637. * @method nextSelectionChart
  638. */
  639. nextSelectionChart:function(event){
  640. event.preventDefault();
  641. this.selectionChartPage++;
  642. this.drawSelectionChart(this.selectionChartPages()[this.selectionChartPage]); //Go to the next page
  643. this.updateSelectionCanvas();
  644. },
  645.  
  646. /**
  647. * This method draws the previous page of the Selection Timeline
  648. * @method prevSelectionChart
  649. */
  650. prevSelectionChart:function(event){
  651. event.preventDefault();
  652. this.selectionChartPage--;
  653. this.drawSelectionChart(this.selectionChartPages()[this.selectionChartPage]); //Go to the previous page
  654. this.updateSelectionCanvas();
  655. },
  656.  
  657. /**
  658. * This method creates an array of events each of which is the first of the related page.
  659. * @method selectionChartPages
  660. * @return {Array} Array of events
  661. */
  662. selectionChartPages:function(){
  663. var numberOfPages, n, nop;
  664. var differentTimestampEvents=[];
  665. if (!this.selectionChartPagesList){
  666. this.selectionChartPagesList=[];
  667. nop=this.environment.config.timeline.maxSelectionChartEvents;
  668.  
  669. this.allEvents.forEachKey(function(key){
  670. if (!differentTimestampEvents.contains(key.getTimestamp()))
  671. differentTimestampEvents.push(key.getTimestamp());
  672. });
  673.  
  674. numberOfPages=Math.ceil(differentTimestampEvents.length/nop);
  675. for (n=0;n<numberOfPages;n++){ //This calculates all pages
  676. this.selectionChartPagesList[n]=this.allEvents.nearest(new Instant({id:0,timestamp:differentTimestampEvents[n*nop]}),true);
  677. }
  678. }
  679. return this.selectionChartPagesList;
  680. },
  681.  
  682. /**
  683. * This method manages the arrows useful to change the page of the Selection Timeline.
  684. * @method manageSelectionChartArrows
  685. */
  686. manageSelectionChartArrows:function(){
  687. if (this.selectionChartPage>0){
  688. this.timelineSelectionPrev.show();
  689. }else{
  690. this.timelineSelectionPrev.hide();
  691. }
  692.  
  693. if (this.selectionChartPage<this.selectionChartPages().length-1){
  694. this.timelineSelectionNext.show();
  695. }else{
  696. this.timelineSelectionNext.hide();
  697. }
  698. },
  699.  
  700.  
  701. /**
  702. * This method updates the representation of the Control Timeline
  703. * @method updateControlCanvas
  704. */
  705. updateControlCanvas:function(){
  706. var positionX,ctx;
  707. if (this)
  708. var $this=this; //else $this must be declared by the calling function (do not separate declaration from initialization)
  709.  
  710. var timeOffsetPosition=(this.bgplay.get("cur_instant").get("timestamp")-this.bgplay.get("starttimestamp"));
  711. var timestampOffsetPosition=timeOffsetPosition+this.bgplay.get("starttimestamp");
  712.  
  713. if (this.environment.config.timeline.disableNotSelectedInstants==false || timestampOffsetPosition>=$this.selectionStart.get("timestamp") && timestampOffsetPosition<=$this.selectionEnd.get("timestamp")){
  714. ctx=$this.contextControlCanvas;
  715. ctx.putImageData($this.controlCanvasCache, 0, 0);
  716. positionX=($this.globalCursorTimeOffset*timeOffsetPosition)-(this.cursorsWidth/2);
  717.  
  718. //Draw the margins for the selected interval
  719. ctx.fillStyle=$this.selectedIntervalCursorColor;
  720. ctx.fillRect(this.sliderLeftPosition,0,this.cursorsWidth,this.selectionChartHeight);
  721. ctx.fillRect(this.sliderRightPosition,0,this.cursorsWidth,this.selectionChartHeight);
  722.  
  723. if (this.environment.config.timeline.darkenDisabledTimeline){
  724. ctx.globalAlpha=0.3;
  725. ctx.fillStyle="black";
  726. ctx.fillRect(0,0,this.sliderLeftPosition,this.selectionChartHeight);
  727. ctx.fillRect(this.sliderRightPosition,0,this.controlChartWidth,this.selectionChartHeight);
  728. ctx.globalAlpha=1;
  729. }
  730.  
  731. //Draw the time cursor
  732. ctx.fillStyle=$this.cursorColor;
  733. ctx.fillRect(positionX,0,$this.cursorsWidth,$this.controlChartHeight);
  734.  
  735. $this.writeTimelineDate($this);
  736. }
  737. },
  738.  
  739. /**
  740. * This method updates the representation of the Selection Timeline
  741. * @method updateSelectionCanvas
  742. */
  743. updateSelectionCanvas:function(){
  744. var selectedEventColor=this.environment.config.bgpEventSelectedColor;
  745. var ctx=this.contextSelectionCanvas;
  746. if (this.selectionCanvasCache==null){
  747. this.selectionCanvasCache=ctx.getImageData(0,0, this.selectionChartWidth,this.selectionChartHeight);
  748. }else{
  749. ctx.putImageData(this.selectionCanvasCache, 0, 0);
  750. }
  751.  
  752. var curInstant=this.bgplay.get("cur_instant");
  753. var eventTmp=this.allEvents.get(curInstant);
  754. var cursorPosition;
  755.  
  756. this.drawIntervalOnSelectionCanvas();
  757.  
  758. if (eventTmp!=null && this.eventOnSelectionChart.containsValue(eventTmp)){ //The cursor is on an event
  759. ctx.fillStyle=selectedEventColor;
  760.  
  761. //uncomment the line below to emphasize the selected event
  762. //ctx.fillRect(eventTmp.drawEventOnselectionCanvasX,eventTmp.drawEventOnselectionCanvasY,eventTmp.drawEventOnselectionCanvasWidth,eventTmp.drawEventOnselectionCanvasHeight);
  763.  
  764. ctx.fillStyle=this.cursorColor;
  765. cursorPosition=eventTmp.drawEventOnselectionCanvasX + (eventTmp.drawEventOnselectionCanvasWidth/2) - this.halfCursorWidth; //Position of the event + half of the width of the event (px) - half cursor size
  766. ctx.fillRect(cursorPosition,0,this.cursorsWidth,this.selectionChartHeight);
  767.  
  768. }else{//The cursor is on a warp
  769. eventTmp=this.allEvents.nearest(curInstant,false,true);
  770. if (this.eventOnSelectionChart.containsValue(eventTmp)){
  771. cursorPosition=eventTmp.drawEventOnselectionCanvasX + this.halfWarpWidth + this.environment.config.timeline.selectionChartSecondToPixels; //Position of the prev Event + the half of the width of the warp
  772. }
  773. ctx.fillStyle=this.cursorColor;
  774. ctx.fillRect(cursorPosition,0,this.cursorsWidth,this.selectionChartHeight);
  775. }
  776. },
  777.  
  778. /**
  779. * This method returns the page of the given event.
  780. * @method calculateSelectionChartPage
  781. * @param {Object} An instance of Event
  782. * @return {Integer} The number of the current page
  783. */
  784. calculateSelectionChartPage:function(event){
  785. var n;
  786. for (n=0;n<this.selectionChartPages().length-1;n++){
  787. if (this.selectionChartPages()[n+1].get("instant").get("timestamp")>event.get("instant").get("timestamp"))
  788. break;
  789. }
  790. return n;
  791. },
  792.  
  793. /**
  794. * This method auto-scrolls the Selection Canvas when the selected instant is represented close to a margin.
  795. * @method scrollSelectionCanvas
  796. * @param {Object} An instance of Instant
  797. */
  798. scrollSelectionCanvas:function(instant) {
  799. var event, curPage, container, sumVisiblePosition, subVisiblePosition, offsetOfVisibility, element, realElementLeft, eventPosition, absRealElementLeft, instantVisiblePosition;
  800.  
  801.  
  802. event = this.allEvents.nearest(instant, false);
  803. if (event == null)
  804. return null;
  805.  
  806. curPage = this.calculateSelectionChartPage(event); //Calculate the number of the page of this event
  807.  
  808. if (curPage != this.selectionChartPage) {
  809. this.selectionChartPage = curPage;
  810. this.drawSelectionChart(this.selectionChartPages()[this.selectionChartPage]); //Go to the right page
  811. }
  812.  
  813. element = this.selectionCanvasDom;
  814.  
  815. container = element.parent();
  816. offsetOfVisibility = (container.width() / 100) * this.offsetOfVisibilityOnSelectionTimeline;
  817.  
  818. realElementLeft = element.position().left;
  819.  
  820. eventPosition = event.drawEventOnselectionCanvasX;
  821.  
  822. absRealElementLeft = Math.abs(realElementLeft);
  823. instantVisiblePosition = eventPosition - absRealElementLeft;
  824. sumVisiblePosition = instantVisiblePosition + offsetOfVisibility;
  825. subVisiblePosition = instantVisiblePosition - offsetOfVisibility;
  826.  
  827. if (sumVisiblePosition <= container.width() &&
  828. subVisiblePosition >= 0)
  829. return null; //Is already visible
  830.  
  831. var newLeft = 0;
  832. if (subVisiblePosition < 0) {
  833. newLeft = (absRealElementLeft - eventPosition) + realElementLeft + offsetOfVisibility;
  834. } else if (sumVisiblePosition > container.width()) {
  835. newLeft = realElementLeft - (sumVisiblePosition - container.width());
  836. }
  837.  
  838. //Check position
  839. if (newLeft > 0)
  840. newLeft = 0;
  841.  
  842. if (newLeft + this.selectionChartWidth < container.width())
  843. newLeft = container.width() - this.selectionChartWidth;
  844.  
  845. element.animate(
  846. {left:newLeft},
  847. 500,
  848. function () {
  849. //end
  850. }
  851. );
  852. },
  853.  
  854. /**
  855. * This method is triggered when a user changes the selection on the Control Timeline.
  856. * @method confirmSlidersSelection
  857. */
  858. confirmSlidersSelection:function($this){
  859. if (this.animation)
  860. return;
  861. var confirmAfter=0; //Seconds between a request and an update in order to prevent flood
  862.  
  863. this.timelineSelectionWaitIco.show();
  864. this.selectionCanvasDom.hide();
  865.  
  866. if ($this.selectionAntifloodTimer!=null){
  867. clearTimeout(this.selectionAntifloodTimer);
  868. }
  869.  
  870. (function() {
  871. function updateSelectionCanvasAntiFlood(){
  872. var pixel2time=$this.globalTimeOffset/$this.controlChartWidth;
  873. var newTimestampLeft=Math.round($this.sliderLeftPosition*pixel2time) + $this.bgplay.get("starttimestamp");
  874. var newTimestampRight=Math.round($this.sliderRightPosition*pixel2time) + $this.bgplay.get("starttimestamp");
  875. $this.selectionStart=new Instant({id:0,timestamp:newTimestampLeft});
  876. $this.selectionEnd=new Instant({id:0,timestamp:newTimestampRight});
  877.  
  878. var numEvents=$this.getNumberOfEventsBetween(newTimestampLeft,newTimestampRight);
  879. $this.writeTimelineInfo("From "+ dateToString(newTimestampLeft) + " to " + dateToString(newTimestampRight)+" ["+numEvents+" events]");
  880.  
  881. $this.eventAggregator.trigger("newSelectionStart",$this.selectionStart);
  882. $this.eventAggregator.trigger("newSelectionEnd",$this.selectionEnd);
  883. $this.eventAggregator.trigger('releasePlayButton',true);
  884. $this.bgplay.setCurInstant($this.selectionStart);
  885.  
  886. $this.timelineSelectionWaitIco.hide();
  887. $this.selectionCanvasDom.show();
  888. }
  889. $this.selectionAntifloodTimer=setTimeout(updateSelectionCanvasAntiFlood, confirmAfter*1000);
  890. })();
  891.  
  892. },
  893.  
  894. /**
  895. * This method provides a color for each type of event.
  896. * @method getEventColor
  897. * @param {String} The type of an event
  898. */
  899. getEventColor:function(eventType){
  900. var color;
  901. var mainType=this.bgplay.get("type");
  902. switch (mainType) {
  903. case "traceroute": //another source type
  904. break;
  905. default: //bgp
  906. switch (eventType) {
  907. case "withdrawal":
  908. color=this.environment.config.bgpEventWithdrawalColor;
  909. break;
  910. case "announce":
  911. color=this.environment.config.bgpEventAnnounceColor;
  912. break;
  913. case "reannounce":
  914. color=this.environment.config.bgpEventReannunceColor;
  915. break;
  916. case "pathchange":
  917. color=this.environment.config.bgpEventPathchangeColor;
  918. break;
  919. case "prepending":
  920. color=this.environment.config.bgpEventPrependingColor;
  921. break;
  922. case "initialstate":
  923. color=this.environment.config.bgpEventInitialstateColor;
  924. break;
  925. default:
  926. color="black";
  927. }
  928. }
  929. return color;
  930. },
  931.  
  932. /**
  933. * This method provides a description for each type of event.
  934. * @method getEventVerboseType
  935. * @param {String} The type of an event
  936. */
  937. getEventVerboseType:function(eventType){
  938. var text;
  939. var mainType=this.bgplay.get("type");
  940. switch (mainType) {
  941. case "traceroute": //another source type
  942. break;
  943. default: //bgp
  944. switch (eventType) {
  945. case "withdrawal":
  946. text="Withdrawal";
  947. break;
  948. case "announce":
  949. text="Announce";
  950. break;
  951. case "pathchange":
  952. text="Path Change";
  953. break;
  954. case "reannounce":
  955. text="Re-announce";
  956. break;
  957. case "prepending":
  958. text="Prepending";
  959. break;
  960. case "initialstate":
  961. text="Initial state";
  962. break;
  963. default:
  964. text="event";
  965. }
  966. }
  967. return text;
  968. },
  969.  
  970. /**
  971. * This method is triggered when a user clicks on the Control Timeline in order to change the current instant
  972. * @method updateCursorPosition
  973. */
  974. updateCursorPosition:function(event){
  975. event.preventDefault();
  976. var instant,newTimestamp,offsetX;
  977. this.stopAnimation();
  978. this.eventAggregator.trigger("animationEnd");
  979. if (!this.stopTriggerEvents){
  980. event=addOffset(event, null, true);
  981. offsetX=event.offsetX;
  982. if (this.environment.config.timeline.disableNotSelectedInstants==false || this.sliderLeftPosition<offsetX && offsetX<this.sliderRightPosition){
  983. newTimestamp=Math.round(offsetX*(this.globalTimeOffset/this.controlChartWidth))+this.bgplay.get("starttimestamp");
  984. instant=new Instant({id:0,timestamp:newTimestamp});
  985. this.bgplay.setCurInstant(instant, false);
  986. }
  987. }
  988. },
  989.  
  990. /**
  991. * This method is triggered when a user clicks on the Selection Timeline in order to change the current event
  992. * @method updateSelectedEvent
  993. */
  994. updateSelectedEvent:function(event){
  995. event.preventDefault();
  996. if (!this.stopTriggerEvents){
  997. event=addOffset(event, null, true);
  998. var offsetX=event.offsetX;
  999. var tmpEvent=this.eventOnSelectionChart.nearest(offsetX,false,true);
  1000.  
  1001. if (this.environment.config.timeline.disableNotSelectedInstants==false ||
  1002. (tmpEvent.get("instant").get("timestamp")>=this.selectionStart.get("timestamp") &&
  1003. tmpEvent.get("instant").get("timestamp")<=this.selectionEnd.get("timestamp"))){ //If the selected event is in the selected interval
  1004.  
  1005. if (tmpEvent!=null && offsetX<tmpEvent.drawEventOnselectionCanvasX+tmpEvent.drawEventOnselectionCanvasWidth){
  1006. this.stopAnimation();
  1007. this.eventAggregator.trigger("animationEnd");
  1008. this.bgplay.setCurInstant(tmpEvent.get("instant"));
  1009. }
  1010. }
  1011. }
  1012. },
  1013.  
  1014. /**
  1015. * This method stops a seek process
  1016. * @method stopAnimation
  1017. */
  1018. stopAnimation:function(){
  1019. var ntimer, length;
  1020. for (ntimer = 0, length = this.seekTimers.length; ntimer<length; ntimer++){
  1021. clearTimeout(this.seekTimers[ntimer]);
  1022. }
  1023. this.seekTimers=[];
  1024. },
  1025.  
  1026. /**
  1027. * This method checks and prevents inconsistent selection of the Control Timeline.
  1028. * @method checkSliderSelection
  1029. */
  1030. checkSliderSelection:function(event,env){
  1031. if (this.animation==true)
  1032. return false;
  1033.  
  1034. this.eventAggregator.trigger('releasePlayButton',false);
  1035. var sliderLeft=this.timelineControlCanvasSliderLeft;
  1036. var sliderRight=this.timelineControlCanvasSliderRight;
  1037. var xLeft= sliderLeft.position().left;
  1038. var xRight= sliderRight.position().left;
  1039.  
  1040. var sliderLeftWidth=sliderLeft.width();
  1041. var halfSliderLeft=sliderLeftWidth/2;
  1042. var halfSliderRight=sliderRight.width()/2;
  1043. var element=$(event.target);
  1044.  
  1045. if (xLeft<-halfSliderLeft){
  1046. element.css('left',-halfSliderLeft+'px');
  1047. return false;//Drag option of jquery-ui expects null or false.
  1048. }
  1049. if (xRight+halfSliderRight>env.controlChartWidth){
  1050. element.css('left',env.controlChartWidth-halfSliderRight+'px');
  1051. return false;//Drag option of jquery-ui expects null or false.
  1052. }
  1053. if (xLeft+sliderLeftWidth>xRight){
  1054. if (element.position().left==xLeft){//The target is the left slider
  1055. element.css('left',xRight-sliderLeftWidth+'px');
  1056. }else{
  1057. element.css('left',xLeft+sliderLeftWidth+'px')
  1058. }
  1059. return false;
  1060. }
  1061.  
  1062. this.sliderLeftPosition = xLeft + halfSliderLeft;
  1063. this.sliderRightPosition = xRight + halfSliderRight;
  1064.  
  1065. if (this.environment.config.timeline.showSelectionInformation){
  1066. var pixel2time=this.globalTimeOffset/this.controlChartWidth;
  1067. var newTimestampLeft=Math.round(this.sliderLeftPosition*pixel2time)+this.bgplay.get("starttimestamp");
  1068. var newTimestampRight=Math.round(this.sliderRightPosition*pixel2time)+this.bgplay.get("starttimestamp");
  1069.  
  1070. this.writeTimelineInfo("From "+ dateToString(newTimestampLeft) + " to " + dateToString(newTimestampRight));
  1071. }
  1072.  
  1073.  
  1074. },
  1075.  
  1076. /**
  1077. * This method draws on the Selection Timeline an interval selected on the Control Timeline
  1078. * @method drawIntervalOnSelectionCanvas
  1079. */
  1080. drawIntervalOnSelectionCanvas:function(){
  1081. var start, stop, imageLeft, imageRight;
  1082. start=this.allEvents.nearest(this.selectionStart,true, true);
  1083. stop=this.allEvents.nearest(this.selectionEnd,false, true);
  1084. imageLeft=this.fileRoot+"view/html/img/leftSlider.png";
  1085. imageRight=this.fileRoot+"view/html/img/rightSlider.png";
  1086. var $this=this;
  1087.  
  1088. if ($this.selectorLeftImageCache==null){
  1089. var imgLeft = new Image();
  1090. imgLeft.onload = function() {
  1091. $this.selectorLeftImageCache=imgLeft;
  1092. var imgRight = new Image();
  1093. imgRight.onload = function() {
  1094. $this.selectorRightImageCache=imgRight;
  1095. draw(start,stop);
  1096. $this.eventAggregator.trigger("moduleLoading", false);
  1097. };
  1098. imgRight.src = imageRight;
  1099. };
  1100. imgLeft.src = imageLeft;
  1101.  
  1102.  
  1103. }else{
  1104. draw(start,stop);
  1105. }
  1106.  
  1107. function draw(start,stop){
  1108. var positionXL,positionXR,warpWidth,halfWarpWidth,sliderWidth,halfSliderWidth,ctx, darkenDisabledParts, darkened;
  1109.  
  1110. darkenDisabledParts=$this.environment.config.timeline.darkenDisabledTimeline;
  1111. warpWidth=$this.environment.config.timeline.timeWarpWidth;
  1112. halfWarpWidth=warpWidth/2;
  1113. sliderWidth=$this.selectorLeftImageCache.naturalWidth;
  1114. halfSliderWidth=sliderWidth/2;
  1115. ctx=$this.contextSelectionCanvas;
  1116. darkened=false;
  1117.  
  1118. if (start!=null && $this.selectionStart!=$this.selectionFirstStart && start.drawEventOnselectionCanvasX!=null && $this.eventOnSelectionChart.containsValue(start)){
  1119.  
  1120. positionXL=start.drawEventOnselectionCanvasX-halfWarpWidth-halfSliderWidth; //Position of the first event included - the half of the warp - the half of the slider image
  1121.  
  1122. //Draw the margins for the selected interval
  1123. ctx.fillStyle=$this.selectedIntervalCursorColor;
  1124. ctx.fillRect(positionXL+halfSliderWidth,0,$this.cursorsWidth,$this.selectionChartHeight);
  1125.  
  1126. if (darkenDisabledParts){
  1127. darkened=true;
  1128. ctx.globalAlpha=0.3;
  1129. ctx.fillStyle="black";
  1130. ctx.fillRect(0,0,positionXL+halfSliderWidth,$this.selectionChartHeight);
  1131. ctx.globalAlpha=1;
  1132. }
  1133.  
  1134. ctx.drawImage($this.selectorLeftImageCache,positionXL,0);
  1135. }
  1136.  
  1137. if (stop!=null && $this.selectionEnd!=$this.selectionFirstEnd && stop.drawEventOnselectionCanvasX!=null && $this.eventOnSelectionChart.containsValue(stop)){
  1138.  
  1139. positionXR=stop.drawEventOnselectionCanvasX + $this.environment.config.timeline.selectionChartSecondToPixels+halfWarpWidth-halfSliderWidth; //Position of the first event included + width of the event + the half of the warp - the half of the slider image
  1140.  
  1141. if (positionXR==positionXL){ //There are no events in the selected interval
  1142. positionXR+=sliderWidth; //Without this line the two sliders will be overlapped
  1143. }
  1144.  
  1145. //Draw the margins for the selected interval
  1146. ctx.fillStyle=$this.selectedIntervalCursorColor;
  1147. ctx.fillRect(positionXR+halfSliderWidth,0,$this.cursorsWidth,$this.selectionChartHeight);
  1148.  
  1149. if (darkenDisabledParts){
  1150. darkened=true;
  1151. ctx.globalAlpha=0.3;
  1152. ctx.fillStyle="black";
  1153. ctx.fillRect(positionXR+halfSliderWidth,0,$this.selectionChartWidth,$this.selectionChartHeight);
  1154. ctx.globalAlpha=1;
  1155. }
  1156.  
  1157. ctx.drawImage($this.selectorRightImageCache,positionXR,0);
  1158. }
  1159.  
  1160. if (darkenDisabledParts && !darkened){
  1161. if ($this.allEvents.compare($this.eventOnSelectionChart.last().get("instant"),start.get("instant"))==-1){
  1162. ctx.globalAlpha=0.3;
  1163. ctx.fillStyle="black";
  1164. ctx.fillRect(0,0,$this.selectionChartWidth,$this.selectionChartHeight);
  1165. ctx.globalAlpha=1;
  1166. }else{
  1167. if ($this.allEvents.compare(stop.get("instant"),$this.eventOnSelectionChart.first().get("instant"))==-1){
  1168. ctx.globalAlpha=0.3;
  1169. ctx.fillStyle="black";
  1170. ctx.fillRect(0,0,$this.selectionChartWidth,$this.selectionChartHeight);
  1171. ctx.globalAlpha=1;
  1172. }
  1173. }
  1174. }
  1175.  
  1176. $this.miniArrowsManager($this);
  1177. }
  1178. },
  1179.  
  1180. /**
  1181. * This method manages the mini arrows.
  1182. * The mini arrows are the two green arrows that appear when the timeline sliders (selectors) disappear from the visible selection timeline.
  1183. * @method miniArrowsManager
  1184. */
  1185. miniArrowsManager:function($this){
  1186. var $this=$this||this;
  1187.  
  1188. var arrowLeftElement,arrowRightElement,container,leftArrow,rightArrow,canvasElementLeft, leftIsVisible,rightIsVisible;
  1189.  
  1190. arrowLeftElement = this.timelineSelectionSliderLeft;
  1191. arrowRightElement = this.timelineSelectionSliderRight;
  1192.  
  1193. container=this.timelineSelectionDiv;
  1194.  
  1195. leftArrow=$this.allEvents.nearest($this.selectionStart,true);
  1196. rightArrow=$this.allEvents.nearest($this.selectionEnd,false);
  1197. canvasElementLeft=$this.selectionCanvasDom.position().left;
  1198.  
  1199. leftIsVisible=false;
  1200. rightIsVisible=false;
  1201. arrowRightElement.hide();
  1202. arrowLeftElement.hide();
  1203.  
  1204. if (leftArrow!=null && rightArrow!=null){
  1205.  
  1206. if ($this.eventOnSelectionChart.containsValue(leftArrow)){ //It is in the current drawn timeline
  1207. if (leftArrow.drawEventOnselectionCanvasX+canvasElementLeft<0){
  1208. leftIsVisible=true; //It is in the current visible part of the current drawn timeline
  1209. }
  1210. }else{ //It is out
  1211. if ($this.eventOnSelectionChart.first().get("instant").get("timestamp")>$this.selectionStart.get("timestamp")){ //Is not in the current visible part, but it is anyway on the left (previous selection timeline page)
  1212. leftIsVisible=true;
  1213. }else{ //It is not in the current visible part, but is on the right (next selection timeline page)
  1214. rightIsVisible=true;
  1215. }
  1216. }
  1217.  
  1218. if ($this.eventOnSelectionChart.containsValue(rightArrow)){ //Is in the current drawn timeline
  1219. if (rightArrow.drawEventOnselectionCanvasX+canvasElementLeft>container.width()){
  1220. rightIsVisible=true; //It is in the current visible part of the current drawn timeline
  1221. }
  1222. }else{ //It is out
  1223. if ($this.eventOnSelectionChart.last().get("instant").get("timestamp")<$this.selectionEnd.get("timestamp")){ //It is not in the current visible part, but it is anyway on the right (next selection timeline page)
  1224. rightIsVisible=true;
  1225. }else{ //It is not in the current visible part, but is on the left (prev selection timeline page)
  1226. leftIsVisible=true;
  1227. }
  1228. }
  1229. }
  1230.  
  1231. if (leftIsVisible)
  1232. arrowLeftElement.show();
  1233.  
  1234. if (rightIsVisible)
  1235. arrowRightElement.show();
  1236.  
  1237. }
  1238. });