/timeplot/scripts/plot.js
JavaScript | 400 lines | 327 code | 36 blank | 37 comment | 64 complexity | 61c2cc859af8d9bd186d0740aadb971f MD5 | raw file
- /**
- * Plot Layer
- *
- * @fileOverview Plot Layer
- * @name Plot
- */
-
- /**
- * A plot layer is the main building block for timeplots and it's the object
- * that is responsible for painting the plot itself. Each plot needs to have
- * a time geometry, either a DataSource (for time series
- * plots) or an EventSource (for event plots) and a value geometry in case
- * of time series plots. Such parameters are passed along
- * in the 'plotInfo' map.
- *
- * @constructor
- */
- Timeplot.Plot = function(timeplot, plotInfo) {
- this._timeplot = timeplot;
- this._canvas = timeplot.getCanvas();
- this._plotInfo = plotInfo;
- this._id = plotInfo.id;
- this._timeGeometry = plotInfo.timeGeometry;
- this._valueGeometry = plotInfo.valueGeometry;
- this._theme = new Timeline.getDefaultTheme();
- this._dataSource = plotInfo.dataSource;
- this._eventSource = plotInfo.eventSource;
- this._bubble = null;
- };
- Timeplot.Plot.prototype = {
-
- /**
- * Initialize the plot layer
- */
- initialize: function() {
- if (this._dataSource && this._dataSource.getValue) {
- this._timeFlag = this._timeplot.putDiv("timeflag","timeplot-timeflag");
- this._valueFlag = this._timeplot.putDiv(this._id + "valueflag","timeplot-valueflag");
- this._valueFlagLineLeft = this._timeplot.putDiv(this._id + "valueflagLineLeft","timeplot-valueflag-line");
- this._valueFlagLineRight = this._timeplot.putDiv(this._id + "valueflagLineRight","timeplot-valueflag-line");
- if (!this._valueFlagLineLeft.firstChild) {
- this._valueFlagLineLeft.appendChild(SimileAjax.Graphics.createTranslucentImage(Timeplot.urlPrefix + "images/line_left.png"));
- this._valueFlagLineRight.appendChild(SimileAjax.Graphics.createTranslucentImage(Timeplot.urlPrefix + "images/line_right.png"));
- }
- this._valueFlagPole = this._timeplot.putDiv(this._id + "valuepole","timeplot-valueflag-pole");
- var opacity = this._plotInfo.valuesOpacity;
-
- SimileAjax.Graphics.setOpacity(this._timeFlag, opacity);
- SimileAjax.Graphics.setOpacity(this._valueFlag, opacity);
- SimileAjax.Graphics.setOpacity(this._valueFlagLineLeft, opacity);
- SimileAjax.Graphics.setOpacity(this._valueFlagLineRight, opacity);
- SimileAjax.Graphics.setOpacity(this._valueFlagPole, opacity);
- var plot = this;
-
- var mouseOverHandler = function(elmt, evt, target) {
- if (plot._plotInfo.showValues) {
- plot._valueFlag.style.display = "block";
- mouseMoveHandler(elmt, evt, target);
- }
- }
-
- var day = 24 * 60 * 60 * 1000;
- var month = 30 * day;
-
- var mouseMoveHandler = function(elmt, evt, target) {
- if (typeof SimileAjax != "undefined" && plot._plotInfo.showValues) {
- var c = plot._canvas;
- var x = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt,plot._canvas).x);
- if (x > c.width) x = c.width;
- if (isNaN(x) || x < 0) x = 0;
- var t = plot._timeGeometry.fromScreen(x);
- 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
- plot._valueFlag.style.display = "none";
- return;
- }
-
- var validTime = plot._dataSource.getClosestValidTime(t);
- x = plot._timeGeometry.toScreen(validTime);
- var v = plot._dataSource.getValue(validTime);
- if (plot._plotInfo.roundValues) v = Math.round(v);
- plot._valueFlag.innerHTML = new String(v);
- var d = new Date(validTime);
- var p = plot._timeGeometry.getPeriod();
- if (p < day) {
- plot._timeFlag.innerHTML = d.toLocaleTimeString();
- } else if (p > month) {
- plot._timeFlag.innerHTML = d.toLocaleDateString();
- } else {
- plot._timeFlag.innerHTML = d.toLocaleString();
- }
- var tw = plot._timeFlag.clientWidth;
- var th = plot._timeFlag.clientHeight;
- var tdw = Math.round(tw / 2);
- var vw = plot._valueFlag.clientWidth;
- var vh = plot._valueFlag.clientHeight;
- var y = plot._valueGeometry.toScreen(v);
- if (x + tdw > c.width) {
- var tx = c.width - tdw;
- } else if (x - tdw < 0) {
- var tx = tdw;
- } else {
- var tx = x;
- }
- if (plot._timeGeometry._timeValuePosition == "top") {
- plot._timeplot.placeDiv(plot._valueFlagPole, {
- left: x,
- top: th - 5,
- height: c.height - y - th + 6,
- display: "block"
- });
- plot._timeplot.placeDiv(plot._timeFlag,{
- left: tx - tdw,
- top: -6,
- display: "block"
- });
- } else {
- plot._timeplot.placeDiv(plot._valueFlagPole, {
- left: x,
- bottom: th - 5,
- height: y - th + 6,
- display: "block"
- });
- plot._timeplot.placeDiv(plot._timeFlag,{
- left: tx - tdw,
- bottom: -6,
- display: "block"
- });
- }
- if (x + vw + 14 > c.width && y + vh + 4 > c.height) {
- plot._valueFlagLineLeft.style.display = "none";
- plot._timeplot.placeDiv(plot._valueFlagLineRight,{
- left: x - 14,
- bottom: y - 14,
- display: "block"
- });
- plot._timeplot.placeDiv(plot._valueFlag,{
- left: x - vw - 13,
- bottom: y - vh - 13,
- display: "block"
- });
- } else if (x + vw + 14 > c.width && y + vh + 4 < c.height) {
- plot._valueFlagLineRight.style.display = "none";
- plot._timeplot.placeDiv(plot._valueFlagLineLeft,{
- left: x - 14,
- bottom: y,
- display: "block"
- });
- plot._timeplot.placeDiv(plot._valueFlag,{
- left: x - vw - 13,
- bottom: y + 13,
- display: "block"
- });
- } else if (x + vw + 14 < c.width && y + vh + 4 > c.height) {
- plot._valueFlagLineRight.style.display = "none";
- plot._timeplot.placeDiv(plot._valueFlagLineLeft,{
- left: x,
- bottom: y - 13,
- display: "block"
- });
- plot._timeplot.placeDiv(plot._valueFlag,{
- left: x + 13,
- bottom: y - 13,
- display: "block"
- });
- } else {
- plot._valueFlagLineLeft.style.display = "none";
- plot._timeplot.placeDiv(plot._valueFlagLineRight,{
- left: x,
- bottom: y,
- display: "block"
- });
- plot._timeplot.placeDiv(plot._valueFlag,{
- left: x + 13,
- bottom: y + 13,
- display: "block"
- });
- }
- }
- }
- var timeplotElement = this._timeplot.getElement();
- SimileAjax.DOM.registerEvent(timeplotElement, "mouseover", mouseOverHandler);
- SimileAjax.DOM.registerEvent(timeplotElement, "mousemove", mouseMoveHandler);
- }
- },
- /**
- * Dispose the plot layer and all the data sources and listeners associated to it
- */
- dispose: function() {
- if (this._dataSource) {
- this._dataSource.removeListener(this._paintingListener);
- this._paintingListener = null;
- this._dataSource.dispose();
- this._dataSource = null;
- }
- },
- /**
- * Hide the values
- */
- hideValues: function() {
- if (this._valueFlag) this._valueFlag.style.display = "none";
- if (this._timeFlag) this._timeFlag.style.display = "none";
- if (this._valueFlagLineLeft) this._valueFlagLineLeft.style.display = "none";
- if (this._valueFlagLineRight) this._valueFlagLineRight.style.display = "none";
- if (this._valueFlagPole) this._valueFlagPole.style.display = "none";
- },
-
- /**
- * Return the data source of this plot layer (it could be either a DataSource or an EventSource)
- */
- getDataSource: function() {
- return (this._dataSource) ? this._dataSource : this._eventSource;
- },
- /**
- * Return the time geometry associated with this plot layer
- */
- getTimeGeometry: function() {
- return this._timeGeometry;
- },
- /**
- * Return the value geometry associated with this plot layer
- */
- getValueGeometry: function() {
- return this._valueGeometry;
- },
- /**
- * Paint this plot layer
- */
- paint: function() {
- var ctx = this._canvas.getContext('2d');
- ctx.lineWidth = this._plotInfo.lineWidth;
- ctx.lineJoin = 'miter';
- if (this._dataSource) {
- if (this._plotInfo.fillColor) {
- if (this._plotInfo.fillGradient) {
- var gradient = ctx.createLinearGradient(0,this._canvas.height,0,0);
- gradient.addColorStop(0,this._plotInfo.fillColor.toString());
- gradient.addColorStop(0.5,this._plotInfo.fillColor.toString());
- gradient.addColorStop(1, 'rgba(255,255,255,0)');
- ctx.fillStyle = gradient;
- } else {
- ctx.fillStyle = this._plotInfo.fillColor.toString();
- }
- ctx.beginPath();
- ctx.moveTo(0,0);
- this._plot(function(x,y) {
- ctx.lineTo(x,y);
- });
- if (this._plotInfo.fillFrom == Number.NEGATIVE_INFINITY) {
- ctx.lineTo(this._canvas.width, 0);
- } else if (this._plotInfo.fillFrom == Number.POSITIVE_INFINITY) {
- ctx.lineTo(this._canvas.width, this._canvas.height);
- ctx.lineTo(0, this._canvas.height);
- } else {
- ctx.lineTo(this._canvas.width, this._valueGeometry.toScreen(this._plotInfo.fillFrom));
- ctx.lineTo(0, this._valueGeometry.toScreen(this._plotInfo.fillFrom));
- }
- ctx.fill();
- }
-
- if (this._plotInfo.lineColor) {
- ctx.strokeStyle = this._plotInfo.lineColor.toString();
- ctx.beginPath();
- var first = true;
- this._plot(function(x,y) {
- if (first) {
- first = false;
- ctx.moveTo(x,y);
- }
- ctx.lineTo(x,y);
- });
- ctx.stroke();
- }
- if (this._plotInfo.dotColor) {
- ctx.fillStyle = this._plotInfo.dotColor.toString();
- var r = this._plotInfo.dotRadius;
- this._plot(function(x,y) {
- ctx.beginPath();
- ctx.arc(x,y,r,0,2*Math.PI,true);
- ctx.fill();
- });
- }
- }
- if (this._eventSource) {
- var gradient = ctx.createLinearGradient(0,0,0,this._canvas.height);
- gradient.addColorStop(1, 'rgba(255,255,255,0)');
- ctx.strokeStyle = gradient;
- ctx.fillStyle = gradient;
- ctx.lineWidth = this._plotInfo.eventLineWidth;
- ctx.lineJoin = 'miter';
-
- var i = this._eventSource.getAllEventIterator();
- while (i.hasNext()) {
- var event = i.next();
- var color = event.getColor();
- color = (color) ? new Timeplot.Color(color) : this._plotInfo.lineColor;
- var eventStart = event.getStart().getTime();
- var eventEnd = event.getEnd().getTime();
- if (eventStart == eventEnd) {
- var c = color.toString();
- gradient.addColorStop(0, c);
- var start = this._timeGeometry.toScreen(eventStart);
- start = Math.floor(start) + 0.5; // center it between two pixels (makes the rendering nicer)
- var end = start;
- ctx.beginPath();
- ctx.moveTo(start,0);
- ctx.lineTo(start,this._canvas.height);
- ctx.stroke();
- var x = start - 4;
- var w = 7;
- } else {
- var c = color.toString(0.5);
- gradient.addColorStop(0, c);
- var start = this._timeGeometry.toScreen(eventStart);
- start = Math.floor(start) + 0.5; // center it between two pixels (makes the rendering nicer)
- var end = this._timeGeometry.toScreen(eventEnd);
- end = Math.floor(end) + 0.5; // center it between two pixels (makes the rendering nicer)
- ctx.fillRect(start,0,end - start, this._canvas.height);
- var x = start;
- var w = end - start - 1;
- }
- var div = this._timeplot.putDiv(event.getID(),"timeplot-event-box",{
- left: Math.round(x),
- width: Math.round(w),
- top: 0,
- height: this._canvas.height - 1
- });
- var plot = this;
- var clickHandler = function(event) {
- return function(elmt, evt, target) {
- var doc = plot._timeplot.getDocument();
- plot._closeBubble();
- var coords = SimileAjax.DOM.getEventPageCoordinates(evt);
- var elmtCoords = SimileAjax.DOM.getPageCoordinates(elmt);
- plot._bubble = SimileAjax.Graphics.createBubbleForPoint(coords.x, elmtCoords.top + plot._canvas.height, plot._plotInfo.bubbleWidth, plot._plotInfo.bubbleHeight, "bottom");
- event.fillInfoBubble(plot._bubble.content, plot._theme, plot._timeGeometry.getLabeler());
- }
- };
- var mouseOverHandler = function(elmt, evt, target) {
- elmt.oldClass = elmt.className;
- elmt.className = elmt.className + " timeplot-event-box-highlight";
- };
- var mouseOutHandler = function(elmt, evt, target) {
- elmt.className = elmt.oldClass;
- elmt.oldClass = null;
- }
-
- if (!div.instrumented) {
- SimileAjax.DOM.registerEvent(div, "click" , clickHandler(event));
- SimileAjax.DOM.registerEvent(div, "mouseover", mouseOverHandler);
- SimileAjax.DOM.registerEvent(div, "mouseout" , mouseOutHandler);
- div.instrumented = true;
- }
- }
- }
- },
- _plot: function(f) {
- var data = this._dataSource.getData();
- if (data) {
- var times = data.times;
- var values = data.values;
- var T = times.length;
- for (var t = 0; t < T; t++) {
- var x = this._timeGeometry.toScreen(times[t]);
- var y = this._valueGeometry.toScreen(values[t]);
- f(x, y);
- }
- }
- },
-
- _closeBubble: function() {
- if (this._bubble != null) {
- this._bubble.close();
- this._bubble = null;
- }
- }
- }