merged cont.
[opensuse:yast-rest-service.git] / webyast / public / javascripts / jquery.jqplot.js
1 /**
2  * Title: jqPlot Charts
3  * 
4  * Pure JavaScript plotting plugin for jQuery.
5  * 
6  * About: Version
7  * 
8  * 0.9.7 
9  * 
10  * About: Copyright & License
11  * 
12  * Copyright (c) 2009 Chris Leonello
13  * jqPlot is currently available for use in all personal or commercial projects 
14  * under both the MIT and GPL version 2.0 licenses. This means that you can 
15  * choose the license that best suits your project and use it accordingly.
16  * 
17  * See <GPL Version 2> and <MIT License> contained within this distribution for further information. 
18  *
19  * The author would appreciate an email letting him know of any substantial
20  * use of jqPlot.  You can reach the author at: chris dot leonello at gmail 
21  * dot com or see http://www.jqplot.com/info.php.  This is, of course, not required.
22  *
23  * If you are feeling kind and generous, consider supporting the project by
24  * making a donation at: http://www.jqplot.com/donate.php.
25  *
26  * 
27  * About: Introduction
28  * 
29  * jqPlot requires jQuery (tested with 1.3.2 or better). jQuery 1.3.2 is included in the distribution.  
30  * To use jqPlot include jQuery, the jqPlot jQuery plugin, the jqPlot css file and optionally 
31  * the excanvas script for IE support in your web page:
32  * 
33  * > <!--[if IE]><script language="javascript" type="text/javascript" src="excanvas.js"></script><![endif]-->
34  * > <script language="javascript" type="text/javascript" src="jquery-1.3.2.min.js"></script>
35  * > <script language="javascript" type="text/javascript" src="jquery.jqplot.min.js"></script>
36  * > <link rel="stylesheet" type="text/css" href="jquery.jqplot.css" />
37  * 
38  * jqPlot can be customized by overriding the defaults of any of the objects which make
39  * up the plot. The general usage of jqplot is:
40  * 
41  * > chart = $.jqplot('targetElemId', [dataArray,...], {optionsObject});
42  * 
43  * The options available to jqplot are detailed in <jqPlot Options> in the jqPlotOptions.txt file.
44  * 
45  * An actual call to $.jqplot() may look like the 
46  * examples below:
47  * 
48  * > chart = $.jqplot('chartdiv',  [[[1, 2],[3,5.12],[5,13.1],[7,33.6],[9,85.9],[11,219.9]]]);
49  * 
50  * or
51  * 
52  * > dataArray = [34,12,43,55,77];
53  * > chart = $.jqplot('targetElemId', [dataArray, ...], {title:'My Plot', axes:{yaxis:{min:20, max:100}}});
54  * 
55  * For more inforrmation, see <jqPlot Usage>.
56  * 
57  * About: Usage
58  * 
59  * See <jqPlot Usage>
60  * 
61  * About: Available Options 
62  * 
63  * See <jqPlot Options> for a list of options available thorugh the options object (not complete yet!)
64  * 
65  * About: Options Usage
66  * 
67  * See <Options Tutorial>
68  * 
69  * About: Changes
70  * 
71  * See <Change Log>
72  * 
73  */
74
75 (function($) {
76     // make sure undefined is undefined
77     var undefined;
78
79     /**
80      * Class: $.jqplot
81      * jQuery function called by the user to create a plot.
82      *  
83      * Parameters:
84      * target - ID of target element to render the plot into.
85      * data - an array of data series.
86      * options - user defined options object.  See the individual classes for available options.
87      * 
88      * Properties:
89      * config - object to hold configuration information for jqPlot plot object.
90      * 
91      * attributes:
92      * enablePlugins - False to disable plugins by default.  Plugins must then be explicitly 
93      *   enabled in the individual plot options.  Default: true.
94      *   This property sets the "show" property of certain plugins to true or false.
95      *   Only plugins that can be immediately active upon loading are affected.  This includes
96      *   non-renderer plugins like cursor, dragable, highlighter, and trendline.
97      * defaultHeight - Default height for plots where no css height specification exists.  This
98      *   is a jqplot wide default.
99      * defaultWidth - Default height for plots where no css height specification exists.  This
100      *   is a jqplot wide default.
101      */
102
103     $.jqplot = function(target, data, options) {
104         var _data, _options;
105         
106         // check to see if only 2 arguments were specified, what is what.
107         if (data == null) {
108             throw "No data specified";
109         }
110         if (data.constructor == Array && data.length == 0 || data[0].constructor != Array) {
111             throw "Improper Data Array";
112         }
113         if (options == null) {
114             if (data instanceof Array) {
115                 _data = data;
116                 _options = null;   
117             }
118             
119             else if (data.constructor == Object) {
120                 _data = null;
121                 _options = data;
122             }
123         }
124         else {
125             _data = data;
126             _options = options;
127         }
128         var plot = new jqPlot();
129         plot.init(target, _data, _options);
130         plot.draw();
131         return plot;
132     };
133         
134     $.jqplot.debug = 1;
135
136     // path to jqplot install, relative to the script that is including jqplot.
137 //     $.jqplot.installPath = 'jqplot';
138 //     alert($.jqplot.installPath);
139 //     $.jqplot.pluginsPath = '/plugins';
140 //     alert($.jqplot.pluginsPath);
141
142     $.jqplot.config = {
143         debug:1,
144         enablePlugins:true,
145         defaultHeight:300,
146         defaultWidth:400
147     };
148     
149     $.jqplot.enablePlugins = $.jqplot.config.enablePlugins;
150     
151     /**
152      * 
153      * Hooks: jqPlot Pugin Hooks
154      * 
155      * $.jqplot.preInitHooks - called before initialization.
156      * $.jqplot.postInitHooks - called after initialization.
157      * $.jqplot.preParseOptionsHooks - called before user options are parsed.
158      * $.jqplot.postParseOptionsHooks - called after user options are parsed.
159      * $.jqplot.preDrawHooks - called before plot draw.
160      * $.jqplot.postDrawHooks - called after plot draw.
161      * $.jqplot.preDrawSeriesHooks - called before each series is drawn.
162      * $.jqplot.postDrawSeriesHooks - called after each series is drawn.
163      * $.jqplot.preDrawLegendHooks - called before the legend is drawn.
164      * $.jqplot.addLegendRowHooks - called at the end of legend draw, so plugins
165      *     can add rows to the legend table.
166      * $.jqplot.preSeriesInitHooks - called before series is initialized.
167      * $.jqplot.postSeriesInitHooks - called after series is initialized.
168      * $.jqplot.preParseSeriesOptionsHooks - called before series related options
169      *     are parsed.
170      * $.jqplot.postParseSeriesOptionsHooks - called after series related options
171      *     are parsed.
172      * $.jqplot.eventListenerHooks - called at the end of plot drawing, binds
173      *     listeners to the event canvas which lays on top of the grid area.
174      * $.jqplot.preDrawSeriesShadowHooks - called before series shadows are drawn.
175      * $.jqplot.postDrawSeriesShadowHooks - called after series shadows are drawn.
176      * 
177      */
178     
179     $.jqplot.preInitHooks = [];
180     $.jqplot.postInitHooks = [];
181     $.jqplot.preParseOptionsHooks = [];
182     $.jqplot.postParseOptionsHooks = [];
183     $.jqplot.preDrawHooks = [];
184     $.jqplot.postDrawHooks = [];
185     $.jqplot.preDrawSeriesHooks = [];
186     $.jqplot.postDrawSeriesHooks = [];
187     $.jqplot.preDrawLegendHooks = [];
188     $.jqplot.addLegendRowHooks = [];
189     $.jqplot.preSeriesInitHooks = [];
190     $.jqplot.postSeriesInitHooks = [];
191     $.jqplot.preParseSeriesOptionsHooks = [];
192     $.jqplot.postParseSeriesOptionsHooks = [];
193     $.jqplot.eventListenerHooks = [];
194     $.jqplot.preDrawSeriesShadowHooks = [];
195     $.jqplot.postDrawSeriesShadowHooks = [];
196
197     // A superclass holding some common properties and methods.
198     $.jqplot.ElemContainer = function() {
199         this._elem;
200         this._plotWidth;
201         this._plotHeight;
202         this._plotDimensions = {height:null, width:null};
203     };
204     
205     $.jqplot.ElemContainer.prototype.getWidth = function() {
206         if (this._elem) {
207             return this._elem.outerWidth(true);
208         }
209         else {
210             return null;
211         }
212     };
213     
214     $.jqplot.ElemContainer.prototype.getHeight = function() {
215         if (this._elem) {
216             return this._elem.outerHeight(true);
217         }
218         else {
219             return null;
220         }
221     };
222     
223     $.jqplot.ElemContainer.prototype.getPosition = function() {
224         if (this._elem) {
225             return this._elem.position();
226         }
227         else {
228             return {top:null, left:null, bottom:null, right:null};
229         }
230     };
231     
232     $.jqplot.ElemContainer.prototype.getTop = function() {
233         return this.getPosition().top;
234     };
235     
236     $.jqplot.ElemContainer.prototype.getLeft = function() {
237         return this.getPosition().left;
238     };
239     
240     $.jqplot.ElemContainer.prototype.getBottom = function() {
241         return this._elem.css('bottom');
242     };
243     
244     $.jqplot.ElemContainer.prototype.getRight = function() {
245         return this._elem.css('right');
246     };
247     
248
249     /**
250      * Class: Axis
251      * An individual axis object.  Cannot be instantiated directly, but created
252      * by the Plot oject.  Axis properties can be set or overriden by the 
253      * options passed in from the user.
254      * 
255      */
256     function Axis(name) {
257         $.jqplot.ElemContainer.call(this);
258         // Group: Properties
259         //
260         // Axes options are specified within an axes object at the top level of the 
261         // plot options like so:
262         // > {
263         // >    axes: {
264         // >        xaxis: {min: 5},
265         // >        yaxis: {min: 2, max: 8, numberTicks:4},
266         // >        x2axis: {pad: 1.5},
267         // >        y2axis: {ticks:[22, 44, 66, 88]}
268         // >        }
269         // > }
270         // There are 4 axes, 'xaxis', 'yaxis', 'x2axis', 'y2axis'.  Any or all of 
271         // which may be specified.
272         this.name = name;
273         this._series = [];
274         // prop: show
275         // Wether to display the axis on the graph.
276         this.show = false;
277         // prop: tickRenderer
278         // A class of a rendering engine for creating the ticks labels displayed on the plot, 
279         // See <$.jqplot.AxisTickRenderer>.
280         this.tickRenderer = $.jqplot.AxisTickRenderer;
281         // prop: tickOptions
282         // Options that will be passed to the tickRenderer, see <$.jqplot.AxisTickRenderer> options.
283         this.tickOptions = {};
284         // prop: labelRenderer
285         // A class of a rendering engine for creating an axis label.
286         this.labelRenderer = $.jqplot.AxisLabelRenderer;
287         // prop: labelOptions
288         // Options passed to the label renderer.
289         this.labelOptions = {};
290         // prop: label
291         // Label for the axis
292         this.label = null;
293         // prop: showLabel
294         // true to show the axis label.
295         this.showLabel = true;
296         // prop: min
297         // minimum value of the axis (in data units, not pixels).
298         this.min=null;
299         // prop: max
300         // maximum value of the axis (in data units, not pixels).
301         this.max=null;
302         // prop: autoscale
303         // Autoscale the axis min and max values to provide sensible tick spacing.
304         // If axis min or max are set, autoscale will be turned off.
305         // The numberTicks, tickInterval and pad options do work with 
306         // autoscale, although tickInterval has not been tested yet.
307         // padMin and padMax do nothing when autoscale is on.
308         this.autoscale = false;
309         // prop: pad
310         // Padding to extend the range above and below the data bounds.
311         // The data range is multiplied by this factor to determine minimum and maximum axis bounds.
312         // A value of 0 will be interpreted to mean no padding, and pad will be set to 1.0.
313         this.pad = 1.2;
314         // prop: padMax
315         // Padding to extend the range above data bounds.
316         // The top of the data range is multiplied by this factor to determine maximum axis bounds.
317         // A value of 0 will be interpreted to mean no padding, and padMax will be set to 1.0.
318         this.padMax = null;
319         // prop: padMin
320         // Padding to extend the range below data bounds.
321         // The bottom of the data range is multiplied by this factor to determine minimum axis bounds.
322         // A value of 0 will be interpreted to mean no padding, and padMin will be set to 1.0.
323         this.padMin = null;
324         // prop: ticks
325         // 1D [val, val, ...] or 2D [[val, label], [val, label], ...] array of ticks for the axis.
326         // If no label is specified, the value is formatted into an appropriate label.
327         this.ticks = [];
328         // prop: numberTicks
329         // Desired number of ticks.  Default is to compute automatically.
330         this.numberTicks;
331         // prop: tickInterval
332         // number of units between ticks.  Mutually exclusive with numberTicks.
333         this.tickInterval;
334         // prop: renderer
335         // A class of a rendering engine that handles tick generation, 
336         // scaling input data to pixel grid units and drawing the axis element.
337         this.renderer = $.jqplot.LinearAxisRenderer;
338         // prop: rendererOptions
339         // renderer specific options.  See <$.jqplot.LinearAxisRenderer> for options.
340         this.rendererOptions = {};
341         // prop: showTicks
342         // wether to show the ticks (both marks and labels) or not.
343         this.showTicks = true;
344         // prop: showTickMarks
345         // wether to show the tick marks (line crossing grid) or not.
346         this.showTickMarks = true;
347         // prop: showMinorTicks
348         // Wether or not to show minor ticks.  This is renderer dependent.
349         // The default <$.jqplot.LinearAxisRenderer> does not have minor ticks.
350         this.showMinorTicks = true;
351         // prop: useSeriesColor
352         // Use the color of the first series associated with this axis for the
353         // tick marks and line bordering this axis.
354         this.useSeriesColor = false;
355         // prop: borderWidth
356         // width of line stroked at the border of the axis.  Defaults
357         // to the width of the grid boarder.
358         this.borderWidth = null;
359         // prop: borderColor
360         // color of the border adjacent to the axis.  Defaults to grid border color.
361         this.borderColor = null;
362         // minimum and maximum values on the axis.
363         this._dataBounds = {min:null, max:null};
364         // pixel position from the top left of the min value and max value on the axis.
365         this._offsets = {min:null, max:null};
366         this._ticks=[];
367         this._label = null;
368         // prop: syncTicks
369         // true to try and synchronize tick spacing across multiple axes so that ticks and
370         // grid lines line up.  This has an impact on autoscaling algorithm, however.
371         // In general, autoscaling an individual axis will work better if it does not
372         // have to sync ticks.
373         this.syncTicks = null;
374         // prop: tickSpacing
375         // Approximate pixel spacing between ticks on graph.  Used during autoscaling.
376         // This number will be an upper bound, actual spacing will be less.
377         this.tickSpacing = 75;
378         // Properties to hold the original values for min, max, ticks, tickInterval and numberTicks
379         // so they can be restored if altered by plugins.
380         this._min = null;
381         this._max = null;
382         this._tickInterval = null;
383         this._numberTicks = null;
384         this.__ticks = null;
385     }
386     
387     Axis.prototype = new $.jqplot.ElemContainer();
388     Axis.prototype.constructor = Axis;
389     
390     Axis.prototype.init = function() {
391         this.renderer = new this.renderer();
392         // set the axis name
393         this.tickOptions.axis = this.name;
394         if (this.label == null || this.label == '') {
395             this.showLabel = false;
396         }
397         else {
398             this.labelOptions.label = this.label;
399         }
400         if (this.showLabel == false) {
401             this.labelOptions.show = false;
402         }
403         // set the default padMax, padMin if not specified
404         // special check, if no padding desired, padding
405         // should be set to 1.0
406         if (this.pad == 0) {
407             this.pad = 1.0;
408         }
409         if (this.padMax == 0) {
410             this.padMax = 1.0;
411         }
412         if (this.padMin == 0) {
413             this.padMin = 1.0;
414         }
415         if (this.padMax == null) {
416             this.padMax = (this.pad-1)/2 + 1;
417         }
418         if (this.padMin == null) {
419             this.padMin = (this.pad-1)/2 + 1;
420         }
421         // now that padMin and padMax are correctly set, reset pad in case user has supplied 
422         // padMin and/or padMax
423         this.pad = this.padMax + this.padMin - 1;
424         if (this.min != null || this.max != null) {
425             this.autoscale = false;
426         }
427         // if not set, sync ticks for y axes but not x by default.
428         if (this.syncTicks == null && this.name.indexOf('y') > -1) {
429             this.syncTicks = true;
430         }
431         else if (this.syncTicks == null){
432             this.syncTicks = false;
433         }
434         this.renderer.init.call(this, this.rendererOptions);
435         
436     };
437     
438     Axis.prototype.draw = function(ctx) {
439         return this.renderer.draw.call(this, ctx);
440         
441     };
442     
443     Axis.prototype.set = function() {
444         this.renderer.set.call(this);
445     };
446     
447     Axis.prototype.pack = function(pos, offsets) {
448         if (this.show) {
449             this.renderer.pack.call(this, pos, offsets);
450         }
451         // these properties should all be available now.
452         if (this._min == null) {
453             this._min = this.min;
454             this._max = this.max;
455             this._tickInterval = this.tickInterval;
456             this._numberTicks = this.numberTicks;
457             this.__ticks = this._ticks;
458         }
459     };
460     
461     // reset the axis back to original values if it has been scaled, zoomed, etc.
462     Axis.prototype.reset = function() {
463         this.renderer.reset.call(this);
464     };
465     
466     Axis.prototype.resetScale = function() {
467         this.min = null;
468         this.max = null;
469         this.numberTicks = null;
470         this.tickInterval = null;
471     };
472
473     /**
474      * Class: Legend
475      * Legend object.  Cannot be instantiated directly, but created
476      * by the Plot oject.  Legend properties can be set or overriden by the 
477      * options passed in from the user.
478      */
479     function Legend(options) {
480         $.jqplot.ElemContainer.call(this);
481         // Group: Properties
482         
483         // prop: show
484         // Wether to display the legend on the graph.
485         this.show = false;
486         // prop: location
487         // Placement of the legend.  one of the compass directions: nw, n, ne, e, se, s, sw, w
488         this.location = 'ne';
489         // prop: xoffset
490         // offset from the inside edge of the plot in the x direction in pixels.
491         this.xoffset = 12;
492         // prop: yoffset
493         // offset from the inside edge of the plot in the y direction in pixels.
494         this.yoffset = 12;
495         // prop: border
496         // css spec for the border around the legend box.
497         this.border;
498         // prop: background
499         // css spec for the background of the legend box.
500         this.background;
501         // prop: textColor
502         // css color spec for the legend text.
503         this.textColor;
504         // prop: fontFamily
505         // css font-family spec for the legend text.
506         this.fontFamily; 
507         // prop: fontSize
508         // css font-size spec for the legend text.
509         this.fontSize ;
510         // prop: rowSpacing
511         // css padding-top spec for the rows in the legend.
512         this.rowSpacing = '0.5em';
513         // renderer
514         // A class that will create a DOM object for the legend,
515         // see <$.jqplot.TableLegendRenderer>.
516         this.renderer = $.jqplot.TableLegendRenderer;
517         // prop: rendererOptions
518         // renderer specific options passed to the renderer.
519         this.rendererOptions = {};
520         // prop: predraw
521         // Wether to draw the legend before the series or not.
522         this.preDraw = false;
523         this.escapeHtml = false;
524         this._series = [];
525         
526         $.extend(true, this, options);
527     }
528     
529     Legend.prototype = new $.jqplot.ElemContainer();
530     Legend.prototype.constructor = Legend;
531     
532     Legend.prototype.init = function() {
533         this.renderer = new this.renderer();
534         this.renderer.init.call(this, this.rendererOptions);
535     };
536     
537     Legend.prototype.draw = function(offsets) {
538         for (var i=0; i<$.jqplot.preDrawLegendHooks.length; i++){
539             $.jqplot.preDrawLegendHooks[i].call(this, offsets);
540         }
541         return this.renderer.draw.call(this, offsets);
542     };
543     
544     Legend.prototype.pack = function(offsets) {
545         this.renderer.pack.call(this, offsets);
546     };
547
548     /**
549      * Class: Title
550      * Plot Title object.  Cannot be instantiated directly, but created
551      * by the Plot oject.  Title properties can be set or overriden by the 
552      * options passed in from the user.
553      * 
554      * Parameters:
555      * text - text of the title.
556      */
557     function Title(text) {
558         $.jqplot.ElemContainer.call(this);
559         // Group: Properties
560         
561         // prop: text
562         // text of the title;
563         this.text = text;
564         // prop: show
565         // wether or not to show the title
566         this.show = true;
567         // prop: fontFamily
568         // css font-family spec for the text.
569         this.fontFamily;
570         // prop: fontSize
571         // css font-size spec for the text.
572         this.fontSize ;
573         // prop: textAlign
574         // css text-align spec for the text.
575         this.textAlign;
576         // prop: textColor
577         // css color spec for the text.
578         this.textColor;
579         // prop: renderer
580         // A class for creating a DOM element for the title,
581         // see <$.jqplot.DivTitleRenderer>.
582         this.renderer = $.jqplot.DivTitleRenderer;
583         // prop: rendererOptions
584         // renderer specific options passed to the renderer.
585         this.rendererOptions = {};   
586     }
587     
588     Title.prototype = new $.jqplot.ElemContainer();
589     Title.prototype.constructor = Title;
590     
591     Title.prototype.init = function() {
592         this.renderer = new this.renderer();
593         this.renderer.init.call(this, this.rendererOptions);
594     };
595     
596     Title.prototype.draw = function(width) {
597         return this.renderer.draw.call(this, width);
598     };
599     
600     Title.prototype.pack = function() {
601         this.renderer.pack.call(this);
602     };
603
604
605     /**
606      * Class: Series
607      * An individual data series object.  Cannot be instantiated directly, but created
608      * by the Plot oject.  Series properties can be set or overriden by the 
609      * options passed in from the user.
610      */
611     function Series() {
612         $.jqplot.ElemContainer.call(this);
613         // Group: Properties
614         // Properties will be assigned from a series array at the top level of the
615         // options.  If you had two series and wanted to change the color and line
616         // width of the first and set the second to use the secondary y axis with
617         // no shadow and supply custom labels for each:
618         // > {
619         // >    series:[
620         // >        {color: '#ff4466', lineWidth: 5, label:'good line'},
621         // >        {yaxis: 'y2axis', shadow: false, label:'bad line'}
622         // >    ]
623         // > }
624         
625         // prop: show
626         // wether or not to draw the series.
627         this.show = true;
628         // prop: xaxis
629         // which x axis to use with this series, either 'xaxis' or 'x2axis'.
630         this.xaxis = 'xaxis';
631         this._xaxis;
632         // prop: yaxis
633         // which y axis to use with this series, either 'yaxis' or 'y2axis'.
634         this.yaxis = 'yaxis';
635         this._yaxis;
636         this.gridBorderWidth = 2.0;
637         // prop: renderer
638         // A class of a renderer which will draw the series, 
639         // see <$.jqplot.LineRenderer>.
640         this.renderer = $.jqplot.LineRenderer;
641         // prop: rendererOptions
642         // Options to pass on to the renderer.
643         this.rendererOptions = {};
644         this.data = [];
645         this.gridData = [];
646         // prop: label
647         // Line label to use in the legend.
648         this.label = '';
649         // prop: showLabel
650         // true to show label for this series in the legend.
651         this.showLabel = true;
652         // prop: color
653         // css color spec for the series
654         this.color;
655         // prop: lineWidth
656         // width of the line in pixels.  May have different meanings depending on renderer.
657         this.lineWidth = 2.5;
658         // prop: shadow
659         // wether or not to draw a shadow on the line
660         this.shadow = true;
661         // prop: shadowAngle
662         // Shadow angle in degrees
663         this.shadowAngle = 45;
664         // prop: shadowOffset
665         // Shadow offset from line in pixels
666         this.shadowOffset = 1.25;
667         // prop: shadowDepth
668         // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
669         this.shadowDepth = 3;
670         // prop: shadowAlpha
671         // Alpha channel transparency of shadow.  0 = transparent.
672         this.shadowAlpha = '0.1';
673         // prop: breakOnNull
674         // Not implemented. wether line segments should be be broken at null value.
675         // False will join point on either side of line.
676         this.breakOnNull = false;
677         // prop: markerRenderer
678         // A class of a renderer which will draw marker (e.g. circle, square, ...) at the data points,
679         // see <$.jqplot.MarkerRenderer>.
680         this.markerRenderer = $.jqplot.MarkerRenderer;
681         // prop: markerOptions
682         // renderer specific options to pass to the markerRenderer,
683         // see <$.jqplot.MarkerRenderer>.
684         this.markerOptions = {};
685         // prop: showLine
686         // wether to actually draw the line or not.  Series will still be renderered, even if no line is drawn.
687         this.showLine = true;
688         // prop: showMarker
689         // wether or not to show the markers at the data points.
690         this.showMarker = true;
691         // prop: index
692         // 0 based index of this series in the plot series array.
693         this.index;
694         // prop: fill
695         // true or false, wether to fill under lines or in bars.
696         // May not be implemented in all renderers.
697         this.fill = false;
698         // prop: fillColor
699         // CSS color spec to use for fill under line.  Defaults to line color.
700         this.fillColor;
701         // prop: fillAlpha
702         // Alpha transparency to apply to the fill under the line.
703         // Use this to adjust alpha separate from fill color.
704         this.fillAlpha;
705         // prop: fillAndStroke
706         // If true will stroke the line (with color this.color) as well as fill under it.
707         // Applies only when fill is true.
708         this.fillAndStroke = false;
709         // prop: disableStack
710         // true to not stack this series with other series in the plot.
711         // To render properly, non-stacked series must come after any stacked series
712         // in the plot's data series array.  So, the plot's data series array would look like:
713         // > [stackedSeries1, stackedSeries2, ..., nonStackedSeries1, nonStackedSeries2, ...]
714         // disableStack will put a gap in the stacking order of series, and subsequent
715         // stacked series will not fill down through the non-stacked series and will
716         // most likely not stack properly on top of the non-stacked series.
717         this.disableStack = false;
718         // _stack is set by the Plot if the plot is a stacked chart.
719         // will stack lines or bars on top of one another to build a "mountain" style chart.
720         // May not be implemented in all renderers.
721         this._stack = false;
722         // prop: neighborThreshold
723         // how close or far (in pixels) the cursor must be from a point marker to detect the point.
724         this.neighborThreshold = 4;
725         // prop: fillToZero
726         // true will force bar and filled series to fill toward zero on the fill Axis.
727         this.fillToZero = false;
728         // prop: fillAxis
729         // Either 'x' or 'y'.  Which axis to fill the line toward if fillToZero is true.
730         // 'y' means fill up/down to 0 on the y axis for this series.
731         this.fillAxis = 'y';
732         // prop: useNegativeColors
733         // true to color negative values differently in filled and bar charts.
734         this.useNegativeColors = true;
735         this._stackData = [];
736         // _plotData accounts for stacking.  If plots not stacked, _plotData and data are same.  If
737         // stacked, _plotData is accumulation of stacking data.
738         this._plotData = [];
739         // _plotValues hold the individual x and y values that will be plotted for this series.
740         this._plotValues = {x:[], y:[]};
741         // statistics about the intervals between data points.  Used for auto scaling.
742         this._intervals = {x:{}, y:{}};
743         // data from the previous series, for stacked charts.
744         this._prevPlotData = [];
745         this._prevGridData = [];
746         this._stackAxis = 'y';
747         this._primaryAxis = '_xaxis';
748         // give each series a canvas to draw on.  This should allow for redrawing speedups.
749         this.canvas = new $.jqplot.GenericCanvas();
750         this.shadowCanvas = new $.jqplot.GenericCanvas();
751         this.plugins = {};
752         // sum of y values in this series.
753         this._sumy = 0;
754         this._sumx = 0;
755     }
756     
757     Series.prototype = new $.jqplot.ElemContainer();
758     Series.prototype.constructor = Series;
759     
760     Series.prototype.init = function(index, gridbw, plot) {
761         // weed out any null values in the data.
762         this.index = index;
763         this.gridBorderWidth = gridbw;
764         var d = this.data;
765         for (var i=0; i<d.length; i++) {
766             if (! this.breakOnNull) {
767                 if (d[i] == null || d[i][0] == null || d[i][1] == null) {
768                     d.splice(i,1);
769                     continue;
770                 }
771             }
772             else {
773                 if (d[i] == null || d[i][0] == null || d[i][1] == null) {
774                     // TODO: figure out what to do with null values
775                     var undefined;
776                 }
777             }
778         }
779         if (!this.fillColor) {
780             this.fillColor = this.color;
781         }
782         if (this.fillAlpha) {
783             var comp = $.jqplot.normalize2rgb(this.fillColor);
784             var comp = $.jqplot.getColorComponents(comp);
785             this.fillColor = 'rgba('+comp[0]+','+comp[1]+','+comp[2]+','+this.fillAlpha+')';
786         }
787         this.renderer = new this.renderer();
788         this.renderer.init.call(this, this.rendererOptions, plot);
789         this.markerRenderer = new this.markerRenderer();
790         if (!this.markerOptions.color) {
791             this.markerOptions.color = this.color;
792         }
793         if (this.markerOptions.show == null) {
794             this.markerOptions.show = this.showMarker;
795         }
796         // the markerRenderer is called within it's own scaope, don't want to overwrite series options!!
797         this.markerRenderer.init(this.markerOptions);
798     };
799     
800     // data - optional data point array to draw using this series renderer
801     // gridData - optional grid data point array to draw using this series renderer
802     // stackData - array of cumulative data for stacked plots.
803     Series.prototype.draw = function(sctx, opts, plot) {
804         var options = (opts == undefined) ? {} : opts;
805         sctx = (sctx == undefined) ? this.canvas._ctx : sctx;
806         // hooks get called even if series not shown
807         // we don't clear canvas here, it would wipe out all other series as well.
808         for (var j=0; j<$.jqplot.preDrawSeriesHooks.length; j++) {
809             $.jqplot.preDrawSeriesHooks[j].call(this, sctx, options);
810         }
811         if (this.show) {
812             this.renderer.setGridData.call(this, plot);
813             if (!options.preventJqPlotSeriesDrawTrigger) {
814                 $(sctx.canvas).trigger('jqplotSeriesDraw', [this.data, this.gridData]);
815             }
816             var data = [];
817             if (options.data) {
818                 data = options.data;
819             }
820             else if (!this._stack) {
821                 data = this.data;
822             }
823             else {
824                 data = this._plotData;
825             }
826             var gridData = options.gridData || this.renderer.makeGridData.call(this, data, plot);
827             this.renderer.draw.call(this, sctx, gridData, options);
828         }
829         
830         for (var j=0; j<$.jqplot.postDrawSeriesHooks.length; j++) {
831             $.jqplot.postDrawSeriesHooks[j].call(this, sctx, options);
832         }
833     };
834     
835     Series.prototype.drawShadow = function(sctx, opts, plot) {
836         var options = (opts == undefined) ? {} : opts;
837         sctx = (sctx == undefined) ? this.shadowCanvas._ctx : sctx;
838         // hooks get called even if series not shown
839         // we don't clear canvas here, it would wipe out all other series as well.
840         for (var j=0; j<$.jqplot.preDrawSeriesShadowHooks.length; j++) {
841             $.jqplot.preDrawSeriesShadowHooks[j].call(this, sctx, options);
842         }
843         if (this.shadow) {
844             this.renderer.setGridData.call(this, plot);
845
846             var data = [];
847             if (options.data) {
848                 data = options.data;
849             }
850             else if (!this._stack) {
851                 data = this.data;
852             }
853             else {
854                 data = this._plotData;
855             }
856             var gridData = options.gridData || this.renderer.makeGridData.call(this, data, plot);
857         
858             this.renderer.drawShadow.call(this, sctx, gridData, options);
859         }
860         
861         for (var j=0; j<$.jqplot.postDrawSeriesShadowHooks.length; j++) {
862             $.jqplot.postDrawSeriesShadowHooks[j].call(this, sctx, options);
863         }
864         
865     };
866     
867
868
869     /**
870      * Class: Grid
871      * 
872      * Object representing the grid on which the plot is drawn.  The grid in this
873      * context is the area bounded by the axes, the area which will contain the series.
874      * Note, the series are drawn on their own canvas.
875      * The Grid object cannot be instantiated directly, but is created by the Plot oject.  
876      * Grid properties can be set or overriden by the options passed in from the user.
877      */
878     function Grid() {
879         $.jqplot.ElemContainer.call(this);
880         // Group: Properties
881         
882         // prop: drawGridlines
883         // wether to draw the gridlines on the plot.
884         this.drawGridlines = true;
885         // prop: gridLineColor
886         // color of the grid lines.
887         this.gridLineColor = '#cccccc';
888         // prop: gridLineWidth
889         // width of the grid lines.
890         this.gridLineWidth = 1.0;
891         // prop: background
892         // css spec for the background color.
893         this.background = '#fffdf6';
894         // prop: borderColor
895         // css spec for the color of the grid border.
896         this.borderColor = '#999999';
897         // prop: borderWidth
898         // width of the border in pixels.
899         this.borderWidth = 2.0;
900         // prop: shadow
901         // wether to show a shadow behind the grid.
902         this.shadow = true;
903         // prop: shadowAngle
904         // shadow angle in degrees
905         this.shadowAngle = 45;
906         // prop: shadowOffset
907         // Offset of each shadow stroke from the border in pixels
908         this.shadowOffset = 1.5;
909         // prop: shadowWidth
910         // width of the stoke for the shadow
911         this.shadowWidth = 3;
912         // prop: shadowDepth
913         // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
914         this.shadowDepth = 3;
915         // prop: shadowAlpha
916         // Alpha channel transparency of shadow.  0 = transparent.
917         this.shadowAlpha = '0.07';
918         this._left;
919         this._top;
920         this._right;
921         this._bottom;
922         this._width;
923         this._height;
924         this._axes = [];
925         // prop: renderer
926         // Instance of a renderer which will actually render the grid,
927         // see <$.jqplot.CanvasGridRenderer>.
928         this.renderer = $.jqplot.CanvasGridRenderer;
929         // prop: rendererOptions
930         // Options to pass on to the renderer,
931         // see <$.jqplot.CanvasGridRenderer>.
932         this.rendererOptions = {};
933         this._offsets = {top:null, bottom:null, left:null, right:null};
934     }
935     
936     Grid.prototype = new $.jqplot.ElemContainer();
937     Grid.prototype.constructor = Grid;
938     
939     Grid.prototype.init = function() {
940         this.renderer = new this.renderer();
941         this.renderer.init.call(this, this.rendererOptions);
942     };
943     
944     Grid.prototype.createElement = function(offsets) {
945         this._offsets = offsets;
946         return this.renderer.createElement.call(this);
947     };
948     
949     Grid.prototype.draw = function() {
950         this.renderer.draw.call(this);
951     };
952     
953     $.jqplot.GenericCanvas = function() {
954         $.jqplot.ElemContainer.call(this);
955         this._ctx;  
956     };
957     
958     $.jqplot.GenericCanvas.prototype = new $.jqplot.ElemContainer();
959     $.jqplot.GenericCanvas.prototype.constructor = $.jqplot.GenericCanvas;
960     
961     $.jqplot.GenericCanvas.prototype.createElement = function(offsets, clss, plotDimensions) {
962         this._offsets = offsets;
963         var klass = 'jqplot';
964         if (clss != undefined) {
965             klass = clss;
966         }
967         var elem = document.createElement('canvas');
968         // if new plotDimensions supplied, use them.
969         if (plotDimensions != undefined) {
970             this._plotDimensions = plotDimensions;
971         }
972         elem.width = this._plotDimensions.width - this._offsets.left - this._offsets.right;
973         elem.height = this._plotDimensions.height - this._offsets.top - this._offsets.bottom;
974         this._elem = $(elem);
975         this._elem.addClass(klass);
976         this._elem.css({ position: 'absolute', left: this._offsets.left, top: this._offsets.top });
977         // borrowed from flot by Ole Laursen
978         if ($.browser.msie) {
979 //             window.G_vmlCanvasManager.init_(document);
980             if (window.G_vmlCanvasManager) {
981                 window.G_vmlCanvasManager.init_(document);
982             }
983         }
984         if ($.browser.msie) {
985 //             elem = window.G_vmlCanvasManager.initElement(elem);
986              if (window.G_vmlCanvasManager) {
987                 elem = window.G_vmlCanvasManager.initElement(elem);
988              }
989         }
990         return this._elem;
991     };
992     
993     $.jqplot.GenericCanvas.prototype.setContext = function() {
994         this._ctx = this._elem.get(0).getContext("2d");
995         return this._ctx;
996     };
997
998     /**
999      * Class: jqPlot
1000      * Plot object returned by call to $.jqplot.  Handles parsing user options,
1001      * creating sub objects (Axes, legend, title, series) and rendering the plot.
1002      */
1003     function jqPlot() {
1004         // Group: Properties
1005         // These properties are specified at the top of the options object
1006         // like so:
1007         // > {
1008         // >     axesDefaults:{min:0},
1009         // >     series:[{color:'#6633dd'}],
1010         // >     title: 'A Plot'
1011         // > }
1012         //
1013         // prop: data
1014         // user's data.  Data should *NOT* be specified in the options object,
1015         // but be passed in as the second argument to the $.jqplot() function.
1016         // The data property is described here soley for reference. 
1017         // The data should be in the form of an array of 2D or 1D arrays like
1018         // > [ [[x1, y1], [x2, y2],...], [y1, y2, ...] ].
1019         this.data = [];
1020         // The id of the dom element to render the plot into
1021         this.targetId = null;
1022         // the jquery object for the dom target.
1023         this.target = null; 
1024         this.defaults = {
1025             // prop: axesDefaults
1026             // default options that will be applied to all axes.
1027             // see <Axis> for axes options.
1028             axesDefaults: {},
1029             axes: {xaxis:{}, yaxis:{}, x2axis:{}, y2axis:{}, y3axis:{}, y4axis:{}, y5axis:{}, y6axis:{}, y7axis:{}, y8axis:{}, y9axis:{}},
1030             // prop: seriesDefaults
1031             // default options that will be applied to all series.
1032             // see <Series> for series options.
1033             seriesDefaults: {},
1034             gridPadding: {top:10, right:10, bottom:23, left:10},
1035             series:[]
1036         };
1037         // prop: series
1038         // Array of series object options.
1039         // see <Series> for series specific options.
1040         this.series = [];
1041         // prop: axes
1042         // up to 4 axes are supported, each with it's own options, 
1043         // See <Axis> for axis specific options.
1044         this.axes = {xaxis: new Axis('xaxis'), yaxis: new Axis('yaxis'), x2axis: new Axis('x2axis'), y2axis: new Axis('y2axis'), y3axis: new Axis('y3axis'), y4axis: new Axis('y4axis'), y5axis: new Axis('y5axis'), y6axis: new Axis('y6axis'), y7axis: new Axis('y7axis'), y8axis: new Axis('y8axis'), y9axis: new Axis('y9axis')};
1045         // prop: grid
1046         // See <Grid> for grid specific options.
1047         this.grid = new Grid();
1048         // prop: legend
1049         // see <$.jqplot.TableLegendRenderer>
1050         this.legend = new Legend();
1051         this.baseCanvas = new $.jqplot.GenericCanvas();
1052         // this.seriesCanvas = new $.jqplot.GenericCanvas();
1053         this.eventCanvas = new $.jqplot.GenericCanvas();
1054         this._width = null;
1055         this._height = null; 
1056         this._plotDimensions = {height:null, width:null};
1057         this._gridPadding = {top:10, right:10, bottom:10, left:10};
1058         // a shortcut for axis syncTicks options.  Not implemented yet.
1059         this.syncXTicks = true;
1060         // a shortcut for axis syncTicks options.  Not implemented yet.
1061         this.syncYTicks = true;
1062         // prop: seriesColors
1063         // Ann array of CSS color specifications that will be applied, in order,
1064         // to the series in the plot.  Colors will wrap around so, if their
1065         // are more series than colors, colors will be reused starting at the
1066         // beginning.  For pie charts, this specifies the colors of the slices.
1067         this.seriesColors = [ "#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"];
1068         this.negativeSeriesColors = [ "#498991", "#C08840", "#9F9274", "#546D61", "#646C4A", "#6F6621", "#6E3F5F", "#4F64B0", "#A89050", "#C45923", "#187399", "#945381", "#959E5C", "#C7AF7B", "#478396", "#907294"];
1069         // prop: sortData
1070         // false to not sort the data passed in by the user.
1071         // Many bar, stakced and other graphs as well as many plugins depend on
1072         // having sorted data.
1073         this.sortData = true;
1074         var seriesColorsIndex = 0;
1075         // prop textColor
1076         // css spec for the css color attribute.  Default for the entire plot.
1077         this.textColor;
1078         // prop; fontFamily
1079         // css spec for the font-family attribute.  Default for the entire plot.
1080         this.fontFamily;
1081         // prop: fontSize
1082         // css spec for the font-size attribute.  Default for the entire plot.
1083         this.fontSize;
1084         // prop: title
1085         // Title object.  See <Title> for specific options.  As a shortcut, you
1086         // can specify the title option as just a string like: title: 'My Plot'
1087         // and this will create a new title object with the specified text.
1088         this.title = new Title();
1089         // container to hold all of the merged options.  Convienence for plugins.
1090         this.options = {};
1091         // prop: stackSeries
1092         // true or false, creates a stack or "mountain" plot.
1093         // Not all series renderers may implement this option.
1094         this.stackSeries = false;
1095         // array to hold the cumulative stacked series data.
1096         // used to ajust the individual series data, which won't have access to other
1097         // series data.
1098         this._stackData = [];
1099         // array that holds the data to be plotted. This will be the series data
1100         // merged with the the appropriate data from _stackData according to the stackAxis.
1101         this._plotData = [];
1102         // Namespece to hold plugins.  Generally non-renderer plugins add themselves to here.
1103         this.plugins = {};
1104         // Count how many times the draw method has been called while the plot is visible.
1105         // Mostly used to test if plot has never been dran (=0), has been successfully drawn
1106         // into a visible container once (=1) or draw more than once into a visible container.
1107         // Can use this in tests to see if plot has been visibly drawn at least one time.
1108         // After plot has been visibly drawn once, it generally doesn't need redrawn if its
1109         // container is hidden and shown.
1110         this._drawCount = 0;
1111         // prop: drawIfHidden
1112         // True to execute the draw method even if the plot target is hidden.
1113         // Generally, this should be false.  Most plot elements will not be sized/
1114         // positioned correclty if renderered into a hidden container.  To render into
1115         // a hidden container, call the replot method when the container is shown.
1116         this.drawIfHidden = false;
1117         // sum of y values for all series in plot.
1118         // used in mekko chart.
1119         this._sumy = 0;
1120         this._sumx = 0;
1121         
1122         this.colorGenerator = $.jqplot.ColorGenerator;
1123         
1124         // Group: methods
1125         //
1126         // method: init
1127         // sets the plot target, checks data and applies user
1128         // options to plot.
1129         this.init = function(target, data, options) {
1130             for (var i=0; i<$.jqplot.preInitHooks.length; i++) {
1131                 $.jqplot.preInitHooks[i].call(this, target, data, options);
1132             }
1133             this.targetId = '#'+target;
1134             this.target = $('#'+target);
1135             if (!this.target.get(0)) {
1136                 throw "No plot target specified";
1137             }
1138             
1139             // make sure the target is positioned by some means and set css
1140             if (this.target.css('position') == 'static') {
1141                 this.target.css('position', 'relative');
1142             }
1143             if (!this.target.hasClass('jqplot-target')) {
1144                 this.target.addClass('jqplot-target');
1145             }
1146             
1147             // if no height or width specified, use a default.
1148             if (!this.target.height()) {
1149                 var h;
1150                 if (options && options.height) {
1151                     h = parseInt(options.height, 10);
1152                 }
1153                 else if (this.target.attr('data-height')) {
1154                     h = parseInt(this.target.attr('data-height'), 10);
1155                 }
1156                 else {
1157                     h = parseInt($.jqplot.config.defaultHeight, 10);
1158                 }
1159                 this._height = h;
1160                 this.target.css('height', h+'px');
1161             }
1162             else {
1163                 this._height = this.target.height();
1164             }
1165             if (!this.target.width()) {
1166                 var w;
1167                 if (options && options.width) {
1168                     w = parseInt(options.width, 10);
1169                 }
1170                 else if (this.target.attr('data-width')) {
1171                     w = parseInt(this.target.attr('data-width'), 10);
1172                 }
1173                 else {
1174                     w = parseInt($.jqplot.config.defaultWidth, 10);
1175                 }
1176                 this._width = w;
1177                 this.target.css('width', w+'px');
1178             }
1179             else {
1180                 this._width = this.target.width();
1181             }
1182             
1183             this._plotDimensions.height = this._height;
1184             this._plotDimensions.width = this._width;
1185             this.grid._plotDimensions = this._plotDimensions;
1186             this.title._plotDimensions = this._plotDimensions;
1187             this.baseCanvas._plotDimensions = this._plotDimensions;
1188             this.eventCanvas._plotDimensions = this._plotDimensions;
1189             this.legend._plotDimensions = this._plotDimensions;
1190             if (this._height <=0 || this._width <=0 || !this._height || !this._width) {
1191                 throw "Canvas dimension not set";
1192             }
1193             
1194             this.data = data;
1195             
1196             this.parseOptions(options);
1197             
1198             if (this.textColor) {
1199                 this.target.css('color', this.textColor);
1200             }
1201             if (this.fontFamily) {
1202                 this.target.css('font-family', this.fontFamily);
1203             }
1204             if (this.fontSize) {
1205                 this.target.css('font-size', this.fontSize);
1206             }
1207             
1208             this.title.init();
1209             this.legend.init();
1210             this._sumy = 0;
1211             this._sumx = 0;
1212             for (var i=0; i<this.series.length; i++) {
1213                 this.series[i].shadowCanvas._plotDimensions = this._plotDimensions;
1214                 this.series[i].canvas._plotDimensions = this._plotDimensions;
1215                 for (var j=0; j<$.jqplot.preSeriesInitHooks.length; j++) {
1216                     $.jqplot.preSeriesInitHooks[j].call(this.series[i], target, data, this.options.seriesDefaults, this.options.series[i]);
1217                 }
1218                 this.populatePlotData(this.series[i], i);
1219                 this.series[i]._plotDimensions = this._plotDimensions;
1220                 this.series[i].init(i, this.grid.borderWidth, this);
1221                 for (var j=0; j<$.jqplot.postSeriesInitHooks.length; j++) {
1222                     $.jqplot.postSeriesInitHooks[j].call(this.series[i], target, data, this.options.seriesDefaults, this.options.series[i]);
1223                 }
1224                 this._sumy += this.series[i]._sumy;
1225                 this._sumx += this.series[i]._sumx;
1226             }
1227
1228             for (var name in this.axes) {
1229                 this.axes[name]._plotDimensions = this._plotDimensions;
1230                 this.axes[name].init();
1231             }
1232             
1233             if (this.sortData) {
1234                 sortData(this.series);
1235             }
1236             this.grid.init();
1237             this.grid._axes = this.axes;
1238             
1239             this.legend._series = this.series;
1240
1241             for (var i=0; i<$.jqplot.postInitHooks.length; i++) {
1242                 $.jqplot.postInitHooks[i].call(this, target, data, options);
1243             }
1244         };  
1245         
1246         // method: resetAxesScale
1247         // Reset the specified axes min, max, numberTicks and tickInterval properties to null
1248         // or reset these properties on all axes if no list of axes is provided.
1249         //
1250         // Parameters:
1251         // axes - Boolean to reset or not reset all axes or an array or object of axis names to reset.
1252         this.resetAxesScale = function(axes) {
1253             var ax = (axes != undefined) ? axes : this.axes;
1254             if (ax === true) {
1255                 ax = this.axes;
1256             }
1257             if (ax.constructor === Array) {
1258                 for (var i = 0; i < ax.length; i++) {
1259                     this.axes[ax[i]].resetScale();
1260                 }
1261             }
1262             else if (ax.constructor === Object) {
1263                 for (var name in ax) {
1264                     this.axes[name].resetScale();
1265                 }
1266             }
1267         };
1268         // method: reInitialize
1269         // reinitialize plot for replotting.
1270         // not called directly.
1271         this.reInitialize = function () {
1272             // Plot should be visible and have a height and width.
1273             // If plot doesn't have height and width for some
1274             // reason, set it by other means.  Plot must not have
1275             // a display:none attribute, however.
1276             if (!this.target.height()) {
1277                 var h;
1278                 if (options && options.height) {
1279                     h = parseInt(options.height, 10);
1280                 }
1281                 else if (this.target.attr('data-height')) {
1282                     h = parseInt(this.target.attr('data-height'), 10);
1283                 }
1284                 else {
1285                     h = parseInt($.jqplot.config.defaultHeight, 10);
1286                 }
1287                 this._height = h;
1288                 this.target.css('height', h+'px');
1289             }
1290             else {
1291                 this._height = this.target.height();
1292             }
1293             if (!this.target.width()) {
1294                 var w;
1295                 if (options && options.width) {
1296                     w = parseInt(options.width, 10);
1297                 }
1298                 else if (this.target.attr('data-width')) {
1299                     w = parseInt(this.target.attr('data-width'), 10);
1300                 }
1301                 else {
1302                     w = parseInt($.jqplot.config.defaultWidth, 10);
1303                 }
1304                 this._width = w;
1305                 this.target.css('width', w+'px');
1306             }
1307             else {
1308                 this._width = this.target.width();
1309             }
1310             
1311             if (this._height <=0 || this._width <=0 || !this._height || !this._width) {
1312                 throw "Target dimension not set";
1313             }
1314             
1315             this._plotDimensions.height = this._height;
1316             this._plotDimensions.width = this._width;
1317             this.grid._plotDimensions = this._plotDimensions;
1318             this.title._plotDimensions = this._plotDimensions;
1319             this.baseCanvas._plotDimensions = this._plotDimensions;
1320             // this.seriesCanvas._plotDimensions = this._plotDimensions;
1321             this.eventCanvas._plotDimensions = this._plotDimensions;
1322             this.legend._plotDimensions = this._plotDimensions;
1323             
1324             for (var n in this.axes) {
1325                 var axis = this.axes[n];
1326                 axis._plotWidth = this._width;
1327                 axis._plotHeight = this._height;
1328             }
1329             
1330             this.title._plotWidth = this._width;
1331             
1332             if (this.textColor) {
1333                 this.target.css('color', this.textColor);
1334             }
1335             if (this.fontFamily) {
1336                 this.target.css('font-family', this.fontFamily);
1337             }
1338             if (this.fontSize) {
1339                 this.target.css('font-size', this.fontSize);
1340             }
1341             
1342             this._sumy = 0;
1343             this._sumx = 0;
1344             for (var i=0; i<this.series.length; i++) {
1345                 this.populatePlotData(this.series[i], i);
1346                 this.series[i]._plotDimensions = this._plotDimensions;
1347                 this.series[i].canvas._plotDimensions = this._plotDimensions;
1348                 //this.series[i].init(i, this.grid.borderWidth);
1349                 this._sumy += this.series[i]._sumy;
1350                 this._sumx += this.series[i]._sumx;
1351             }
1352             
1353             for (var name in this.axes) {
1354                 this.axes[name]._plotDimensions = this._plotDimensions;
1355                 this.axes[name]._ticks = [];
1356                 this.axes[name].renderer.init.call(this.axes[name], {});
1357             }
1358             
1359             if (this.sortData) {
1360                 sortData(this.series);
1361             }
1362             
1363             this.grid._axes = this.axes;
1364             
1365             this.legend._series = this.series;
1366         };
1367         
1368         // sort the series data in increasing order.
1369         function sortData(series) {
1370             var d, ret;
1371             for (var i=0; i<series.length; i++) {
1372                 d = series[i].data;
1373                 var check = true;
1374                 if (series[i]._stackAxis == 'x') {
1375                     for (var j = 0; j < d.length; j++) {
1376                         if (typeof(d[j][1]) != "number") {
1377                             check = false;
1378                             break;
1379                         }
1380                     }
1381                     if (check) {
1382                         d.sort(function(a,b) { return a[1] - b[1]; });
1383                     }
1384                 }
1385                 else {
1386                     for (var j = 0; j < d.length; j++) {
1387                         if (typeof(d[j][0]) != "number") {
1388                             check = false;
1389                             break;
1390                         }
1391                     }
1392                     if (check) {
1393                         d.sort(function(a,b) { return a[0] - b[0]; });
1394                     }
1395                 }
1396             }
1397         }
1398         
1399         // populate the _stackData and _plotData arrays for the plot and the series.
1400         this.populatePlotData = function(series, index) {
1401             // if a stacked chart, compute the stacked data
1402             this._plotData = [];
1403             this._stackData = [];
1404             series._stackData = [];
1405             series._plotData = [];
1406             var plotValues = {x:[], y:[]};
1407             if (this.stackSeries && !series.disableStack) {
1408                 series._stack = true;
1409                 var sidx = series._stackAxis == 'x' ? 0 : 1;
1410                 var idx = sidx ? 0 : 1;
1411                 // push the current data into stackData
1412                 //this._stackData.push(this.series[i].data);
1413                 var temp = $.extend(true, [], series.data);
1414                 // create the data that will be plotted for this series
1415                 var plotdata = $.extend(true, [], series.data);
1416                 // for first series, nothing to add to stackData.
1417                 for (var j=0; j<index; j++) {
1418                     var cd = this.series[j].data;
1419                     for (var k=0; k<cd.length; k++) {
1420                         temp[k][0] += cd[k][0];
1421                         temp[k][1] += cd[k][1];
1422                         // only need to sum up the stack axis column of data
1423                         plotdata[k][sidx] += cd[k][sidx];
1424                     }
1425                 }
1426                 for (var i=0; i<plotdata.length; i++) {
1427                     plotValues.x.push(plotdata[i][0]);
1428                     plotValues.y.push(plotdata[i][1]);
1429                 }
1430                 this._plotData.push(plotdata);
1431                 this._stackData.push(temp);
1432                 series._stackData = temp;
1433                 series._plotData = plotdata;
1434                 series._plotValues = plotValues;
1435             }
1436             else {
1437                 for (var i=0; i<series.data.length; i++) {
1438                     plotValues.x.push(series.data[i][0]);
1439                     plotValues.y.push(series.data[i][1]);
1440                 }
1441                 this._stackData.push(series.data);
1442                 this.series[index]._stackData = series.data;
1443                 this._plotData.push(series.data);
1444                 series._plotData = series.data;
1445                 series._plotValues = plotValues;
1446             }
1447             if (index>0) {
1448                 series._prevPlotData = this.series[index-1]._plotData;
1449             }
1450             series._sumy = 0;
1451             series._sumx = 0;
1452             for (i=series.data.length-1; i>-1; i--) {
1453                 series._sumy += series.data[i][1];
1454                 series._sumx += series.data[i][0];
1455             }
1456         };
1457         
1458         // function to safely return colors from the color array and wrap around at the end.
1459         this.getNextSeriesColor = (function(t) {
1460             var idx = 0;
1461             var sc = t.seriesColors;
1462             
1463             return function () { 
1464                 if (idx < sc.length) {
1465                     return sc[idx++];
1466                 }
1467                 else {
1468                     idx = 0;
1469                     return sc[idx++];
1470                 }
1471             };
1472         })(this);
1473     
1474         this.parseOptions = function(options){
1475             for (var i=0; i<$.jqplot.preParseOptionsHooks.length; i++) {
1476                 $.jqplot.preParseOptionsHooks[i].call(this, options);
1477             }
1478             this.options = $.extend(true, {}, this.defaults, options);
1479             this.stackSeries = this.options.stackSeries;
1480             if (this.options.seriesColors) {
1481                 this.seriesColors = this.options.seriesColors;
1482             }
1483             var cg = new this.colorGenerator(this.seriesColors);
1484             // this._gridPadding = this.options.gridPadding;
1485             $.extend(true, this._gridPadding, this.options.gridPadding);
1486             this.sortData = (this.options.sortData != null) ? this.options.sortData : this.sortData;
1487             for (var n in this.axes) {
1488                 var axis = this.axes[n];
1489                 $.extend(true, axis, this.options.axesDefaults, this.options.axes[n]);
1490                 axis._plotWidth = this._width;
1491                 axis._plotHeight = this._height;
1492             }
1493             if (this.data.length == 0) {
1494                 this.data = [];
1495                 for (var i=0; i<this.options.series.length; i++) {
1496                     this.data.push(this.options.series.data);
1497                 }    
1498             }
1499                 
1500             var normalizeData = function(data, dir) {
1501                 // return data as an array of point arrays,
1502                 // in form [[x1,y1...], [x2,y2...], ...]
1503                 var temp = [];
1504                 var i;
1505                 dir = dir || 'vertical';
1506                 if (!(data[0] instanceof Array)) {
1507                     // we have a series of scalars.  One line with just y values.
1508                     // turn the scalar list of data into a data array of form:
1509                     // [[1, data[0]], [2, data[1]], ...]
1510                     for (var i=0; i<data.length; i++) {
1511                         if (dir == 'vertical') {
1512                             temp.push([i+1, data[i]]);   
1513                         }
1514                         else {
1515                             temp.push([data[i], i+1]);
1516                         }
1517                     }
1518                 }            
1519                 else {
1520                     // we have a properly formatted data series, copy it.
1521                     $.extend(true, temp, data);
1522                 }
1523                 return temp;
1524             };
1525
1526             for (var i=0; i<this.data.length; i++) { 
1527                 var temp = new Series();
1528                 for (var j=0; j<$.jqplot.preParseSeriesOptionsHooks.length; j++) {
1529                     $.jqplot.preParseSeriesOptionsHooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]);
1530                 }
1531                 $.extend(true, temp, {seriesColors:this.seriesColors, negativeSeriesColors:this.negativeSeriesColors}, this.options.seriesDefaults, this.options.series[i]);
1532                 var dir = 'vertical';
1533                 if (temp.renderer.constructor == $.jqplot.barRenderer && temp.rendererOptions && temp.rendererOptions.barDirection == 'horizontal') {
1534                     dir = 'horizontal';
1535                 }
1536                 temp.data = normalizeData(this.data[i], dir);
1537                 switch (temp.xaxis) {
1538                     case 'xaxis':
1539                         temp._xaxis = this.axes.xaxis;
1540                         break;
1541                     case 'x2axis':
1542                         temp._xaxis = this.axes.x2axis;
1543                         break;
1544                     default:
1545                         break;
1546                 }
1547                 temp._yaxis = this.axes[temp.yaxis];
1548                 temp._xaxis._series.push(temp);
1549                 temp._yaxis._series.push(temp);
1550                 if (temp.show) {
1551                     temp._xaxis.show = true;
1552                     temp._yaxis.show = true;
1553                 }
1554
1555                 // parse the renderer options and apply default colors if not provided
1556                 if (!temp.color && temp.show != false) {
1557                     temp.color = cg.next();
1558                 }
1559                 if (!temp.label) {
1560                     temp.label = 'Series '+ (i+1).toString();
1561                 }
1562                 // temp.rendererOptions.show = temp.show;
1563                 // $.extend(true, temp.renderer, {color:this.seriesColors[i]}, this.rendererOptions);
1564                 this.series.push(temp);  
1565                 for (var j=0; j<$.jqplot.postParseSeriesOptionsHooks.length; j++) {
1566                     $.jqplot.postParseSeriesOptionsHooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]);
1567                 }
1568             }
1569             
1570             // copy the grid and title options into this object.
1571             $.extend(true, this.grid, this.options.grid);
1572             // if axis border properties aren't set, set default.
1573             for (var n in this.axes) {
1574                 var axis = this.axes[n];
1575                 if (axis.borderWidth == null) {
1576                     axis.borderWidth =this.grid.borderWidth;
1577                 }
1578                 if (axis.borderColor == null) {
1579                     if (n != 'xaxis' && n != 'x2axis' && axis.useSeriesColor === true && axis.show) {
1580                         axis.borderColor = axis._series[0].color;
1581                     }
1582                     else {
1583                         axis.borderColor = this.grid.borderColor;
1584                     }
1585                 }
1586             }
1587             
1588             if (typeof this.options.title == 'string') {
1589                 this.title.text = this.options.title;
1590             }
1591             else if (typeof this.options.title == 'object') {
1592                 $.extend(true, this.title, this.options.title);
1593             }
1594             this.title._plotWidth = this._width;
1595             $.extend(true, this.legend, this.options.legend);
1596             
1597             for (var i=0; i<$.jqplot.postParseOptionsHooks.length; i++) {
1598                 $.jqplot.postParseOptionsHooks[i].call(this, options);
1599             }
1600         };
1601         
1602         // method: replot
1603         // Does a reinitialization of the plot followed by
1604         // a redraw.  Method could be used to interactively
1605         // change plot characteristics and then replot.
1606         //
1607         // Parameters:
1608         // options - Options used for replotting.
1609         //
1610         // Properties:
1611         // clear - false to not clear (empty) the plot container before replotting (default: true).
1612         // resetAxes - true to reset all axes min, max, numberTicks and tickInterval setting so axes will rescale themselves.
1613         //             optionally pass in list of axes to reset (e.g. ['xaxis', 'y2axis']) (default: false).
1614         this.replot = function(options) {
1615             var opts = (options != undefined) ? options : {};
1616             var clear = (opts.clear != undefined) ? opts.clear : true;
1617             var resetAxes = (opts.resetAxes != undefined) ? opts.resetAxes : false;
1618             this.target.trigger('jqplotPreReplot');
1619             if (clear) {
1620                 this.target.empty();
1621             }
1622             if (resetAxes) {
1623                 this.resetAxesScale(resetAxes);
1624             }
1625             this.reInitialize();
1626             this.draw();
1627             this.target.trigger('jqplotPostReplot');
1628         };
1629         
1630         // method: redraw
1631         // Empties the plot target div and redraws the plot.
1632         // This enables plot data and properties to be changed
1633         // and then to comletely clear the plot and redraw.
1634         // redraw *will not* reinitialize any plot elements.
1635         // That is, axes will not be autoscaled and defaults
1636         // will not be reapplied to any plot elements.  redraw
1637         // is used primarily with zooming. 
1638         //
1639         // Parameters:
1640         // clear - false to not clear (empty) the plot container before redrawing (default: true).
1641         this.redraw = function(clear) {
1642             clear = (clear != null) ? clear : true;
1643             this.target.trigger('jqplotPreRedraw');
1644             if (clear) {
1645                 this.target.empty();
1646             }
1647              for (var ax in this.axes) {
1648                 this.axes[ax]._ticks = [];
1649             }
1650             for (var i=0; i<this.series.length; i++) {
1651                 this.populatePlotData(this.series[i], i);
1652             }
1653             this._sumy = 0;
1654             this._sumx = 0;
1655             for (i=0; i<this.series.length; i++) {
1656                 this._sumy += this.series[i]._sumy;
1657                 this._sumx += this.series[i]._sumx;
1658             }
1659             this.draw();
1660             this.target.trigger('jqplotPostRedraw');
1661         };
1662         
1663         // method: draw
1664         // Draws all elements of the plot into the container.
1665         // Does not clear the container before drawing.
1666         this.draw = function(){
1667             if (this.drawIfHidden || this.target.is(':visible')) {
1668                 this.target.trigger('jqplotPreDraw');
1669                 var i;
1670                 for (i=0; i<$.jqplot.preDrawHooks.length; i++) {
1671                     $.jqplot.preDrawHooks[i].call(this);
1672                 }
1673                 // create an underlying canvas to be used for special features.
1674                 this.target.append(this.baseCanvas.createElement({left:0, right:0, top:0, bottom:0}, 'jqplot-base-canvas'));
1675                 var bctx = this.baseCanvas.setContext();
1676                 this.target.append(this.title.draw());
1677                 this.title.pack({top:0, left:0});
1678                 for (var name in this.axes) {
1679                     this.target.append(this.axes[name].draw(bctx));
1680                     this.axes[name].set();
1681                 }
1682                 if (this.axes.yaxis.show) {
1683                     this._gridPadding.left = this.axes.yaxis.getWidth();
1684                 }
1685                 var ra = ['y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis'];
1686                 var rapad = [0, 0, 0, 0];
1687                 var gpr = 0;
1688                 var n, ax;
1689                 for (n=8; n>0; n--) {
1690                     ax = this.axes[ra[n-1]];
1691                     if (ax.show) {
1692                         rapad[n-1] = gpr;
1693                         gpr += ax.getWidth();
1694                     }
1695                 }
1696                 if (gpr > this._gridPadding.right) {
1697                     this._gridPadding.right = gpr;
1698                 }
1699                 if (this.title.show && this.axes.x2axis.show) {
1700                     this._gridPadding.top = this.title.getHeight() + this.axes.x2axis.getHeight();
1701                 }
1702                 else if (this.title.show) {
1703                     this._gridPadding.top = this.title.getHeight();
1704                 }
1705                 else if (this.axes.x2axis.show) {
1706                     this._gridPadding.top = this.axes.x2axis.getHeight();
1707                 }
1708                 if (this.axes.xaxis.show) {
1709                     this._gridPadding.bottom = this.axes.xaxis.getHeight();
1710                 }
1711             
1712                 this.axes.xaxis.pack({position:'absolute', bottom:0, left:0, width:this._width}, {min:this._gridPadding.left, max:this._width - this._gridPadding.right});
1713                 this.axes.yaxis.pack({position:'absolute', top:0, left:0, height:this._height}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top});
1714                 this.axes.x2axis.pack({position:'absolute', top:this.title.getHeight(), left:0, width:this._width}, {min:this._gridPadding.left, max:this._width - this._gridPadding.right});
1715                 for (i=8; i>0; i--) {
1716                     this.axes[ra[i-1]].pack({position:'absolute', top:0, right:rapad[i-1]}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top});
1717                 }
1718                 // this.axes.y2axis.pack({position:'absolute', top:0, right:0}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top});
1719             
1720                 this.target.append(this.grid.createElement(this._gridPadding));
1721                 this.grid.draw();
1722                 
1723                 // put the shadow canvases behind the series canvases so shadows don't overlap on stacked bars.
1724                 for (i=0; i<this.series.length; i++) {
1725                     this.target.append(this.series[i].shadowCanvas.createElement(this._gridPadding, 'jqplot-series-canvas jqplot-shadow'));
1726                     this.series[i].shadowCanvas.setContext();
1727                 }
1728                 
1729                 for (i=0; i<this.series.length; i++) {
1730                     this.target.append(this.series[i].canvas.createElement(this._gridPadding, 'jqplot-series-canvas'));
1731                     this.series[i].canvas.setContext();
1732                 }
1733                 
1734                 // var sctx = this.seriesCanvas.setContext();
1735                 this.target.append(this.eventCanvas.createElement(this._gridPadding, 'jqplot-event-canvas'));
1736                 var ectx = this.eventCanvas.setContext();
1737                 ectx.fillStyle = 'rgba(0,0,0,0)';
1738                 ectx.fillRect(0,0,ectx.canvas.width, ectx.canvas.height);
1739             
1740                 // bind custom event handlers to regular events.
1741                 this.bindCustomEvents();
1742             
1743                 // draw legend before series if the series needs to know the legend dimensions.
1744                 if (this.legend.preDraw) {  
1745                     this.target.append(this.legend.draw());
1746                     this.legend.pack(this._gridPadding);
1747                     if (this.legend._elem) {
1748                         this.drawSeries({legendInfo:{location:this.legend.location, width:this.legend.getWidth(), height:this.legend.getHeight(), xoffset:this.legend.xoffset, yoffset:this.legend.yoffset}});
1749                     }
1750                     else {
1751                         this.drawSeries();
1752                     }
1753                 }
1754                 else {  // draw series before legend
1755                     this.drawSeries();
1756                     $(this.series[this.series.length-1].canvas._elem).after(this.legend.draw());
1757                     // this.target.append(this.legend.draw());
1758                     this.legend.pack(this._gridPadding);                
1759                 }
1760             
1761                 // register event listeners on the overlay canvas
1762                 for (var i=0; i<$.jqplot.eventListenerHooks.length; i++) {
1763                     var h = $.jqplot.eventListenerHooks[i];
1764                     // in the handler, this will refer to the eventCanvas dom element.
1765                     // make sure there are references back into plot objects.
1766                     this.eventCanvas._elem.bind(h[0], {plot:this}, h[1]);
1767                 }
1768
1769                 for (var i=0; i<$.jqplot.postDrawHooks.length; i++) {
1770                     $.jqplot.postDrawHooks[i].call(this);
1771                 }
1772             
1773                 if (this.target.is(':visible')) {
1774                     this._drawCount += 1;
1775                 }
1776             
1777                 this.target.trigger('jqplotPostDraw', [this]);
1778             }
1779         };
1780         
1781         this.bindCustomEvents = function() {
1782             this.eventCanvas._elem.bind('click', {plot:this}, this.onClick);
1783             this.eventCanvas._elem.bind('dblclick', {plot:this}, this.onDblClick);
1784             this.eventCanvas._elem.bind('mousedown', {plot:this}, this.onMouseDown);
1785             this.eventCanvas._elem.bind('mouseup', {plot:this}, this.onMouseUp);
1786             this.eventCanvas._elem.bind('mousemove', {plot:this}, this.onMouseMove);
1787             this.eventCanvas._elem.bind('mouseenter', {plot:this}, this.onMouseEnter);
1788             this.eventCanvas._elem.bind('mouseleave', {plot:this}, this.onMouseLeave);
1789         };
1790         
1791         function getEventPosition(ev) {
1792             var plot = ev.data.plot;
1793             // var xaxis = plot.axes.xaxis;
1794             // var x2axis = plot.axes.x2axis;
1795             // var yaxis = plot.axes.yaxis;
1796             // var y2axis = plot.axes.y2axis;
1797             var offsets = plot.eventCanvas._elem.offset();
1798             var gridPos = {x:ev.pageX - offsets.left, y:ev.pageY - offsets.top};
1799             // var dataPos = {x1y1:{x:null, y:null}, x1y2:{x:null, y:null}, x2y1:{x:null, y:null}, x2y2:{x:null, y:null}};
1800             var dataPos = {xaxis:null, yaxis:null, x2axis:null, y2axis:null, y3axis:null, y4axis:null, y5axis:null, y6axis:null, y7axis:null, y8axis:null, y9axis:null};
1801             
1802             var an = ['xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis'];
1803             var ax = plot.axes;
1804             for (var n=11; n>0; n--) {
1805                 var axis = an[n-1];
1806                 if (ax[axis].show) {
1807                     dataPos[axis] = ax[axis].series_p2u(gridPos[axis.charAt(0)]);
1808                 }
1809             }
1810
1811             return ({offsets:offsets, gridPos:gridPos, dataPos:dataPos});
1812         }
1813         
1814         function getNeighborPoint(plot, x, y) {
1815             var ret = null;
1816             var s, i, d0, d, j, r;
1817             var threshold;
1818             for (var i=0; i<plot.series.length; i++) {
1819                 s = plot.series[i];
1820                 r = s.renderer;
1821                 if (s.show) {
1822                     threshold = Math.abs(s.markerRenderer.size/2+s.neighborThreshold);
1823                     for (var j=0; j<s.gridData.length; j++) {
1824                         p = s.gridData[j];
1825                         // neighbor looks different to OHLC chart.
1826                         if (r.constructor == $.jqplot.OHLCRenderer) {
1827                             if (r.candleStick) {
1828                                 var yp = s._yaxis.series_u2p;
1829                                 if (x >= p[0]-r._bodyWidth/2 && x <= p[0]+r._bodyWidth/2 && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) {
1830                                     ret = {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
1831                                 }
1832                             }
1833                             // if an open hi low close chart
1834                             else if (!r.hlc){
1835                                 var yp = s._yaxis.series_u2p;
1836                                 if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) {
1837                                     ret = {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
1838                                 }
1839                             }
1840                             // a hi low close chart
1841                             else {
1842                                 var yp = s._yaxis.series_u2p;
1843                                 if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) {
1844                                     ret = {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
1845                                 }
1846                             }
1847                             
1848                         }
1849                         else {
1850                             d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) );
1851                             if (d <= threshold && (d <= d0 || d0 == null)) {
1852                                d0 = d;
1853                                ret = {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
1854                             }
1855                         }
1856                     } 
1857                 }
1858             }
1859             return ret;
1860         }
1861         
1862         this.onClick = function(ev) {
1863             // Event passed in is unnormalized and will have data attribute.
1864             // Event passed out in normalized and won't have data attribute.
1865             var positions = getEventPosition(ev);
1866             var p = ev.data.plot;
1867             var neighbor = getNeighborPoint(p, positions.gridPos.x, positions.gridPos.y);
1868             ev.data.plot.eventCanvas._elem.trigger('jqplotClick', [positions.gridPos, positions.dataPos, neighbor, p]);
1869         };
1870         
1871         this.onDblClick = function(ev) {
1872             // Event passed in is unnormalized and will have data attribute.
1873             // Event passed out in normalized and won't have data attribute.
1874             var positions = getEventPosition(ev);
1875             var p = ev.data.plot;
1876             var neighbor = getNeighborPoint(p, positions.gridPos.x, positions.gridPos.y);
1877             ev.data.plot.eventCanvas._elem.trigger('jqplotDblClick', [positions.gridPos, positions.dataPos, neighbor, p]);
1878         };
1879         
1880         this.onMouseDown = function(ev) {
1881             var positions = getEventPosition(ev);
1882             var p = ev.data.plot;
1883             var neighbor = getNeighborPoint(p, positions.gridPos.x, positions.gridPos.y);
1884             ev.data.plot.eventCanvas._elem.trigger('jqplotMouseDown', [positions.gridPos, positions.dataPos, neighbor, p]);
1885         };
1886         
1887         this.onMouseUp = function(ev) {
1888             var positions = getEventPosition(ev);
1889             ev.data.plot.eventCanvas._elem.trigger('jqplotMouseUp', [positions.gridPos, positions.dataPos, null, ev.data.plot]);
1890         };
1891         
1892         this.onMouseMove = function(ev) {
1893             var positions = getEventPosition(ev);
1894             var p = ev.data.plot;
1895             var neighbor = getNeighborPoint(p, positions.gridPos.x, positions.gridPos.y);
1896             ev.data.plot.eventCanvas._elem.trigger('jqplotMouseMove', [positions.gridPos, positions.dataPos, neighbor, p]);
1897         };
1898         
1899         this.onMouseEnter = function(ev) {
1900             var positions = getEventPosition(ev);
1901             var p = ev.data.plot;
1902             ev.data.plot.eventCanvas._elem.trigger('jqplotMouseEnter', [positions.gridPos, positions.dataPos, null, p]);
1903         };
1904         
1905         this.onMouseLeave = function(ev) {
1906             var positions = getEventPosition(ev);
1907             var p = ev.data.plot;
1908             ev.data.plot.eventCanvas._elem.trigger('jqplotMouseLeave', [positions.gridPos, positions.dataPos, null, p]);
1909         };
1910         
1911         // convienece function to draw series shadows and series.
1912         this.drawSeries = function(options, idx){
1913             var i, series, ctx;
1914             // draw specified series
1915             if (idx != undefined) {
1916                 series = this.series[idx];
1917                 ctx = series.shadowCanvas._ctx;
1918                 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
1919                 series.drawShadow(ctx, options, this);
1920                 ctx = series.canvas._ctx;
1921                 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
1922                 series.draw(ctx, options, this);
1923             }
1924             
1925             else {
1926                 // if call series drawShadow method first, in case all series shadows
1927                 // should be drawn before any series.  This will ensure, like for 
1928                 // stacked bar plots, that shadows don't overlap series.
1929                 for (i=0; i<this.series.length; i++) {
1930                     // first clear the canvas
1931                     series = this.series[i];
1932                     ctx = series.shadowCanvas._ctx;
1933                     ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
1934                     series.drawShadow(ctx, options, this);
1935                     ctx = series.canvas._ctx;
1936                     ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
1937                     series.draw(ctx, options, this);
1938                 }
1939             }
1940         };
1941     }
1942     
1943         
1944    $.jqplot.ColorGenerator = function(colors) {
1945         var idx = 0;
1946         
1947         this.next = function () { 
1948             if (idx < colors.length) {
1949                 return colors[idx++];
1950             }
1951             else {
1952                 idx = 0;
1953                 return colors[idx++];
1954             }
1955         };
1956         
1957         this.previous = function () { 
1958             if (idx > 0) {
1959                 return colors[idx--];
1960             }
1961             else {
1962                 idx = colors.length-1;
1963                 return colors[idx];
1964             }
1965         };
1966         
1967         // get a color by index without advancing pointer.
1968         this.get = function(i) {
1969             return colors[i];
1970         };
1971         
1972         this.setColors = function(c) {
1973             colors = c;
1974         };
1975         
1976         this.reset = function() {
1977             idx = 0;
1978         };
1979     };
1980
1981     // convert a hex color string to rgb string.
1982     // h - 3 or 6 character hex string, with or without leading #
1983     // a - optional alpha
1984     $.jqplot.hex2rgb = function(h, a) {
1985         h = h.replace('#', '');
1986         if (h.length == 3) {
1987             h = h[0]+h[0]+h[1]+h[1]+h[2]+h[2];
1988         }
1989         var rgb;
1990         rgb = 'rgba('+parseInt(h.slice(0,2), 16)+', '+parseInt(h.slice(2,4), 16)+', '+parseInt(h.slice(4,6), 16);
1991         if (a) {
1992             rgb += ', '+a;
1993         }
1994         rgb += ')';
1995         return rgb;
1996     };
1997     
1998     // convert an rgb color spec to a hex spec.  ignore any alpha specification.
1999     $.jqplot.rgb2hex = function(s) {
2000         var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *(?:, *[0-9.]*)?\)/;
2001         var m = s.match(pat);
2002         var h = '#';
2003         for (i=1; i<4; i++) {
2004             var temp;
2005             if (m[i].search(/%/) != -1) {
2006                 temp = parseInt(255*m[i]/100, 10).toString(16);
2007                 if (temp.length == 1) {
2008                     temp = '0'+temp;
2009                 }
2010             }
2011             else {
2012                 temp = parseInt(m[i], 10).toString(16);
2013                 if (temp.length == 1) {
2014                     temp = '0'+temp;
2015                 }
2016             }
2017             h += temp;
2018         }
2019         return h;
2020     };
2021     
2022     // given a css color spec, return an rgb css color spec
2023     $.jqplot.normalize2rgb = function(s, a) {
2024         if (s.search(/^ *rgba?\(/) != -1) {
2025             return s; 
2026         }
2027         else if (s.search(/^ *#?[0-9a-fA-F]?[0-9a-fA-F]/) != -1) {
2028             return $.jqplot.hex2rgb(s, a);
2029         }
2030         else {
2031             throw 'invalid color spec';
2032         }
2033     };
2034     
2035     // extract the r, g, b, a color components out of a css color spec.
2036     $.jqplot.getColorComponents = function(s) {
2037         var rgb = $.jqplot.normalize2rgb(s);
2038         var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *,? *([0-9.]* *)?\)/;
2039         var m = rgb.match(pat);
2040         var ret = [];
2041         for (i=1; i<4; i++) {
2042             if (m[i].search(/%/) != -1) {
2043                 ret[i-1] = parseInt(255*m[i]/100, 10);
2044             }
2045             else {
2046                 ret[i-1] = parseInt(m[i], 10);
2047             }
2048         }
2049         ret[3] = parseFloat(m[4]) ? parseFloat(m[4]) : 1.0;
2050         return ret;
2051     };
2052         
2053     // Convienence function that won't hang IE.
2054     $.jqplot.log = function() {
2055         if (window.console && $.jqplot.debug) {
2056            if (arguments.length == 1) {
2057                console.log (arguments[0]);
2058             }
2059            else {
2060                console.log(arguments);
2061             }
2062         }
2063     };
2064     var log = $.jqplot.log;
2065     
2066
2067     // class: $.jqplot.AxisLabelRenderer
2068     // Renderer to place labels on the axes.
2069     $.jqplot.AxisLabelRenderer = function(options) {
2070         // Group: Properties
2071         $.jqplot.ElemContainer.call(this);
2072         // name of the axis associated with this tick
2073         this.axis;
2074         // prop: show
2075         // wether or not to show the tick (mark and label).
2076         this.show = true;
2077         // prop: label
2078         // The text or html for the label.
2079         this.label = '';
2080         this._elem;
2081         // prop: escapeHTML
2082         // true to escape HTML entities in the label.
2083         this.escapeHTML = false;
2084         
2085         $.extend(true, this, options);
2086     };
2087     
2088     $.jqplot.AxisLabelRenderer.prototype = new $.jqplot.ElemContainer();
2089     $.jqplot.AxisLabelRenderer.prototype.constructor = $.jqplot.AxisLabelRenderer;
2090     
2091     $.jqplot.AxisLabelRenderer.prototype.init = function(options) {
2092         $.extend(true, this, options);
2093     };
2094     
2095     $.jqplot.AxisLabelRenderer.prototype.draw = function() {
2096         this._elem = $('<div style="position:absolute;" class="jqplot-'+this.axis+'-label"></div>');
2097         
2098         if (Number(this.label)) {
2099             this._elem.css('white-space', 'nowrap');
2100         }
2101         
2102         if (!this.escapeHTML) {
2103             this._elem.html(this.label);
2104         }
2105         else {
2106             this._elem.text(this.label);
2107         }
2108         
2109         return this._elem;
2110     };
2111     
2112     $.jqplot.AxisLabelRenderer.prototype.pack = function() {
2113     };
2114
2115     // class: $.jqplot.AxisTickRenderer
2116     // A "tick" object showing the value of a tick/gridline on the plot.
2117     $.jqplot.AxisTickRenderer = function(options) {
2118         // Group: Properties
2119         $.jqplot.ElemContainer.call(this);
2120         // prop: mark
2121         // tick mark on the axis.  One of 'inside', 'outside', 'cross', '' or null.
2122         this.mark = 'outside';
2123         // name of the axis associated with this tick
2124         this.axis;
2125         // prop: showMark
2126         // wether or not to show the mark on the axis.
2127         this.showMark = true;
2128         // prop: showGridline
2129         // wether or not to draw the gridline on the grid at this tick.
2130         this.showGridline = true;
2131         // prop: isMinorTick
2132         // if this is a minor tick.
2133         this.isMinorTick = false;
2134         // prop: size
2135         // Length of the tick beyond the grid in pixels.
2136         // DEPRECATED: This has been superceeded by markSize
2137         this.size = 4;
2138         // prop:  markSize
2139         // Length of the tick marks in pixels.  For 'cross' style, length
2140         // will be stoked above and below axis, so total length will be twice this.
2141         this.markSize = 6;
2142         // prop: show
2143         // wether or not to show the tick (mark and label).
2144         // Setting this to false requires more testing.  It is recommended
2145         // to set showLabel and showMark to false instead.
2146         this.show = true;
2147         // prop: showLabel
2148         // wether or not to show the label.
2149         this.showLabel = true;
2150         this.label = '';
2151         this.value = null;
2152         this._styles = {};
2153         // prop: formatter
2154         // A class of a formatter for the tick text.  sprintf by default.
2155         this.formatter = $.jqplot.DefaultTickFormatter;
2156         // prop: formatString
2157         // string passed to the formatter.
2158         this.formatString = '';
2159         // prop: fontFamily
2160         // css spec for the font-family css attribute.
2161         this.fontFamily;
2162         // prop: fontSize
2163         // css spec for the font-size css attribute.
2164         this.fontSize;
2165         // prop: textColor
2166         // css spec for the color attribute.
2167         this.textColor;
2168         this._elem;
2169         
2170         $.extend(true, this, options);
2171     };
2172     
2173     $.jqplot.AxisTickRenderer.prototype.init = function(options) {
2174         $.extend(true, this, options);
2175     };
2176     
2177     $.jqplot.AxisTickRenderer.prototype = new $.jqplot.ElemContainer();
2178     $.jqplot.AxisTickRenderer.prototype.constructor = $.jqplot.AxisTickRenderer;
2179     
2180     $.jqplot.AxisTickRenderer.prototype.setTick = function(value, axisName, isMinor) {
2181         this.value = value;
2182         this.axis = axisName;
2183         if (isMinor) {
2184             this.isMinorTick = true;
2185         }
2186         return this;
2187     };
2188     
2189     $.jqplot.AxisTickRenderer.prototype.draw = function() {
2190         if (!this.label) {
2191             this.label = this.formatter(this.formatString, this.value);
2192         }
2193         style ='style="position:absolute;';
2194         if (Number(this.label)) {
2195             style +='white-space:nowrap;';
2196         }
2197         style += '"';
2198         this._elem = $('<div '+style+' class="jqplot-'+this.axis+'-tick">'+this.label+'</div>');
2199         for (var s in this._styles) {
2200             this._elem.css(s, this._styles[s]);
2201         }
2202         if (this.fontFamily) {
2203             this._elem.css('font-family', this.fontFamily);
2204         }
2205         if (this.fontSize) {
2206             this._elem.css('font-size', this.fontSize);
2207         }
2208         if (this.textColor) {
2209             this._elem.css('color', this.textColor);
2210         }
2211         return this._elem;
2212     };
2213         
2214     $.jqplot.DefaultTickFormatter = function (format, val) {
2215         if (typeof val == 'number') {
2216             if (!format) {
2217                 format = '%.1f';
2218             }
2219             return $.jqplot.sprintf(format, val);
2220         }
2221         else {
2222             return String(val);
2223         }
2224     };
2225     
2226     $.jqplot.AxisTickRenderer.prototype.pack = function() {
2227     };
2228      
2229     // Class: $.jqplot.CanvasGridRenderer
2230     // The default jqPlot grid renderer, creating a grid on a canvas element.
2231     // The renderer has no additional options beyond the <Grid> class.
2232     $.jqplot.CanvasGridRenderer = function(){
2233         this.shadowRenderer = new $.jqplot.ShadowRenderer();
2234     };
2235     
2236     // called with context of Grid object
2237     $.jqplot.CanvasGridRenderer.prototype.init = function(options) {
2238         this._ctx;
2239         $.extend(true, this, options);
2240         // set the shadow renderer options
2241         var sopts = {lineJoin:'miter', lineCap:'round', fill:false, isarc:false, angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, depth:this.shadowDepth, lineWidth:this.shadowWidth, closePath:false};
2242         this.renderer.shadowRenderer.init(sopts);
2243     };
2244     
2245     // called with context of Grid.
2246     $.jqplot.CanvasGridRenderer.prototype.createElement = function() {
2247         var elem = document.createElement('canvas');
2248         var w = this._plotDimensions.width;
2249         var h = this._plotDimensions.height;
2250         elem.width = w;
2251         elem.height = h;
2252         this._elem = $(elem);
2253         this._elem.addClass('jqplot-grid-canvas');
2254         this._elem.css({ position: 'absolute', left: 0, top: 0 });
2255         if ($.browser.msie) {
2256 //             window.G_vmlCanvasManager.init_(document);
2257              if (window.G_vmlCanvasManager) {
2258                 window.G_vmlCanvasManager.init_(document);
2259               }
2260         }
2261         if ($.browser.msie) {
2262 //             elem = window.G_vmlCanvasManager.initElement(elem);
2263               if (window.G_vmlCanvasManager) {
2264                 elem = window.G_vmlCanvasManager.initElement(elem);
2265               }
2266         }
2267         this._top = this._offsets.top;
2268         this._bottom = h - this._offsets.bottom;
2269         this._left = this._offsets.left;
2270         this._right = w - this._offsets.right;
2271         this._width = this._right - this._left;
2272         this._height = this._bottom - this._top;
2273         return this._elem;
2274     };
2275     
2276     $.jqplot.CanvasGridRenderer.prototype.draw = function() {
2277         this._ctx = this._elem.get(0).getContext("2d");
2278         var ctx = this._ctx;
2279         var axes = this._axes;
2280         // Add the grid onto the grid canvas.  This is the bottom most layer.
2281         ctx.save();
2282         ctx.fillStyle = this.background;
2283         ctx.fillRect(this._left, this._top, this._width, this._height);
2284         
2285         if (this.drawGridlines) {
2286             ctx.save();
2287             ctx.lineJoin = 'miter';
2288             ctx.lineCap = 'butt';
2289             ctx.lineWidth = this.gridLineWidth;
2290             ctx.strokeStyle = this.gridLineColor;
2291             var b, e;
2292             var ax = ['xaxis', 'yaxis', 'x2axis', 'y2axis'];
2293             for (var i=4; i>0; i--) {
2294                 var name = ax[i-1];
2295                 var axis = axes[name];
2296                 var ticks = axis._ticks;
2297                 if (axis.show) {
2298                     for (var j=ticks.length; j>0; j--) {
2299                         var t = ticks[j-1];
2300                         if (t.show) {
2301                             var pos = Math.round(axis.u2p(t.value)) + 0.5;
2302                             switch (name) {
2303                                 case 'xaxis':
2304                                     // draw the grid line
2305                                     if (t.showGridline) {
2306                                         drawLine(pos, this._top, pos, this._bottom);
2307                                     }
2308                                     
2309                                     // draw the mark
2310                                     if (t.showMark && t.mark) {
2311                                         s = t.markSize;
2312                                         m = t.mark;
2313                                         var pos = Math.round(axis.u2p(t.value)) + 0.5;
2314                                         switch (m) {
2315                                             case 'outside':
2316                                                 b = this._bottom;
2317                                                 e = this._bottom+s;
2318                                                 break;
2319                                             case 'inside':
2320                                                 b = this._bottom-s;
2321                                                 e = this._bottom;
2322                                                 break;
2323                                             case 'cross':
2324                                                 b = this._bottom-s;
2325                                                 e = this._bottom+s;
2326                                                 break;
2327                                             default:
2328                                                 b = this._bottom;
2329                                                 e = this._bottom+s;
2330                                                 break;
2331                                         }
2332                                         // draw the shadow
2333                                         if (this.shadow) {
2334                                             this.renderer.shadowRenderer.draw(ctx, [[pos,b],[pos,e]], {lineCap:'butt', lineWidth:this.gridLineWidth, offset:this.gridLineWidth*0.75, depth:2, fill:false, closePath:false});
2335                                         }
2336                                         // draw the line
2337                                         drawLine(pos, b, pos, e);
2338                                     }
2339                                     break;
2340                                 case 'yaxis':
2341                                     // draw the grid line
2342                                     if (t.showGridline) {
2343                                         drawLine(this._right, pos, this._left, pos);
2344                                     }
2345                                     // draw the mark
2346                                     if (t.showMark && t.mark) {
2347                                         s = t.markSize;
2348                                         m = t.mark;
2349                                         var pos = Math.round(axis.u2p(t.value)) + 0.5;
2350                                         switch (m) {
2351                                             case 'outside':
2352                                                 b = this._left-s;
2353                                                 e = this._left;
2354                                                 break;
2355                                             case 'inside':
2356                                                 b = this._left;
2357                                                 e = this._left+s;
2358                                                 break;
2359                                             case 'cross':
2360                                                 b = this._left-s;
2361                                                 e = this._left+s;
2362                                                 break;
2363                                             default:
2364                                                 b = this._left-s;
2365                                                 e = this._left;
2366                                                 break;
2367                                                 }
2368                                         // draw the shadow
2369                                         if (this.shadow) {
2370                                             this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false});
2371                                         }
2372                                         drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
2373                                     }
2374                                     break;
2375                                 case 'x2axis':
2376                                     // draw the grid line
2377                                     if (t.showGridline) {
2378                                         drawLine(pos, this._bottom, pos, this._top);
2379                                     }
2380                                     // draw the mark
2381                                     if (t.showMark && t.mark) {
2382                                         s = t.markSize;
2383                                         m = t.mark;
2384                                         var pos = Math.round(axis.u2p(t.value)) + 0.5;
2385                                         switch (m) {
2386                                             case 'outside':
2387                                                 b = this._top-s;
2388                                                 e = this._top;
2389                                                 break;
2390                                             case 'inside':
2391                                                 b = this._top;
2392                                                 e = this._top+s;
2393                                                 break;
2394                                             case 'cross':
2395                                                 b = this._top-s;
2396                                                 e = this._top+s;
2397                                                 break;
2398                                             default:
2399                                                 b = this._top-s;
2400                                                 e = this._top;
2401                                                 break;
2402                                                 }
2403                                         // draw the shadow
2404                                         if (this.shadow) {
2405                                             this.renderer.shadowRenderer.draw(ctx, [[pos,b],[pos,e]], {lineCap:'butt', lineWidth:this.gridLineWidth, offset:this.gridLineWidth*0.75, depth:2, fill:false, closePath:false});
2406                                         }
2407                                         drawLine(pos, b, pos, e);
2408                                     }
2409                                     break;
2410                                 case 'y2axis':
2411                                     // draw the grid line
2412                                     if (t.showGridline) {
2413                                         drawLine(this._left, pos, this._right, pos);
2414                                     }
2415                                     // draw the mark
2416                                     if (t.showMark && t.mark) {
2417                                         s = t.markSize;
2418                                         m = t.mark;
2419                                         var pos = Math.round(axis.u2p(t.value)) + 0.5;
2420                                         switch (m) {
2421                                             case 'outside':
2422                                                 b = this._right;
2423                                                 e = this._right+s;
2424                                                 break;
2425                                             case 'inside':
2426                                                 b = this._right-s;
2427                                                 e = this._right;
2428                                                 break;
2429                                             case 'cross':
2430                                                 b = this._right-s;
2431                                                 e = this._right+s;
2432                                                 break;
2433                                             default:
2434                                                 b = this._right;
2435                                                 e = this._right+s;
2436                                                 break;
2437                                                 }
2438                                         // draw the shadow
2439                                         if (this.shadow) {
2440                                             this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false});
2441                                         }
2442                                         drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
2443                                     }
2444                                     break;
2445                                 default:
2446                                     break;
2447                             }
2448                         }
2449                     }
2450                 }
2451             }
2452             // Now draw grid lines for additional y axes
2453             ax = ['y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis'];
2454             for (var i=7; i>0; i--) {
2455                 var axis = axes[ax[i-1]];
2456                 var ticks = axis._ticks;
2457                 if (axis.show) {
2458                     var tn = ticks[axis.numberTicks-1];
2459                     var t0 = ticks[0];
2460                     var left = axis.getLeft();
2461                     var points = [[left, tn.getTop() + tn.getHeight()/2], [left, t0.getTop() + t0.getHeight()/2 + 1.0]];
2462                     // draw the shadow
2463                     if (this.shadow) {
2464                         this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', fill:false, closePath:false});
2465                     }
2466                     // draw the line
2467                     drawLine(points[0][0], points[0][1], points[1][0], points[1][1], {lineCap:'butt', strokeStyle:axis.borderColor, lineWidth:axis.borderWidth});
2468                     // draw the tick marks
2469                     for (var j=ticks.length; j>0; j--) {
2470                         var t = ticks[j-1];
2471                         s = t.markSize;
2472                         m = t.mark;
2473                         var pos = Math.round(axis.u2p(t.value)) + 0.5;
2474                         if (t.showMark && t.mark) {
2475                             switch (m) {
2476                                 case 'outside':
2477                                     b = left;
2478                                     e = left+s;
2479                                     break;
2480                                 case 'inside':
2481                                     b = left-s;
2482                                     e = left;
2483                                     break;
2484                                 case 'cross':
2485                                     b = left-s;
2486                                     e = left+s;
2487                                     break;
2488                                 default:
2489                                     b = left;
2490                                     e = left+s;
2491                                     break;
2492                             }
2493                             points = [[b,pos], [e,pos]];
2494                             // draw the shadow
2495                             if (this.shadow) {
2496                                 this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false});
2497                             }
2498                             // draw the line
2499                             drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
2500                         }
2501                     }
2502                 }
2503             }
2504             
2505             ctx.restore();
2506         }
2507         
2508         function drawLine(bx, by, ex, ey, opts) {
2509             ctx.save();
2510             opts = opts || {};
2511             $.extend(true, ctx, opts);
2512             ctx.beginPath();
2513             ctx.moveTo(bx, by);
2514             ctx.lineTo(ex, ey);
2515             ctx.stroke();
2516             ctx.restore();
2517         }
2518         
2519         if (this.shadow) {
2520             var points = [[this._left, this._bottom], [this._right, this._bottom], [this._right, this._top]];
2521             this.renderer.shadowRenderer.draw(ctx, points);
2522         }
2523         // Now draw border around grid.  Use axis border definitions. start at
2524         // upper left and go clockwise.
2525         drawLine (this._left, this._top, this._right, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth});
2526         drawLine (this._right, this._top, this._right, this._bottom, {lineCap:'round', strokeStyle:axes.y2axis.borderColor, lineWidth:axes.y2axis.borderWidth});
2527         drawLine (this._right, this._bottom, this._left, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth});
2528         drawLine (this._left, this._bottom, this._left, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth});
2529         // ctx.lineWidth = this.borderWidth;
2530         // ctx.strokeStyle = this.borderColor;
2531         // ctx.strokeRect(this._left, this._top, this._width, this._height);
2532         
2533     
2534         ctx.restore();
2535     };
2536    
2537     /**
2538      * Date instance methods
2539      *
2540      * @author Ken Snyder (ken d snyder at gmail dot com)
2541      * @date 2008-09-10
2542      * @version 2.0.2 (http://kendsnyder.com/sandbox/date/)     
2543      * @license Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/)
2544      *
2545      * @contributions Chris Leonello
2546      * @comment Bug fix to 12 hour time and additions to handle milliseconds and 
2547      * @comment 24 hour time without am/pm suffix
2548      *
2549      */
2550  
2551     // begin by creating a scope for utility variables
2552     
2553     //
2554     // pre-calculate the number of milliseconds in a day
2555     //  
2556     
2557     var day = 24 * 60 * 60 * 1000;
2558     //
2559     // function to add leading zeros
2560     //
2561     var zeroPad = function(number, digits) {
2562         number = String(number);
2563         while (number.length < digits) {
2564             number = '0' + number;
2565         }
2566         return number;
2567     };
2568     //
2569     // set up integers and functions for adding to a date or subtracting two dates
2570     //
2571     var multipliers = {
2572         millisecond: 1,
2573         second: 1000,
2574         minute: 60 * 1000,
2575         hour: 60 * 60 * 1000,
2576         day: day,
2577         week: 7 * day,
2578         month: {
2579             // add a number of months
2580             add: function(d, number) {
2581                 // add any years needed (increments of 12)
2582                 multipliers.year.add(d, Math[number > 0 ? 'floor' : 'ceil'](number / 12));
2583                 // ensure that we properly wrap betwen December and January
2584                 var prevMonth = d.getMonth() + (number % 12);
2585                 if (prevMonth == 12) {
2586                     prevMonth = 0;
2587                     d.setYear(d.getFullYear() + 1);
2588                 } else if (prevMonth == -1) {
2589                     prevMonth = 11;
2590                     d.setYear(d.getFullYear() - 1);
2591                 }
2592                 d.setMonth(prevMonth);
2593             },
2594             // get the number of months between two Date objects (decimal to the nearest day)
2595             diff: function(d1, d2) {
2596                 // get the number of years
2597                 var diffYears = d1.getFullYear() - d2.getFullYear();
2598                 // get the number of remaining months
2599                 var diffMonths = d1.getMonth() - d2.getMonth() + (diffYears * 12);
2600                 // get the number of remaining days
2601                 var diffDays = d1.getDate() - d2.getDate();
2602                 // return the month difference with the days difference as a decimal
2603                 return diffMonths + (diffDays / 30);
2604             }
2605         },
2606         year: {
2607             // add a number of years
2608             add: function(d, number) {
2609                 d.setYear(d.getFullYear() + Math[number > 0 ? 'floor' : 'ceil'](number));
2610             },
2611             // get the number of years between two Date objects (decimal to the nearest day)
2612             diff: function(d1, d2) {
2613                 return multipliers.month.diff(d1, d2) / 12;
2614             }
2615         }        
2616     };
2617     //
2618     // alias each multiplier with an 's' to allow 'year' and 'years' for example
2619     //
2620     for (var unit in multipliers) {
2621         if (unit.substring(unit.length - 1) != 's') { // IE will iterate newly added properties :|
2622             multipliers[unit + 's'] = multipliers[unit];
2623         }
2624     }
2625     //
2626     // take a date instance and a format code and return the formatted value
2627     //
2628     var format = function(d, code) {
2629             if (Date.prototype.strftime.formatShortcuts[code]) {
2630                     // process any shortcuts recursively
2631                     return d.strftime(Date.prototype.strftime.formatShortcuts[code]);
2632             } else {
2633                     // get the format code function and toPaddedString() argument
2634                     var getter = (Date.prototype.strftime.formatCodes[code] || '').split('.');
2635                     var nbr = d['get' + getter[0]] ? d['get' + getter[0]]() : '';
2636                     // run toPaddedString() if specified
2637                     if (getter[1]) {
2638                         nbr = zeroPad(nbr, getter[1]);
2639                     }
2640                     // prepend the leading character
2641                     return nbr;
2642             }       
2643     };
2644     //
2645     // Add methods to Date instances
2646     //
2647     var instanceMethods = {
2648         //
2649         // Return a date one day ahead (or any other unit)
2650         //
2651         // @param string unit
2652         // units: year | month | day | week | hour | minute | second | millisecond
2653         // @return object Date
2654         //
2655         succ: function(unit) {
2656             return this.clone().add(1, unit);
2657         },
2658         //
2659         // Add an arbitrary amount to the currently stored date
2660         //
2661         // @param integer/float number      
2662         // @param string unit
2663         // @return object Date (chainable)      
2664         //
2665         add: function(number, unit) {
2666             var factor = multipliers[unit] || multipliers.day;
2667             if (typeof factor == 'number') {
2668                 this.setTime(this.getTime() + (factor * number));
2669             } else {
2670                 factor.add(this, number);
2671             }
2672             return this;
2673         },
2674         //
2675         // Find the difference between the current and another date
2676         //
2677         // @param string/object dateObj
2678         // @param string unit
2679         // @param boolean allowDecimal
2680         // @return integer/float
2681         //
2682         diff: function(dateObj, unit, allowDecimal) {
2683             // ensure we have a Date object
2684             dateObj = Date.create(dateObj);
2685             if (dateObj === null) {
2686                 return null;
2687             }
2688             // get the multiplying factor integer or factor function
2689             var factor = multipliers[unit] || multipliers.day;
2690             if (typeof factor == 'number') {
2691                 // multiply
2692                 var unitDiff = (this.getTime() - dateObj.getTime()) / factor;
2693             } else {
2694                 // run function
2695                 var unitDiff = factor.diff(this, dateObj);
2696             }
2697             // if decimals are not allowed, round toward zero
2698             return (allowDecimal ? unitDiff : Math[unitDiff > 0 ? 'floor' : 'ceil'](unitDiff));          
2699         },
2700         //
2701         // Convert a date to a string using traditional strftime format codes
2702         //
2703         // @param string formatStr
2704         // @return string
2705         //
2706         strftime: function(formatStr) {
2707             // default the format string to year-month-day
2708             var source = formatStr || '%Y-%m-%d', result = '', match;
2709             // replace each format code
2710             while (source.length > 0) {
2711                 if (match = source.match(Date.prototype.strftime.formatCodes.matcher)) {
2712                     result += source.slice(0, match.index);
2713                     result += (match[1] || '') + format(this, match[2]);
2714                     source = source.slice(match.index + match[0].length);
2715                 } else {
2716                     result += source;
2717                     source = '';
2718                 }
2719             }
2720             return result;
2721         },
2722         //
2723         // Return a proper two-digit year integer
2724         //
2725         // @return integer
2726         //
2727         getShortYear: function() {
2728             return this.getYear() % 100;
2729         },
2730         //
2731         // Get the number of the current month, 1-12
2732         //
2733         // @return integer
2734         //
2735         getMonthNumber: function() {
2736             return this.getMonth() + 1;
2737         },
2738         //
2739         // Get the name of the current month
2740         //
2741         // @return string
2742         //
2743         getMonthName: function() {
2744             return Date.MONTHNAMES[this.getMonth()];
2745         },
2746         //
2747         // Get the abbreviated name of the current month
2748         //
2749         // @return string
2750         //
2751         getAbbrMonthName: function() {
2752             return Date.ABBR_MONTHNAMES[this.getMonth()];
2753         },
2754         //
2755         // Get the name of the current week day
2756         //
2757         // @return string
2758         //      
2759         getDayName: function() {
2760             return Date.DAYNAMES[this.getDay()];
2761         },
2762         //
2763         // Get the abbreviated name of the current week day
2764         //
2765         // @return string
2766         //