PageRenderTime 39ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/timeplot/scripts/plot.js

http://showslow.googlecode.com/
JavaScript | 400 lines | 327 code | 36 blank | 37 comment | 64 complexity | 61c2cc859af8d9bd186d0740aadb971f MD5 | raw file
  1. /**
  2. * Plot Layer
  3. *
  4. * @fileOverview Plot Layer
  5. * @name Plot
  6. */
  7. /**
  8. * A plot layer is the main building block for timeplots and it's the object
  9. * that is responsible for painting the plot itself. Each plot needs to have
  10. * a time geometry, either a DataSource (for time series
  11. * plots) or an EventSource (for event plots) and a value geometry in case
  12. * of time series plots. Such parameters are passed along
  13. * in the 'plotInfo' map.
  14. *
  15. * @constructor
  16. */
  17. Timeplot.Plot = function(timeplot, plotInfo) {
  18. this._timeplot = timeplot;
  19. this._canvas = timeplot.getCanvas();
  20. this._plotInfo = plotInfo;
  21. this._id = plotInfo.id;
  22. this._timeGeometry = plotInfo.timeGeometry;
  23. this._valueGeometry = plotInfo.valueGeometry;
  24. this._theme = new Timeline.getDefaultTheme();
  25. this._dataSource = plotInfo.dataSource;
  26. this._eventSource = plotInfo.eventSource;
  27. this._bubble = null;
  28. };
  29. Timeplot.Plot.prototype = {
  30. /**
  31. * Initialize the plot layer
  32. */
  33. initialize: function() {
  34. if (this._dataSource && this._dataSource.getValue) {
  35. this._timeFlag = this._timeplot.putDiv("timeflag","timeplot-timeflag");
  36. this._valueFlag = this._timeplot.putDiv(this._id + "valueflag","timeplot-valueflag");
  37. this._valueFlagLineLeft = this._timeplot.putDiv(this._id + "valueflagLineLeft","timeplot-valueflag-line");
  38. this._valueFlagLineRight = this._timeplot.putDiv(this._id + "valueflagLineRight","timeplot-valueflag-line");
  39. if (!this._valueFlagLineLeft.firstChild) {
  40. this._valueFlagLineLeft.appendChild(SimileAjax.Graphics.createTranslucentImage(Timeplot.urlPrefix + "images/line_left.png"));
  41. this._valueFlagLineRight.appendChild(SimileAjax.Graphics.createTranslucentImage(Timeplot.urlPrefix + "images/line_right.png"));
  42. }
  43. this._valueFlagPole = this._timeplot.putDiv(this._id + "valuepole","timeplot-valueflag-pole");
  44. var opacity = this._plotInfo.valuesOpacity;
  45. SimileAjax.Graphics.setOpacity(this._timeFlag, opacity);
  46. SimileAjax.Graphics.setOpacity(this._valueFlag, opacity);
  47. SimileAjax.Graphics.setOpacity(this._valueFlagLineLeft, opacity);
  48. SimileAjax.Graphics.setOpacity(this._valueFlagLineRight, opacity);
  49. SimileAjax.Graphics.setOpacity(this._valueFlagPole, opacity);
  50. var plot = this;
  51. var mouseOverHandler = function(elmt, evt, target) {
  52. if (plot._plotInfo.showValues) {
  53. plot._valueFlag.style.display = "block";
  54. mouseMoveHandler(elmt, evt, target);
  55. }
  56. }
  57. var day = 24 * 60 * 60 * 1000;
  58. var month = 30 * day;
  59. var mouseMoveHandler = function(elmt, evt, target) {
  60. if (typeof SimileAjax != "undefined" && plot._plotInfo.showValues) {
  61. var c = plot._canvas;
  62. var x = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt,plot._canvas).x);
  63. if (x > c.width) x = c.width;
  64. if (isNaN(x) || x < 0) x = 0;
  65. var t = plot._timeGeometry.fromScreen(x);
  66. if (t == 0 || plot._dataSource == null) { // something is wrong; IE is the only one that claims _dataSource is ever null, and only after items have changed
  67. plot._valueFlag.style.display = "none";
  68. return;
  69. }
  70. var validTime = plot._dataSource.getClosestValidTime(t);
  71. x = plot._timeGeometry.toScreen(validTime);
  72. var v = plot._dataSource.getValue(validTime);
  73. if (plot._plotInfo.roundValues) v = Math.round(v);
  74. plot._valueFlag.innerHTML = new String(v);
  75. var d = new Date(validTime);
  76. var p = plot._timeGeometry.getPeriod();
  77. if (p < day) {
  78. plot._timeFlag.innerHTML = d.toLocaleTimeString();
  79. } else if (p > month) {
  80. plot._timeFlag.innerHTML = d.toLocaleDateString();
  81. } else {
  82. plot._timeFlag.innerHTML = d.toLocaleString();
  83. }
  84. var tw = plot._timeFlag.clientWidth;
  85. var th = plot._timeFlag.clientHeight;
  86. var tdw = Math.round(tw / 2);
  87. var vw = plot._valueFlag.clientWidth;
  88. var vh = plot._valueFlag.clientHeight;
  89. var y = plot._valueGeometry.toScreen(v);
  90. if (x + tdw > c.width) {
  91. var tx = c.width - tdw;
  92. } else if (x - tdw < 0) {
  93. var tx = tdw;
  94. } else {
  95. var tx = x;
  96. }
  97. if (plot._timeGeometry._timeValuePosition == "top") {
  98. plot._timeplot.placeDiv(plot._valueFlagPole, {
  99. left: x,
  100. top: th - 5,
  101. height: c.height - y - th + 6,
  102. display: "block"
  103. });
  104. plot._timeplot.placeDiv(plot._timeFlag,{
  105. left: tx - tdw,
  106. top: -6,
  107. display: "block"
  108. });
  109. } else {
  110. plot._timeplot.placeDiv(plot._valueFlagPole, {
  111. left: x,
  112. bottom: th - 5,
  113. height: y - th + 6,
  114. display: "block"
  115. });
  116. plot._timeplot.placeDiv(plot._timeFlag,{
  117. left: tx - tdw,
  118. bottom: -6,
  119. display: "block"
  120. });
  121. }
  122. if (x + vw + 14 > c.width && y + vh + 4 > c.height) {
  123. plot._valueFlagLineLeft.style.display = "none";
  124. plot._timeplot.placeDiv(plot._valueFlagLineRight,{
  125. left: x - 14,
  126. bottom: y - 14,
  127. display: "block"
  128. });
  129. plot._timeplot.placeDiv(plot._valueFlag,{
  130. left: x - vw - 13,
  131. bottom: y - vh - 13,
  132. display: "block"
  133. });
  134. } else if (x + vw + 14 > c.width && y + vh + 4 < c.height) {
  135. plot._valueFlagLineRight.style.display = "none";
  136. plot._timeplot.placeDiv(plot._valueFlagLineLeft,{
  137. left: x - 14,
  138. bottom: y,
  139. display: "block"
  140. });
  141. plot._timeplot.placeDiv(plot._valueFlag,{
  142. left: x - vw - 13,
  143. bottom: y + 13,
  144. display: "block"
  145. });
  146. } else if (x + vw + 14 < c.width && y + vh + 4 > c.height) {
  147. plot._valueFlagLineRight.style.display = "none";
  148. plot._timeplot.placeDiv(plot._valueFlagLineLeft,{
  149. left: x,
  150. bottom: y - 13,
  151. display: "block"
  152. });
  153. plot._timeplot.placeDiv(plot._valueFlag,{
  154. left: x + 13,
  155. bottom: y - 13,
  156. display: "block"
  157. });
  158. } else {
  159. plot._valueFlagLineLeft.style.display = "none";
  160. plot._timeplot.placeDiv(plot._valueFlagLineRight,{
  161. left: x,
  162. bottom: y,
  163. display: "block"
  164. });
  165. plot._timeplot.placeDiv(plot._valueFlag,{
  166. left: x + 13,
  167. bottom: y + 13,
  168. display: "block"
  169. });
  170. }
  171. }
  172. }
  173. var timeplotElement = this._timeplot.getElement();
  174. SimileAjax.DOM.registerEvent(timeplotElement, "mouseover", mouseOverHandler);
  175. SimileAjax.DOM.registerEvent(timeplotElement, "mousemove", mouseMoveHandler);
  176. }
  177. },
  178. /**
  179. * Dispose the plot layer and all the data sources and listeners associated to it
  180. */
  181. dispose: function() {
  182. if (this._dataSource) {
  183. this._dataSource.removeListener(this._paintingListener);
  184. this._paintingListener = null;
  185. this._dataSource.dispose();
  186. this._dataSource = null;
  187. }
  188. },
  189. /**
  190. * Hide the values
  191. */
  192. hideValues: function() {
  193. if (this._valueFlag) this._valueFlag.style.display = "none";
  194. if (this._timeFlag) this._timeFlag.style.display = "none";
  195. if (this._valueFlagLineLeft) this._valueFlagLineLeft.style.display = "none";
  196. if (this._valueFlagLineRight) this._valueFlagLineRight.style.display = "none";
  197. if (this._valueFlagPole) this._valueFlagPole.style.display = "none";
  198. },
  199. /**
  200. * Return the data source of this plot layer (it could be either a DataSource or an EventSource)
  201. */
  202. getDataSource: function() {
  203. return (this._dataSource) ? this._dataSource : this._eventSource;
  204. },
  205. /**
  206. * Return the time geometry associated with this plot layer
  207. */
  208. getTimeGeometry: function() {
  209. return this._timeGeometry;
  210. },
  211. /**
  212. * Return the value geometry associated with this plot layer
  213. */
  214. getValueGeometry: function() {
  215. return this._valueGeometry;
  216. },
  217. /**
  218. * Paint this plot layer
  219. */
  220. paint: function() {
  221. var ctx = this._canvas.getContext('2d');
  222. ctx.lineWidth = this._plotInfo.lineWidth;
  223. ctx.lineJoin = 'miter';
  224. if (this._dataSource) {
  225. if (this._plotInfo.fillColor) {
  226. if (this._plotInfo.fillGradient) {
  227. var gradient = ctx.createLinearGradient(0,this._canvas.height,0,0);
  228. gradient.addColorStop(0,this._plotInfo.fillColor.toString());
  229. gradient.addColorStop(0.5,this._plotInfo.fillColor.toString());
  230. gradient.addColorStop(1, 'rgba(255,255,255,0)');
  231. ctx.fillStyle = gradient;
  232. } else {
  233. ctx.fillStyle = this._plotInfo.fillColor.toString();
  234. }
  235. ctx.beginPath();
  236. ctx.moveTo(0,0);
  237. this._plot(function(x,y) {
  238. ctx.lineTo(x,y);
  239. });
  240. if (this._plotInfo.fillFrom == Number.NEGATIVE_INFINITY) {
  241. ctx.lineTo(this._canvas.width, 0);
  242. } else if (this._plotInfo.fillFrom == Number.POSITIVE_INFINITY) {
  243. ctx.lineTo(this._canvas.width, this._canvas.height);
  244. ctx.lineTo(0, this._canvas.height);
  245. } else {
  246. ctx.lineTo(this._canvas.width, this._valueGeometry.toScreen(this._plotInfo.fillFrom));
  247. ctx.lineTo(0, this._valueGeometry.toScreen(this._plotInfo.fillFrom));
  248. }
  249. ctx.fill();
  250. }
  251. if (this._plotInfo.lineColor) {
  252. ctx.strokeStyle = this._plotInfo.lineColor.toString();
  253. ctx.beginPath();
  254. var first = true;
  255. this._plot(function(x,y) {
  256. if (first) {
  257. first = false;
  258. ctx.moveTo(x,y);
  259. }
  260. ctx.lineTo(x,y);
  261. });
  262. ctx.stroke();
  263. }
  264. if (this._plotInfo.dotColor) {
  265. ctx.fillStyle = this._plotInfo.dotColor.toString();
  266. var r = this._plotInfo.dotRadius;
  267. this._plot(function(x,y) {
  268. ctx.beginPath();
  269. ctx.arc(x,y,r,0,2*Math.PI,true);
  270. ctx.fill();
  271. });
  272. }
  273. }
  274. if (this._eventSource) {
  275. var gradient = ctx.createLinearGradient(0,0,0,this._canvas.height);
  276. gradient.addColorStop(1, 'rgba(255,255,255,0)');
  277. ctx.strokeStyle = gradient;
  278. ctx.fillStyle = gradient;
  279. ctx.lineWidth = this._plotInfo.eventLineWidth;
  280. ctx.lineJoin = 'miter';
  281. var i = this._eventSource.getAllEventIterator();
  282. while (i.hasNext()) {
  283. var event = i.next();
  284. var color = event.getColor();
  285. color = (color) ? new Timeplot.Color(color) : this._plotInfo.lineColor;
  286. var eventStart = event.getStart().getTime();
  287. var eventEnd = event.getEnd().getTime();
  288. if (eventStart == eventEnd) {
  289. var c = color.toString();
  290. gradient.addColorStop(0, c);
  291. var start = this._timeGeometry.toScreen(eventStart);
  292. start = Math.floor(start) + 0.5; // center it between two pixels (makes the rendering nicer)
  293. var end = start;
  294. ctx.beginPath();
  295. ctx.moveTo(start,0);
  296. ctx.lineTo(start,this._canvas.height);
  297. ctx.stroke();
  298. var x = start - 4;
  299. var w = 7;
  300. } else {
  301. var c = color.toString(0.5);
  302. gradient.addColorStop(0, c);
  303. var start = this._timeGeometry.toScreen(eventStart);
  304. start = Math.floor(start) + 0.5; // center it between two pixels (makes the rendering nicer)
  305. var end = this._timeGeometry.toScreen(eventEnd);
  306. end = Math.floor(end) + 0.5; // center it between two pixels (makes the rendering nicer)
  307. ctx.fillRect(start,0,end - start, this._canvas.height);
  308. var x = start;
  309. var w = end - start - 1;
  310. }
  311. var div = this._timeplot.putDiv(event.getID(),"timeplot-event-box",{
  312. left: Math.round(x),
  313. width: Math.round(w),
  314. top: 0,
  315. height: this._canvas.height - 1
  316. });
  317. var plot = this;
  318. var clickHandler = function(event) {
  319. return function(elmt, evt, target) {
  320. var doc = plot._timeplot.getDocument();
  321. plot._closeBubble();
  322. var coords = SimileAjax.DOM.getEventPageCoordinates(evt);
  323. var elmtCoords = SimileAjax.DOM.getPageCoordinates(elmt);
  324. plot._bubble = SimileAjax.Graphics.createBubbleForPoint(coords.x, elmtCoords.top + plot._canvas.height, plot._plotInfo.bubbleWidth, plot._plotInfo.bubbleHeight, "bottom");
  325. event.fillInfoBubble(plot._bubble.content, plot._theme, plot._timeGeometry.getLabeler());
  326. }
  327. };
  328. var mouseOverHandler = function(elmt, evt, target) {
  329. elmt.oldClass = elmt.className;
  330. elmt.className = elmt.className + " timeplot-event-box-highlight";
  331. };
  332. var mouseOutHandler = function(elmt, evt, target) {
  333. elmt.className = elmt.oldClass;
  334. elmt.oldClass = null;
  335. }
  336. if (!div.instrumented) {
  337. SimileAjax.DOM.registerEvent(div, "click" , clickHandler(event));
  338. SimileAjax.DOM.registerEvent(div, "mouseover", mouseOverHandler);
  339. SimileAjax.DOM.registerEvent(div, "mouseout" , mouseOutHandler);
  340. div.instrumented = true;
  341. }
  342. }
  343. }
  344. },
  345. _plot: function(f) {
  346. var data = this._dataSource.getData();
  347. if (data) {
  348. var times = data.times;
  349. var values = data.values;
  350. var T = times.length;
  351. for (var t = 0; t < T; t++) {
  352. var x = this._timeGeometry.toScreen(times[t]);
  353. var y = this._valueGeometry.toScreen(values[t]);
  354. f(x, y);
  355. }
  356. }
  357. },
  358. _closeBubble: function() {
  359. if (this._bubble != null) {
  360. this._bubble.close();
  361. this._bubble = null;
  362. }
  363. }
  364. }