source: Dev/branches/jQueryUI/client/RGraph/libraries/RGraph.scatter.js @ 249

Last change on this file since 249 was 249, checked in by hendrikvanantwerpen, 13 years ago

This one's for Subversion, because it's so close...

First widget (stripped down sequencer).
Seperated client and server code in two direcotry trees.

File size: 57.8 KB
Line 
1    /**
2    * o------------------------------------------------------------------------------o
3    * | This file is part of the RGraph package - you can learn more at:             |
4    * |                                                                              |
5    * |                          http://www.rgraph.net                               |
6    * |                                                                              |
7    * | This package is licensed under the RGraph license. For all kinds of business |
8    * | purposes there is a small one-time licensing fee to pay and for non          |
9    * | commercial  purposes it is free to use. You can read the full license here:  |
10    * |                                                                              |
11    * |                      http://www.rgraph.net/LICENSE.txt                       |
12    * o------------------------------------------------------------------------------o
13    */
14
15    if (typeof(RGraph) == 'undefined') RGraph = {};
16
17    /**
18    * The scatter graph constructor
19    *
20    * @param object canvas The cxanvas object
21    * @param array  data   The chart data
22    */
23    RGraph.Scatter = function (id, data)
24    {
25        // Get the canvas and context objects
26        this.id                = id;
27        this.canvas            = document.getElementById(id);
28        this.canvas.__object__ = this;
29        this.context           = this.canvas.getContext ? this.canvas.getContext("2d") : null;
30        this.max               = 0;
31        this.coords            = [];
32        this.data              = [];
33        this.type              = 'scatter';
34        this.isRGraph          = true;
35
36
37        /**
38        * Compatibility with older browsers
39        */
40        RGraph.OldBrowserCompat(this.context);
41
42
43        // Various config properties
44        this.properties = {
45            'chart.background.barcolor1':   'rgba(0,0,0,0)',
46            'chart.background.barcolor2':   'rgba(0,0,0,0)',
47            'chart.background.grid':        true,
48            'chart.background.grid.width':  1,
49            'chart.background.grid.color':  '#ddd',
50            'chart.background.grid.hsize':  20,
51            'chart.background.grid.vsize':  20,
52            'chart.background.hbars':       null,
53            'chart.background.vbars':       null,
54            'chart.background.grid.vlines': true,
55            'chart.background.grid.hlines': true,
56            'chart.background.grid.border': true,
57            'chart.background.grid.autofit':false,
58            'chart.background.grid.autofit.numhlines': 7,
59            'chart.background.grid.autofit.numvlines': 20,
60            'chart.text.size':              10,
61            'chart.text.angle':             0,
62            'chart.text.color':             'black',
63            'chart.text.font':              'Verdana',
64            'chart.tooltips.effect':         'fade',
65            'chart.tooltips.hotspot':        3,
66            'chart.tooltips.css.class':      'RGraph_tooltip',
67            'chart.tooltips.highlight':      true,
68            'chart.tooltips.coords.adjust':  [0,0],
69            'chart.units.pre':              '',
70            'chart.units.post':             '',
71            'chart.tickmarks':              'cross',
72            'chart.ticksize':               5,
73            'chart.xticks':                 true,
74            'chart.xaxis':                  true,
75            'chart.gutter.left':            25,
76            'chart.gutter.right':           25,
77            'chart.gutter.top':             25,
78            'chart.gutter.bottom':          25,
79            'chart.xmax':                   0,
80            'chart.ymax':                   null,
81            'chart.ymin':                   null,
82            'chart.scale.decimals':         null,
83            'chart.scale.point':            '.',
84            'chart.scale.thousand':         ',',
85            'chart.title':                  '',
86            'chart.title.background':       null,
87            'chart.title.hpos':             null,
88            'chart.title.vpos':             null,
89            'chart.title.xaxis':            '',
90            'chart.title.yaxis':            '',
91            'chart.title.xaxis.pos':        0.25,
92            'chart.title.yaxis.pos':        0.25,
93            'chart.labels':                 [],
94            'chart.labels.ingraph':         null,
95            'chart.labels.above':           false,
96            'chart.labels.above.size':      8,
97            'chart.labels.above.decimals':  0,
98            'chart.ylabels':                true,
99            'chart.ylabels.count':          5,
100            'chart.ylabels.invert':         false,
101            'chart.contextmenu':            null,
102            'chart.defaultcolor':           'black',
103            'chart.xaxispos':               'bottom',
104            'chart.yaxispos':               'left',
105            'chart.noendxtick':             false,
106            'chart.crosshairs':             false,
107            'chart.crosshairs.color':       '#333',
108            'chart.crosshairs.linewidth':   1,
109            'chart.crosshairs.coords':      false,
110            'chart.crosshairs.coords.fixed':true,
111            'chart.crosshairs.coords.fadeout':false,
112            'chart.crosshairs.coords.labels.x': 'X',
113            'chart.crosshairs.coords.labels.y': 'Y',
114            'chart.annotatable':            false,
115            'chart.annotate.color':         'black',
116            'chart.line':                   false,
117            'chart.line.linewidth':         1,
118            'chart.line.colors':            ['green', 'red'],
119            'chart.line.shadow.color':      'rgba(0,0,0,0)',
120            'chart.line.shadow.blur':       2,
121            'chart.line.shadow.offsetx':    3,
122            'chart.line.shadow.offsety':    3,
123            'chart.line.stepped':           false,
124            'chart.noaxes':                 false,
125            'chart.key':                    [],
126            'chart.key.background':         'white',
127            'chart.key.position':           'graph',
128            'chart.key.halign':             'right',
129            'chart.key.shadow':             false,
130            'chart.key.shadow.color':       '#666',
131            'chart.key.shadow.blur':        3,
132            'chart.key.shadow.offsetx':     2,
133            'chart.key.shadow.offsety':     2,
134            'chart.key.position.gutter.boxed': true,
135            'chart.key.position.x':         null,
136            'chart.key.position.y':         null,
137            'chart.key.color.shape':        'square',
138            'chart.key.rounded':            true,
139            'chart.key.linewidth':          1,
140            'chart.axis.color':             'black',
141            'chart.zoom.factor':            1.5,
142            'chart.zoom.fade.in':           true,
143            'chart.zoom.fade.out':          true,
144            'chart.zoom.hdir':              'right',
145            'chart.zoom.vdir':              'down',
146            'chart.zoom.frames':            10,
147            'chart.zoom.delay':             50,
148            'chart.zoom.shadow':            true,
149            'chart.zoom.mode':              'canvas',
150            'chart.zoom.thumbnail.width':   75,
151            'chart.zoom.thumbnail.height':  75,
152            'chart.zoom.background':        true,
153            'chart.zoom.action':            'zoom',
154            'chart.boxplot.width':          8,
155            'chart.resizable':              false,
156            'chart.resize.handle.adjust':   [0,0],
157            'chart.resize.handle.background': null,
158            'chart.xmin':                   0,
159            'chart.labels.specific.align':  'left'
160        }
161
162        // Handle multiple datasets being given as one argument
163        if (arguments[1][0] && arguments[1][0][0] && typeof(arguments[1][0][0][0]) == 'number') {
164            // Store the data set(s)
165            for (var i=0; i<arguments[1].length; ++i) {
166                this.data[i] = arguments[1][i];
167            }
168
169        // Handle multiple data sets being supplied as seperate arguments
170        } else {
171            // Store the data set(s)
172            for (var i=1; i<arguments.length; ++i) {
173                this.data[i - 1] = arguments[i];
174            }
175        }
176
177        // Check for support
178        if (!this.canvas) {
179            alert('[SCATTER] No canvas support');
180            return;
181        }
182
183
184        /**
185        * Set the .getShape commonly named method
186        */
187        this.getShape = this.getPoint;
188    }
189
190
191    /**
192    * A simple setter
193    *
194    * @param string name  The name of the property to set
195    * @param string value The value of the property
196    */
197    RGraph.Scatter.prototype.Set = function (name, value)
198    {
199        /**
200        * This is here because the key expects a name of "chart.colors"
201        */
202        if (name == 'chart.line.colors') {
203            this.properties['chart.colors'] = value;
204        }
205       
206        /**
207        * Allow compatibility with older property names
208        */
209        if (name == 'chart.tooltip.hotspot') {
210            name = 'chart.tooltips.hotspot';
211        }
212       
213        /**
214        * chart.yaxispos should be left or right
215        */
216        if (name == 'chart.yaxispos' && value != 'left' && value != 'right') {
217            alert("[SCATTER] chart.yaxispos should be left or right. You've set it to: '" + value + "' Changing it to left");
218            value = 'left';
219        }
220       
221        /**
222        * Check for xaxispos
223        */
224        if (name == 'chart.xaxispos' ) {
225            if (value != 'bottom' && value != 'center') {
226                alert('[SCATTER] (' + this.id + ') chart.xaxispos should be center or bottom. Tried to set it to: ' + value + ' Changing it to center');
227                value = 'center';
228            }
229        }
230
231        this.properties[name.toLowerCase()] = value;
232    }
233
234
235    /**
236    * A simple getter
237    *
238    * @param string name  The name of the property to set
239    */
240    RGraph.Scatter.prototype.Get = function (name)
241    {
242        return this.properties[name];
243    }
244
245
246    /**
247    * The function you call to draw the line chart
248    */
249    RGraph.Scatter.prototype.Draw = function ()
250    {
251        // MUST be the first thing done!
252        if (typeof(this.Get('chart.background.image')) == 'string' && !this.__background_image__) {
253            RGraph.DrawBackgroundImage(this);
254            return;
255        }
256
257        /**
258        * Fire the onbeforedraw event
259        */
260        RGraph.FireCustomEvent(this, 'onbeforedraw');
261
262        /**
263        * Clear all of this canvases event handlers (the ones installed by RGraph)
264        */
265        RGraph.ClearEventListeners(this.id);
266       
267        /**
268        * This is new in May 2011 and facilitates indiviual gutter settings,
269        * eg chart.gutter.left
270        */
271        this.gutterLeft   = this.Get('chart.gutter.left');
272        this.gutterRight  = this.Get('chart.gutter.right');
273        this.gutterTop    = this.Get('chart.gutter.top');
274        this.gutterBottom = this.Get('chart.gutter.bottom');
275
276        // Go through all the data points and see if a tooltip has been given
277        this.Set('chart.tooltips', false);
278        this.hasTooltips = false;
279        var overHotspot  = false;
280
281        // Reset the coords array
282        this.coords = [];
283
284        if (!RGraph.isIE8()) {
285            for (var i=0; i<this.data.length; ++i) {
286                for (var j =0;j<this.data[i].length; ++j) {
287                    if (this.data[i][j] && this.data[i][j][3] && typeof(this.data[i][j][3]) == 'string' && this.data[i][j][3].length) {
288                        this.Set('chart.tooltips', [1]); // An array
289                        this.hasTooltips = true;
290                    }
291                }
292            }
293        }
294
295        // Reset the maximum value
296        this.max = 0;
297
298        // Work out the maximum Y value
299        if (this.Get('chart.ymax') && this.Get('chart.ymax') > 0) {
300
301            this.scale = [];
302            this.max   = this.Get('chart.ymax');
303            this.min   = this.Get('chart.ymin') ? this.Get('chart.ymin') : 0;
304
305            this.scale[0] = ((this.max - this.min) * (1/5)) + this.min;
306            this.scale[1] = ((this.max - this.min) * (2/5)) + this.min;
307            this.scale[2] = ((this.max - this.min) * (3/5)) + this.min;
308            this.scale[3] = ((this.max - this.min) * (4/5)) + this.min;
309            this.scale[4] = ((this.max - this.min) * (5/5)) + this.min;
310
311            var decimals = this.Get('chart.scale.decimals');
312
313            this.scale = [
314                          Number(this.scale[0]).toFixed(decimals),
315                          Number(this.scale[1]).toFixed(decimals),
316                          Number(this.scale[2]).toFixed(decimals),
317                          Number(this.scale[3]).toFixed(decimals),
318                          Number(this.scale[4]).toFixed(decimals)
319                         ];
320
321        } else {
322
323            var i = 0;
324            var j = 0;
325
326            for (i=0; i<this.data.length; ++i) {
327                for (j=0; j<this.data[i].length; ++j) {
328                    this.max = Math.max(this.max, typeof(this.data[i][j][1]) == 'object' ? RGraph.array_max(this.data[i][j][1]) : Math.abs(this.data[i][j][1]));
329                }
330            }
331
332            this.scale = RGraph.getScale(this.max, this);
333
334            this.max   = this.scale[4];
335            this.min   = this.Get('chart.ymin') ? this.Get('chart.ymin') : 0;
336
337            if (this.min) {
338                this.scale[0] = ((this.max - this.min) * (1/5)) + this.min;
339                this.scale[1] = ((this.max - this.min) * (2/5)) + this.min;
340                this.scale[2] = ((this.max - this.min) * (3/5)) + this.min;
341                this.scale[3] = ((this.max - this.min) * (4/5)) + this.min;
342                this.scale[4] = ((this.max - this.min) * (5/5)) + this.min;
343            }
344
345
346            if (typeof(this.Get('chart.scale.decimals')) == 'number') {
347                var decimals = this.Get('chart.scale.decimals');
348   
349                this.scale = [
350                              Number(this.scale[0]).toFixed(decimals),
351                              Number(this.scale[1]).toFixed(decimals),
352                              Number(this.scale[2]).toFixed(decimals),
353                              Number(this.scale[3]).toFixed(decimals),
354                              Number(this.scale[4]).toFixed(decimals)
355                             ];
356            }
357        }
358
359        this.grapharea = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom;
360
361        // Progressively Draw the chart
362        RGraph.background.Draw(this);
363
364        /**
365        * Draw any horizontal bars that have been specified
366        */
367        if (this.Get('chart.background.hbars') && this.Get('chart.background.hbars').length) {
368            RGraph.DrawBars(this);
369        }
370
371        /**
372        * Draw any vertical bars that have been specified
373        */
374        if (this.Get('chart.background.vbars') && this.Get('chart.background.vbars').length) {
375            this.DrawVBars();
376        }
377
378        if (!this.Get('chart.noaxes')) {
379            this.DrawAxes();
380        }
381
382        this.DrawLabels();
383
384        i = 0;
385        for(i=0; i<this.data.length; ++i) {
386            this.DrawMarks(i);
387
388            // Set the shadow
389            this.context.shadowColor   = this.Get('chart.line.shadow.color');
390            this.context.shadowOffsetX = this.Get('chart.line.shadow.offsetx');
391            this.context.shadowOffsetY = this.Get('chart.line.shadow.offsety');
392            this.context.shadowBlur    = this.Get('chart.line.shadow.blur');
393           
394            this.DrawLine(i);
395
396            // Turn the shadow off
397            RGraph.NoShadow(this);
398        }
399
400
401        if (this.Get('chart.line')) {
402            for (var i=0;i<this.data.length; ++i) {
403                this.DrawMarks(i); // Call this again so the tickmarks appear over the line
404            }
405        }
406
407
408
409        /**
410        * Setup the context menu if required
411        */
412        if (this.Get('chart.contextmenu')) {
413            RGraph.ShowContext(this);
414        }
415
416        /**
417        * Install the event handler for tooltips
418        */
419        if (this.hasTooltips) {
420
421            /**
422            * Register all charts
423            */
424            RGraph.Register(this);
425
426            var overHotspot = false;
427
428            var canvas_onmousemove_func = function (e)
429            {
430                e = RGraph.FixEventObject(e);
431
432                var canvas      = e.target;
433                var obj         = canvas.__object__;
434                var context     = obj.context;
435                var mouseCoords = RGraph.getMouseXY(e);
436                var point       = obj.getPoint(e);
437                var overHotspot = false;
438
439                if (point) {
440
441                    var __dataset__ = point[2];
442                    var __index__   = point[3];
443                    var __text__    = point[4];
444                    var overHotspot = true;
445
446                    if (point[4]) {
447                        canvas.style.cursor = 'pointer';
448
449                        if (
450                            !RGraph.Registry.Get('chart.tooltip') ||
451                            RGraph.Registry.Get('chart.tooltip').__text__ != __text__ ||
452                            RGraph.Registry.Get('chart.tooltip').__index__ != __index__ ||
453                            RGraph.Registry.Get('chart.tooltip').__dataset__ != __dataset__
454                           ) {
455
456                            if (obj.Get('chart.tooltips.highlight')) {
457                                RGraph.Redraw();
458                            }
459   
460                            /**
461                            * Get the tooltip text
462                            */
463                            if (typeof(__text__) == 'function') {
464                                var text = String(__text__(i));
465   
466                            } else {
467                                var text = String(__text__);
468                            }
469   
470                            RGraph.Tooltip(canvas, text, e.pageX, e.pageY, __index__);
471                           
472                            RGraph.Registry.Get('chart.tooltip').__index__ = __index__;
473                           
474                            if (RGraph.Registry.Get('chart.tooltip')) {
475                                RGraph.Registry.Get('chart.tooltip').__dataset__ = __dataset__;
476                            }
477                                   
478                            /**
479                            * Draw a circle around the mark
480                            */
481                            if (obj.Get('chart.tooltips.highlight')) {
482                                context.beginPath();
483                                context.fillStyle = 'rgba(255,255,255,0.5)';
484                                context.arc(point[0], point[1], 3, 0, 6.28, 0);
485                                context.fill();
486                            }
487                        }
488                    }
489                }
490
491                /**
492                * Reset the pointer
493                */
494                if (!overHotspot || !point[4]) {
495                    canvas.style.cursor = 'default';
496                }
497            }
498            this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
499            RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
500        }
501       
502       
503        /**
504        * Draw the key if necessary
505        */
506        if (this.Get('chart.key') && this.Get('chart.key').length) {
507            RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.line.colors'));
508        }
509
510
511        /**
512        * Draw " above" labels if enabled
513        */
514        if (this.Get('chart.labels.above')) {
515            this.DrawAboveLabels();
516        }
517
518        /**
519        * Draw the "in graph" labels, using the member function, NOT the shared function in RGraph.common.core.js
520        */
521        this.DrawInGraphLabels(this);
522
523
524        /**
525        * Draw crosschairs
526        */
527        RGraph.DrawCrosshairs(this);
528
529       
530        /**
531        * If the canvas is annotatable, do install the event handlers
532        */
533        if (this.Get('chart.annotatable')) {
534            RGraph.Annotate(this);
535        }
536       
537        /**
538        * This bit shows the mini zoom window if requested
539        */
540        if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
541            RGraph.ShowZoomWindow(this);
542        }
543
544       
545        /**
546        * This function enables resizing
547        */
548        if (this.Get('chart.resizable')) {
549            RGraph.AllowResizing(this);
550        }
551       
552        /**
553        * Fire the RGraph ondraw event
554        */
555        RGraph.FireCustomEvent(this, 'ondraw');
556    }
557
558
559    /**
560    * Draws the axes of the scatter graph
561    */
562    RGraph.Scatter.prototype.DrawAxes = function ()
563    {
564        var canvas      = this.canvas;
565        var context     = this.context;
566        var graphHeight = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom;
567
568        context.beginPath();
569        context.strokeStyle = this.Get('chart.axis.color');
570        context.lineWidth   = 1;
571
572        // Draw the Y axis
573        if (this.Get('chart.yaxispos') == 'left') {
574            context.moveTo(this.gutterLeft, this.gutterTop);
575            context.lineTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom);
576        } else {
577            context.moveTo(RGraph.GetWidth(this) - this.gutterRight, this.gutterTop);
578            context.lineTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom);
579        }
580
581
582        // Draw the X axis
583        if (this.Get('chart.xaxis')) {
584            if (this.Get('chart.xaxispos') == 'center') {
585                context.moveTo(this.gutterLeft, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / 2));
586                context.lineTo(RGraph.GetWidth(this) - this.gutterRight, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / 2));
587            } else {
588                context.moveTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom);
589                context.lineTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom);
590            }
591        }
592
593        /**
594        * Draw the Y tickmarks
595        */
596        for (y=this.gutterTop; y < RGraph.GetHeight(this) - this.gutterBottom + (this.Get('chart.xaxispos') == 'center' ? 1 : 0) ; y+=(graphHeight / 5) / 2) {
597
598            // This is here to accomodate the X axis being at the center
599            if (y == (this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / 2)) ) continue;
600
601            if (this.Get('chart.yaxispos') == 'left') {
602                context.moveTo(this.gutterLeft, y);
603                context.lineTo(this.gutterLeft - 3, y);
604            } else {
605                context.moveTo(RGraph.GetWidth(this) - this.gutterRight +3, y);
606                context.lineTo(RGraph.GetWidth(this) - this.gutterRight, y);
607            }
608        }
609
610
611        /**
612        * Draw the X tickmarks
613        */
614        if (this.Get('chart.xticks') && this.Get('chart.xaxis')) {
615
616            var x  = 0;
617            var y  =  (this.Get('chart.xaxispos') == 'center') ? this.gutterTop + (this.grapharea / 2): (this.canvas.height - this.gutterBottom);
618            this.xTickGap = (this.Get('chart.labels') && this.Get('chart.labels').length) ? ((this.canvas.width - this.gutterLeft - this.gutterRight ) / this.Get('chart.labels').length) : (this.canvas.width - this.gutterLeft - this.gutterRight) / 10;
619
620            for (x =  (this.gutterLeft + (this.Get('chart.yaxispos') == 'left' ? this.xTickGap : 0) );
621                 x <= (this.canvas.width - this.gutterRight - (this.Get('chart.yaxispos') == 'left' ? 0 : 1));
622                 x += this.xTickGap) {
623
624                if (this.Get('chart.yaxispos') == 'left' && this.Get('chart.noendxtick') == true && x == (RGraph.GetWidth(this) - this.gutterLeft) ) {
625                    continue;
626                } else if (this.Get('chart.yaxispos') == 'right' && this.Get('chart.noendxtick') == true && x == this.gutterLeft) {
627                    continue;
628                }
629
630                context.moveTo(x, y - (this.Get('chart.xaxispos') == 'center' ? 3 : 0));
631                context.lineTo(x, y + 3);
632            }
633        }
634
635        context.stroke();
636    }
637
638
639
640
641
642
643
644
645
646
647
648    /**
649    * Draws the labels on the scatter graph
650    */
651    RGraph.Scatter.prototype.DrawLabels = function ()
652    {
653        this.context.fillStyle = this.Get('chart.text.color');
654        var font       = this.Get('chart.text.font');
655        var xMin       = this.Get('chart.xmin');
656        var xMax       = this.Get('chart.xmax');
657        var yMax       = this.scale[4];
658        var yMin       = this.Get('chart.ymin');
659        var text_size  = this.Get('chart.text.size');
660        var units_pre  = this.Get('chart.units.pre');
661        var units_post = this.Get('chart.units.post');
662        var numYLabels = this.Get('chart.ylabels.count');
663        var invert     = this.Get('chart.ylabels.invert');
664        var context    = this.context;
665        var canvas     = this.canvas;
666
667        this.halfTextHeight = text_size / 2;
668
669           
670        this.halfGraphHeight = (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / 2;
671
672        /**
673        * Draw the Y yaxis labels, be it at the top or center
674        */
675        if (this.Get('chart.ylabels')) {
676
677            var xPos  = this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - 5 : RGraph.GetWidth(this) - this.gutterRight + 5;
678            var align = this.Get('chart.yaxispos') == 'right' ? 'left' : 'right';
679
680            if (this.Get('chart.xaxispos') == 'center') {
681
682
683                /**
684                * Specific Y labels
685                */
686                if (typeof(this.Get('chart.ylabels.specific')) == 'object') {
687               
688                    var labels = this.Get('chart.ylabels.specific');
689               
690                    for (var i=0; i<this.Get('chart.ylabels.specific').length; ++i) {
691                        var y = this.gutterTop + (i * (this.grapharea / (labels.length * 2) ) );
692                        RGraph.Text(context, font, text_size, xPos, y, labels[i], 'center', align);
693                    }
694                   
695                    var reversed_labels = RGraph.array_reverse(labels);
696               
697                    for (var i=0; i<reversed_labels.length; ++i) {
698                        var y = this.gutterTop + (this.grapharea / 2) + ((i+1) * (this.grapharea / (labels.length * 2) ) );
699                       
700                        RGraph.Text(context, font, text_size, xPos, y, reversed_labels[i], 'center', align);
701                    }
702               
703                    return;
704                }
705
706
707                if (numYLabels == 1 || numYLabels == 3 || numYLabels == 5) {
708                    // Draw the top halves labels
709                    RGraph.Text(context, font, text_size, xPos, this.gutterTop, RGraph.number_format(this, this.scale[4], units_pre, units_post), 'center', align);
710                   
711                   
712                    if (numYLabels >= 5) {
713                        RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (1/10) ), RGraph.number_format(this, this.scale[3], units_pre, units_post), 'center', align);
714                        RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (3/10) ), RGraph.number_format(this, this.scale[1], units_pre, units_post), 'center', align);
715                    }
716       
717                    if (numYLabels >= 3) {
718                        RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (2/10) ), RGraph.number_format(this, this.scale[2], units_pre, units_post), 'center', align);
719                        RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (4/10) ), RGraph.number_format(this, this.scale[0], units_pre, units_post), 'center', align);
720                    }
721                   
722                    // Draw the bottom halves labels
723                    if (numYLabels >= 3) {
724                        RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (1/10) ) + this.halfGraphHeight, '-' + RGraph.number_format(this, this.scale[0], units_pre, units_post), 'center', align);
725                        RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (3/10) ) + this.halfGraphHeight, '-' + RGraph.number_format(this, this.scale[2], units_pre, units_post), 'center', align);
726                    }
727       
728                    if (numYLabels == 5) {
729                        RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (2/10) ) + this.halfGraphHeight, '-' + RGraph.number_format(this, this.scale[1], units_pre, units_post), 'center', align);
730                        RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (4/10) ) + this.halfGraphHeight, '-' + RGraph.number_format(this, this.scale[3], units_pre, units_post), 'center', align);
731                    }
732       
733                    RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (5/10) ) + this.halfGraphHeight, '-' + RGraph.number_format(this, this.scale[4], units_pre, units_post), 'center', align);
734               
735                } else if (numYLabels == 10) {
736                    // 10 Y labels
737                    var interval = (this.grapharea / numYLabels) / 2;
738               
739                    for (var i=0; i<numYLabels; ++i) {
740                        RGraph.Text(context, font, text_size, xPos,this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (i/20) ),RGraph.number_format(this,
741                       
742                        (this.max - (this.max * (i/10))).toFixed(this.Get('chart.scale.decimals')),
743                       
744                        units_pre, units_post),'center', align);
745                        RGraph.Text(context, font, text_size, xPos,this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (i/20) ) + (this.grapharea / 2) + (this.grapharea / 20),'-' + RGraph.number_format(this, ((this.max * (i/10)) + (this.max * (1/10))).toFixed((this.Get('chart.scale.decimals'))), units_pre, units_post), 'center', align);
746                    }
747
748                } else {
749                    alert('[SCATTER SCALE] Number of Y labels can be 1/3/5/10 only');
750                }
751   
752            } else {
753               
754                var xPos  = this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - 5 : RGraph.GetWidth(this) - this.gutterRight + 5;
755                var align = this.Get('chart.yaxispos') == 'right' ? 'left' : 'right';
756
757                /**
758                * Specific Y labels
759                */
760                if (typeof(this.Get('chart.ylabels.specific')) == 'object' && this.Get('chart.ylabels.specific')) {
761
762                    var labels = this.Get('chart.ylabels.specific');
763
764                    for (var i=0; i<this.Get('chart.ylabels.specific').length; ++i) {
765                        var y = this.gutterTop + (i * (this.grapharea / labels.length) );
766                       
767                        RGraph.Text(context, font, text_size, xPos, y, labels[i], 'center', align);
768                    }
769
770                    return;
771                }
772
773                if (numYLabels == 1 || numYLabels == 3 || numYLabels == 5) {
774                    if (invert) {
775                        RGraph.Text(context, font, text_size, xPos, this.gutterTop, RGraph.number_format(this, 0, units_pre, units_post), 'center', align);
776                        RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (5/5) ), RGraph.number_format(this, this.scale[4], units_pre, units_post), 'center', align);
777       
778                        if (numYLabels >= 5) {
779                            RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (2/5) ), RGraph.number_format(this, this.scale[1], units_pre, units_post), 'center', align);
780                            RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (4/5) ), RGraph.number_format(this, this.scale[3], units_pre, units_post), 'center', align);
781                        }
782       
783                        if (numYLabels >= 3) {
784                            RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (3/5) ), RGraph.number_format(this, this.scale[2], units_pre, units_post), 'center', align);
785                            RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (1/5) ), RGraph.number_format(this, this.scale[0], units_pre, units_post), 'center', align);
786                        }
787                    } else {
788                        RGraph.Text(context, font, text_size, xPos, this.gutterTop, RGraph.number_format(this, this.scale[4], units_pre, units_post), 'center', align);
789       
790                        if (numYLabels >= 5) {
791                            RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (1/5) ), RGraph.number_format(this, this.scale[3], units_pre, units_post), 'center', align);
792                            RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (3/5) ), RGraph.number_format(this, this.scale[1], units_pre, units_post), 'center', align);
793                        }
794       
795                        if (numYLabels >= 3) {
796                            RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (2/5) ), RGraph.number_format(this, this.scale[2], units_pre, units_post), 'center', align);
797                            RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (4/5) ), RGraph.number_format(this, this.scale[0], units_pre, units_post), 'center', align);
798                        }
799                    }
800                } else if (numYLabels == 10) {
801                    var interval = (this.grapharea / numYLabels) / 2;
802                    if (invert) {
803                        for (var i=numYLabels; i>=0; --i) {
804                            RGraph.Text(context, font, text_size, xPos,this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * ((10-i)/10) ),
805                           
806                            RGraph.number_format(this,(this.max - (this.max * (i/10))).toFixed((this.Get('chart.scale.decimals'))), units_pre, units_post),
807                           
808                            'center', align);
809                        }
810                    } else {
811                        // 10 Y labels
812                        for (var i=0; i<numYLabels; ++i) {
813
814                            RGraph.Text(context, font, text_size, xPos,this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (i/10) ),
815
816                            RGraph.number_format(this,
817                                                 (this.max - ((this.max - this.min) * (i/10))).toFixed((this.Get('chart.scale.decimals'))),
818                                                 units_pre,
819                                                 units_post),
820                            'center', align);
821                        }
822                    }
823                } else {
824                    alert('[SCATTER SCALE] Number of Y labels can be 1/3/5/10 only');
825                }
826               
827                if (this.Get('chart.ymin')) {
828                    RGraph.Text(context, font, text_size, xPos, this.canvas.height - this.gutterBottom,RGraph.number_format(this, this.Get('chart.ymin').toFixed(this.Get('chart.scale.decimals')), units_pre, units_post),'center', align);
829                }
830            }
831        }
832
833
834
835
836
837
838        // Put the text on the X axis
839        var graphArea = RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight;
840        var xInterval = graphArea / this.Get('chart.labels').length;
841        var xPos      = this.gutterLeft;
842        var yPos      = (RGraph.GetHeight(this) - this.gutterBottom) + 15;
843        var labels    = this.Get('chart.labels');
844
845        /**
846        * Text angle
847        */
848        var angle  = 0;
849        var valign = null;
850        var halign = 'center';
851
852        if (this.Get('chart.text.angle') > 0) {
853            angle  = -1 * this.Get('chart.text.angle');
854            valign = 'center';
855            halign = 'right';
856            yPos -= 10;
857        }
858
859        for (i=0; i<labels.length; ++i) {
860           
861            if (typeof(labels[i]) == 'object') {
862           
863                if (this.Get('chart.labels.specific.align') == 'center') {
864                    var rightEdge = 0;
865
866                    if (labels[i+1] && labels[i+1][1]) {
867                        rightEdge = labels[i+1][1];
868                    } else {
869                        rightEdge = this.Get('chart.xmax');
870                    }
871                   
872                    var offset = (rightEdge - labels[i][1]) / 2;
873
874                } else {
875                    var offset = 0;
876                }
877           
878
879                RGraph.Text(context,
880                            font,
881                            this.Get('chart.text.size'),
882                            this.gutterLeft + (graphArea * ((labels[i][1] - xMin + offset) / (this.Get('chart.xmax') - xMin))) + 5,
883                            yPos,
884                            String(labels[i][0]),
885                            valign,
886                            angle != 0 ? 'right' : (this.Get('chart.labels.specific.align') == 'center' ? 'center' : 'left'),
887                            null,
888                            angle
889                           );
890               
891                /**
892                * Draw the gray indicator line
893                */
894                this.context.beginPath();
895                    this.context.strokeStyle = '#bbb';
896                    this.context.moveTo(this.gutterLeft + (graphArea * ((labels[i][1] - xMin)/ (this.Get('chart.xmax') - xMin))), RGraph.GetHeight(this) - this.gutterBottom);
897                    this.context.lineTo(this.gutterLeft + (graphArea * ((labels[i][1] - xMin)/ (this.Get('chart.xmax') - xMin))), RGraph.GetHeight(this) - this.gutterBottom + 20);
898                this.context.stroke();
899           
900            } else {
901                RGraph.Text(context, font, this.Get('chart.text.size'), xPos + (this.xTickGap / 2), yPos, String(labels[i]), valign, halign, null, angle);
902            }
903           
904            // Do this for the next time around
905            xPos += xInterval;
906        }
907
908        /**
909        * Draw the final indicator line
910        */
911        if (typeof(labels[0]) == 'object') {
912            this.context.beginPath();
913                this.context.strokeStyle = '#bbb';
914                this.context.moveTo(this.gutterLeft + graphArea, RGraph.GetHeight(this) - this.gutterBottom);
915                this.context.lineTo(this.gutterLeft + graphArea, RGraph.GetHeight(this) - this.gutterBottom + 20);
916            this.context.stroke();
917        }
918    }
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933    /**
934    * Draws the actual scatter graph marks
935    *
936    * @param i integer The dataset index
937    */
938    RGraph.Scatter.prototype.DrawMarks = function (i)
939    {
940        /**
941        *  Reset the coords array
942        */
943        this.coords[i] = [];
944
945        /**
946        * Plot the values
947        */
948        var xmax          = this.Get('chart.xmax');
949        var default_color = this.Get('chart.defaultcolor');
950
951        for (var j=0; j<this.data[i].length; ++j) {
952            /**
953            * This is here because tooltips are optional
954            */
955            var data_point = this.data[i];
956
957            var xCoord = data_point[j][0];
958            var yCoord = data_point[j][1];
959            var color  = data_point[j][2] ? data_point[j][2] : default_color;
960            var tooltip = (data_point[j] && data_point[j][3]) ? data_point[j][3] : null;
961
962           
963            this.DrawMark(
964                          i,
965                          xCoord,
966                          yCoord,
967                          xmax,
968                          this.scale[4],
969                          color,
970                          tooltip,
971                          this.coords[i],
972                          data_point
973                         );
974        }
975    }
976
977
978    /**
979    * Draws a single scatter mark
980    */
981    RGraph.Scatter.prototype.DrawMark = function (index, x, y, xMax, yMax, color, tooltip, coords, data)
982    {
983        /**
984        * Inverted Y scale handling
985        */
986        if (this.Get('chart.ylabels.invert')) {
987            if (typeof(y) == 'number') {
988                y = yMax - y;
989            }
990        }
991
992        var tickmarks = this.Get('chart.tickmarks');
993        var tickSize  = this.Get('chart.ticksize');
994        var xMin      = this.Get('chart.xmin');
995        var x = ((x - xMin) / (xMax - xMin)) * (RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight);
996        var originalX = x;
997        var originalY = y;
998       
999       
1000        /**
1001        * This allows chart.tickmarks to be an array
1002        */
1003
1004        if (tickmarks && typeof(tickmarks) == 'object') {
1005            tickmarks = tickmarks[index];
1006        }
1007
1008
1009        /**
1010        * This allows chart.ticksize to be an array
1011        */
1012        if (typeof(tickSize) == 'object') {
1013            var tickSize     = tickSize[index];
1014            var halfTickSize = tickSize / 2;
1015        } else {
1016            var halfTickSize = tickSize / 2;
1017        }
1018
1019
1020        /**
1021        * This bit is for boxplots only
1022        */
1023        if (   typeof(y) == 'object'
1024            && typeof(y[0]) == 'number'
1025            && typeof(y[1]) == 'number'
1026            && typeof(y[2]) == 'number'
1027            && typeof(y[3]) == 'number'
1028            && typeof(y[4]) == 'number'
1029           ) {
1030
1031            var yMin = this.Get('chart.ymin') ? this.Get('chart.ymin') : 0;
1032            this.Set('chart.boxplot', true);
1033            this.graphheight = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom;
1034           
1035            if (this.Get('chart.xaxispos') == 'center') {
1036                this.graphheight /= 2;
1037            }
1038
1039            var y0 = (this.graphheight) - ((y[4] - yMin) / (yMax - yMin)) * (this.graphheight);
1040            var y1 = (this.graphheight) - ((y[3] - yMin) / (yMax - yMin)) * (this.graphheight);
1041            var y2 = (this.graphheight) - ((y[2] - yMin) / (yMax - yMin)) * (this.graphheight);
1042            var y3 = (this.graphheight) - ((y[1] - yMin) / (yMax - yMin)) * (this.graphheight);
1043            var y4 = (this.graphheight) - ((y[0] - yMin) / (yMax - yMin)) * (this.graphheight);
1044           
1045            /**
1046            * Inverted labels
1047            */
1048            if (this.Get('chart.ylabels.invert')) {
1049                y0 = this.graphheight - y0;
1050                y1 = this.graphheight - y1;
1051                y2 = this.graphheight - y2;
1052                y3 = this.graphheight - y3;
1053                y4 = this.graphheight - y4;
1054            }
1055
1056            var col1  = y[5];
1057            var col2  = y[6];
1058
1059            // Override the boxWidth
1060            if (typeof(y[7]) == 'number') {
1061                var boxWidth = y[7];
1062            }
1063           
1064            var y = this.graphheight - y2;
1065
1066        } else {
1067            var yMin = this.Get('chart.ymin') ? this.Get('chart.ymin') : 0;
1068            var y = (( (y - yMin) / (yMax - yMin)) * (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom));
1069        }
1070
1071        /**
1072        * Account for the X axis being at the centre
1073        */
1074        if (this.Get('chart.xaxispos') == 'center') {
1075            y /= 2;
1076            y += this.halfGraphHeight;
1077        }
1078
1079        // This is so that points are on the graph, and not the gutter
1080        x += this.gutterLeft;
1081        y = RGraph.GetHeight(this) - this.gutterBottom - y;
1082
1083        this.context.beginPath();
1084       
1085        // Color
1086        this.context.strokeStyle = color;
1087
1088        /**
1089        * Boxplots
1090        */
1091        if (   this.Get('chart.boxplot')
1092            && typeof(y0) == 'number'
1093            && typeof(y1) == 'number'
1094            && typeof(y2) == 'number'
1095            && typeof(y3) == 'number'
1096            && typeof(y4) == 'number'
1097           ) {
1098
1099            var boxWidth = boxWidth ? boxWidth : this.Get('chart.boxplot.width');
1100            var halfBoxWidth = boxWidth / 2;
1101
1102            this.context.beginPath();
1103
1104            // Draw the upper coloured box if a value is specified
1105            if (col1) {
1106                this.context.fillStyle = col1;
1107                this.context.fillRect(x - halfBoxWidth, y1 + this.gutterTop, boxWidth, y2 - y1);
1108            }
1109
1110            // Draw the lower coloured box if a value is specified
1111            if (col2) {
1112                this.context.fillStyle = col2;
1113                this.context.fillRect(x - halfBoxWidth, y2 + this.gutterTop, boxWidth, y3 - y2);
1114            }
1115
1116            this.context.strokeRect(x - halfBoxWidth, y1 + this.gutterTop, boxWidth, y3 - y1);
1117            this.context.stroke();
1118
1119            // Now draw the whiskers
1120            this.context.beginPath();
1121            this.context.moveTo(x - halfBoxWidth, y0 + this.gutterTop);
1122            this.context.lineTo(x + halfBoxWidth, y0 + this.gutterTop);
1123
1124            this.context.moveTo(x, y0 + this.gutterTop);
1125            this.context.lineTo(x, y1 + this.gutterTop);
1126
1127            this.context.moveTo(x - halfBoxWidth, y4 + this.gutterTop);
1128            this.context.lineTo(x + halfBoxWidth, y4 + this.gutterTop);
1129
1130            this.context.moveTo(x, y4 + this.gutterTop);
1131            this.context.lineTo(x, y3 + this.gutterTop);
1132
1133            this.context.stroke();
1134        }
1135
1136
1137        /**
1138        * Draw the tickmark, but not for boxplots
1139        */
1140        if (!y0 && !y1 && !y2 && !y3 && !y4) {
1141           
1142            this.graphheight = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom;
1143
1144
1145           
1146            if (tickmarks == 'circle') {
1147                this.context.arc(x, y, halfTickSize, 0, 6.28, 0);
1148                this.context.fillStyle = color;
1149                this.context.fill();
1150           
1151            } else if (tickmarks == 'plus') {
1152
1153                this.context.moveTo(x, y - halfTickSize);
1154                this.context.lineTo(x, y + halfTickSize);
1155                this.context.moveTo(x - halfTickSize, y);
1156                this.context.lineTo(x + halfTickSize, y);
1157                this.context.stroke();
1158           
1159            } else if (tickmarks == 'square') {
1160                this.context.strokeStyle = color;
1161                this.context.fillStyle = color;
1162                this.context.fillRect(
1163                                      x - halfTickSize,
1164                                      y - halfTickSize,
1165                                      tickSize,
1166                                      tickSize
1167                                     );
1168                //this.context.fill();
1169
1170            } else if (tickmarks == 'cross') {
1171
1172                this.context.moveTo(x - halfTickSize, y - halfTickSize);
1173                this.context.lineTo(x + halfTickSize, y + halfTickSize);
1174                this.context.moveTo(x + halfTickSize, y - halfTickSize);
1175                this.context.lineTo(x - halfTickSize, y + halfTickSize);
1176               
1177                this.context.stroke();
1178           
1179            /**
1180            * Diamond shape tickmarks
1181            */
1182            } else if (tickmarks == 'diamond') {
1183                this.context.fillStyle = this.context.strokeStyle;
1184
1185                this.context.moveTo(x, y - halfTickSize);
1186                this.context.lineTo(x + halfTickSize, y);
1187                this.context.lineTo(x, y + halfTickSize);
1188                this.context.lineTo(x - halfTickSize, y);
1189                this.context.lineTo(x, y - halfTickSize);
1190               
1191                this.context.fill();
1192                this.context.stroke();
1193
1194            /**
1195            * Custom tickmark style
1196            */
1197            } else if (typeof(tickmarks) == 'function') {
1198
1199                var graphWidth = RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight
1200                var xVal = ((x - this.gutterLeft) / graphWidth) * xMax;
1201                var yVal = ((this.graphheight - (y - this.gutterTop)) / this.graphheight) * yMax;
1202
1203                tickmarks(this, data, x, y, xVal, yVal, xMax, yMax, color)
1204
1205            /**
1206            * No tickmarks
1207            */
1208            } else if (tickmarks == null) {
1209   
1210            /**
1211            * Unknown tickmark type
1212            */
1213            } else {
1214                alert('[SCATTER] (' + this.id + ') Unknown tickmark style: ' + tickmarks );
1215            }
1216        }
1217
1218        /**
1219        * Add the tickmark to the coords array
1220        */
1221        coords.push([x, y, tooltip]);
1222    }
1223   
1224   
1225    /**
1226    * Draws an optional line connecting the tick marks.
1227    *
1228    * @param i The index of the dataset to use
1229    */
1230    RGraph.Scatter.prototype.DrawLine = function (i)
1231    {
1232        if (this.Get('chart.line') && this.coords[i].length >= 2) {
1233
1234            this.context.lineCap     = 'round';
1235            this.context.lineJoin    = 'round';
1236            this.context.lineWidth   = this.GetLineWidth(i);// i is the index of the set of coordinates
1237            this.context.strokeStyle = this.Get('chart.line.colors')[i];
1238            this.context.beginPath();
1239           
1240            var len = this.coords[i].length;
1241
1242            for (var j=0; j<this.coords[i].length; ++j) {
1243
1244                var xPos = this.coords[i][j][0];
1245                var yPos = this.coords[i][j][1];
1246
1247                if (j == 0) {
1248                    this.context.moveTo(xPos, yPos);
1249                } else {
1250               
1251                    // Stepped?
1252                    var stepped = this.Get('chart.line.stepped');
1253
1254                    if (   (typeof(stepped) == 'boolean' && stepped)
1255                        || (typeof(stepped) == 'object' && stepped[i])
1256                       ) {
1257                        this.context.lineTo(this.coords[i][j][0], this.coords[i][j - 1][1]);
1258                    }
1259
1260                    this.context.lineTo(xPos, yPos);
1261                }
1262            }
1263           
1264            this.context.stroke();
1265        }
1266       
1267        /**
1268        * Set the linewidth back to 1
1269        */
1270        this.context.lineWidth = 1;
1271    }
1272
1273
1274    /**
1275    * Returns the linewidth
1276    *
1277    * @param number i The index of the "line" (/set of coordinates)
1278    */
1279    RGraph.Scatter.prototype.GetLineWidth = function (i)
1280    {
1281        var linewidth = this.Get('chart.line.linewidth');
1282       
1283        if (typeof(linewidth) == 'number') {
1284            return linewidth;
1285       
1286        } else if (typeof(linewidth) == 'object') {
1287            if (linewidth[i]) {
1288                return linewidth[i];
1289            } else {
1290                return linewidth[0];
1291            }
1292
1293            alert('[SCATTER] Error! chart.linewidth should be a single number or an array of one or more numbers');
1294        }
1295    }
1296
1297
1298    /**
1299    * Draws vertical bars. Line chart doesn't use a horizontal scale, hence this function
1300    * is not common
1301    */
1302    RGraph.Scatter.prototype.DrawVBars = function ()
1303    {
1304        var canvas  = this.canvas;
1305        var context = this.context;
1306        var vbars = this.Get('chart.background.vbars');
1307        var graphWidth = RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight;
1308       
1309        if (vbars) {
1310       
1311            var xmax = this.Get('chart.xmax');
1312
1313            for (var i=0; i<vbars.length; ++i) {
1314                var startX = ((vbars[i][0] / xmax) * graphWidth) + this.gutterLeft;
1315                var width  = (vbars[i][1] / xmax) * graphWidth;
1316
1317                context.beginPath();
1318                    context.fillStyle = vbars[i][2];
1319                    context.fillRect(startX, this.gutterTop, width, (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom));
1320                context.fill();
1321            }
1322        }
1323    }
1324
1325
1326
1327
1328
1329    /**
1330    * Draws in-graph labels.
1331    *
1332    * @param object obj The graph object
1333    */
1334    RGraph.Scatter.prototype.DrawInGraphLabels = function (obj)
1335    {
1336        var canvas  = obj.canvas;
1337        var context = obj.context;
1338        var labels  = obj.Get('chart.labels.ingraph');
1339        var labels_processed = [];
1340
1341        // Defaults
1342        var fgcolor   = 'black';
1343        var bgcolor   = 'white';
1344        var direction = 1;
1345
1346        if (!labels) {
1347            return;
1348        }
1349
1350        /**
1351        * Preprocess the labels array. Numbers are expanded
1352        */
1353        for (var i=0; i<labels.length; ++i) {
1354            if (typeof(labels[i]) == 'number') {
1355                for (var j=0; j<labels[i]; ++j) {
1356                    labels_processed.push(null);
1357                }
1358            } else if (typeof(labels[i]) == 'string' || typeof(labels[i]) == 'object') {
1359                labels_processed.push(labels[i]);
1360           
1361            } else {
1362                labels_processed.push('');
1363            }
1364        }
1365
1366        /**
1367        * Turn off any shadow
1368        */
1369        RGraph.NoShadow(obj);
1370
1371        if (labels_processed && labels_processed.length > 0) {
1372
1373            var i=0;
1374
1375            for (var set=0; set<obj.coords.length; ++set) {
1376                for (var point = 0; point<obj.coords[set].length; ++point) {
1377                    if (labels_processed[i]) {
1378                        var x = obj.coords[set][point][0];
1379                        var y = obj.coords[set][point][1];
1380                        var length = typeof(labels_processed[i][4]) == 'number' ? labels_processed[i][4] : 25;
1381                           
1382                        var text_x = x;
1383                        var text_y = y - 5 - length;
1384
1385                        context.moveTo(x, y - 5);
1386                        context.lineTo(x, y - 5 - length);
1387                       
1388                        context.stroke();
1389                        context.beginPath();
1390                       
1391                        // This draws the arrow
1392                        context.moveTo(x, y - 5);
1393                        context.lineTo(x - 3, y - 10);
1394                        context.lineTo(x + 3, y - 10);
1395                        context.closePath();
1396
1397
1398                        context.beginPath();
1399                           
1400                            // Fore ground color
1401                            context.fillStyle = (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][1]) == 'string') ? labels_processed[i][1] : 'black';
1402
1403                            RGraph.Text(context,
1404                                        obj.Get('chart.text.font'),
1405                                        obj.Get('chart.text.size'),
1406                                        text_x,
1407                                        text_y,
1408                                        (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][0]) == 'string') ? labels_processed[i][0] : labels_processed[i],
1409                                        'bottom',
1410                                        'center',
1411                                        true,
1412                                        null,
1413                                        (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][2]) == 'string') ? labels_processed[i][2] : 'white');
1414                        context.fill();
1415                    }
1416                   
1417                    i++;
1418                }
1419            }
1420        }
1421    }
1422
1423
1424    /**
1425    * This function makes it much easier to get the (if any) point that is currently being hovered over.
1426    *
1427    * @param object e The event object
1428    */
1429    RGraph.Scatter.prototype.getPoint = function (e)
1430    {
1431        var canvas      = e.target;
1432        var obj         = canvas.__object__;
1433        var context     = obj.context;
1434        var context     = obj.context;
1435        var mouseXY     = RGraph.getMouseXY(e);
1436        var mouseX      = mouseXY[0];
1437        var mouseY      = mouseXY[1];
1438        var overHotspot = false;
1439        var offset = obj.Get('chart.tooltips.hotspot'); // This is how far the hotspot extends
1440
1441        for (var set=0; set<obj.coords.length; ++set) {
1442            for (var i=0; i<obj.coords[set].length; ++i) {
1443               
1444                var xCoord = obj.coords[set][i][0];
1445                var yCoord = obj.coords[set][i][1];
1446
1447                if (mouseX <= (xCoord + offset) &&
1448                    mouseX >= (xCoord - offset) &&
1449                    mouseY <= (yCoord + offset) &&
1450                    mouseY >= (yCoord - offset)) {
1451                   
1452                    return [xCoord, yCoord, set, i, obj.data[set][i][3]];
1453                }
1454            }
1455        }
1456    }
1457
1458
1459    /**
1460    * Draws the above line labels
1461    */
1462    RGraph.Scatter.prototype.DrawAboveLabels = function ()
1463    {
1464        var context    = this.context;
1465        var size       = this.Get('chart.labels.above.size');
1466        var font       = this.Get('chart.text.font');
1467        var units_pre  = this.Get('chart.units.pre');
1468        var units_post = this.Get('chart.units.post');
1469
1470
1471        for (var set=0; set<this.coords.length; ++set) {
1472            for (var point=0; point<this.coords[set].length; ++point) {
1473               
1474                var x_val = this.data[set][point][0];
1475                var y_val = this.data[set][point][1];
1476               
1477               
1478                var x_pos = this.coords[set][point][0];
1479                var y_pos = this.coords[set][point][1];
1480
1481                RGraph.Text(context,
1482                            font,
1483                            size,
1484                            x_pos,
1485                            y_pos - 5 - size,
1486                            x_val.toFixed(this.Get('chart.labels.above.decimals')) + ', ' + y_val.toFixed(this.Get('chart.labels.above.decimals')),
1487                            'center',
1488                            'center',
1489                            true,
1490                            null,
1491                            'rgba(255, 255, 255, 0.7)');
1492            }
1493        }
1494    }
Note: See TracBrowser for help on using the repository browser.