TimelineRulerView.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. 'use strict';
  2. /**
  3. * Licensed Materials - Property of IBM
  4. * IBM Cognos Products: Storytelling
  5. * (C) Copyright IBM Corp. 2014, 2018
  6. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  7. */
  8. define(['baglass/core-client/js/core-client/ui/core/View', 'text!./templates/TimelineRulerView.html', 'jquery'], function (View, Template, $) {
  9. var MINOR_TICK_COLOUR = '#cccccc';
  10. var MAJOR_TICK_COLOUR = '#c0c0c0';
  11. var DEFAULT_FILL_COLOUR = '#888888';
  12. var HIGH_CONTRAST_COLOUR = '#AFAFAF';
  13. var TimelineRulerView = View.extend({
  14. templateString: Template,
  15. lastOffset: 0,
  16. init: function init(options) {
  17. TimelineRulerView.inherited('init', this, arguments);
  18. // Use a different colour that passes contrast ratio standard when in high contrast mode
  19. if ($('body').hasClass('highcontrast')) {
  20. this.majorTickColour = HIGH_CONTRAST_COLOUR;
  21. this.minorTickColour = HIGH_CONTRAST_COLOUR;
  22. this.fillColour = HIGH_CONTRAST_COLOUR;
  23. } else {
  24. this.majorTickColour = MAJOR_TICK_COLOUR;
  25. this.minorTickColour = MINOR_TICK_COLOUR;
  26. this.fillColour = DEFAULT_FILL_COLOUR;
  27. }
  28. this.controller = options.controller;
  29. this.scaleManager = options.scaleManager;
  30. this._useDiv = options.useDiv === true;
  31. },
  32. remove: function remove() {
  33. $(window).off('resize.privateViewEvents' + this.viewId);
  34. if (this.$ruler) {
  35. this.$ruler.off('click');
  36. this.$ruler = null;
  37. }
  38. TimelineRulerView.inherited('remove', this, arguments);
  39. },
  40. /**
  41. * Renders the timeline.
  42. *
  43. * @returns
  44. */
  45. render: function render() {
  46. var sHtml = this.dotTemplate({
  47. canvas: !this._useDiv
  48. });
  49. this.$el.html(sHtml);
  50. // this.$ruler will be a canvas element if _useDiv is false otherwise it is a div element
  51. this.$ruler = this.$el.find('.ruler');
  52. this.$ruler.on('click', this.onRulerClick.bind(this));
  53. $(window).off('resize.privateViewEvents' + this.viewId).on('resize.privateViewEvents' + this.viewId, this.onResize.bind(this));
  54. this._updateCanvasWidth();
  55. },
  56. _updateCanvasWidth: function _updateCanvasWidth() {
  57. var width = this.$el.outerWidth(false);
  58. var height = this.$el.outerHeight(false);
  59. if (!this._useDiv) {
  60. this.$ruler[0].width = width;
  61. this.$ruler[0].height = height;
  62. this.context = this.$ruler[0].getContext('2d');
  63. this.context.fillStyle = this.fillColour;
  64. this.context.font = '100 14px HelvNeueforIBM,“Helvetica Neue”,Helvetica,Arial,sans-serif';
  65. } else {
  66. this.$ruler.css('width', '100%');
  67. this.$ruler.css('height', height);
  68. }
  69. },
  70. drawTicks: function drawTicks() {
  71. if (!this._useDiv) {
  72. // Clear the canvas.
  73. this.context.clearRect(0, 0, this.$ruler[0].width, this.$ruler[0].height);
  74. var tickWidth = this.scaleInfo.tickWidth;
  75. var startCount = 0;
  76. var offsetX = 0;
  77. if (this.lastOffset > 0) {
  78. var ratio = this.lastOffset / tickWidth;
  79. startCount = Math.floor(ratio);
  80. offsetX = (startCount - ratio) * tickWidth;
  81. }
  82. var options = {
  83. offsetCount: startCount,
  84. offsetX: offsetX,
  85. depth: this.scaleInfo.depth
  86. };
  87. this._drawTicksRecursively(options, this.scaleInfo);
  88. }
  89. },
  90. setOffsetLeft: function setOffsetLeft(offset) {
  91. if (offset !== this.lastOffset) {
  92. this.lastOffset = offset;
  93. this.refresh();
  94. }
  95. },
  96. refresh: function refresh() {
  97. this.scaleInfo = this._calculateScaleInfo();
  98. this._updateCanvasWidth();
  99. this.drawTicks();
  100. },
  101. /*
  102. * View events.
  103. */
  104. onResize: function onResize() {
  105. if (this.scaleInfo && this.$el.is(':visible')) {
  106. // We only resize if the view is visible.
  107. this._updateCanvasWidth();
  108. this.drawTicks();
  109. }
  110. },
  111. onRulerClick: function onRulerClick(event) {
  112. var time = this._getTimeForPosition(event.pageX);
  113. var playing = this.controller.isPlaying();
  114. if (playing) {
  115. this.controller.pause();
  116. }
  117. this.controller.setCurrentTime(time);
  118. if (playing) {
  119. this.controller.play();
  120. }
  121. },
  122. /*
  123. * Helpers
  124. */
  125. _drawTicksRecursively: function _drawTicksRecursively(options, info, currentDepth) {
  126. var context = this.context;
  127. currentDepth = currentDepth || 0;
  128. if (currentDepth === 0) {
  129. this._drawMajorTicks(options, info);
  130. } else {
  131. this._drawMinorTicks(options, info, currentDepth);
  132. }
  133. if (info.minorTicks) {
  134. // Start recursively drawing minor ticks.
  135. this._drawTicksRecursively(options, info.minorTicks, currentDepth + 1);
  136. } else if (currentDepth >= options.depth - 1) {
  137. // Base case in the recursive function. Draw the path for all the depths >= 1 (minor ticks).
  138. context.stroke();
  139. }
  140. },
  141. _drawMajorTicks: function _drawMajorTicks(options, info) {
  142. var count = options.offsetCount;
  143. var width = this.$ruler[0].width;
  144. var height = this.$ruler[0].height;
  145. var context = this.context;
  146. context.strokeStyle = this.majorTickColour;
  147. context.beginPath();
  148. var i, y, timeLabel;
  149. var prevTimeLabel = '';
  150. var tickIncrement = info.tickWidth;
  151. for (i = options.offsetX; i < width; i += tickIncrement) {
  152. timeLabel = this.controller.getTimeLabel(count * info.tickDuration);
  153. // To get the major ticks to display more and look better, tickDuration can be adjusted to a smaller number based on scale factors
  154. // When tickDuration becomes smaller, the timeLabel can be returned the same with tickIncrements
  155. // Check and don't draw major ticks and label twice
  156. if (timeLabel && timeLabel !== prevTimeLabel) {
  157. prevTimeLabel = timeLabel;
  158. y = 15;
  159. context.fillText(timeLabel, i + 3, y + 11);
  160. if (i > 0) {
  161. // Don't draw a line at the start.
  162. context.moveTo(i, y);
  163. context.lineTo(i, height);
  164. }
  165. }
  166. count++;
  167. }
  168. context.stroke();
  169. },
  170. _drawMinorTicks: function _drawMinorTicks(options, info, currentDepth) {
  171. var width = this.$ruler[0].width;
  172. var height = this.$ruler[0].height;
  173. var context = this.context;
  174. if (currentDepth === 1) {
  175. // Minor ticks are at depth >= 1, set different styles.
  176. context.strokeStyle = this.minorTickColour;
  177. context.beginPath();
  178. }
  179. var i, y;
  180. var tickIncrement = info.tickWidth * 2;
  181. for (i = options.offsetX + tickIncrement; i < width; i += tickIncrement) {
  182. if (i > 0) {
  183. y = 40;
  184. // Don't draw a line at the start.
  185. context.moveTo(i, y);
  186. context.lineTo(i, height);
  187. }
  188. }
  189. },
  190. _getScaleInfo: function _getScaleInfo(scale) {
  191. var tickDuration;
  192. var minScale = this.scaleManager.getMinScale();
  193. var maxScale = this.scaleManager.getMaxScale();
  194. var factor = (scale - minScale) / (maxScale - minScale);
  195. if (factor >= 1) {
  196. tickDuration = 500;
  197. } else if (factor >= 0.5) {
  198. tickDuration = 1000;
  199. } else if (factor >= 0.25) {
  200. tickDuration = 2000;
  201. } else if (factor >= 0.125) {
  202. tickDuration = 2500;
  203. } else if (factor >= 0.0625) {
  204. tickDuration = 5000;
  205. } else if (factor >= 0.02) {
  206. tickDuration = 10000;
  207. } else if (factor >= 0.01) {
  208. tickDuration = 20000;
  209. } else if (factor > 0) {
  210. tickDuration = 50000;
  211. } else {
  212. tickDuration = 100000;
  213. }
  214. var tickWidth = this.scaleManager.convertTimeToPosition(tickDuration);
  215. var info = {
  216. tickWidth: tickWidth,
  217. tickDuration: tickDuration,
  218. depth: 1
  219. };
  220. var minorTicks = this._getScaleInfoHelper(info, scale, tickWidth / 2);
  221. if (minorTicks) {
  222. info.minorTicks = minorTicks;
  223. }
  224. return info;
  225. },
  226. _getScaleInfoHelper: function _getScaleInfoHelper(context, scale, tickWidth) {
  227. var info = null;
  228. if (tickWidth > 10) {
  229. info = {
  230. tickWidth: tickWidth
  231. };
  232. context.depth++;
  233. // Change the factor to determine how many minor ticks drawn within major ticks
  234. // The tickWidth check will have to be adjusted as well to accommodate the drawing
  235. // E.g. if the factor is changed to 10, then the condition tickWidth > 5 will have to be done
  236. // in that case, the minor ticks can be 10 or 20 for 1 sec depends on the resolution of the device
  237. var nestedInfo = this._getScaleInfoHelper(context, scale, tickWidth / 5);
  238. if (nestedInfo) {
  239. info.minorTicks = nestedInfo;
  240. }
  241. }
  242. return info;
  243. },
  244. _calculateScaleInfo: function _calculateScaleInfo() {
  245. var scale = this.scaleManager.getScale();
  246. return this._getScaleInfo(scale);
  247. },
  248. _getTimeForPosition: function _getTimeForPosition(position) {
  249. var info = this.scaleInfo,
  250. tickWidth = info.tickWidth,
  251. tickDuration = info.tickDuration;
  252. return (position - this.$el.offset().left + this.lastOffset) / tickWidth * tickDuration;
  253. }
  254. });
  255. return TimelineRulerView;
  256. });
  257. //# sourceMappingURL=TimelineRulerView.js.map