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