Free cookie consent management tool by TermsFeed Policy Generator

source: branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/jqplot/src/jqplot.lineRenderer.js @ 13401

Last change on this file since 13401 was 9062, checked in by fschoepp, 12 years ago

#1888:
Backend changes:

  • Simplified job state detection (only one hive call will be made to detect all states now, instead of one additional call per job)
  • Reorganized classes (moved model classes into Model folder)

Website changes:

  • Website now heavily uses JavaScript to achieve better user experience
  • JavaScript degrades gracefully, except for plots
  • Tables: Added jquery-datatable-plugin to extend tables (pagination + search functionality)
  • OaaS-Website now uses the design of the HL websites (found in WebApplication branch)
  • Added jqplot to render zoomable line plots for HL-Datatables
  • Styling.js: Plots will be generated by using an ajax call; additional jquery-styling occurs within this file.
  • Added jquery-ui-1.9.2 which is capable of handling/rendering tabs, accordions and resizers.
File size: 23.2 KB
Line 
1/**
2 * jqPlot
3 * Pure JavaScript plotting plugin using jQuery
4 *
5 * Version: @VERSION
6 *
7 * Copyright (c) 2009-2011 Chris Leonello
8 * jqPlot is currently available for use in all personal or commercial projects
9 * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
10 * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
11 * choose the license that best suits your project and use it accordingly.
12 *
13 * Although not required, the author would appreciate an email letting him
14 * know of any substantial use of jqPlot.  You can reach the author at:
15 * chris at jqplot dot com or see http://www.jqplot.com/info.php .
16 *
17 * If you are feeling kind and generous, consider supporting the project by
18 * making a donation at: http://www.jqplot.com/donate.php .
19 *
20 * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
21 *
22 *     version 2007.04.27
23 *     author Ash Searle
24 *     http://hexmen.com/blog/2007/03/printf-sprintf/
25 *     http://hexmen.com/js/sprintf.js
26 *     The author (Ash Searle) has placed this code in the public domain:
27 *     "This code is unrestricted: you are free to use it however you like."
28 *
29 */
30(function($) {
31    // Class: $.jqplot.LineRenderer
32    // The default line renderer for jqPlot, this class has no options beyond the <Series> class.
33    // Draws series as a line.
34    $.jqplot.LineRenderer = function(){
35        this.shapeRenderer = new $.jqplot.ShapeRenderer();
36        this.shadowRenderer = new $.jqplot.ShadowRenderer();
37    };
38   
39    // called with scope of series.
40    $.jqplot.LineRenderer.prototype.init = function(options, plot) {
41        options = options || {};
42        this._type='line';
43        var lopts = {highlightMouseOver: options.highlightMouseOver, highlightMouseDown: options.highlightMouseDown, highlightColor: options.highlightColor};
44       
45        delete (options.highlightMouseOver);
46        delete (options.highlightMouseDown);
47        delete (options.highlightColor);
48       
49        $.extend(true, this.renderer, options);
50        // set the shape renderer options
51        var opts = {lineJoin:this.lineJoin, lineCap:this.lineCap, fill:this.fill, isarc:false, strokeStyle:this.color, fillStyle:this.fillColor, lineWidth:this.lineWidth, closePath:this.fill};
52        this.renderer.shapeRenderer.init(opts);
53        // set the shadow renderer options
54        // scale the shadowOffset to the width of the line.
55        if (this.lineWidth > 2.5) {
56            var shadow_offset = this.shadowOffset* (1 + (Math.atan((this.lineWidth/2.5))/0.785398163 - 1)*0.6);
57            // var shadow_offset = this.shadowOffset;
58        }
59        // for skinny lines, don't make such a big shadow.
60        else {
61            var shadow_offset = this.shadowOffset*Math.atan((this.lineWidth/2.5))/0.785398163;
62        }
63        var sopts = {lineJoin:this.lineJoin, lineCap:this.lineCap, fill:this.fill, isarc:false, angle:this.shadowAngle, offset:shadow_offset, alpha:this.shadowAlpha, depth:this.shadowDepth, lineWidth:this.lineWidth, closePath:this.fill};
64        this.renderer.shadowRenderer.init(sopts);
65        this._areaPoints = [];
66        this._boundingBox = [[],[]];
67       
68        if (!this.isTrendline && this.fill) {
69       
70            // prop: highlightMouseOver
71            // True to highlight area on a filled plot when moused over.
72            // This must be false to enable highlightMouseDown to highlight when clicking on an area on a filled plot.
73            this.highlightMouseOver = true;
74            // prop: highlightMouseDown
75            // True to highlight when a mouse button is pressed over an area on a filled plot.
76            // This will be disabled if highlightMouseOver is true.
77            this.highlightMouseDown = false;
78            // prop: highlightColor
79            // color to use when highlighting an area on a filled plot.
80            this.highlightColor = null;
81            // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
82            if (lopts.highlightMouseDown && lopts.highlightMouseOver == null) {
83                lopts.highlightMouseOver = false;
84            }
85       
86            $.extend(true, this, {highlightMouseOver: lopts.highlightMouseOver, highlightMouseDown: lopts.highlightMouseDown, highlightColor: lopts.highlightColor});
87           
88            if (!this.highlightColor) {
89                this.highlightColor = $.jqplot.computeHighlightColors(this.fillColor);
90            }
91            // turn off (disable) the highlighter plugin
92            if (this.highlighter) {
93                this.highlighter.show = false;
94            }
95        }
96       
97        if (!this.isTrendline && plot) {
98            plot.plugins.lineRenderer = {};
99            plot.postInitHooks.addOnce(postInit);
100            plot.postDrawHooks.addOnce(postPlotDraw);
101            plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
102            plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown);
103            plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp);
104            plot.eventListenerHooks.addOnce('jqplotClick', handleClick);
105            plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick);
106        }
107
108    };
109   
110    // Method: setGridData
111    // converts the user data values to grid coordinates and stores them
112    // in the gridData array.
113    // Called with scope of a series.
114    $.jqplot.LineRenderer.prototype.setGridData = function(plot) {
115        // recalculate the grid data
116        var xp = this._xaxis.series_u2p;
117        var yp = this._yaxis.series_u2p;
118        var data = this._plotData;
119        var pdata = this._prevPlotData;
120        this.gridData = [];
121        this._prevGridData = [];
122        for (var i=0; i<this.data.length; i++) {
123            // if not a line series or if no nulls in data, push the converted point onto the array.
124            if (data[i][0] != null && data[i][1] != null) {
125                this.gridData.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]);
126            }
127            // else if there is a null, preserve it.
128            else if (data[i][0] == null) {
129                this.gridData.push([null, yp.call(this._yaxis, data[i][1])]);
130            }
131            else if (data[i][1] == null) {
132                this.gridData.push([xp.call(this._xaxis, data[i][0]), null]);
133            }
134            // if not a line series or if no nulls in data, push the converted point onto the array.
135            if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] != null) {
136                this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), yp.call(this._yaxis, pdata[i][1])]);
137            }
138            // else if there is a null, preserve it.
139            else if (pdata[i] != null && pdata[i][0] == null) {
140                this._prevGridData.push([null, yp.call(this._yaxis, pdata[i][1])]);
141            } 
142            else if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] == null) {
143                this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), null]);
144            }
145        }
146    };
147   
148    // Method: makeGridData
149    // converts any arbitrary data values to grid coordinates and
150    // returns them.  This method exists so that plugins can use a series'
151    // linerenderer to generate grid data points without overwriting the
152    // grid data associated with that series.
153    // Called with scope of a series.
154    $.jqplot.LineRenderer.prototype.makeGridData = function(data, plot) {
155        // recalculate the grid data
156        var xp = this._xaxis.series_u2p;
157        var yp = this._yaxis.series_u2p;
158        var gd = [];
159        var pgd = [];
160        for (var i=0; i<data.length; i++) {
161            // if not a line series or if no nulls in data, push the converted point onto the array.
162            if (data[i][0] != null && data[i][1] != null) {
163                gd.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]);
164            }
165            // else if there is a null, preserve it.
166            else if (data[i][0] == null) {
167                gd.push([null, yp.call(this._yaxis, data[i][1])]);
168            }
169            else if (data[i][1] == null) {
170                gd.push([xp.call(this._xaxis, data[i][0]), null]);
171            }
172        }
173        return gd;
174    };
175   
176
177    // called within scope of series.
178    $.jqplot.LineRenderer.prototype.draw = function(ctx, gd, options) {
179        var i;
180        var opts = (options != undefined) ? options : {};
181        var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
182        var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
183        var fill = (opts.fill != undefined) ? opts.fill : this.fill;
184        var fillAndStroke = (opts.fillAndStroke != undefined) ? opts.fillAndStroke : this.fillAndStroke;
185        var xmin, ymin, xmax, ymax;
186        ctx.save();
187        if (gd.length) {
188            if (showLine) {
189                // if we fill, we'll have to add points to close the curve.
190                if (fill) {
191                    if (this.fillToZero) {
192                        // have to break line up into shapes at axis crossings
193                        var negativeColors = new $.jqplot.ColorGenerator(this.negativeSeriesColors);
194                        var negativeColor = negativeColors.get(this.index);
195                        if (! this.useNegativeColors) {
196                            negativeColor = opts.fillStyle;
197                        }
198                        var isnegative = false;
199                        var posfs = opts.fillStyle;
200                   
201                        // if stoking line as well as filling, get a copy of line data.
202                        if (fillAndStroke) {
203                            var fasgd = gd.slice(0);
204                        }
205                        // if not stacked, fill down to axis
206                        if (this.index == 0 || !this._stack) {
207                       
208                            var tempgd = [];
209                            this._areaPoints = [];
210                            var pyzero = this._yaxis.series_u2p(this.fillToValue);
211                            var pxzero = this._xaxis.series_u2p(this.fillToValue);
212                           
213                            if (this.fillAxis == 'y') {
214                                tempgd.push([gd[0][0], pyzero]);
215                                this._areaPoints.push([gd[0][0], pyzero]);
216                               
217                                for (var i=0; i<gd.length-1; i++) {
218                                    tempgd.push(gd[i]);
219                                    this._areaPoints.push(gd[i]);
220                                    // do we have an axis crossing?
221                                    if (this._plotData[i][1] * this._plotData[i+1][1] < 0) {
222                                        if (this._plotData[i][1] < 0) {
223                                            isnegative = true;
224                                            opts.fillStyle = negativeColor;
225                                        }
226                                        else {
227                                            isnegative = false;
228                                            opts.fillStyle = posfs;
229                                        }
230                                       
231                                        var xintercept = gd[i][0] + (gd[i+1][0] - gd[i][0]) * (pyzero-gd[i][1])/(gd[i+1][1] - gd[i][1]);
232                                        tempgd.push([xintercept, pyzero]);
233                                        this._areaPoints.push([xintercept, pyzero]);
234                                        // now draw this shape and shadow.
235                                        if (shadow) {
236                                            this.renderer.shadowRenderer.draw(ctx, tempgd, opts);
237                                        }
238                                        this.renderer.shapeRenderer.draw(ctx, tempgd, opts);
239                                        // now empty temp array and continue
240                                        tempgd = [[xintercept, pyzero]];
241                                        // this._areaPoints = [[xintercept, pyzero]];
242                                    }   
243                                }
244                                if (this._plotData[gd.length-1][1] < 0) {
245                                    isnegative = true;
246                                    opts.fillStyle = negativeColor;
247                                }
248                                else {
249                                    isnegative = false;
250                                    opts.fillStyle = posfs;
251                                }
252                                tempgd.push(gd[gd.length-1]);
253                                this._areaPoints.push(gd[gd.length-1]);
254                                tempgd.push([gd[gd.length-1][0], pyzero]);
255                                this._areaPoints.push([gd[gd.length-1][0], pyzero]);
256                            }
257                            // now draw this shape and shadow.
258                            if (shadow) {
259                                this.renderer.shadowRenderer.draw(ctx, tempgd, opts);
260                            }
261                            this.renderer.shapeRenderer.draw(ctx, tempgd, opts);
262                           
263                           
264                            // var gridymin = this._yaxis.series_u2p(0);
265                            // // IE doesn't return new length on unshift
266                            // gd.unshift([gd[0][0], gridymin]);
267                            // len = gd.length;
268                            // gd.push([gd[len - 1][0], gridymin]);                   
269                        }
270                        // if stacked, fill to line below
271                        else {
272                            var prev = this._prevGridData;
273                            for (var i=prev.length; i>0; i--) {
274                                gd.push(prev[i-1]);
275                                // this._areaPoints.push(prev[i-1]);
276                            }
277                            if (shadow) {
278                                this.renderer.shadowRenderer.draw(ctx, gd, opts);
279                            }
280                            this._areaPoints = gd;
281                            this.renderer.shapeRenderer.draw(ctx, gd, opts);
282                        }
283                    }
284                    /////////////////////////
285                    // Not filled to zero
286                    ////////////////////////
287                    else {                   
288                        // if stoking line as well as filling, get a copy of line data.
289                        if (fillAndStroke) {
290                            var fasgd = gd.slice(0);
291                        }
292                        // if not stacked, fill down to axis
293                        if (this.index == 0 || !this._stack) {
294                            // var gridymin = this._yaxis.series_u2p(this._yaxis.min) - this.gridBorderWidth / 2;
295                            var gridymin = ctx.canvas.height;
296                            // IE doesn't return new length on unshift
297                            gd.unshift([gd[0][0], gridymin]);
298                            var len = gd.length;
299                            gd.push([gd[len - 1][0], gridymin]);                   
300                        }
301                        // if stacked, fill to line below
302                        else {
303                            var prev = this._prevGridData;
304                            for (var i=prev.length; i>0; i--) {
305                                gd.push(prev[i-1]);
306                            }
307                        }
308                        this._areaPoints = gd;
309                       
310                        if (shadow) {
311                            this.renderer.shadowRenderer.draw(ctx, gd, opts);
312                        }
313           
314                        this.renderer.shapeRenderer.draw(ctx, gd, opts);                       
315                    }
316                    if (fillAndStroke) {
317                        var fasopts = $.extend(true, {}, opts, {fill:false, closePath:false});
318                        this.renderer.shapeRenderer.draw(ctx, fasgd, fasopts);
319                        //////////
320                        // TODO: figure out some way to do shadows nicely
321                        // if (shadow) {
322                        //     this.renderer.shadowRenderer.draw(ctx, fasgd, fasopts);
323                        // }
324                        // now draw the markers
325                        if (this.markerRenderer.show) {
326                            for (i=0; i<fasgd.length; i++) {
327                                this.markerRenderer.draw(fasgd[i][0], fasgd[i][1], ctx, opts.markerOptions);
328                            }
329                        }
330                    }
331                }
332                else {
333                    if (shadow) {
334                        this.renderer.shadowRenderer.draw(ctx, gd, opts);
335                    }
336   
337                    this.renderer.shapeRenderer.draw(ctx, gd, opts);
338                }
339            }
340            // calculate the bounding box
341            var xmin = xmax = ymin = ymax = null;
342            for (i=0; i<this._areaPoints.length; i++) {
343                var p = this._areaPoints[i];
344                if (xmin > p[0] || xmin == null) {
345                    xmin = p[0];
346                }
347                if (ymax < p[1] || ymax == null) {
348                    ymax = p[1];
349                }
350                if (xmax < p[0] || xmax == null) {
351                    xmax = p[0];
352                }
353                if (ymin > p[1] || ymin == null) {
354                    ymin = p[1];
355                }
356            }
357            this._boundingBox = [[xmin, ymax], [xmax, ymin]];
358       
359            // now draw the markers
360            if (this.markerRenderer.show && !fill) {
361                for (i=0; i<gd.length; i++) {
362                    if (gd[i][0] != null && gd[i][1] != null) {
363                        this.markerRenderer.draw(gd[i][0], gd[i][1], ctx, opts.markerOptions);
364                    }
365                }
366            }
367        }
368       
369        ctx.restore();
370    }; 
371   
372    $.jqplot.LineRenderer.prototype.drawShadow = function(ctx, gd, options) {
373        // This is a no-op, shadows drawn with lines.
374    };
375   
376    // called with scope of plot.
377    // make sure to not leave anything highlighted.
378    function postInit(target, data, options) {
379        for (var i=0; i<this.series.length; i++) {
380            if (this.series[i].renderer.constructor == $.jqplot.LineRenderer) {
381                // don't allow mouseover and mousedown at same time.
382                if (this.series[i].highlightMouseOver) {
383                    this.series[i].highlightMouseDown = false;
384                }
385            }
386        }
387        this.target.bind('mouseout', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
388    } 
389   
390    // called within context of plot
391    // create a canvas which we can draw on.
392    // insert it before the eventCanvas, so eventCanvas will still capture events.
393    function postPlotDraw() {
394        // Memory Leaks patch   
395        if (this.plugins.lineRenderer && this.plugins.lineRenderer.highlightCanvas) {
396          this.plugins.lineRenderer.highlightCanvas.resetCanvas();
397          this.plugins.lineRenderer.highlightCanvas = null;
398        }
399       
400        this.plugins.lineRenderer.highlightedSeriesIndex = null;
401        this.plugins.lineRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
402       
403        this.eventCanvas._elem.before(this.plugins.lineRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-lineRenderer-highlight-canvas', this._plotDimensions, this));
404        this.plugins.lineRenderer.highlightCanvas.setContext();
405    }
406   
407    function highlight (plot, sidx, pidx, points) {
408        var s = plot.series[sidx];
409        var canvas = plot.plugins.lineRenderer.highlightCanvas;
410        canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height);
411        s._highlightedPoint = pidx;
412        plot.plugins.lineRenderer.highlightedSeriesIndex = sidx;
413        var opts = {fillStyle: s.highlightColor};
414        s.renderer.shapeRenderer.draw(canvas._ctx, points, opts);
415        canvas = null;
416    }
417   
418    function unhighlight (plot) {
419        var canvas = plot.plugins.lineRenderer.highlightCanvas;
420        canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
421        for (var i=0; i<plot.series.length; i++) {
422            plot.series[i]._highlightedPoint = null;
423        }
424        plot.plugins.lineRenderer.highlightedSeriesIndex = null;
425        plot.target.trigger('jqplotDataUnhighlight');
426        canvas = null;
427    }
428   
429   
430    function handleMove(ev, gridpos, datapos, neighbor, plot) {
431        if (neighbor) {
432            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
433            var evt1 = jQuery.Event('jqplotDataMouseOver');
434            evt1.pageX = ev.pageX;
435            evt1.pageY = ev.pageY;
436            plot.target.trigger(evt1, ins);
437            if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.lineRenderer.highlightedSeriesIndex)) {
438                var evt = jQuery.Event('jqplotDataHighlight');
439                evt.pageX = ev.pageX;
440                evt.pageY = ev.pageY;
441                plot.target.trigger(evt, ins);
442                highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points);
443            }
444        }
445        else if (neighbor == null) {
446            unhighlight (plot);
447        }
448    }
449   
450    function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
451        if (neighbor) {
452            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
453            if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.lineRenderer.highlightedSeriesIndex)) {
454                var evt = jQuery.Event('jqplotDataHighlight');
455                evt.pageX = ev.pageX;
456                evt.pageY = ev.pageY;
457                plot.target.trigger(evt, ins);
458                highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points);
459            }
460        }
461        else if (neighbor == null) {
462            unhighlight (plot);
463        }
464    }
465   
466    function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
467        var idx = plot.plugins.lineRenderer.highlightedSeriesIndex;
468        if (idx != null && plot.series[idx].highlightMouseDown) {
469            unhighlight(plot);
470        }
471    }
472   
473    function handleClick(ev, gridpos, datapos, neighbor, plot) {
474        if (neighbor) {
475            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
476            var evt = jQuery.Event('jqplotDataClick');
477            evt.pageX = ev.pageX;
478            evt.pageY = ev.pageY;
479            plot.target.trigger(evt, ins);
480        }
481    }
482   
483    function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
484        if (neighbor) {
485            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
486            var idx = plot.plugins.lineRenderer.highlightedSeriesIndex;
487            if (idx != null && plot.series[idx].highlightMouseDown) {
488                unhighlight(plot);
489            }
490            var evt = jQuery.Event('jqplotDataRightClick');
491            evt.pageX = ev.pageX;
492            evt.pageY = ev.pageY;
493            plot.target.trigger(evt, ins);
494        }
495    }
496   
497})(jQuery);   
Note: See TracBrowser for help on using the repository browser.