PageSourceSearch

https://www.mocerf.org/CERFPlan/_include/js/jqplot/jquery.jqplot.js

js mocerf.org collected 2026-10-04 18:32:05 UTC 305,620 bytes, 7,004 lines download raw bytes

1/**
2 * Title: jqPlot Charts
3 * 
4 * Pure JavaScript plotting plugin for jQuery.
5 * 
6 * About: Version
7 * 
8 * 0.9.7r618 
9 * 
10 * About: Copyright & License
11 * 
12 * Copyright (c) 2009 - 2010 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 at jqplot dot com 
21 * 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 * jqPlot includes `date instance methods and printf/sprintf functions by other authors:
27 * 
28 * Date instance methods:
29 *
30 *     author Ken Snyder (ken d snyder at gmail dot com)
31 *     date 2008-09-10
32 *     version 2.0.2 (http://kendsnyder.com/sandbox/date/)     
33 *     license Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/)
34 *
35 * JavaScript printf/sprintf functions:
36 *
37 *     version 2007.04.27
38 *     author Ash Searle
39 *     http://hexmen.com/blog/2007/03/printf-sprintf/
40 *     http://hexmen.com/js/sprintf.js
41 *     The author (Ash Searle) has placed this code in the public domain:
42 *     "This code is unrestricted: you are free to use it however you like."
43 * 
44 * 
45 * About: Introduction
46 * 
47 * jqPlot requires jQuery (1.4+ required for certain features). jQuery 1.4.1 is included in the distribution.  
48 * To use jqPlot include jQuery, the jqPlot jQuery plugin, the jqPlot css file and optionally 
49 * the excanvas script for IE support in your web page:
50 * 
51 * > <!--[if IE]><script language="javascript" type="text/javascript" src="excanvas.js"></script><![endif]-->
52 * > <script language="javascript" type="text/javascript" src="jquery-1.4.2.min.js"></script>
53 * > <script language="javascript" type="text/javascript" src="jquery.jqplot.min.js"></script>
54 * > <link rel="stylesheet" type="text/css" href="jquery.jqplot.css" />
55 * 
56 * jqPlot can be customized by overriding the defaults of any of the objects which make
57 * up the plot. The general usage of jqplot is:
58 * 
59 * > chart = $.jqplot('targetElemId', [dataArray,...], {optionsObject});
60 * 
61 * The options available to jqplot are detailed in <jqPlot Options> in the jqPlotOptions.txt file.
62 * 
63 * An actual call to $.jqplot() may look like the 
64 * examples below:
65 * 
66 * > chart = $.jqplot('chartdiv',  [[[1, 2],[3,5.12],[5,13.1],[7,33.6],[9,85.9],[11,219.9]]]);
67 * 
68 * or
69 * 
70 * > dataArray = [34,12,43,55,77];
71 * > chart = $.jqplot('targetElemId', [dataArray, ...], {title:'My Plot', axes:{yaxis:{min:20, max:100}}});
72 * 
73 * For more inforrmation, see <jqPlot Usage>.
74 * 
75 * About: Usage
76 * 
77 * See <jqPlot Usage>
78 * 
79 * About: Available Options 
80 * 
81 * See <jqPlot Options> for a list of options available thorugh the options object (not complete yet!)
82 * 
83 * About: Options Usage
84 * 
85 * See <Options Tutorial>
86 * 
87 * About: Changes
88 * 
89 * See <Change Log>
90 * 
91 */
92
93(function($) {
94    // make sure undefined is undefined
95    var undefined;
96
97    /**
98     * Class: $.jqplot
99     * jQuery function called by the user to create a plot.
100     *  
101     * Parameters:
102     * target - ID of target element to render the plot into.
103     * data - an array of data series.
104     * options - user defined options object.  See the individual classes for available options.
105     * 
106     * Properties:
107     * config - object to hold configuration information for jqPlot plot object.
108     * 
109     * attributes:
110     * enablePlugins - False to disable plugins by default.  Plugins must then be explicitly 
111     *   enabled in the individual plot options.  Default: false.
112     *   This property sets the "show" property of certain plugins to true or false.
113     *   Only plugins that can be immediately active upon loading are affected.  This includes
114     *   non-renderer plugins like cursor, dragable, highlighter, and trendline.
115     * defaultHeight - Default height for plots where no css height specification exists.  This
116     *   is a jqplot wide default.
117     * defaultWidth - Default height for plots where no css height specification exists.  This
118     *   is a jqplot wide default.
119     */
120
121    $.jqplot = function(target, data, options) {
122        var _data, _options;
123        
124        if (options == null) {
125            if (data instanceof Array) {
126                _data = data;
127                _options = null;   
128            }
129            
130            else if (data.constructor == Object) {
131                _data = null;
132                _options = data;
133            }
134        }
135        else {
136            _data = data;
137            _options = options;
138        }
139        var plot = new jqPlot();
140        // remove any error class that may be stuck on target.
141        $('#'+target).removeClass('jqplot-error');
142        
143        if ($.jqplot.config.catchErrors) {
144            try {
145                plot.init(target, _data, _options);
146                plot.draw();
147                plot.themeEngine.init.call(plot);
148                return plot;
149            }
150            catch(e) {
151                var msg = $.jqplot.config.errorMessage || e.message;
152                $('#'+target).append('<div class="jqplot-error-message">'+msg+'</div>');
153                $('#'+target).addClass('jqplot-error');
154                document.getElementById(target).style.background = $.jqplot.config.errorBackgroun
154d;
155                document.getElementById(target).style.border = $.jqplot.config.errorBorder;
156                document.getElementById(target).style.fontFamily = $.jqplot.config.errorFontFamily;
157                document.getElementById(target).style.fontSize = $.jqplot.config.errorFontSize;
158                document.getElementById(target).style.fontStyle = $.jqplot.config.errorFontStyle;
159                document.getElementById(target).style.fontWeight = $.jqplot.config.errorFontWeight;
160            }
161        }
162        else {        
163            plot.init(target, _data, _options);
164            plot.draw();
165            plot.themeEngine.init.call(plot);
166            return plot;
167        }
168    };
169        
170    $.jqplot.debug = 1;
171    $.jqplot.config = {
172        debug:1,
173        enablePlugins:false,
174        defaultHeight:300,
175        defaultWidth:400,
176        UTCAdjust:false,
177        timezoneOffset: new Date(new Date().getTimezoneOffset() * 60000),
178        errorMessage: '',
179        errorBackground: '',
180        errorBorder: '',
181        errorFontFamily: '',
182        errorFontSize: '',
183        errorFontStyle: '',
184        errorFontWeight: '',
185        catchErrors: false,
186        defaultTickFormatString: "%.1f"
187    };
188    
189    $.jqplot.enablePlugins = $.jqplot.config.enablePlugins;
190    
191    /**
192     * 
193     * Hooks: jqPlot Pugin Hooks
194     * 
195     * $.jqplot.preInitHooks - called before initialization.
196     * $.jqplot.postInitHooks - called after initialization.
197     * $.jqplot.preParseOptionsHooks - called before user options are parsed.
198     * $.jqplot.postParseOptionsHooks - called after user options are parsed.
199     * $.jqplot.preDrawHooks - called before plot draw.
200     * $.jqplot.postDrawHooks - called after plot draw.
201     * $.jqplot.preDrawSeriesHooks - called before each series is drawn.
202     * $.jqplot.postDrawSeriesHooks - called after each series is drawn.
203     * $.jqplot.preDrawLegendHooks - called before the legend is drawn.
204     * $.jqplot.addLegendRowHooks - called at the end of legend draw, so plugins
205     *     can add rows to the legend table.
206     * $.jqplot.preSeriesInitHooks - called before series is initialized.
207     * $.jqplot.postSeriesInitHooks - called after series is initialized.
208     * $.jqplot.preParseSeriesOptionsHooks - called before series related options
209     *     are parsed.
210     * $.jqplot.postParseSeriesOptionsHooks - called after series related options
211     *     are parsed.
212     * $.jqplot.eventListenerHooks - called at the end of plot drawing, binds
213     *     listeners to the event canvas which lays on top of the grid area.
214     * $.jqplot.preDrawSeriesShadowHooks - called before series shadows are drawn.
215     * $.jqplot.postDrawSeriesShadowHooks - called after series shadows are drawn.
216     * 
217     */
218    
219    $.jqplot.preInitHooks = [];
220    $.jqplot.postInitHooks = [];
221    $.jqplot.preParseOptionsHooks = [];
222    $.jqplot.postParseOptionsHooks = [];
223    $.jqplot.preDrawHooks = [];
224    $.jqplot.postDrawHooks = [];
225    $.jqplot.preDrawSeriesHooks = [];
226    $.jqplot.postDrawSeriesHooks = [];
227    $.jqplot.preDrawLegendHooks = [];
228    $.jqplot.addLegendRowHooks = [];
229    $.jqplot.preSeriesInitHooks = [];
230    $.jqplot.postSeriesInitHooks = [];
231    $.jqplot.preParseSeriesOptionsHooks = [];
232    $.jqplot.postParseSeriesOptionsHooks = [];
233    $.jqplot.eventListenerHooks = [];
234    $.jqplot.preDrawSeriesShadowHooks = [];
235    $.jqplot.postDrawSeriesShadowHooks = [];
236
237    // A superclass holding some common properties and methods.
238    $.jqplot.ElemContainer = function() {
239        this._elem;
240        this._plotWidth;
241        this._plotHeight;
242        this._plotDimensions = {height:null, width:null};
243    };
244    
245    $.jqplot.ElemContainer.prototype.createElement = function(el, offsets, clss, cssopts, attrib) {
246        this._offsets = offsets;
247        var klass = clss || 'jqplot';
248        var elem = document.createElement(el);
249        this._elem = $(elem);
250        this._elem.addClass(klass);
251        this._elem.css(cssopts);
252        this._elem.attr(attrib);
253        return this._elem;
254    };
255    
256    $.jqplot.ElemContainer.prototype.getWidth = function() {
257        if (this._elem) {
258            return this._elem.outerWidth(true);
259        }
260        else {
261            return null;
262        }
263    };
264    
265    $.jqplot.ElemContainer.prototype.getHeight = function() {
266        if (this._elem) {
267            return this._elem.outerHeight(true);
268        }
269        else {
270            return null;
271        }
272    };
273    
274    $.jqplot.ElemContainer.prototype.getPosition = function() {
275        if (this._elem) {
276            return this._elem.position();
277        }
278        else {
279            return {top:null, left:null, bottom:null, right:null};
280        }
281    };
282    
283    $.jqplot.ElemContainer.prototype.getTop = function() {
284        return this.getPosition().top;
285    };
286    
287    $.jqplot.ElemContainer.prototype.getLeft = function() {
288        return this.getPosition().left;
289    };
290    
291    $.jqplot.ElemContainer.prototype.getBottom = function() {
292        return this._elem.css('bottom');
293    };
294    
295    $.jqplot.ElemContainer.prototype.getRight = function() {
296        return this._elem.css('right');
297    };
298    
299
300    /**
301     * Class: Axis
302     * An individual axis object.  Cannot be instantiated directly, but created
303     * by the Plot oject.  Axis properties can be set or overriden by the 
304     * options passed in from the user.
305     * 
306     */
307    function Axis(name) {
308        $.jqplot.ElemContainer.call(this);
309        // Group: Properties
310        //
311        // Axes options are specified within an axes object at the top level of the 
312        // plot options like so:
313        // > {
314        // >    axes: {
315        // >        xaxis: {min: 5},
316        // >        yaxis: {min: 2, max: 8, numberTicks:4},
317        // >        x2axis: {pad: 1.5},
318        // >        y2axis: {ticks:[22, 44, 66, 88]}
319        // >        }
320        // > }
321        // There are 4 axes, 'xaxis', 'yaxis', 'x2axis', 'y2axis'.  Any or all of 
322        // which may be specified.
323        this.name = name;
324        this._series = [];
325        // prop: show
326        // Wether to display the axis on the graph.
327        this.show = false;
328        // prop: tickRenderer
329        // A class of a rendering engine for creating the ticks labels displayed on the plot, 
330        // See <$.jqplot.AxisTickRenderer>.
331        this.tickRenderer = $.jqplot.AxisTickRenderer;
332        // prop: tickOptions
333        // Options that will be passed to the tickRenderer, see <$.jqplot.AxisTickRenderer> options.
334        this.tickOptions = {};
335        // prop: labelRenderer
336        // A class of a rendering engine for creating an axis label.
337        this.labelRenderer = $.jqplot.AxisLabelRenderer;
338        // prop: labelOptions
339        // Options passed to the label renderer.
340        this.labelOptions = {};
341        // prop: label
342        // Label for the axis
343        this.label = null;
344        // prop: showLabel
345        // true to show the axis label.
346        this.showLabel = true;
347        // prop: min
348        // minimum value of the axis (in data units, not pixels).
349        this.min=null;
350        // prop: max
351        // maximum value of the axis (in data units, not pixels).
352        this.max=null;
353        // prop: autoscale
354        // Autoscale the axis min and max values to provide sensible tick spacing.
355        // If axis min or max are set, autoscale will be turned off.
356        // The numberTicks, tickInterval and pad options do work with 
357        // autoscale, although tickInterval has not been tested yet.
358        // padMin and padMax do nothing when autoscale is on.
359        this.autoscale = false;
360        // prop: pad
361        // Padding to extend the range above and below the data bounds.
362        // The data range is multiplied by this factor to determine minimum and maximum axis boun
362ds.
363        // A value of 0 will be interpreted to mean no padding, and pad will be set to 1.0.
364        this.pad = 1.2;
365        // prop: padMax
366        // Padding to extend the range above data bounds.
367        // The top of the data range is multiplied by this factor to determine maximum axis bounds.
368        // A value of 0 will be interpreted to mean no padding, and padMax will be set to 1.0.
369        this.padMax = null;
370        // prop: padMin
371        // Padding to extend the range below data bounds.
372        // The bottom of the data range is multiplied by this factor to determine minimum axis bounds.
373        // A value of 0 will be interpreted to mean no padding, and padMin will be set to 1.0.
374        this.padMin = null;
375        // prop: ticks
376        // 1D [val, val, ...] or 2D [[val, label], [val, label], ...] array of ticks for the axis.
377        // If no label is specified, the value is formatted into an appropriate label.
378        this.ticks = [];
379        // prop: numberTicks
380        // Desired number of ticks.  Default is to compute automatically.
381        this.numberTicks;
382        // prop: tickInterval
383        // number of units between ticks.  Mutually exclusive with numberTicks.
384        this.tickInterval;
385        // prop: renderer
386        // A class of a rendering engine that handles tick generation, 
387        // scaling input data to pixel grid units and drawing the axis element.
388        this.renderer = $.jqplot.LinearAxisRenderer;
389        // prop: rendererOptions
390        // renderer specific options.  See <$.jqplot.LinearAxisRenderer> for options.
391        this.rendererOptions = {};
392        // prop: showTicks
393        // wether to show the ticks (both marks and labels) or not.
394        this.showTicks = true;
395        // prop: showTickMarks
396        // wether to show the tick marks (line crossing grid) or not.
397        this.showTickMarks = true;
398        // prop: showMinorTicks
399        // Wether or not to show minor ticks.  This is renderer dependent.
400        // The default <$.jqplot.LinearAxisRenderer> does not have minor ticks.
401        this.showMinorTicks = true;
402        // prop: useSeriesColor
403        // Use the color of the first series associated with this axis for the
404        // tick marks and line bordering this axis.
405        this.useSeriesColor = false;
406        // prop: borderWidth
407        // width of line stroked at the border of the axis.  Defaults
408        // to the width of the grid boarder.
409        this.borderWidth = null;
410        // prop: borderColor
411        // color of the border adjacent to the axis.  Defaults to grid border color.
412        this.borderColor = null;
413        // minimum and maximum values on the axis.
414        this._dataBounds = {min:null, max:null};
415        // pixel position from the top left of the min value and max value on the axis.
416        this._offsets = {min:null, max:null};
417        this._ticks=[];
418        this._label = null;
419        // prop: syncTicks
420        // true to try and synchronize tick spacing across multiple axes so that ticks and
421        // grid lines line up.  This has an impact on autoscaling algorithm, however.
422        // In general, autoscaling an individual axis will work better if it does not
423        // have to sync ticks.
424        this.syncTicks = null;
425        // prop: tickSpacing
426        // Approximate pixel spacing between ticks on graph.  Used during autoscaling.
427        // This number will be an upper bound, actual spacing will be less.
428        this.tickSpacing = 75;
429        // Properties to hold the original values for min, max, ticks, tickInterval and numberTicks
430        // so they can be restored if altered by plugins.
431        this._min = null;
432        this._max = null;
433        this._tickInterval = null;
434        this._numberTicks = null;
435        this.__ticks = null;
436    }
437    
438    Axis.prototype = new $.jqplot.ElemContainer();
439    Axis.prototype.constructor = Axis;
440    
441    Axis.prototype.init = function() {
442        this.renderer = new this.renderer();
443        // set the axis name
444        this.tickOptions.axis = this.name;
445        if (this.label == null || this.label == '') {
446            this.showLabel = false;
447        }
448        else {
449            this.labelOptions.label = this.label;
450        }
451        if (this.showLabel == false) {
452            this.labelOptions.show = false;
453        }
454        // set the default padMax, padMin if not specified
455        // special check, if no padding desired, padding
456        // should be set to 1.0
457        if (this.pad == 0) {
458            this.pad = 1.0;
459        }
460        if (this.padMax == 0) {
461            this.padMax = 1.0;
462        }
463        if (this.padMin == 0) {
464            this.padMin = 1.0;
465        }
466        if (this.padMax == null) {
467            this.padMax = (this.pad-1)/2 + 1;
468        }
469        if (this.padMin == null) {
470            this.padMin = (this.pad-1)/2 + 1;
471        }
472        // now that padMin and padMax are correctly set, reset pad in case user has supplied 
473        // padMin and/or padMax
474        this.pad = this.padMax + this.padMin - 1;
475        if (this.min != null || this.max != null) {
476            this.autoscale = false;
477        }
478        // if not set, sync ticks for y axes but not x by default.
479        if (this.syncTicks == null && this.name.indexOf('y') > -1) {
480            this.syncTicks = true;
481        }
482        else if (this.syncTicks == null){
483            this.syncTicks = false;
484        }
485        this.renderer.init.call(this, this.rendererOptions);
486        
487    };
488    
489    Axis.prototype.draw = function(ctx) {
490        return this.renderer.draw.call(this, ctx);
491        
492    };
493    
494    Axis.prototype.set = function() {
495        this.renderer.set.call(this);
496    };
497    
498    Axis.prototype.pack = function(pos, offsets) {
499        if (this.show) {
500            this.renderer.pack.call(this, pos, offsets);
501        }
502        // these properties should all be available now.
503        if (this._min == null) {
504            this._min = this.min;
505            this._max = this.max;
506            this._tickInterval = this.tickInterval;
507            this._numberTicks = this.numberTicks;
508            this.__ticks = this._ticks;
509        }
510    };
511    
512    // reset the axis back to original values if it has been scaled, zoomed, etc.
513    Axis.prototype.reset = function() {
514        this.renderer.reset.call(this);
515    };
516    
517    Axis.prototype.resetScale = function() {
518        this.min = null;
519        this.max = null;
520        this.numberTicks = null;
521        this.tickInterval = null;
522    };
523
524    /**
525     * Class: Legend
526     * Legend object.  Cannot be instantiated directly, but created
527     * by the Plot oject.  Legend properties can be set or overriden by the 
528     * options passed in from the user.
529     */
530    function Legend(options) {
531        $.jqplot.ElemContainer.call(this);
532        // Group: Properties
533        
534        // prop: show
535        // Wether to display the legend on the graph.
536        this.show = false;
537        // prop: location
538        // Placement of the legend.  one of the compass directions: nw, n, ne, e, se, s, sw, w
539        this.location = 'ne';
540        // prop: labels
541        // Array of labels to use.  By default the renderer will look for labels on the series.
542        // Labels specified in this array will override labels specified on the series.
543        this.labels = [];
544        // prop: showLabels
545        // true to show the label text on the  legend.
546        this.showLabels = true;
547        // prop: showSwatch
548        // true to show the color swatches on the legend.
549        this.showSwatches = true;
550        // prop: placement
551        // "insideGrid" places legend inside the grid area of the plot.
552        // "outsideGrid" places the legend outside the grid but inside the plot container, 
553        // shrinking the grid to accomodate the legend.
554        // "inside" synonym for "insideGrid", 
555        // "outside" places the legend ouside the grid area, but does not shrink the grid which
556        // can cause the legend to overflow the plot container.
557        this.placement = "insideGrid";
558        // prop: xoffset
559        // DEPRECATED.  Set the margins on the legend using the marginTop, marginLeft, etc. 
560        // properties or via CSS margin styling of the .jqplot-table-legend class.
561        this.xoffset = 0;
562        // prop: yoffset
563        // DEPRECATED.  Set the margins on the legend using the marginTop, marginLeft, etc. 
564        // properties or via CSS margin styling of the .jqplot-table-legend class.
565        this.yoffset = 0;
566        // prop: border
567        // css spec for the border around the legend box.
568        this.border;
569        // prop: background
570        // css spec for the background of the legend box.
571        this.background;
572        // prop: textColor
573        // css color spec for the legend text.
574        this.textColor;
575        // prop: fontFamily
576        // css font-family spec for the legend text.
577        this.fontFamily; 
578        // prop: fontSize
579        // css font-size spec for the legend text.
580        this.fontSize ;
581        // prop: rowSpacing
582        // css padding-top spec for the rows in the legend.
583        this.rowSpacing = '0.5em';
584        // renderer
585        // A class that will create a DOM object for the legend,
586        // see <$.jqplot.TableLegendRenderer>.
587        this.renderer = $.jqplot.TableLegendRenderer;
588        // prop: rendererOptions
589        // renderer specific options passed to the renderer.
590        this.rendererOptions = {};
591        // prop: predraw
592        // Wether to draw the legend before the series or not.
593        // Used with series specific legend renderers for pie, donut, mekko charts, etc.
594        this.preDraw = false;
595        // prop: marginTop
596        // CSS margin for the legend DOM element. This will set an element 
597        // CSS style for the margin which will override any style sheet setting.
598        // The default will be taken from the stylesheet.
599        this.marginTop = null;
600        // prop: marginRight
601        // CSS margin for the legend DOM element. This will set an element 
602        // CSS style for the margin which will override any style sheet setting.
603        // The default will be taken from the stylesheet.
604        this.marginRight = null;
605        // prop: marginBottom
606        // CSS margin for the legend DOM element. This will set an element 
607        // CSS style for the margin which will override any style sheet setting.
608        // The default will be taken from the stylesheet.
609        this.marginBottom = null;
610        // prop: marginLeft
611        // CSS margin for the legend DOM element. This will set an element 
612        // CSS style for the margin which will override any style sheet setting.
613        // The default will be taken from the stylesheet.
614        this.marginLeft = null;
615        
616        this.escapeHtml = false;
617        this._series = [];
618        
619        $.extend(true, this, options);
620    }
621    
622    Legend.prototype = new $.jqplot.ElemContainer();
623    Legend.prototype.constructor = Legend;
624    
625    Legend.prototype.setOptions = function(options) {
626        $.extend(true, this, options);
627        
628        // Try to emulate deprecated behaviour
629        // if user has specified xoffset or yoffset, copy these to
630        // the margin properties.
631        
632        if (this.placement ==  'inside') this.placement = 'insideGrid';
633        
634        if (this.xoffset >0) {
635            if (this.placement == 'insideGrid') {
636                switch (this.location) {
637                    case 'nw':
638                    case 'w':
639                    case 'sw':
640                        if (this.marginLeft == null) {
641                            this.marginLeft = this.xoffset + 'px';
642                        }
643                        this.marginRight = '0px';
644                        break;
645                    case 'ne':
646                    case 'e':
647                    case 'se':
648                    default:
649                        if (this.marginRight == null) {
650                            this.marginRight = this.xoffset + 'px';
651                        }
652                        this.marginLeft = '0px';
653                        break;
654                }
655            }
656            else if (this.placement == 'outside') {
657                switch (this.location) {
658                    case 'nw':
659                    case 'w':
660                    case 'sw':
661                        if (this.marginRight == null) {
662                            this.marginRight = this.xoffset + 'px';
663                        }
664                        this.marginLeft = '0px';
665                        break;
666                    case 'ne':
667                    case 'e':
668                    case 'se':
669                    default:
670                        if (this.marginLeft == null) {
671                            this.marginLeft = this.xoffset + 'px';
672                        }
673                        this.marginRight = '0px';
674                        break;
675                }
676            }
677            this.xoffset = 0;
678        }
679        
680        if (this.yoffset >0) {
681            if (this.placement == 'outside') {
682                switch (this.location) {
683                    case 'sw':
684                    case 's':
685                    case 'se':
686                        if (this.marginTop == null) {
687                            this.marginTop = this.yoffset + 'px';
688                        }
689                        this.marginBottom = '0px';
690                        break;
691                    case 'ne':
692                    case 'n':
693                    case 'nw':
694                    default:
695                        if (this.marginBottom == null) {
696                            this.marginBottom = this.yoffset + 'px';
697                        }
698                        this.marginTop = '0px';
699                        break;
700                }
701            }
702            else if (this.placement == 'insideGrid') {
703                switch (this.location) {
704                    case 'sw':
705                    case 's':
706                    case 'se':
707                        if (this.marginBottom == null) {
708                            this.marginBottom = this.yoffset + 'px';
709                        }
710                        this.marginTop = '0px';
711                        break;
712                    case 'ne':
713                    case 'n':
714                    case 'nw':
715                    default:
716                        if (this.marginTop == null) {
717                            this.marginTop = this.yoffset + 'px';
718                        }
719                        this.marginBottom = '0px';
720                        break;
721                }
722            }
723            this.yoffset = 0;
724        }
725        
726        // TO-DO:
727        // Handle case where offsets are < 0.
728        //
729    };
730    
731    Legend.prototype.init = function() {
732        this.renderer = new this.renderer();
733        this.renderer.init.call(this, this.rendererOptions);
734    };
735    
736    Legend.prototype.draw = function(offsets) {
737        for (var i=0; i<$.jqplot.preDrawLegendHooks.length; i++){
738            $.jqplot.preDrawLegendHooks[i].call(this, offsets);
739        }
740        return this.renderer.draw.call(this, offsets);
741    };
742    
743    Legend.prototype.pack = function(offsets) {
744        this.renderer.pack.call(this, offsets);
745    };
746
747    /**
748     * Class: Title
749     * Plot Title object.  Cannot be instantiated directly, but created
750     * by the Plot oject.  Title properties can be set or overriden by the 
751     * options passed in from the user.
752     * 
753     * Parameters:
754     * text - text of the title.
755     */
756    function Title(text) {
757        $.jqplot.ElemContainer.call(this);
758        // Group: Properties
759        
760        // prop: text
761        // text of the title;
762        this.text = text;
763        // prop: show
764        // wether or not to show the title
765        this.show = true;
766        // prop: fontFamily
767        // css font-family spec for the text.
768        this.fontFamily;
769        // prop: fontSize
770        // css font-size spec for the text.
771        this.fontSize ;
772        // prop: textAlign
773        // css text-align spec for the text.
774        this.textAlign;
775        // prop: textColor
776        // css color spec for the text.
777        this.textColor;
778        // prop: renderer
779        // A class for creating a DOM element for the title,
780        // see <$.jqplot.DivTitleRenderer>.
781        this.renderer = $.jqplot.DivTitleRenderer;
782        // prop: rendererOptions
783        // renderer specific options passed to the renderer.
784        this.rendererOptions = {};   
785    }
786    
787    Title.prototype = new $.jqplot.ElemContainer();
788    Title.prototype.constructor = Title;
789    
790    Title.prototype.init = function() {
791        this.renderer = new this.renderer();
792        this.renderer.init.call(this, this.rendererOptions);
793    };
794    
795    Title.prototype.draw = function(width) {
796        return this.renderer.draw.call(this, width);
797    };
798    
799    Title.prototype.pack = function() {
800        this.renderer.pack.call(this);
801    };
802
803
804    /**
805     * Class: Series
806     * An individual data series object.  Cannot be instantiated directly, but created
807     * by the Plot oject.  Series properties can be set or overriden by the 
808     * options passed in from the user.
809     */
810    function Series() {
811        $.jqplot.ElemContainer.call(this);
812        // Group: Properties
813        // Properties will be assigned from a series array at the top level of the
814        // options.  If you had two series and wanted to change the color and line
815        // width of the first and set the second to use the secondary y axis with
816        // no shadow and supply custom labels for each:
817        // > {
818        // >    series:[
819        // >        {color: '#ff4466', lineWidth: 5, label:'good line'},
820        // >        {yaxis: 'y2axis', shadow: false, label:'bad line'}
821        // >    ]
822        // > }
823        
824        // prop: show
825        // wether or not to draw the series.
826        this.show = true;
827        // prop: xaxis
828        // which x axis to use with this series, either 'xaxis' or 'x2axis'.
829        this.xaxis = 'xaxis';
830        this._xaxis;
831        // prop: yaxis
832        // which y axis to use with this series, either 'yaxis' or 'y2axis'.
833        this.yaxis = 'yaxis';
834        this._yaxis;
835        this.gridBorderWidth = 2.0;
836        // prop: renderer
837        // A class of a renderer which will draw the series, 
838        // see <$.jqplot.LineRenderer>.
839        this.renderer = $.jqplot.LineRenderer;
840        // prop: rendererOptions
841        // Options to pass on to the renderer.
842        this.rendererOptions = {};
843        this.data = [];
844        this.gridData = [];
845        // prop: label
846        // Line label to use in the legend.
847        this.label = '';
848        // prop: showLabel
849        // true to show label for this series in the legend.
850        this.showLabel = true;
851        // prop: color
852        // css color spec for the series
853        this.color;
854        // prop: lineWidth
855        // width of the line in pixels.  May have different meanings depending on renderer.
856        this.lineWidth = 2.5;
857        // prop: shadow
858        // wether or not to draw a shadow on the line
859        this.shadow = true;
860        // prop: shadowAngle
861        // Shadow angle in degrees
862        this.shadowAngle = 45;
863        // prop: shadowOffset
864        // Shadow offset from line in pixels
865        this.shadowOffset = 1.25;
866        // prop: shadowDepth
867        // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
868        this.shadowDepth = 3;
869        // prop: shadowAlpha
870        // Alpha channel transparency of shadow.  0 = transparent.
871        this.shadowAlpha = '0.1';
872        // prop: breakOnNull
873        // Not implemented. wether line segments should be be broken at null value.
874        // False will join point on either side of line.
875        this.breakOnNull = false;
876        // prop: markerRenderer
877        // A class of a renderer which will draw marker (e.g. circle, square, ...) at the data points,
878        // see <$.jqplot.MarkerRenderer>.
879        this.markerRenderer = $.jqplot.MarkerRenderer;
880        // prop: markerOptions
881        // renderer specific options to pass to the markerRenderer,
882        // see <$.jqplot.MarkerRenderer>.
883        this.markerOptions = {};
884        // prop: showLine
885        // wether to actually draw the line or not.  Series will still be renderered, even if no line is drawn.
886        this.showLine = true;
887        // prop: showMarker
888        // wether or not to show the markers at the data points.
889        this.showMarker = true;
890        // prop: index
891        // 0 based index of this series in the plot series array.
892        this.index;
893        // prop: fill
894        // true or false, wether to fill under lines or in bars.
895        // May not be implemented in all renderers.
896        this.fill = false;
897        // prop: fillColor
898        // CSS color spec to use for fill under line.  Defaults to line color.
899        this.fillColor;
900        // prop: fillAlpha
901        // Alpha transparency to apply to the fill under the line.
902        // Use this to adjust alpha separate from fill color.
903        this.fillAlpha;
904        // prop: fillAndStroke
905        // If true will stroke the line (with color this.color) as well as fill under it.
906        // Applies only when fill is true.
907        this.fillAndStroke = false;
908        // prop: disableStack
909        // true to not stack this series with other series in the plot.
910        // To render properly, non-stacked series must come after any stacked series
911        // in the plot's data series array.  So, the plot's data series array would look like:
912        // > [stackedSeries1, stackedSeries2, ..., nonStackedSeries1, nonStackedSeries2, ...]
913        // disableStack will put a gap in the stacking order of series, and subsequent
914        // stacked series will not fill down through the non-stacked series and will
915        // most likely not stack properly on top of the non-stacked series.
916        this.disableStack = false;
917        // _stack is set by the Plot if the plot is a stacked chart.
918        // will stack lines or bars on top of one another to build a "mountain" style chart.
919        // May not be implemented in all renderers.
920        this._stack = false;
921        // prop: neighborThreshold
922        // how close or far (in pixels) the cursor must be from a point marker to detect the point.
923        this.neighborThreshold = 4;
924        // prop: fillToZero
925        // true will force bar and filled series to fill toward zero on the fill Axis.
926        this.fillToZero = false;
927        // prop: fillToValue
928        // fill a filled series to this value on the fill axis.
929        // Works in conjunction with fillToZero, so that must be true.
930        this.fillToValue = 0;
931        // prop: fillAxis
932        // Either 'x' or 'y'.  Which axis to fill the line toward if fillToZero is true.
933        // 'y' means fill up/down to 0 on the y axis for this series.
934        this.fillAxis = 'y';
935        // prop: useNegativeColors
936        // true to color negative values differently in filled and bar charts.
937        this.useNegativeColors = true;
938        this._stackData = [];
939        // _plotData accounts for stacking.  If plots not stacked, _plotData and data are same.  If
940        // stacked, _plotData is accumulation of stacking data.
941        this._plotData = [];
942        // _plotValues hold the individual x and y values that will be plotted for this series.
943        this._plotValues = {x:[], y:[]};
944        // statistics about the intervals between data points.  Used for auto scaling.
945        this._intervals = {x:{}, y:{}};
946        // data from the previous series, for stacked charts.
947        this._prevPlotData = [];
948        this._prevGridData = [];
949        this._stackAxis = 'y';
950        this._primaryAxis = '_xaxis';
951        // give each series a canvas to draw on.  This should allow for redrawing speedups.
952        this.canvas = new $.jqplot.GenericCanvas();
953        this.shadowCanvas = new $.jqplot.GenericCanvas();
954        this.plugins = {};
955        // sum of y values in this series.
956        this._sumy = 0;
957        this._sumx = 0;
958    }
959    
960    Series.prototype = new $.jqplot.ElemContainer();
961    Series.prototype.constructor = Series;
962    
963    Series.prototype.init = function(index, gridbw, plot) {
964        // weed out any null values in the data.
965        this.index = index;
966        this.gridBorderWidth = gridbw;
967        var d = this.data;
968        var temp = [], i;
969        for (i=0; i<d.length; i++) {
970            if (! this.breakOnNull) {
971                if (d[i] == null || d[i][0] == null || d[i][1] == null) {
972                    continue;
973                }
974                else {
975                    temp.push(d[i]);
976                }
977            }
978            else {
979                // TODO: figure out what to do with null values
980                // probably involve keeping nulls in data array
981                // and then updating renderers to break line
982                // when it hits null value.
983                // For now, just keep value.
984                temp.push(d[i]);
985            }
986        }
987        this.data = temp;
988        if (!this.fillColor) {
989            this.fillColor = this.color;
990        }
991        if (this.fillAlpha) {
992            var comp = $.jqplot.normalize2rgb(this.fillColor);
993            var comp = $.jqplot.getColorComponents(comp);
994            this.fillColor = 'rgba('+comp[0]+','+comp[1]+','+comp[2]+','+this.fillAlpha+')';
995        }
996        this.renderer = new this.renderer();
997        this.renderer.init.call(this, this.rendererOptions, plot);
998        this.markerRenderer = new this.markerRenderer();
999        if (!this.markerOptions.color) {
1000            this.markerOptions.color = this.color;
1001        }
1002        if (this.markerOptions.show == null) {
1003            this.markerOptions.show = this.showMarker;
1004        }
1005        this.showMarker = this.markerOptions.show;
1006        // the markerRenderer is called within it's own scaope, don't want to overwrite series options!!
1007        this.markerRenderer.init(this.markerOptions);
1008    };
1009    
1010    // data - optional data point array to draw using this series renderer
1011    // gridData - optional grid data point array to draw using this series renderer
1012    // stackData - array of cumulative data for stacked plots.
1013    Series.prototype.draw = function(sctx, opts, plot) {
1014        var options = (opts == undefined) ? {} : opts;
1015        sctx = (sctx == undefined) ? this.canvas._ctx : sctx;
1016        // hooks get called even if series not shown
1017        // we don't clear canvas here, it would wipe out all other series as well.
1018        for (var j=0; j<$.jqplot.preDrawSeriesHooks.length; j++) {
1019            $.jqplot.preDrawSeriesHooks[j].call(this, sctx, options);
1020        }
1021        if (this.show) {
1022            this.renderer.setGridData.call(this, plot);
1023            if (!options.preventJqPlotSeriesDrawTrigger) {
1024                $(sctx.canvas).trigger('jqplotSeriesDraw', [this.data, this.gridData]);
1025            }
1026            var data = [];
1027            if (options.data) {
1028                data = options.data;
1029            }
1030            else if (!this._stack) {
1031                data = this.data;
1032            }
1033            else {
1034                data = this._plotData;
1035            }
1036            var gridData = options.gridData || this.renderer.makeGridData.call(this, data, plot);
1037            this.renderer.draw.call(this, sctx, gridData, options, plot);
1038        }
1039        
1040        for (var j=0; j<$.jqplot.postDrawSeriesHooks.length; j++) {
1041            $.jqplot.postDrawSeriesHooks[j].call(this, sctx, options);
1042        }
1043    };
1044    
1045    Series.prototype.drawShadow = function(sctx, opts, plot) {
1046        var options = (opts == undefined) ? {} : opts;
1047        sctx = (sctx == undefined) ? this.shadowCanvas._ctx : sctx;
1048        // hooks get called even if series not shown
1049        // we don't clear canvas here, it would wipe out all other series as well.
1050        for (var j=0; j<$.jqplot.preDrawSeriesShadowHooks.length; j++) {
1051            $.jqplot.preDrawSeriesShadowHooks[j].call(this, sctx, options);
1052        }
1053        if (this.shadow) {
1054            this.renderer.setGridData.call(this, plot);
1055
1056            var data = [];
1057            if (options.data) {
1058                data = options.data;
1059            }
1060            else if (!this._stack) {
1061                data = this.data;
1062            }
1063            else {
1064                data = this._plotData;
1065            }
1066            var gridData = options.gridData || this.renderer.makeGridData.call(this, data, plot);
1067        
1068            this.renderer.drawShadow.call(this, sctx, gridData, options);
1069        }
1070        
1071        for (var j=0; j<$.jqplot.postDrawSeriesShadowHooks.length; j++) {
1072            $.jqplot.postDrawSeriesShadowHooks[j].call(this, sctx, options);
1073        }
1074        
1075    };
1076    
1077    // toggles series display on plot, e.g. show/hide series
1078    Series.prototype.toggleDisplay = function(ev) {
1079        var s, speed;
1080        if (ev.data.series) {
1081            s = ev.data.series;
1082        }
1083        else {
1084            s = this;
1085        }
1086        if (ev.data.speed) {
1087            speed = ev.data.speed;
1088        }
1089        if (speed) {
1090            if (s.canvas._elem.is(':hidden')) {
1091                if (s.shadowCanvas._elem) {
1092                    s.shadowCanvas._elem.fadeIn(speed);
1093                }
1094                s.canvas._elem.fadeIn(speed);
1095                s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).fadeIn(speed);
1096            }
1097            else {
1098                if (s.shadowCanvas._elem) {
1099                    s.shadowCanvas._elem.fadeOut(speed);
1100                }
1101                s.canvas._elem.fadeOut(speed);
1102                s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).fadeOut(speed);
1103            }
1104        }
1105        else {
1106            if (s.canvas._elem.is(':hidden')) {
1107                if (s.shadowCanvas._elem) {
1108                    s.shadowCanvas._elem.show();
1109                }
1110                s.canvas._elem.show();
1111                s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).show();
1112            }
1113            else {
1114                if (s.shadowCanvas._elem) {
1115                    s.shadowCanvas._elem.hide();
1116                }
1117                s.canvas._elem.hide();
1118                s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).hide();
1119            }
1120        }
1121    };
1122    
1123
1124
1125    /**
1126     * Class: Grid
1127     * 
1128     * Object representing the grid on which the plot is drawn.  The grid in this
1129     * context is the area bounded by the axes, the area which will contain the series.
1130     * Note, the series are drawn on their own canvas.
1131     * The Grid object cannot be instantiated directly, but is created by the Plot oject.  
1132     * Grid properties can be set or overriden by the options passed in from the user.
1133     */
1134    function Grid() {
1135        $.jqplot.ElemContainer.call(this);
1136        // Group: Properties
1137        
1138        // prop: drawGridlines
1139        // wether to draw the gridlines on the plot.
1140        this.drawGridlines = true;
1141        // prop: gridLineColor
1142        // color of the grid lines.
1143        this.gridLineColor = '#cccccc';
1144        // prop: gridLineWidth
1145        // width of the grid lines.
1146        this.gridLineWidth = 1.0;
1147        // prop: background
1148        // css spec for the background color.
1149        this.background = '#fffdf6';
1150        // prop: borderColor
1151        // css spec for the color of the grid border.
1152        this.borderColor = '#999999';
1153        // prop: borderWidth
1154        // width of the border in pixels.
1155        this.borderWidth = 2.0;
1156        // prop: drawBorder
1157        // True to draw border around grid.
1158        this.drawBorder = true;
1159        // prop: shadow
1160        // wether to show a shadow behind the grid.
1161        this.shadow = true;
1162        // prop: shadowAngle
1163        // shadow angle in degrees
1164        this.shadowAngle = 45;
1165        // prop: shadowOffset
1166        // Offset of each shadow stroke from the border in pixels
1167        this.shadowOffset = 1.5;
1168        // prop: shadowWidth
1169        // width of the stoke for the shadow
1170        this.shadowWidth = 3;
1171        // prop: shadowDepth
1172        // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
1173        this.shadowDepth = 3;
1174        // prop: shadowColor
1175        // an optional css color spec for the shadow in 'rgba(n, n, n, n)' form
1176        this.shadowColor = null;
1177        // prop: shadowAlpha
1178        // Alpha channel transparency of shadow.  0 = transparent.
1179        this.shadowAlpha = '0.07';
1180        this._left;
1181        this._top;
1182        this._right;
1183        this._bottom;
1184        this._width;
1185        this._height;
1186        this._axes = [];
1187        // prop: renderer
1188        // Instance of a renderer which will actually render the grid,
1189        // see <$.jqplot.CanvasGridRenderer>.
1190        this.renderer = $.jqplot.CanvasGridRenderer;
1191        // prop: rendererOptions
1192        // Options to pass on to the renderer,
1193        // see <$.jqplot.CanvasGridRenderer>.
1194        this.rendererOptions = {};
1195        this._offsets = {top:null, bottom:null, left:null, right:null};
1196    }
1197    
1198    Grid.prototype = new $.jqplot.ElemContainer();
1199    Grid.prototype.constructor = Grid;
1200    
1201    Grid.prototype.init = function() {
1202        this.renderer = new this.renderer();
1203        this.renderer.init.call(this, this.rendererOptions);
1204    };
1205    
1206    Grid.prototype.createElement = function(offsets) {
1207        this._offsets = offsets;
1208        return this.renderer.createElement.call(this);
1209    };
1210    
1211    Grid.prototype.draw = function() {
1212        this.renderer.draw.call(this);
1213    };
1214    
1215    $.jqplot.GenericCanvas = function() {
1216        $.jqplot.ElemContainer.call(this);
1217        this._ctx;  
1218    };
1219    
1220    $.jqplot.GenericCanvas.prototype = new $.jqplot.ElemContainer();
1221    $.jqplot.GenericCanvas.prototype.constructor = $.jqplot.GenericCanvas;
1222    
1223    $.jqplot.GenericCanvas.prototype.createElement = function(offsets, clss, plotDimensions) {
1224        this._offsets = offsets;
1225        var klass = 'jqplot';
1226        if (clss != undefined) {
1227            klass = clss;
1228        }
1229        var elem;
1230        // if this canvas already has a dom element, don't make a new one.
1231        if (this._elem) {
1232            elem = this._elem.get(0);
1233        }
1234        else {
1235            elem = document.createElement('canvas');
1236        }
1237        // if new plotDimensions supplied, use them.
1238        if (plotDimensions != undefined) {
1239            this._plotDimensions = plotDimensions;
1240        }
1241        
1242        elem.width = this._plotDimensions.width - this._offsets.left - this._offsets.right;
1243        elem.height = this._plotDimensions.height - this._offsets.top - this._offsets.bottom;
1244        this._elem = $(elem);
1245        this._elem.css({ position: 'absolute', left: this._offsets.left, top: this._offsets.top });
1246        
1247        this._elem.addClass(klass);
1248        if ($.browser.msie) {
1249            window.G_vmlCanvasManager.init_(document);
1250            elem = window.G_vmlCanvasManager.initElement(elem);
1251        }
1252        return this._elem;
1253    };
1254    
1255    $.jqplot.GenericCanvas.prototype.setContext = function() {
1256        this._ctx = this._elem.get(0).getContext("2d");
1257        return this._ctx;
1258    };
1259    
1260    $.jqplot.HooksManager = function () {
1261        this.hooks =[];
1262    };
1263    
1264    $.jqplot.HooksManager.prototype.addOnce = function(fn) {
1265        var havehook = false, i;
1266        for (i=0; i<this.hooks.length; i++) {
1267            if (this.hooks[i][0] == fn) {
1268                havehook = true;
1269            }
1270        }
1271        if (!havehook) {
1272            this.hooks.push(fn);
1273        }
1274    };
1275    
1276    $.jqplot.HooksManager.prototype.add = function(fn) {
1277        this.hooks.push(fn);
1278    };
1279    
1280    $.jqplot.EventListenerManager = function () {
1281        this.hooks =[];
1282    };
1283    
1284    $.jqplot.EventListenerManager.prototype.addOnce = function(ev, fn) {
1285        var havehook = false, h, i;
1286        for (i=0; i<this.hooks.length; i++) {
1287            h = this.hooks[i];
1288            if (h[0] == ev && h[1] == fn) {
1289                havehook = true;
1290            }
1291        }
1292        if (!havehook) {
1293            this.hooks.push([ev, fn]);
1294        }
1295    };
1296    
1297    $.jqplot.EventListenerManager.prototype.add = function(ev, fn) {
1298        this.hooks.push([ev, fn]);
1299    };
1300
1301    /**
1302     * Class: jqPlot
1303     * Plot object returned by call to $.jqplot.  Handles parsing user options,
1304     * creating sub objects (Axes, legend, title, series) and rendering the plot.
1305     */
1306    function jqPlot() {
1307        // Group: Properties
1308        // These properties are specified at the top of the options object
1309        // like so:
1310        // > {
1311        // >     axesDefaults:{min:0},
1312        // >     series:[{color:'#6633dd'}],
1313        // >     title: 'A Plot'
1314        // > }
1315        //
1316        // prop: data
1317        // user's data.  Data should *NOT* be specified in the options object,
1318        // but be passed in as the second argument to the $.jqplot() function.
1319        // The data property is described here soley for reference. 
1320        // The data should be in the form of an array of 2D or 1D arrays like
1321        // > [ [[x1, y1], [x2, y2],...], [y1, y2, ...] ].
1322        this.data = [];
1323        // The id of the dom element to render the plot into
1324        this.targetId = null;
1325        // the jquery object for the dom target.
1326        this.target = null; 
1327        this.defaults = {
1328            // prop: axesDefaults
1329            // default options that will be applied to all axes.
1330            // see <Axis> for axes options.
1331            axesDefaults: {},
1332            axes: {xaxis:{}, yaxis:{}, x2axis:{}, y2axis:{}, y3axis:{}, y4axis:{}, y5axis:{}, y6axis:{}, y7axis:{}, y8axis:{}, y9axis:{}},
1333            // prop: seriesDefaults
1334            // default options that will be applied to all series.
1335            // see <Series> for series options.
1336            seriesDefaults: {},
1337            gridPadding: {top:10, right:10, bottom:23, left:10},
1338            series:[]
1339        };
1340        // prop: series
1341        // Array of series object options.
1342        // see <Series> for series specific options.
1343        this.series = [];
1344        // prop: axes
1345        // up to 4 axes are supported, each with it's own options, 
1346        // See <Axis> for axis specific options.
1347        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')};
1348        // prop: grid
1349        // See <Grid> for grid specific options.
1350        this.grid = new Grid();
1351        // prop: legend
1352        // see <$.jqplot.TableLegendRenderer>
1353        this.legend = new Legend();
1354        this.baseCanvas = new $.jqplot.GenericCanvas();
1355        // array of series indicies. Keep track of order
1356        // which series canvases are displayed, lowest
1357        // to highest, back to front.
1358        this.seriesStack = [];
1359        this.previousSeriesStack = [];
1360        this.eventCanvas = new $.jqplot.GenericCanvas();
1361        this._width = null;
1362        this._height = null; 
1363        this._plotDimensions = {height:null, width:null};
1364        this._gridPadding = {top:10, right:10, bottom:10, left:10};
1365        // a shortcut for axis syncTicks options.  Not implemented yet.
1366        this.syncXTicks = true;
1367        // a shortcut for axis syncTicks options.  Not implemented yet.
1368        this.syncYTicks = true;
1369        // prop: seriesColors
1370        // Ann array of CSS color specifications that will be applied, in order,
1371        // to the series in the plot.  Colors will wrap around so, if their
1372        // are more series than colors, colors will be reused starting at the
1373        // beginning.  For pie charts, this specifies the colors of the slices.
1374        this.seriesColors = [ "#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"];
1375        this.negativeSeriesColors = [ "#498991", "#C08840", "#9F9274", "#546D61", "#646C4A", "#6F6621", "#6E3F5F", "#4F64B0", "#A89050", "#C45923", "#187399", "#945381", "#959E5C", "#C7AF7B", "#478396", "#907294"];
1376        // prop: sortData
1377        // false to not sort the data passed in by the user.
1378        // Many bar, stakced and other graphs as well as many plugins depend on
1379        // having sorted data.
1380        this.sortData = true;
1381        var seriesColorsIndex = 0;
1382        // prop textColor
1383        // css spec for the css color attribute.  Default for the entire plot.
1384        this.textColor;
1385        // prop; fontFamily
1386        // css spec for the font-family attribute.  Default for the entire plot.
1387        this.fontFamily;
1388        // prop: fontSize
1389        // css spec for the font-size attribute.  Default for the entire plot.
1390        this.fontSize;
1391        // prop: title
1392        // Title object.  See <Title> for specific options.  As a shortcut, you
1393        // can specify the title option as just a string like: title: 'My Plot'
1394        // and this will create a new title object with the specified text.
1395        this.title = new Title();
1396        // container to hold all of the merged options.  Convienence for plugins.
1397        this.options = {};
1398        // prop: stackSeries
1399        // true or false, creates a stack or "mountain" plot.
1400        // Not all series renderers may implement this option.
1401        this.stackSeries = false;
1402        // prop: defaultAxisStart
1403        // 1-D data series are internally converted into 2-D [x,y] data point arrays
1404        // by jqPlot.  This is the default starting value for the missing x or y value.
1405        // The added data will be a monotonically increasing series (e.g. [1, 2, 3, ...])
1406        // starting at this value.
1407        this.defaultAxisStart = 1;
1408        // array to hold the cumulative stacked series data.
1409        // used to ajust the individual series data, which won't have access to other
1410        // series data.
1411        this._stackData = [];
1412        // array that holds the data to be plotted. This will be the series data
1413        // merged with the the appropriate data from _stackData according to the stackAxis.
1414        this._plotData = [];
1415        // Namespece to hold plugins.  Generally non-renderer plugins add themselves to here.
1416        this.plugins = {};
1417        // Count how many times the draw method has been called while the plot is visible.
1418        // Mostly used to test if plot has never been dran (=0), has been successfully drawn
1419        // into a visible container once (=1) or draw more than once into a visible container.
1420        // Can use this in tests to see if plot has been visibly drawn at least one time.
1421        // After plot has been visibly drawn once, it generally doesn't need redrawn if its
1422        // container is hidden and shown.
1423        this._drawCount = 0;
1424        // this.doCustomEventBinding = true;
1425        // prop: drawIfHidden
1426        // True to execute the draw method even if the plot target is hidden.
1427        // Generally, this should be false.  Most plot elements will not be sized/
1428        // positioned correclty if renderered into a hidden container.  To render into
1429        // a hidden container, call the replot method when the container is shown.
1430        this.drawIfHidden = false;
1431        // true to intercept right click events and fire a 'jqplotRightClick' event.
1432        // this will also block the context menu.
1433        this.captureRightClick = false;
1434        this.themeEngine = new $.jqplot.ThemeEngine();
1435        // sum of y values for all series in plot.
1436        // used in mekko chart.
1437        this._sumy = 0;
1438        this._sumx = 0;
1439        this.preInitHooks = new $.jqplot.HooksManager();
1440        this.postInitHooks = new $.jqplot.HooksManager();
1441        this.preParseOptionsHooks = new $.jqplot.HooksManager();
1442        this.postParseOptionsHooks = new $.jqplot.HooksManager();
1443        this.preDrawHooks = new $.jqplot.HooksManager();
1444        this.postDrawHooks = new $.jqplot.HooksManager();
1445        this.preDrawSeriesHooks = new $.jqplot.HooksManager();
1446        this.postDrawSeriesHooks = new $.jqplot.HooksManager();
1447        this.preDrawLegendHooks = new $.jqplot.HooksManager();
1448        this.addLegendRowHooks = new $.jqplot.HooksManager();
1449        this.preSeriesInitHooks = new $.jqplot.HooksManager();
1450        this.postSeriesInitHooks = new $.jqplot.HooksManager();
1451        this.preParseSeriesOptionsHooks = new $.jqplot.HooksManager();
1452        this.postParseSeriesOptionsHooks = new $.jqplot.HooksManager();
1453        this.eventListenerHooks = new $.jqplot.EventListenerManager();
1454        this.preDrawSeriesShadowHooks = new $.jqplot.HooksManager();
1455        this.postDrawSeriesShadowHooks = new $.jqplot.HooksManager();
1456        
1457        this.colorGenerator = $.jqplot.ColorGenerator;
1458        
1459        // Group: methods
1460        //
1461        // method: init
1462        // sets the plot target, checks data and applies user
1463        // options to plot.
1464        this.init = function(target, data, options) {
1465            for (var i=0; i<$.jqplot.preInitHooks.length; i++) {
1466                $.jqplot.preInitHooks[i].call(this, target, data, options);
1467            }
1468
1469            for (var i=0; i<this.preInitHooks.hooks.length; i++) {
1470                this.preInitHooks.hooks[i].call(this, target, data, options);
1471            }
1472            
1473            this.targetId = '#'+target;
1474            this.target = $('#'+target);
1475            // remove any error class that may be stuck on target.
1476            this.target.removeClass('jqplot-error');
1477            if (!this.target.get(0)) {
1478                throw "No plot target specified";
1479            }
1480            
1481            // make sure the target is positioned by some means and set css
1482            if (this.target.css('position') == 'static') {
1483                this.target.css('position', 'relative');
1484            }
1485            if (!this.target.hasClass('jqplot-target')) {
1486                this.target.addClass('jqplot-target');
1487            }
1488            
1489            // if no height or width specified, use a default.
1490            if (!this.target.height()) {
1491                var h;
1492                if (options && options.height) {
1493                    h = parseInt(options.height, 10);
1494                }
1495                else if (this.target.attr('data-height')) {
1496                    h = parseInt(this.target.attr('data-height'), 10);
1497                }
1498                else {
1499                    h = parseInt($.jqplot.config.defaultHeight, 10);
1500                }
1501                this._height = h;
1502                this.target.css('height', h+'px');
1503            }
1504            else {
1505                this._height = this.target.height();
1506            }
1507            if (!this.target.width()) {
1508                var w;
1509                if (options && options.width) {
1510                    w = parseInt(options.width, 10);
1511                }
1512                else if (this.target.attr('data-width')) {
1513                    w = parseInt(this.target.attr('data-width'), 10);
1514                }
1515                else {
1516                    w = parseInt($.jqplot.config.defaultWidth, 10);
1517                }
1518                this._width = w;
1519                this.target.css('width', w+'px');
1520            }
1521            else {
1522                this._width = this.target.width();
1523            }
1524            
1525            this._plotDimensions.height = this._height;
1526            this._plotDimensions.width = this._width;
1527            this.grid._plotDimensions = this._plotDimensions;
1528            this.title._plotDimensions = this._plotDimensions;
1529            this.baseCanvas._plotDimensions = this._plotDimensions;
1530            this.eventCanvas._plotDimensions = this._plotDimensions;
1531            this.legend._plotDimensions = this._plotDimensions;
1532            if (this._height <=0 || this._width <=0 || !this._height || !this._width) {
1533                throw "Canvas dimension not set";
1534            }
1535            
1536            if (data == null) {
1537                throw{
1538                    name: "DataError",
1539                    message: "No data to plot."
1540                };
1541            }
1542            if (data.constructor != Array || data.length == 0 || data[0].constructor != Array || data[0].length == 0) {
1543                throw{
1544                    name: "DataError",
1545                    message: "No data to plot."
1546                };
1547            }
1548            
1549            this.data = data;
1550            
1551            this.parseOptions(options);
1552            
1553            if (this.textColor) {
1554                this.target.css('color', this.textColor);
1555            }
1556            if (this.fontFamily) {
1557                this.target.css('font-family', this.fontFamily);
1558            }
1559            if (this.fontSize) {
1560                this.target.css('font-size', this.fontSize);
1561            }
1562            
1563            this.title.init();
1564            this.legend.init();
1565            this._sumy = 0;
1566            this._sumx = 0;
1567            for (var i=0; i<this.series.length; i++) {
1568                // set default stacking order for series canvases
1569                this.seriesStack.push(i);
1570                this.previousSeriesStack.push(i);
1571                this.series[i].shadowCanvas._plotDimensions = this._plotDimensions;
1572                this.series[i].canvas._plotDimensions = this._plotDimensions;
1573                for (var j=0; j<$.jqplot.preSeriesInitHooks.length; j++) {
1574                    $.jqplot.preSeriesInitHooks[j].call(this.series[i], target, data, this.options.seriesDefaults, this.options.series[i], this);
1575                }
1576                for (var j=0; j<this.preSeriesInitHooks.hooks.length; j++) {
1577                    this.preSeriesInitHooks.hooks[j].call(this.series[i], target, data, this.options.seriesDefaults, this.options.series[i], this);
1578                }
1579                this.populatePlotData(this.series[i], i);
1580                this.series[i]._plotDimensions = this._plotDimensions;
1581                this.series[i].init(i, this.grid.borderWidth, this);
1582                for (var j=0; j<$.jqplot.postSeriesInitHooks.length; j++) {
1583                    $.jqplot.postSeriesInitHooks[j].call(this.series[i], target, data, this.options.seriesDefaults, this.options.series[i], this);
1584                }
1585                for (var j=0; j<this.postSeriesInitHooks.hooks.length; j++) {
1586                    this.postSeriesInitHooks.hooks[j].call(this.series[i], target, data, this.options.seriesDefaults, this.options.series[i], this);
1587                }
1588                this._sumy += this.series[i]._sumy;
1589                this._sumx += this.series[i]._sumx;
1590            }
1591
1592            for (var name in this.axes) {
1593                this.axes[name]._plotDimensions = this._plotDimensions;
1594                this.axes[name].init();
1595            }
1596            
1597            if (this.sortData) {
1598                sortData(this.series);
1599            }
1600            this.grid.init();
1601            this.grid._axes = this.axes;
1602            
1603            this.legend._series = this.series;
1604
1605            for (var i=0; i<$.jqplot.postInitHooks.length; i++) {
1606                $.jqplot.postInitHooks[i].call(this, target, data, options);
1607            }
1608
1609            for (var i=0; i<this.postInitHooks.hooks.length; i++) {
1610                this.postInitHooks.hooks[i].call(this, target, data, options);
1611            }
1612        };  
1613        
1614        // method: resetAxesScale
1615        // Reset the specified axes min, max, numberTicks and tickInterval properties to null
1616        // or reset these properties on all axes if no list of axes is provided.
1617        //
1618        // Parameters:
1619        // axes - Boolean to reset or not reset all axes or an array or object of axis names to reset.
1620        this.resetAxesScale = function(axes) {
1621            var ax = (axes != undefined) ? axes : this.axes;
1622            if (ax === true) {
1623                ax = this.axes;
1624            }
1625            if (ax.constructor === Array) {
1626                for (var i = 0; i < ax.length; i++) {
1627                    this.axes[ax[i]].resetScale();
1628                }
1629            }
1630            else if (ax.constructor === Object) {
1631                for (var name in ax) {
1632                    this.axes[name].resetScale();
1633                }
1634            }
1635        };
1636        // method: reInitialize
1637        // reinitialize plot for replotting.
1638        // not called directly.
1639        this.reInitialize = function () {
1640            // Plot should be visible and have a height and width.
1641            // If plot doesn't have height and width for some
1642            // reason, set it by other means.  Plot must not have
1643            // a display:none attribute, however.
1644            if (!this.target.height()) {
1645                var h;
1646                if (options && options.height) {
1647                    h = parseInt(options.height, 10);
1648                }
1649                else if (this.target.attr('data-height')) {
1650                    h = parseInt(this.target.attr('data-height'), 10);
1651                }
1652                else {
1653                    h = parseInt($.jqplot.config.defaultHeight, 10);
1654                }
1655                this._height = h;
1656                this.target.css('height', h+'px');
1657            }
1658            else {
1659                this._height = this.target.height();
1660            }
1661            if (!this.target.width()) {
1662                var w;
1663                if (options && options.width) {
1664                    w = parseInt(options.width, 10);
1665                }
1666                else if (this.target.attr('data-width')) {
1667                    w = parseInt(this.target.attr('data-width'), 10);
1668                }
1669                else {
1670                    w = parseInt($.jqplot.config.defaultWidth, 10);
1671                }
1672                this._width = w;
1673                this.target.css('width', w+'px');
1674            }
1675            else {
1676                this._width = this.target.width();
1677            }
1678            
1679            if (this._height <=0 || this._width <=0 || !this._height || !this._width) {
1680                throw "Target dimension not set";
1681            }
1682            
1683            this._plotDimensions.height = this._height;
1684            this._plotDimensions.width = this._width;
1685            this.grid._plotDimensions = this._plotDimensions;
1686            this.title._plotDimensions = this._plotDimensions;
1687            this.baseCanvas._plotDimensions = this._plotDimensions;
1688            this.eventCanvas._plotDimensions = this._plotDimensions;
1689            this.legend._plotDimensions = this._plotDimensions;
1690            
1691            for (var n in this.axes) {
1692                this.axes[n]._plotWidth = this._width;
1693                this.axes[n]._plotHeight = this._height;
1694            }
1695            
1696            this.title._plotWidth = this._width;
1697            
1698            if (this.textColor) {
1699                this.target.css('color', this.textColor);
1700            }
1701            if (this.fontFamily) {
1702                this.target.css('font-family', this.fontFamily);
1703            }
1704            if (this.fontSize) {
1705                this.target.css('font-size', this.fontSize);
1706            }
1707            
1708            this._sumy = 0;
1709            this._sumx = 0;
1710            for (var i=0; i<this.series.length; i++) {
1711                this.populatePlotData(this.series[i], i);
1712                this.series[i]._plotDimensions = this._plotDimensions;
1713                this.series[i].canvas._plotDimensions = this._plotDimensions;
1714                //this.series[i].init(i, this.grid.borderWidth);
1715                this._sumy += this.series[i]._sumy;
1716                this._sumx += this.series[i]._sumx;
1717            }
1718            
1719            for (var name in this.axes) {
1720                this.axes[name]._plotDimensions = this._plotDimensions;
1721                this.axes[name]._ticks = [];
1722                this.axes[name].renderer.init.call(this.axes[name], {});
1723            }
1724            
1725            if (this.sortData) {
1726                sortData(this.series);
1727            }
1728            
1729            this.grid._axes = this.axes;
1730            
1731            this.legend._series = this.series;
1732        };
1733        
1734        // sort the series data in increasing order.
1735        function sortData(series) {
1736            var d, sd, pd, ppd, ret;
1737            for (var i=0; i<series.length; i++) {
1738                // d = series[i].data;
1739                // sd = series[i]._stackData;
1740                // pd = series[i]._plotData;
1741                // ppd = series[i]._prevPlotData;
1742                var check;
1743                var bat = [series[i].data, series[i]._stackData, series[i]._plotData, series[i]._prevPlotData];
1744                for (var n=0; n<4; n++) {
1745                    check = true;
1746                    d = bat[n];
1747                    if (series[i]._stackAxis == 'x') {
1748                        for (var j = 0; j < d.length; j++) {
1749                            if (typeof(d[j][1]) != "number") {
1750                                check = false;
1751                                break;
1752                            }
1753                        }
1754                        if (check) {
1755                            d.sort(function(a,b) { return a[1] - b[1]; });
1756                            // sd.sort(function(a,b) { return a[1] - b[1]; });
1757                            // pd.sort(function(a,b) { return a[1] - b[1]; });
1758                            // ppd.sort(function(a,b) { return a[1] - b[1]; });
1759                        }
1760                    }
1761                    else {
1762                        for (var j = 0; j < d.length; j++) {
1763                            if (typeof(d[j][0]) != "number") {
1764                                check = false;
1765                                break;
1766                            }
1767                        }
1768                        if (check) {
1769                            d.sort(function(a,b) { return a[0] - b[0]; });
1770                            // sd.sort(function(a,b) { return a[0] - b[0]; });
1771                            // pd.sort(function(a,b) { return a[0] - b[0]; });
1772                            // ppd.sort(function(a,b) { return a[0] - b[0]; });
1773                        }
1774                    }
1775                }
1776               
1777            }
1778        }
1779        
1780        // populate the _stackData and _plotData arrays for the plot and the series.
1781        this.populatePlotData = function(series, index) {
1782            // if a stacked chart, compute the stacked data
1783            this._plotData = [];
1784            this._stackData = [];
1785            series._stackData = [];
1786            series._plotData = [];
1787            var plotValues = {x:[], y:[]};
1788            if (this.stackSeries && !series.disableStack) {
1789                series._stack = true;
1790                var sidx = series._stackAxis == 'x' ? 0 : 1;
1791                var idx = sidx ? 0 : 1;
1792                // push the current data into stackData
1793                //this._stackData.push(this.series[i].data);
1794                var temp = $.extend(true, [], series.data);
1795                // create the data that will be plotted for this series
1796                var plotdata = $.extend(true, [], series.data);
1797                // for first series, nothing to add to stackData.
1798                for (var j=0; j<index; j++) {
1799                    var cd = this.series[j].data;
1800                    for (var k=0; k<cd.length; k++) {
1801                        temp[k][0] += cd[k][0];
1802                        temp[k][1] += cd[k][1];
1803                        // only need to sum up the stack axis column of data
1804                        plotdata[k][sidx] += cd[k][sidx];
1805                    }
1806                }
1807                for (var i=0; i<plotdata.length; i++) {
1808                    plotValues.x.push(plotdata[i][0]);
1809                    plotValues.y.push(plotdata[i][1]);
1810                }
1811                this._plotData.push(plotdata);
1812                this._stackData.push(temp);
1813                series._stackData = temp;
1814                series._plotData = plotdata;
1815                series._plotValues = plotValues;
1816            }
1817            else {
1818                for (var i=0; i<series.data.length; i++) {
1819                    plotValues.x.push(series.data[i][0]);
1820                    plotValues.y.push(series.data[i][1]);
1821                }
1822                this._stackData.push(series.data);
1823                this.series[index]._stackData = series.data;
1824                this._plotData.push(series.data);
1825                series._plotData = series.data;
1826                series._plotValues = plotValues;
1827            }
1828            if (index>0) {
1829                series._prevPlotData = this.series[index-1]._plotData;
1830            }
1831            series._sumy = 0;
1832            series._sumx = 0;
1833            for (i=series.data.length-1; i>-1; i--) {
1834                series._sumy += series.data[i][1];
1835                series._sumx += series.data[i][0];
1836            }
1837        };
1838        
1839        // function to safely return colors from the color array and wrap around at the end.
1840        this.getNextSeriesColor = (function(t) {
1841            var idx = 0;
1842            var sc = t.seriesColors;
1843            
1844            return function () { 
1845                if (idx < sc.length) {
1846                    return sc[idx++];
1847                }
1848                else {
1849                    idx = 0;
1850                    return sc[idx++];
1851                }
1852            };
1853        })(this);
1854    
1855        this.parseOptions = function(options){
1856            for (var i=0; i<this.preParseOptionsHooks.hooks.length; i++) {
1857                this.preParseOptionsHooks.hooks[i].call(this, options);
1858            }
1859            for (var i=0; i<$.jqplot.preParseOptionsHooks.length; i++) {
1860                $.jqplot.preParseOptionsHooks[i].call(this, options);
1861            }
1862            this.options = $.extend(true, {}, this.defaults, options);
1863            this.stackSeries = this.options.stackSeries;
1864            if (this.options.seriesColors) {
1865                this.seriesColors = this.options.seriesColors;
1866            }
1867            if (this.options.negativeSeriesColors) {
1868                this.negativeSeriesColors = this.options.negativeSeriesColors;
1869            }
1870            if (this.options.captureRightClick) {
1871                this.captureRightClick = this.options.captureRightClick;
1872            }
1873            this.defaultAxisStart = (options && options.defaultAxisStart != null) ? options.defaultAxisStart : this.defaultAxisStart;
1874            var cg = new this.colorGenerator(this.seriesColors);
1875            // this._gridPadding = this.options.gridPadding;
1876            $.extend(true, this._gridPadding, this.options.gridPadding);
1877            this.sortData = (this.options.sortData != null) ? this.options.sortData : this.sortData;
1878            for (var n in this.axes) {
1879                var axis = this.axes[n];
1880                $.extend(true, axis, this.options.axesDefaults, this.options.axes[n]);
1881                axis._plotWidth = this._width;
1882                axis._plotHeight = this._height;
1883            }
1884            if (this.data.length == 0) {
1885                this.data = [];
1886                for (var i=0; i<this.options.series.length; i++) {
1887                    this.data.push(this.options.series.data);
1888                }    
1889            }
1890                
1891            var normalizeData = function(data, dir, start) {
1892                // return data as an array of point arrays,
1893                // in form [[x1,y1...], [x2,y2...], ...]
1894                var temp = [];
1895                var i;
1896                dir = dir || 'vertical';
1897                if (!(data[0] instanceof Array)) {
1898                    // we have a series of scalars.  One line with just y values.
1899                    // turn the scalar list of data into a data array of form:
1900                    // [[1, data[0]], [2, data[1]], ...]
1901                    for (i=0; i<data.length; i++) {
1902                        if (dir == 'vertical') {
1903                            temp.push([start + i, data[i]]);   
1904                        }
1905                        else {
1906                            temp.push([data[i], start+i]);
1907                        }
1908                    }
1909                }            
1910                else {
1911                    // we have a properly formatted data series, copy it.
1912                    $.extend(true, temp, data);
1913                }
1914                return temp;
1915            };
1916
1917            for (var i=0; i<this.data.length; i++) { 
1918                var temp = new Series();
1919                for (var j=0; j<$.jqplot.preParseSeriesOptionsHooks.length; j++) {
1920                    $.jqplot.preParseSeriesOptionsHooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]);
1921                }
1922                for (var j=0; j<this.preParseSeriesOptionsHooks.hooks.length; j++) {
1923                    this.preParseSeriesOptionsHooks.hooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]);
1924                }
1925                $.extend(true, temp, {seriesColors:this.seriesColors, negativeSeriesColors:this.negativeSeriesColors}, this.options.seriesDefaults, this.options.series[i]);
1926                var dir = 'vertical';
1927                if (temp.renderer.constructor == $.jqplot.barRenderer && temp.rendererOptions && temp.rendererOptions.barDirection == 'horizontal') {
1928                    dir = 'horizontal';
1929                }
1930                temp.data = normalizeData(this.data[i], dir, this.defaultAxisStart);
1931                switch (temp.xaxis) {
1932                    case 'xaxis':
1933                        temp._xaxis = this.axes.xaxis;
1934                        break;
1935                    case 'x2axis':
1936                        temp._xaxis = this.axes.x2axis;
1937                        break;
1938                    default:
1939                        break;
1940                }
1941                temp._yaxis = this.axes[temp.yaxis];
1942                temp._xaxis._series.push(temp);
1943                temp._yaxis._series.push(temp);
1944                if (temp.show) {
1945                    temp._xaxis.show = true;
1946                    temp._yaxis.show = true;
1947                }
1948
1949                // parse the renderer options and apply default colors if not provided
1950                if (!temp.color && temp.show != false) {
1951                    temp.color = cg.next();
1952                }
1953                if (!temp.label) {
1954                    temp.label = 'Series '+ (i+1).toString();
1955                }
1956                // temp.rendererOptions.show = temp.show;
1957                // $.extend(true, temp.renderer, {color:this.seriesColors[i]}, this.rendererOptions);
1958                this.series.push(temp);  
1959                for (var j=0; j<$.jqplot.postParseSeriesOptionsHooks.length; j++) {
1960                    $.jqplot.postParseSeriesOptionsHooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]);
1961                }
1962                for (var j=0; j<this.postParseSeriesOptionsHooks.hooks.length; j++) {
1963                    this.postParseSeriesOptionsHooks.hooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]);
1964                }
1965            }
1966            
1967            // copy the grid and title options into this object.
1968            $.extend(true, this.grid, this.options.grid);
1969            // if axis border properties aren't set, set default.
1970            for (var n in this.axes) {
1971                var axis = this.axes[n];
1972                if (axis.borderWidth == null) {
1973                    axis.borderWidth =this.grid.borderWidth;
1974                }
1975                if (axis.borderColor == null) {
1976                    if (n != 'xaxis' && n != 'x2axis' && axis.useSeriesColor === true && axis.show) {
1977                        axis.borderColor = axis._series[0].color;
1978                    }
1979                    else {
1980                        axis.borderColor = this.grid.borderColor;
1981                    }
1982                }
1983            }
1984            
1985            if (typeof this.options.title == 'string') {
1986                this.title.text = this.options.title;
1987            }
1988            else if (typeof this.options.title == 'object') {
1989                $.extend(true, this.title, this.options.title);
1990            }
1991            this.title._plotWidth = this._width;
1992            this.legend.setOptions(this.options.legend);
1993            
1994            for (var i=0; i<$.jqplot.postParseOptionsHooks.length; i++) {
1995                $.jqplot.postParseOptionsHooks[i].call(this, options);
1996            }
1997            for (var i=0; i<this.postParseOptionsHooks.hooks.length; i++) {
1998                this.postParseOptionsHooks.hooks[i].call(this, options);
1999            }
2000        };
2001        
2002        // method: replot
2003        // Does a reinitialization of the plot followed by
2004        // a redraw.  Method could be used to interactively
2005        // change plot characteristics and then replot.
2006        //
2007        // Parameters:
2008        // options - Options used for replotting.
2009        //
2010        // Properties:
2011        // clear - false to not clear (empty) the plot container before replotting (default: true).
2012        // resetAxes - true to reset all axes min, max, numberTicks and tickInterval setting so axes will rescale themselves.
2013        //             optionally pass in list of axes to reset (e.g. ['xaxis', 'y2axis']) (default: false).
2014        this.replot = function(options) {
2015            var opts = (options != undefined) ? options : {};
2016            var clear = (opts.clear != undefined) ? opts.clear : true;
2017            var resetAxes = (opts.resetAxes != undefined) ? opts.resetAxes : false;
2018            this.target.trigger('jqplotPreReplot');
2019            if (clear) {
2020                this.target.empty();
2021            }
2022            if (resetAxes) {
2023                this.resetAxesScale(resetAxes);
2024            }
2025            this.reInitialize();
2026            this.draw();
2027            this.target.trigger('jqplotPostReplot');
2028        };
2029        
2030        // method: redraw
2031        // Empties the plot target div and redraws the plot.
2032        // This enables plot data and properties to be changed
2033        // and then to comletely clear the plot and redraw.
2034        // redraw *will not* reinitialize any plot elements.
2035        // That is, axes will not be autoscaled and defaults
2036        // will not be reapplied to any plot elements.  redraw
2037        // is used primarily with zooming. 
2038        //
2039        // Parameters:
2040        // clear - false to not clear (empty) the plot container before redrawing (default: true).
2041        this.redraw = function(clear) {
2042            clear = (clear != null) ? clear : true;
2043            this.target.trigger('jqplotPreRedraw');
2044            if (clear) {
2045                this.target.empty();
2046            }
2047             for (var ax in this.axes) {
2048                this.axes[ax]._ticks = [];
2049            }
2050            for (var i=0; i<this.series.length; i++) {
2051                this.populatePlotData(this.series[i], i);
2052            }
2053            this._sumy = 0;
2054            this._sumx = 0;
2055            for (i=0; i<this.series.length; i++) {
2056                this._sumy += this.series[i]._sumy;
2057                this._sumx += this.series[i]._sumx;
2058            }
2059            this.draw();
2060            this.target.trigger('jqplotPostRedraw');
2061        };
2062        
2063        // method: draw
2064        // Draws all elements of the plot into the container.
2065        // Does not clear the container before drawing.
2066        this.draw = function(){
2067            if (this.drawIfHidden || this.target.is(':visible')) {
2068                this.target.trigger('jqplotPreDraw');
2069                var i, j;
2070                for (i=0; i<$.jqplot.preDrawHooks.length; i++) {
2071                    $.jqplot.preDrawHooks[i].call(this);
2072                }
2073                for (i=0; i<this.preDrawHooks.hooks.length; i++) {
2074                    this.preDrawHooks.hooks[i].call(this);
2075                }
2076                // create an underlying canvas to be used for special features.
2077                this.target.append(this.baseCanvas.createElement({left:0, right:0, top:0, bottom:0}, 'jqplot-base-canvas'));
2078                this.baseCanvas.setContext();
2079                this.target.append(this.title.draw());
2080                this.title.pack({top:0, left:0});
2081                
2082                // make room  for the legend between the grid and the edge.
2083                var legendElem = this.legend.draw();
2084                
2085                var gridPadding = {top:0, left:0, bottom:0, right:0};
2086                
2087                if (this.legend.placement == "outsideGrid") {
2088                    // temporarily append the legend to get dimensions
2089                    this.target.append(legendElem);
2090                    switch (this.legend.location) {
2091                        case 'n':
2092                            gridPadding.top += this.legend.getHeight();
2093                            break;
2094                        case 's':
2095                            gridPadding.bottom += this.legend.getHeight();
2096                            break;
2097                        case 'ne':
2098                        case 'e':
2099                        case 'se':
2100                            gridPadding.right += this.legend.getWidth();
2101                            break;
2102                        case 'nw':
2103                        case 'w':
2104                        case 'sw':
2105                            gridPadding.left += this.legend.getWidth();
2106                            break;
2107                        default:  // same as 'ne'
2108                            gridPadding.right += this.legend.getWidth();
2109                            break;
2110                    }
2111                    legendElem = legendElem.detach();
2112                }
2113                
2114                var ax = this.axes;
2115                for (var name in ax) {
2116                    this.target.append(ax[name].draw(this.baseCanvas._ctx));
2117                    ax[name].set();
2118                }
2119                if (ax.yaxis.show) {
2120                    gridPadding.left += ax.yaxis.getWidth();
2121                }
2122                var ra = ['y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis'];
2123                var rapad = [0, 0, 0, 0, 0, 0, 0, 0];
2124                var gpr = 0;
2125                var n;
2126                for (n=0; n<8; n++) {
2127                    if (ax[ra[n]].show) {
2128                        gpr += ax[ra[n]].getWidth();
2129                        rapad[n] = gpr;
2130                    }
2131                }
2132                gridPadding.right += gpr;
2133                if (ax.x2axis.show) {
2134                    gridPadding.top += ax.x2axis.getHeight();
2135                }
2136                if (this.title.show) {
2137                    gridPadding.top += this.title.getHeight();
2138                }
2139                if (ax.xaxis.show) {
2140                    gridPadding.bottom += ax.xaxis.getHeight();
2141                }
2142                
2143                // end of gridPadding adjustments.
2144                var arr = ['top', 'bottom', 'left', 'right'];
2145                for (var n in arr) {
2146                    if (gridPadding[arr[n]]) {
2147                        this._gridPadding[arr[n]] = gridPadding[arr[n]];
2148                    }
2149                }
2150                
2151                var legendPadding = (this.legend.placement == 'outsideGrid') ? {top:this.title.getHeight(), left: 0, right: 0, bottom: 0} : this._gridPadding;
2152            
2153                ax.xaxis.pack({position:'absolute', bottom:this._gridPadding.bottom - ax.xaxis.getHeight(), left:0, width:this._width}, {min:this._gridPadding.left, max:this._width - this._gridPadding.right});
2154                ax.yaxis.pack({position:'absolute', top:0, left:this._gridPadding.left - ax.yaxis.getWidth(), height:this._height}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top});
2155                ax.x2axis.pack({position:'absolute', top:this._gridPadding.top - ax.x2axis.getHeight(), left:0, width:this._width}, {min:this._gridPadding.left, max:this._width - this._gridPadding.right});
2156                for (i=8; i>0; i--) {
2157                    ax[ra[i-1]].pack({position:'absolute', top:0, right:this._gridPadding.right - rapad[i-1]}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top});
2158                }
2159                // ax.y2axis.pack({position:'absolute', top:0, right:0}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top});
2160            
2161                this.target.append(this.grid.createElement(this._gridPadding));
2162                this.grid.draw();
2163                
2164                // put the shadow canvases behind the series canvases so shadows don't overlap on stacked bars.
2165                for (i=0; i<this.series.length; i++) {
2166                    // draw series in order of stacking.  This affects only
2167                    // order in which canvases are added to dom.
2168                    j = this.seriesStack[i];
2169                    this.target.append(this.series[j].shadowCanvas.createElement(this._gridPadding, 'jqplot-series-shadowCanvas'));
2170                    this.series[j].shadowCanvas.setContext();
2171                    this.series[j].shadowCanvas._elem.data('seriesIndex', j);
2172                }
2173                
2174                for (i=0; i<this.series.length; i++) {
2175                    // draw series in order of stacking.  This affects only
2176                    // order in which canvases are added to dom.
2177                    j = this.seriesStack[i];
2178                    this.target.append(this.series[j].canvas.createElement(this._gridPadding, 'jqplot-series-canvas'));
2179                    this.series[j].canvas.setContext();
2180                    this.series[j].canvas._elem.data('seriesIndex', j);
2181                }
2182                // Need to use filled canvas to capture events in IE.
2183                // Also, canvas seems to block selection of other elements in document on FF.
2184                this.target.append(this.eventCanvas.createElement(this._gridPadding, 'jqplot-event-canvas'));
2185                this.eventCanvas.setContext();
2186                this.eventCanvas._ctx.fillStyle = 'rgba(0,0,0,0)';
2187                this.eventCanvas._ctx.fillRect(0,0,this.eventCanvas._ctx.canvas.width, this.eventCanvas._ctx.canvas.height);
2188            
2189                // bind custom event handlers to regular events.
2190                this.bindCustomEvents();
2191            
2192                // draw legend before series if the series needs to know the legend dimensions.
2193                if (this.legend.preDraw) {  
2194                    this.eventCanvas._elem.before(legendElem);
2195                    this.legend.pack(legendPadding);
2196                    if (this.legend._elem) {
2197                        this.drawSeries({legendInfo:{location:this.legend.location, placement:this.legend.placement, width:this.legend.getWidth(), height:this.legend.getHeight(), xoffset:this.legend.xoffset, yoffset:this.legend.yoffset}});
2198                    }
2199                    else {
2200                        this.drawSeries();
2201                    }
2202                }
2203                else {  // draw series before legend
2204                    this.drawSeries();
2205                    $(this.series[this.series.length-1].canvas._elem).after(legendElem);
2206                    this.legend.pack(legendPadding);                
2207                }
2208            
2209                // register event listeners on the overlay canvas
2210                for (var i=0; i<$.jqplot.eventListenerHooks.length; i++) {
2211                    // in the handler, this will refer to the eventCanvas dom element.
2212                    // make sure there are references back into plot objects.
2213                    this.eventCanvas._elem.bind($.jqplot.eventListenerHooks[i][0], {plot:this}, $.jqplot.eventListenerHooks[i][1]);
2214                }
2215            
2216                // register event listeners on the overlay canvas
2217                for (var i=0; i<this.eventListenerHooks.hooks.length; i++) {
2218                    // in the handler, this will refer to the eventCanvas dom element.
2219                    // make sure there are references back into plot objects.
2220                    this.eventCanvas._elem.bind(this.eventListenerHooks.hooks[i][0], {plot:this}, this.eventListenerHooks.hooks[i][1]);
2221                }
2222
2223                for (var i=0; i<$.jqplot.postDrawHooks.length; i++) {
2224                    $.jqplot.postDrawHooks[i].call(this);
2225                }
2226
2227                for (var i=0; i<this.postDrawHooks.hooks.length; i++) {
2228                    this.postDrawHooks.hooks[i].call(this);
2229                }
2230            
2231                if (this.target.is(':visible')) {
2232                    this._drawCount += 1;
2233                }
2234            
2235                this.target.trigger('jqplotPostDraw', [this]);
2236            }
2237        };
2238        
2239        this.bindCustomEvents = function() {
2240            this.eventCanvas._elem.bind('click', {plot:this}, this.onClick);
2241            this.eventCanvas._elem.bind('dblclick', {plot:this}, this.onDblClick);
2242            this.eventCanvas._elem.bind('mousedown', {plot:this}, this.onMouseDown);
2243            this.eventCanvas._elem.bind('mousemove', {plot:this}, this.onMouseMove);
2244            this.eventCanvas._elem.bind('mouseenter', {plot:this}, this.onMouseEnter);
2245            this.eventCanvas._elem.bind('mouseleave', {plot:this}, this.onMouseLeave);
2246            if (this.captureRightClick) {
2247                this.eventCanvas._elem.bind('mouseup', {plot:this}, this.onRightClick);
2248                this.eventCanvas._elem.get(0).oncontextmenu = function() {
2249					return false;
2250				};
2251            }
2252            else {
2253                this.eventCanvas._elem.bind('mouseup', {plot:this}, this.onMouseUp);
2254            }
2255        };
2256        
2257        function getEventPosition(ev) {
2258            var plot = ev.data.plot;
2259            var go = plot.eventCanvas._elem.offset();
2260            var gridPos = {x:ev.pageX - go.left, y:ev.pageY - go.top};
2261            var dataPos = {xaxis:null, yaxis:null, x2axis:null, y2axis:null, y3axis:null, y4axis:null, y5axis:null, y6axis:null, y7axis:null, y8axis:null, y9axis:null};
2262            var an = ['xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis'];
2263            var ax = plot.axes;
2264            var n, axis;
2265            for (n=11; n>0; n--) {
2266                axis = an[n-1];
2267                if (ax[axis].show) {
2268                    dataPos[axis] = ax[axis].series_p2u(gridPos[axis.charAt(0)]);
2269                }
2270            }
2271
2272            return {offsets:go, gridPos:gridPos, dataPos:dataPos};
2273        }
2274        
2275        // function getNeighborPoint(plot, x, y) {
2276        //     var ret = null;
2277        //     var s, i, d0, d, j, r, k;
2278        //     var threshold, t;
2279        //     for (var k=plot.seriesStack.length-1; k>-1; k--) {
2280        //         i = plot.seriesStack[k];
2281        //         s = plot.series[i];
2282        //         r = s.renderer;
2283        //         if (s.show) {
2284        //             t = s.markerRenderer.size/2+s.neighborThreshold;
2285        //             threshold = (t > 0) ? t : 0;
2286        //             for (var j=0; j<s.gridData.length; j++) {
2287        //                 p = s.gridData[j];
2288        //                 // neighbor looks different to OHLC chart.
2289        //                 if (r.constructor == $.jqplot.OHLCRenderer) {
2290        //                     if (r.candleStick) {
2291        //                         var yp = s._yaxis.series_u2p;
2292        //                         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])) {
2293        //                             return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
2294        //                         }
2295        //                     }
2296        //                     // if an open hi low close chart
2297        //                     else if (!r.hlc){
2298        //                         var yp = s._yaxis.series_u2p;
2299        //                         if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) {
2300        //                             return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
2301        //                         }
2302        //                     }
2303        //                     // a hi low close chart
2304        //                     else {
2305        //                         var yp = s._yaxis.series_u2p;
2306        //                         if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) {
2307        //                             return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
2308        //                         }
2309        //                     }
2310        //                     
2311        //                 }
2312        //                 else {
2313        //                     d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) );
2314        //                     if (d <= threshold && (d <= d0 || d0 == null)) {
2315        //                        d0 = d;
2316        //                        return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
2317        //                     }
2318        //                 }
2319        //             } 
2320        //         }
2321        //     }
2322        //     return ret;
2323        // }
2324        
2325        
2326        // function to check if event location is over a area area
2327        function checkIntersection(gridpos, plot) {
2328            var series = plot.series;
2329            var i, j, k, s, r, x, y, theta, sm, sa, minang, maxang;
2330            var d0, d, p, pp, points, bw;
2331            var threshold, t;
2332            for (k=plot.seriesStack.length-1; k>=0; k--) {
2333                i = plot.seriesStack[k];
2334                s = series[i];
2335                switch (s.renderer.constructor) {
2336                    case $.jqplot.BarRenderer:
2337                        x = gridpos.x;
2338                        y = gridpos.y;
2339                        for (j=s.gridData.length-1; j>=0; j--) {
2340                            points = s._barPoints[j];
2341                            if (x>points[0][0] && x<points[2][0] && y>points[2][1] && y<points[0][1]) {
2342                                return {seriesIndex:s.index, pointIndex:j, gridData:p, data:s.data[j], points:s._barPoints[j]};
2343                            }
2344                        }
2345                        break;
2346                    
2347                    case $.jqplot.DonutRenderer:
2348                        sa = s.startAngle/180*Math.PI;
2349                        x = gridpos.x - s._center[0];
2350                        y = gridpos.y - s._center[1];
2351                        r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
2352                        if (x > 0 && -y >= 0) {
2353                            theta = 2*Math.PI - Math.atan(-y/x);
2354                        }
2355                        else if (x > 0 && -y < 0) {
2356                            theta = -Math.atan(-y/x);
2357                        }
2358                        else if (x < 0) {
2359                            theta = Math.PI - Math.atan(-y/x);
2360                        }
2361                        else if (x == 0 && -y > 0) {
2362                            theta = 3*Math.PI/2;
2363                        }
2364                        else if (x == 0 && -y < 0) {
2365                            theta = Math.PI/2;
2366                        }
2367                        else if (x == 0 && y == 0) {
2368                            theta = 0;
2369                        }
2370                        if (sa) {
2371                            theta -= sa;
2372                            if (theta < 0) {
2373                                theta += 2*Math.PI;
2374                            }
2375                            else if (theta > 2*Math.PI) {
2376                                theta -= 2*Math.PI;
2377                            }
2378                        }
2379            
2380                        sm = s.sliceMargin/180*Math.PI;
2381                        if (r < s._radius && r > s._innerRadius) {
2382                            for (j=0; j<s.gridData.length; j++) {
2383                                minang = (j>0) ? s.gridData[j-1][1]+sm : sm;
2384                                maxang = s.gridData[j][1];
2385                                if (theta > minang && theta < maxang) {
2386                                    return {seriesIndex:s.index, pointIndex:j, gridData:s.gridData[j], data:s.data[j]};
2387                                }
2388                            }
2389                        }
2390                        break;
2391                        
2392                    case $.jqplot.PieRenderer:
2393                        sa = s.startAngle/180*Math.PI;
2394                        x = gridpos.x - s._center[0];
2395                        y = gridpos.y - s._center[1];
2396                        r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
2397                        if (x > 0 && -y >= 0) {
2398                            theta = 2*Math.PI - Math.atan(-y/x);
2399                        }
2400                        else if (x > 0 && -y < 0) {
2401                            theta = -Math.atan(-y/x);
2402                        }
2403                        else if (x < 0) {
2404                            theta = Math.PI - Math.atan(-y/x);
2405                        }
2406                        else if (x == 0 && -y > 0) {
2407                            theta = 3*Math.PI/2;
2408                        }
2409                        else if (x == 0 && -y < 0) {
2410                            theta = Math.PI/2;
2411                        }
2412                        else if (x == 0 && y == 0) {
2413                            theta = 0;
2414                        }
2415                        if (sa) {
2416                            theta -= sa;
2417                            if (theta < 0) {
2418                                theta += 2*Math.PI;
2419                            }
2420                            else if (theta > 2*Math.PI) {
2421                                theta -= 2*Math.PI;
2422                            }
2423                        }
2424            
2425                        sm = s.sliceMargin/180*Math.PI;
2426                        if (r < s._radius) {
2427                            for (j=0; j<s.gridData.length; j++) {
2428                                minang = (j>0) ? s.gridData[j-1][1]+sm : sm;
2429                                maxang = s.gridData[j][1];
2430                                if (theta > minang && theta < maxang) {
2431                                    return {seriesIndex:s.index, pointIndex:j, gridData:s.gridData[j], data:s.data[j]};
2432                                }
2433                            }
2434                        }
2435                        break;
2436                        
2437                    case $.jqplot.BubbleRenderer:
2438                        x = gridpos.x;
2439                        y = gridpos.y;
2440                        var ret = null;
2441                        
2442                        if (s.show) {
2443                            for (var j=0; j<s.gridData.length; j++) {
2444                                p = s.gridData[j];
2445                                d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-
2445p[1]) * (y-p[1]) );
2446                                if (d <= p[2] && (d <= d0 || d0 == null)) {
2447                                   d0 = d;
2448                                   ret = {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
2449                                }
2450                            }
2451                            if (ret != null) return ret;
2452                        }
2453                        break;
2454                        
2455                    case $.jqplot.FunnelRenderer:
2456                        x = gridpos.x;
2457                        y = gridpos.y;
2458                        var v = s._vertices,
2459                            vfirst = v[0],
2460                            vlast = v[v.length-1],
2461                            lex,
2462                            rex;
2463    
2464                        // equations of right and left sides, returns x, y values given height of section (y value and 2 points)
2465    
2466                        function findedge (l, p1 , p2) {
2467                            var m = (p1[1] - p2[1])/(p1[0] - p2[0]);
2468                            var b = p1[1] - m*p1[0];
2469                            var y = l + p1[1];
2470        
2471                            return [(y - b)/m, y];
2472                        }
2473    
2474                        // check each section
2475                        lex = findedge(y, vfirst[0], vlast[3]);
2476                        rex = findedge(y, vfirst[1], vlast[2]);
2477                        for (j=0; j<v.length; j++) {
2478                            cv = v[j];
2479                            if (y >= cv[0][1] && y <= cv[3][1] && x >= lex[0] && x <= rex[0]) {
2480                                return {seriesIndex:s.index, pointIndex:j, gridData:null, data:s.data[j]};
2481                            }
2482                        }         
2483                        break;           
2484                    
2485                    case $.jqplot.LineRenderer:
2486                        x = gridpos.x;
2487                        y = gridpos.y;
2488                        r = s.renderer;
2489                        if (s.show) {
2490                            if (s.fill) {
2491                                // first check if it is in bounding box
2492                                var inside = false;
2493                                if (x>s._boundingBox[0][0] && x<s._boundingBox[1][0] && y>s._boundingBox[1][1] && y<s._boundingBox[0][1]) { 
2494                                    // now check the crossing number   
2495                                    
2496                                    var numPoints = s._areaPoints.length;
2497                                    var ii;
2498                                    var j = numPoints-1;
2499
2500                                    for(var ii=0; ii < numPoints; ii++) { 
2501                                    	var vertex1 = [s._areaPoints[ii][0], s._areaPoints[ii][1]];
2502                                    	var vertex2 = [s._areaPoints[j][0], s._areaPoints[j][1]];
2503
2504                                    	if (vertex1[1] < y && vertex2[1] >= y || vertex2[1] < y && vertex1[1] >= y)	 {
2505                                    		if (vertex1[0] + (y - vertex1[1]) / (vertex2[1] - vertex1[1]) * (vertex2[0] - vertex1[0]) < x) {
2506                                    			inside = !inside;
2507                                    		}
2508                                    	}
2509
2510                                    	j = ii;
2511                                    }        
2512                                }
2513                                if (inside) {
2514                                    return {seriesIndex:i, pointIndex:null, gridData:s.gridData, data:s.data, points:s._areaPoints};
2515                                }
2516                                break;
2517                                
2518                            }
2519                            else {
2520                                t = s.markerRenderer.size/2+s.neighborThreshold;
2521                                threshold = (t > 0) ? t : 0;
2522                                for (var j=0; j<s.gridData.length; j++) {
2523                                    p = s.gridData[j];
2524                                    // neighbor looks different to OHLC chart.
2525                                    if (r.constructor == $.jqplot.OHLCRenderer) {
2526                                        if (r.candleStick) {
2527                                            var yp = s._yaxis.series_u2p;
2528                                            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])) {
2529                                                return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
2530                                            }
2531                                        }
2532                                        // if an open hi low close chart
2533                                        else if (!r.hlc){
2534                                            var yp = s._yaxis.series_u2p;
2535                                            if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) {
2536                                                return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
2537                                            }
2538                                        }
2539                                        // a hi low close chart
2540                                        else {
2541                                            var yp = s._yaxis.series_u2p;
2542                                            if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) {
2543                                                return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
2544                                            }
2545                                        }
2546                            
2547                                    }
2548                                    else if (p[0] != null && p[1] != null){
2549                                        d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-
2549p[1]) * (y-p[1]) );
2550                                        if (d <= threshold && (d <= d0 || d0 == null)) {
2551                                           d0 = d;
2552                                           return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
2553                                        }
2554                                    }
2555                                } 
2556                            }
2557                        }
2558                        break;
2559                        
2560                    default:
2561                        x = gridpos.x;
2562                        y = gridpos.y;
2563                        r = s.renderer;
2564                        if (s.show) {
2565                            t = s.markerRenderer.size/2+s.neighborThreshold;
2566                            threshold = (t > 0) ? t : 0;
2567                            for (var j=0; j<s.gridData.length; j++) {
2568                                p = s.gridData[j];
2569                                // neighbor looks different to OHLC chart.
2570                                if (r.constructor == $.jqplot.OHLCRenderer) {
2571                                    if (r.candleStick) {
2572                                        var yp = s._yaxis.series_u2p;
2573                                        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])) {
2574                                            return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
2575                                        }
2576                                    }
2577                                    // if an open hi low close chart
2578                                    else if (!r.hlc){
2579                                        var yp = s._yaxis.series_u2p;
2580                                        if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) {
2581                                            return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
2582                                        }
2583                                    }
2584                                    // a hi low close chart
2585                                    else {
2586                                        var yp = s._yaxis.series_u2p;
2587                                        if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) {
2588                                            return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
2589                                        }
2590                                    }
2591                            
2592                                }
2593                                else {
2594                                    d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) );
2595                                    if (d <= threshold && (d <= d0 || d0 == null)) {
2596                                       d0 = d;
2597                                       return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
2598                                    }
2599                                }
2600                            } 
2601                        }
2602                        break;
2603                }
2604            }
2605            
2606            return null;
2607        }
2608        
2609        
2610        
2611        this.onClick = function(ev) {
2612            // Event passed in is normalized and will have data attribute.
2613            // Event passed out is unnormalized.
2614            var positions = getEventPosition(ev);
2615            var p = ev.data.plot;
2616            var neighbor = checkIntersection(positions.gridPos, p);
2617            var evt = jQuery.Event('jqplotClick');
2618            evt.pageX = ev.pageX;
2619            evt.pageY = ev.pageY;
2620            $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
2621        };
2622        
2623        this.onDblClick = function(ev) {
2624            // Event passed in is normalized and will have data attribute.
2625            // Event passed out is unnormalized.
2626            var positions = getEventPosition(ev);
2627            var p = ev.data.plot;
2628            var neighbor = checkIntersection(positions.gridPos, p);
2629            var evt = jQuery.Event('jqplotDblClick');
2630            evt.pageX = ev.pageX;
2631            evt.pageY = ev.pageY;
2632            $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
2633        };
2634        
2635        this.onMouseDown = function(ev) {
2636            var positions = getEventPosition(ev);
2637            var p = ev.data.plot;
2638            var neighbor = checkIntersection(positions.gridPos, p);
2639            var evt = jQuery.Event('jqplotMouseDown');
2640            evt.pageX = ev.pageX;
2641            evt.pageY = ev.pageY;
2642            $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
2643        };
2644        
2645        this.onMouseUp = function(ev) {
2646            var positions = getEventPosition(ev);
2647            var evt = jQuery.Event('jqplotMouseUp');
2648            evt.pageX = ev.pageX;
2649            evt.pageY = ev.pageY;
2650            $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, ev.data.plot]);
2651        };
2652        
2653        this.onRightClick = function(ev) {
2654            var positions = getEventPosition(ev);
2655            var p = ev.data.plot;
2656            var neighbor = checkIntersection(positions.gridPos, p);
2657            if (p.captureRightClick) {
2658                if (ev.which == 3) {
2659                var evt = jQuery.Event('jqplotRightClick');
2660                evt.pageX = ev.pageX;
2661                evt.pageY = ev.pageY;
2662                    $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
2663                }
2664                else {
2665                var evt = jQuery.Event('jqplotMouseUp');
2666                evt.pageX = ev.pageX;
2667                evt.pageY = ev.pageY;
2668                    $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
2669                }
2670            }
2671        };
2672        
2673        this.onMouseMove = function(ev) {
2674            var positions = getEventPosition(ev);
2675            var p = ev.data.plot;
2676            var neighbor = checkIntersection(positions.gridPos, p);
2677            var evt = jQuery.Event('jqplotMouseMove');
2678            evt.pageX = ev.pageX;
2679            evt.pageY = ev.pageY;
2680            $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
2681        };
2682        
2683        this.onMouseEnter = function(ev) {
2684            var positions = getEventPosition(ev);
2685            var p = ev.data.plot;
2686            var evt = jQuery.Event('jqplotMouseEnter');
2687            evt.pageX = ev.pageX;
2688            evt.pageY = ev.pageY;
2689            $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, p]);
2690        };
2691        
2692        this.onMouseLeave = function(ev) {
2693            var positions = getEventPosition(ev);
2694            var p = ev.data.plot;
2695            var evt = jQuery.Event('jqplotMouseLeave');
2696            evt.pageX = ev.pageX;
2697            evt.pageY = ev.pageY;
2698            $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, p]);
2699        };
2700        
2701        // method: drawSeries
2702        // Redraws all or just one series on the plot.  No axis scaling
2703        // is performed and no other elements on the plot are redrawn.
2704        // options is an options object to pass on to the series renderers.
2705        // It can be an empty object {}.  idx is the series index
2706        // to redraw if only one series is to be redrawn.
2707        this.drawSeries = function(options, idx){
2708            var i, series, ctx;
2709            // if only one argument passed in and it is a number, use it ad idx.
2710            idx = (typeof(options) == "number" && idx == null) ? options : idx;
2711            options = (typeof(options) == "object") ? options : {};
2712            // draw specified series
2713            if (idx != undefined) {
2714                series = this.series[idx];
2715                ctx = series.shadowCanvas._ctx;
2716                ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
2717                series.drawShadow(ctx, options, this);
2718                ctx = series.canvas._ctx;
2719                ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
2720                series.draw(ctx, options, this);
2721                if (series.renderer.constructor == $.jqplot.BezierCurveRenderer) {
2722                    if (idx < this.series.length - 1) {
2723                        this.drawSeries(idx+1); 
2724                    }
2725                }
2726            }
2727            
2728            else {
2729                // if call series drawShadow method first, in case all series shadows
2730                // should be drawn before any series.  This will ensure, like for 
2731                // stacked bar plots, that shadows don't overlap series.
2732                for (i=0; i<this.series.length; i++) {
2733                    // first clear the canvas
2734                    series = this.series[i];
2735                    ctx = series.shadowCanvas._ctx;
2736                    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
2737                    series.drawShadow(ctx, options, this);
2738                    ctx = series.canvas._ctx;
2739                    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
2740                    series.draw(ctx, options, this);
2741                }
2742            }
2743        };
2744        
2745        // method: moveSeriesToFront
2746        // This method requires jQuery 1.4+
2747        // Moves the specified series canvas in front of all other series canvases.
2748        // This effectively "draws" the specified series on top of all other series,
2749        // although it is performed through DOM manipulation, no redrawing is performed.
2750        //
2751        // Parameters:
2752        // idx - 0 based index of the series to move.  This will be the index of the series
2753        // as it was first passed into the jqplot function.
2754        this.moveSeriesToFront = function (idx) { 
2755            idx = parseInt(idx, 10);
2756            var stackIndex = $.inArray(idx, this.seriesStack);
2757            // if already in front, return
2758            if (stackIndex == -1) {
2759                return;
2760            }
2761            if (stackIndex == this.seriesStack.length -1) {
2762                this.previousSeriesStack = this.seriesStack.slice(0);
2763                return;
2764            }
2765            var opidx = this.seriesStack[this.seriesStack.length -1];
2766            var serelem = this.series[idx].canvas._elem.detach();
2767            var shadelem = this.series[idx].shadowCanvas._elem.detach();
2768            this.series[opidx].shadowCanvas._elem.after(shadelem);
2769            this.series[opidx].canvas._elem.after(serelem);
2770            this.previousSeriesStack = this.seriesStack.slice(0);
2771            this.seriesStack.splice(stackIndex, 1);
2772            this.seriesStack.push(idx);
2773        };
2774        
2775        // method: moveSeriesToBack
2776        // This method requires jQuery 1.4+
2777        // Moves the specified series canvas behind all other series canvases.
2778        //
2779        // Parameters:
2780        // idx - 0 based index of the series to move.  This will be the index of the series
2781        // as it was first passed into the jqplot function.
2782        this.moveSeriesToBack = function (idx) {
2783            idx = parseInt(idx, 10);
2784            var stackIndex = $.inArray(idx, this.seriesStack);
2785            // if already in back, return
2786            if (stackIndex == 0 || stackIndex == -1) {
2787                return;
2788            }
2789            var opidx = this.seriesStack[0];
2790            var serelem = this.series[idx].canvas._elem.detach();
2791            var shadelem = this.series[idx].shadowCanvas._elem.detach();
2792            this.series[opidx].shadowCanvas._elem.before(shadelem);
2793            this.series[opidx].canvas._elem.before(serelem);
2794            this.previousSeriesStack = this.seriesStack.slice(0);
2795            this.seriesStack.splice(stackIndex, 1);
2796            this.seriesStack.unshift(idx);
2797        };
2798        
2799        // method: restorePreviousSeriesOrder
2800        // This method requires jQuery 1.4+
2801        // Restore the series canvas order to its previous state.
2802        // Useful to put a series back where it belongs after moving
2803        // it to the front.
2804        this.restorePreviousSeriesOrder = function () {
2805            var i, j, serelem, shadelem, temp;
2806            // if no change, return.
2807            if (this.seriesStack == this.previousSeriesStack) {
2808                return;
2809            }
2810            for (i=1; i<this.previousSeriesStack.length; i++) {
2811                move = this.previousSeriesStack[i];
2812                keep = this.previousSeriesStack[i-1];
2813                serelem = this.series[move].canvas._elem.detach();
2814                shadelem = this.series[move].shadowCanvas._elem.detach();
2815                this.series[keep].shadowCanvas._elem.after(shadelem);
2816                this.series[keep].canvas._elem.after(serelem);
2817            }
2818            temp = this.seriesStack.slice(0);
2819            this.seriesStack = this.previousSeriesStack.slice(0);
2820            this.previousSeriesStack = temp;
2821        };
2822        
2823        // method: restoreOriginalSeriesOrder
2824        // This method requires jQuery 1.4+
2825        // Restore the series canvas order to its original order
2826        // when the plot was created.
2827        this.restoreOriginalSeriesOrder = function () {
2828            var i, j, arr=[];
2829            for (i=0; i<this.series.length; i++) {
2830                arr.push(i);
2831            }
2832            if (this.seriesStack == arr) {
2833                return;
2834            }
2835            this.previousSeriesStack = this.seriesStack.slice(0);
2836            this.seriesStack = arr;
2837            for (i=1; i<this.seriesStack.length; i++) {
2838                serelem = this.series[i].canvas._elem.detach();
2839                shadelem = this.series[i].shadowCanvas._elem.detach();
2840                this.series[i-1].shadowCanvas._elem.after(shadelem);
2841                this.series[i-1].canvas._elem.after(serelem);
2842            }
2843        };
2844        
2845        this.activateTheme = function (name) {
2846            this.themeEngine.activate(this, name);
2847        };
2848    }
2849    
2850    
2851    // conpute a highlight color or array of highlight colors from given colors.
2852    $.jqplot.computeHighlightColors  = function(colors) {
2853        var ret;
2854        if (typeof(colors) == "array") {
2855            ret = [];
2856            for (var i=0; i<colors.length; i++){
2857                var rgba = $.jqplot.getColorComponents(colors[i]);
2858                var newrgb = [rgba[0], rgba[1], rgba[2]];
2859                var sum = newrgb[0] + newrgb[1] + newrgb[2];
2860                for (var j=0; j<3; j++) {
2861                    // when darkening, lowest color component can be is 60.
2862                    newrgb[j] = (sum > 570) ?  newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]);
2863                    newrgb[j] = parseInt(newrgb[j], 10);
2864                }
2865                ret.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')');
2866            }
2867        }
2868        else {
2869            var rgba = $.jqplot.getColorComponents(colors);
2870            var newrgb = [rgba[0], rgba[1], rgba[2]];
2871            var sum = newrgb[0] + newrgb[1] + newrgb[2];
2872            for (var j=0; j<3; j++) {
2873                // when darkening, lowest color component can be is 60.
2874                newrgb[j] = (sum > 570) ?  newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]);
2875                newrgb[j] = parseInt(newrgb[j], 10);
2876            }
2877            ret = 'rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')';
2878        }
2879        return ret;
2880    };
2881        
2882   $.jqplot.ColorGenerator = function(colors) {
2883        var idx = 0;
2884        
2885        this.next = function () { 
2886            if (idx < colors.length) {
2887                return colors[idx++];
2888            }
2889            else {
2890                idx = 0;
2891                return colors[idx++];
2892            }
2893        };
2894        
2895        this.previous = function () { 
2896            if (idx > 0) {
2897                return colors[idx--];
2898            }
2899            else {
2900                idx = colors.length-1;
2901                return colors[idx];
2902            }
2903        };
2904        
2905        // get a color by index without advancing pointer.
2906        this.get = function(i) {
2907            var idx = i - colors.length * Math.floor(i/colors.length);
2908            return colors[idx];
2909        };
2910        
2911        this.setColors = function(c) {
2912            colors = c;
2913        };
2914        
2915        this.reset = function() {
2916            idx = 0;
2917        };
2918    };
2919
2920    // convert a hex color string to rgb string.
2921    // h - 3 or 6 character hex string, with or without leading #
2922    // a - optional alpha
2923    $.jqplot.hex2rgb = function(h, a) {
2924        h = h.replace('#', '');
2925        if (h.length == 3) {
2926            h = h[0]+h[0]+h[1]+h[1]+h[2]+h[2];
2927        }
2928        var rgb;
2929        rgb = 'rgba('+parseInt(h.slice(0,2), 16)+', '+parseInt(h.slice(2,4), 16)+', '+parseInt(h.slice(4,6), 16);
2930        if (a) {
2931            rgb += ', '+a;
2932        }
2933        rgb += ')';
2934        return rgb;
2935    };
2936    
2937    // convert an rgb color spec to a hex spec.  ignore any alpha specification.
2938    $.jqplot.rgb2hex = function(s) {
2939        var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *(?:, *[0-9.]*)?\)/;
2940        var m = s.match(pat);
2941        var h = '#';
2942        for (i=1; i<4; i++) {
2943            var temp;
2944            if (m[i].search(/%/) != -1) {
2945                temp = parseInt(255*m[i]/100, 10).toString(16);
2946                if (temp.length == 1) {
2947                    temp = '0'+temp;
2948                }
2949            }
2950            else {
2951                temp = parseInt(m[i], 10).toString(16);
2952                if (temp.length == 1) {
2953                    temp = '0'+temp;
2954                }
2955            }
2956            h += temp;
2957        }
2958        return h;
2959    };
2960    
2961    // given a css color spec, return an rgb css color spec
2962    $.jqplot.normalize2rgb = function(s, a) {
2963        if (s.search(/^ *rgba?\(/) != -1) {
2964            return s; 
2965        }
2966        else if (s.search(/^ *#?[0-9a-fA-F]?[0-9a-fA-F]/) != -1) {
2967            return $.jqplot.hex2rgb(s, a);
2968        }
2969        else {
2970            throw 'invalid color spec';
2971        }
2972    };
2973    
2974    // extract the r, g, b, a color components out of a css color spec.
2975    $.jqplot.getColorComponents = function(s) {
2976        // check to see if a color keyword.
2977        s = $.jqplot.colorKeywordMap[s] || s;
2978        var rgb = $.jqplot.normalize2rgb(s);
2979        var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *,? *([0-9.]* *)?\)/;
2980        var m = rgb.match(pat);
2981        var ret = [];
2982        for (i=1; i<4; i++) {
2983            if (m[i].search(/%/) != -1) {
2984                ret[i-1] = parseInt(255*m[i]/100, 10);
2985            }
2986            else {
2987                ret[i-1] = parseInt(m[i], 10);
2988            }
2989        }
2990        ret[3] = parseFloat(m[4]) ? parseFloat(m[4]) : 1.0;
2991        return ret;
2992    };
2993    
2994    $.jqplot.colorKeywordMap = {aliceblue: 'rgb(240, 248, 255)', antiquewhite: 'rgb(250, 235, 215)', aqua: 'rgb( 0, 255, 255)', aquamarine: 'rgb(127, 255, 212)', azure: 'rgb(240, 255, 255)', beige: 'rgb(245, 245, 220)', bisque: 'rgb(255, 228, 196)', black: 'rgb( 0, 0, 0)', blanchedalmond: 'rgb(255, 235, 205)', blue: 'rgb( 0, 0, 255)', blueviolet: 'rgb(138, 43, 226)', brown: 'rgb(165, 42, 42)', burlywood: 'rgb(222, 184, 135)', cadetblue: 'rgb( 95, 158, 160)', chartreuse: 'rgb(127, 255, 0)', chocolate: 'rgb(210, 105, 30)', coral: 'rgb(255, 127, 80)', cornflowerblue: 'rgb(100, 149, 237)', cornsilk: 'rgb(255, 248, 220)', crimson: 'rgb(220, 20, 60)', cyan: 'rgb( 0, 255, 255)', darkblue: 'rgb( 0, 0, 139)', darkcyan: 'rgb( 0, 139, 139)', darkgoldenrod: 'rgb(184, 134, 11)', darkgray: 'rgb(169, 169, 169)', darkgreen: 'rgb( 0, 100, 0)', darkgrey: 'rgb(169, 169, 169)', darkkhaki: 'rgb(189, 183, 107)', darkmagenta: 'rgb(139, 0, 139)', darkolivegreen: 'rgb( 85, 107, 47)', darkorange: 'rgb(255, 140, 0)', darkorchid: 'rgb(153, 50, 204)', darkred: 'rgb(139, 0, 0
2994)', darksalmon: 'rgb(233, 150, 122)', darkseagreen: 'rgb(143, 188, 143)', darkslateblue: 'rgb( 72, 61, 139)', darkslategray: 'rgb( 47, 79, 79)', darkslategrey: 'rgb( 47, 79, 79)', darkturquoise: 'rgb( 0, 206, 209)', darkviolet: 'rgb(148, 0, 211)', deeppink: 'rgb(255, 20, 147)', deepskyblue: 'rgb( 0, 191, 255)', dimgray: 'rgb(105, 105, 105)', dimgrey: 'rgb(105, 105, 105)', dodgerblue: 'rgb( 30, 144, 255)', firebrick: 'rgb(178, 34, 34)', floralwhite: 'rgb(255, 250, 240)', forestgreen: 'rgb( 34, 139, 34)', fuchsia: 'rgb(255, 0, 255)', gainsboro: 'rgb(220, 220, 220)', ghostwhite: 'rgb(248, 248, 255)', gold: 'rgb(255, 215, 0)', goldenrod: 'rgb(218, 165, 32)', gray: 'rgb(128, 128, 128)', grey: 'rgb(128, 128, 128)', green: 'rgb( 0, 128, 0)', greenyellow: 'rgb(173, 255, 47)', honeydew: 'rgb(240, 255, 240)', hotpink: 'rgb(255, 105, 180)', indianred: 'rgb(205, 92, 92)', indigo: 'rgb( 75, 0, 130)', ivory: 'rgb(255, 255, 240)', khaki: 'rgb(240, 230, 140)', lavender: 'rgb(230, 230, 250)', lavenderblush: 'rgb(255, 240, 245)', lawngreen: 'rgb(124, 252, 0)', lemonchiffon: 'rgb(255, 250, 205)', lightblue: 'rgb(173, 216, 230)', lightcoral: 'rgb(240, 128, 128)', lightcyan: 'rgb(224, 255, 255)', lightgoldenrodyellow: 'rgb(250, 250, 210)', lightgray: 'rgb(211, 211, 211)', lightgreen: 'rgb(144, 238, 144)', lightgrey: 'rgb(211, 211, 211)', lightpink: 'rgb(255, 182, 193)', lightsalmon: 'rgb(255, 160, 122)', lightseagreen: 'rgb( 32, 178, 170)', lightskyblue: 'rgb(135, 206, 250)', lightslategray: 'rgb(119, 136, 153)', lightslategrey: 'rgb(119, 136, 153)', lightsteelblue: 'rgb(176, 196, 222)', lightyellow: 'rgb(255, 255, 224)', lime: 'rgb( 0, 255, 0)', limegreen: 'rgb( 50, 205, 50)', linen: 'rgb(250, 240, 230)', magenta: 'rgb(255, 0, 255)', maroon: 'rgb(128, 0, 0
2994)', mediumaquamarine: 'rgb(102, 205, 170)', mediumblue: 'rgb( 0, 0, 205)', mediumorchid: 'rgb(186, 85, 211)', mediumpurple: 'rgb(147, 112, 219)', mediumseagreen: 'rgb( 60, 179, 113)', mediumslateblue: 'rgb(123, 104, 238)', mediumspringgreen: 'rgb( 0, 250, 154)', mediumturquoise: 'rgb( 72, 209, 204)', mediumvioletred: 'rgb(199, 21, 133)', midnightblue: 'rgb( 25, 25, 112)', mintcream: 'rgb(245, 255, 250)', mistyrose: 'rgb(255, 228, 225)', moccasin: 'rgb(255, 228, 181)', navajowhite: 'rgb(255, 222, 173)', navy: 'rgb( 0, 0, 128)', oldlace: 'rgb(253, 245, 230)', olive: 'rgb(128, 128, 0)', olivedrab: 'rgb(107, 142, 35)', orange: 'rgb(255, 165, 0)', orangered: 'rgb(255, 69, 0)', orchid: 'rgb(218, 112, 214)', palegoldenrod: 'rgb(238, 232, 170)', palegreen: 'rgb(152, 251, 152)', paleturquoise: 'rgb(175, 238, 238)', palevioletred: 'rgb(219, 112, 147)', papayawhip: 'rgb(255, 239, 213)', peachpuff: 'rgb(255, 218, 185)', peru: 'rgb(205, 133, 63)', pink: 'rgb(255, 192, 203)', plum: 'rgb(221, 160, 221)', powderblue: 'rgb(176, 224, 230)', purple: 'rgb(128, 0, 128)', red: 'rgb(255, 0, 0)', rosybrown: 'rgb(188, 143, 143)', royalblue: 'rgb( 65, 105, 225)', saddlebrown: 'rgb(139, 69, 19)', salmon: 'rgb(250, 128, 114)', sandybrown: 'rgb(244, 164, 96)', seagreen: 'rgb( 46, 139, 87)', seashell: 'rgb(255, 245, 238)', sienna: 'rgb(160, 82, 45)', silver: 'rgb(192, 192, 192)', skyblue: 'rgb(135, 206, 235)', slateblue: 'rgb(106, 90, 205)', slategray: 'rgb(112, 128, 144)', slategrey: 'rgb(112, 128, 144)', snow: 'rgb(255, 250, 250)', springgreen: 'rgb( 0, 255, 127)', steelblue: 'rgb( 70, 130, 180)', tan: 'rgb(210, 180, 140)', teal: 'rgb( 0, 128, 128)', thistle: 'rgb(216, 191, 216)', tomato: 'rgb(255, 99, 71)', turquoise: 'rgb( 64, 224, 208)', violet: 'rgb(238, 130, 238)', wheat: 'rgb(245, 222, 179)', white: 'rgb(255, 255, 255)', whitesmoke: 'rgb(245, 245, 245)', yellow: 'rgb(255, 255, 0)', yellowgreen: 'rgb(154, 205, 50)'};
2995        
2996    // Convienence function that won't hang IE.
2997    $.jqplot.log = function() {
2998        if (window.console && $.jqplot.debug) {
2999           if (arguments.length == 1) {
3000               console.log (arguments[0]);
3001            }
3002           else {
3003               console.log(arguments);
3004            }
3005        }
3006    };
3007    var log = $.jqplot.log;
3008    
3009
3010    // class: $.jqplot.AxisLabelRenderer
3011    // Renderer to place labels on the axes.
3012    $.jqplot.AxisLabelRenderer = function(options) {
3013        // Group: Properties
3014        $.jqplot.ElemContainer.call(this);
3015        // name of the axis associated with this tick
3016        this.axis;
3017        // prop: show
3018        // wether or not to show the tick (mark and label).
3019        this.show = true;
3020        // prop: label
3021        // The text or html for the label.
3022        this.label = '';
3023        this.fontFamily = null;
3024        this.fontSize = null;
3025        this.textColor = null;
3026        this._elem;
3027        // prop: escapeHTML
3028        // true to escape HTML entities in the label.
3029        this.escapeHTML = false;
3030        
3031        $.extend(true, this, options);
3032    };
3033    
3034    $.jqplot.AxisLabelRenderer.prototype = new $.jqplot.ElemContainer();
3035    $.jqplot.AxisLabelRenderer.prototype.constructor = $.jqplot.AxisLabelRenderer;
3036    
3037    $.jqplot.AxisLabelRenderer.prototype.init = function(options) {
3038        $.extend(true, this, options);
3039    };
3040    
3041    $.jqplot.AxisLabelRenderer.prototype.draw = function() {
3042        this._elem = $('<div style="position:absolute;" class="jqplot-'+this.axis+'-label"></div>');
3043        
3044        if (Number(this.label)) {
3045            this._elem.css('white-space', 'nowrap');
3046        }
3047        
3048        if (!this.escapeHTML) {
3049            this._elem.html(this.label);
3050        }
3051        else {
3052            this._elem.text(this.label);
3053        }
3054        if (this.fontFamily) {
3055            this._elem.css('font-family', this.fontFamily);
3056        }
3057        if (this.fontSize) {
3058            this._elem.css('font-size', this.fontSize);
3059        }
3060        if (this.textColor) {
3061            this._elem.css('color', this.textColor);
3062        }
3063        
3064        return this._elem;
3065    };
3066    
3067    $.jqplot.AxisLabelRenderer.prototype.pack = function() {
3068    };
3069
3070    // class: $.jqplot.AxisTickRenderer
3071    // A "tick" object showing the value of a tick/gridline on the plot.
3072    $.jqplot.AxisTickRenderer = function(options) {
3073        // Group: Properties
3074        $.jqplot.ElemContainer.call(this);
3075        // prop: mark
3076        // tick mark on the axis.  One of 'inside', 'outside', 'cross', '' or null.
3077        this.mark = 'outside';
3078        // name of the axis associated with this tick
3079        this.axis;
3080        // prop: showMark
3081        // wether or not to show the mark on the axis.
3082        this.showMark = true;
3083        // prop: showGridline
3084        // wether or not to draw the gridline on the grid at this tick.
3085        this.showGridline = true;
3086        // prop: isMinorTick
3087        // if this is a minor tick.
3088        this.isMinorTick = false;
3089        // prop: size
3090        // Length of the tick beyond the grid in pixels.
3091        // DEPRECATED: This has been superceeded by markSize
3092        this.size = 4;
3093        // prop:  markSize
3094        // Length of the tick marks in pixels.  For 'cross' style, length
3095        // will be stoked above and below axis, so total length will be twice this.
3096        this.markSize = 6;
3097        // prop: show
3098        // wether or not to show the tick (mark and label).
3099        // Setting this to false requires more testing.  It is recommended
3100        // to set showLabel and showMark to false instead.
3101        this.show = true;
3102        // prop: showLabel
3103        // wether or not to show the label.
3104        this.showLabel = true;
3105        this.label = '';
3106        this.value = null;
3107        this._styles = {};
3108        // prop: formatter
3109        // A class of a formatter for the tick text.  sprintf by default.
3110        this.formatter = $.jqplot.DefaultTickFormatter;
3111        // prop: prefix
3112        // string appended to the tick label if no formatString is specified.
3113        this.prefix = '';
3114        // prop: formatString
3115        // string passed to the formatter.
3116        this.formatString = '';
3117        // prop: fontFamily
3118        // css spec for the font-family css attribute.
3119        this.fontFamily;
3120        // prop: fontSize
3121        // css spec for the font-size css attribute.
3122        this.fontSize;
3123        // prop: textColor
3124        // css spec for the color attribute.
3125        this.textColor;
3126        this._elem;
3127        
3128        $.extend(true, this, options);
3129    };
3130    
3131    $.jqplot.AxisTickRenderer.prototype.init = function(options) {
3132        $.extend(true, this, options);
3133    };
3134    
3135    $.jqplot.AxisTickRenderer.prototype = new $.jqplot.ElemContainer();
3136    $.jqplot.AxisTickRenderer.prototype.constructor = $.jqplot.AxisTickRenderer;
3137    
3138    $.jqplot.AxisTickRenderer.prototype.setTick = function(value, axisName, isMinor) {
3139        this.value = value;
3140        this.axis = axisName;
3141        if (isMinor) {
3142            this.isMinorTick = true;
3143        }
3144        return this;
3145    };
3146    
3147    $.jqplot.AxisTickRenderer.prototype.draw = function() {
3148        if (!this.label) {
3149            this.label = this.formatter(this.formatString, this.value);
3150        }
3151        // add prefix if needed
3152        if (this.prefix && !this.formatString) {
3153            this.label = this.prefix + this.label;
3154        }
3155        style ='style="position:absolute;';
3156        if (Number(this.label)) {
3157            style +='white-space:nowrap;';
3158        }
3159        style += '"';
3160        this._elem = $('<div '+style+' class="jqplot-'+this.axis+'-tick">'+this.label+'</div>');
3161        for (var s in this._styles) {
3162            this._elem.css(s, this._styles[s]);
3163        }
3164        if (this.fontFamily) {
3165            this._elem.css('font-family', this.fontFamily);
3166        }
3167        if (this.fontSize) {
3168            this._elem.css('font-size', this.fontSize);
3169        }
3170        if (this.textColor) {
3171            this._elem.css('color', this.textColor);
3172        }
3173        return this._elem;
3174    };
3175        
3176    $.jqplot.DefaultTickFormatter = function (format, val) {
3177        if (typeof val == 'number') {
3178            if (!format) {
3179                format = $.jqplot.config.defaultTickFormatString;
3180            }
3181            return $.jqplot.sprintf(format, val);
3182        }
3183        else {
3184            return String(val);
3185        }
3186    };
3187    
3188    $.jqplot.AxisTickRenderer.prototype.pack = function() {
3189    };
3190     
3191    // Class: $.jqplot.CanvasGridRenderer
3192    // The default jqPlot grid renderer, creating a grid on a canvas element.
3193    // The renderer has no additional options beyond the <Grid> class.
3194    $.jqplot.CanvasGridRenderer = function(){
3195        this.shadowRenderer = new $.jqplot.ShadowRenderer();
3196    };
3197    
3198    // called with context of Grid object
3199    $.jqplot.CanvasGridRenderer.prototype.init = function(options) {
3200        this._ctx;
3201        $.extend(true, this, options);
3202        // set the shadow renderer options
3203        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, strokeStyle:this.shadowColor};
3204        this.renderer.shadowRenderer.init(sopts);
3205    };
3206    
3207    // called with context of Grid.
3208    $.jqplot.CanvasGridRenderer.prototype.createElement = function() {
3209        var elem = document.createElement('canvas');
3210        var w = this._plotDimensions.width;
3211        var h = this._plotDimensions.height;
3212        elem.width = w;
3213        elem.height = h;
3214        this._elem = $(elem);
3215        this._elem.addClass('jqplot-grid-canvas');
3216        this._elem.css({ position: 'absolute', left: 0, top: 0 });
3217        if ($.browser.msie) {
3218            window.G_vmlCanvasManager.init_(document);
3219        }
3220        if ($.browser.msie) {
3221            elem = window.G_vmlCanvasManager.initElement(elem);
3222        }
3223        this._top = this._offsets.top;
3224        this._bottom = h - this._offsets.bottom;
3225        this._left = this._offsets.left;
3226        this._right = w - this._offsets.right;
3227        this._width = this._right - this._left;
3228        this._height = this._bottom - this._top;
3229        return this._elem;
3230    };
3231    
3232    $.jqplot.CanvasGridRenderer.prototype.draw = function() {
3233        this._ctx = this._elem.get(0).getContext("2d");
3234        var ctx = this._ctx;
3235        var axes = this._axes;
3236        // Add the grid onto the grid canvas.  This is the bottom most layer.
3237        ctx.save();
3238        ctx.clearRect(0, 0, this._plotDimensions.width, this._plotDimensions.height);
3239        ctx.fillStyle = this.backgroundColor || this.background;
3240        ctx.fillRect(this._left, this._top, this._width, this._height);
3241        
3242        if (this.drawGridlines) {
3243            ctx.save();
3244            ctx.lineJoin = 'miter';
3245            ctx.lineCap = 'butt';
3246            ctx.lineWidth = this.gridLineWidth;
3247            ctx.strokeStyle = this.gridLineColor;
3248            var b, e;
3249            var ax = ['xaxis', 'yaxis', 'x2axis', 'y2axis'];
3250            for (var i=4; i>0; i--) {
3251                var name = ax[i-1];
3252                var axis = axes[name];
3253                var ticks = axis._ticks;
3254                if (axis.show) {
3255                    for (var j=ticks.length; j>0; j--) {
3256                        var t = ticks[j-1];
3257                        if (t.show) {
3258                            var pos = Math.round(axis.u2p(t.value)) + 0.5;
3259                            switch (name) {
3260                                case 'xaxis':
3261                                    // draw the grid line
3262                                    if (t.showGridline) {
3263                                        drawLine(pos, this._top, pos, this._bottom);
3264                                    }
3265                                    
3266                                    // draw the mark
3267                                    if (t.showMark && t.mark) {
3268                                        s = t.markSize;
3269                                        m = t.mark;
3270                                        var pos = Math.round(axis.u2p(t.value)) + 0.5;
3271                                        switch (m) {
3272                                            case 'outside':
3273                                                b = this._bottom;
3274                                                e = this._bottom+s;
3275                                                break;
3276                                            case 'inside':
3277                                                b = this._bottom-s;
3278                                                e = this._bottom;
3279                                                break;
3280                                            case 'cross':
3281                                                b = this._bottom-s;
3282                                                e = this._bottom+s;
3283                                                break;
3284                                            default:
3285                                                b = this._bottom;
3286                                                e = this._bottom+s;
3287                                                break;
3288                                        }
3289                                        // draw the shadow
3290                                        if (this.shadow) {
3291                                            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});
3292                                        }
3293                                        // draw the line
3294                                        drawLine(pos, b, pos, e);
3295                                    }
3296                                    break;
3297                                case 'yaxis':
3298                                    // draw the grid line
3299                                    if (t.showGridline) {
3300                                        drawLine(this._right, pos, this._left, pos);
3301                                    }
3302                                    // draw the mark
3303                                    if (t.showMark && t.mark) {
3304                                        s = t.markSize;
3305                                        m = t.mark;
3306                                        var pos = Math.round(axis.u2p(t.value)) + 0.5;
3307                                        switch (m) {
3308                                            case 'outside':
3309                                                b = this._left-s;
3310                                                e = this._left;
3311                                                break;
3312                                            case 'inside':
3313                                                b = this._left;
3314                                                e = this._left+s;
3315                                                break;
3316                                            case 'cross':
3317                                                b = this._left-s;
3318                                                e = this._left+s;
3319                                                break;
3320                                            default:
3321                                                b = this._left-s;
3322                                                e = this._left;
3323                                                break;
3324                                                }
3325                                        // draw the shadow
3326                                        if (this.shadow) {
3327                                            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});
3328                                        }
3329                                        drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
3330                                    }
3331                                    break;
3332                                case 'x2axis':
3333                                    // draw the grid line
3334                                    if (t.showGridline) {
3335                                        drawLine(pos, this._bottom, pos, this._top);
3336                                    }
3337                                    // draw the mark
3338                                    if (t.showMark && t.mark) {
3339                                        s = t.markSize;
3340                                        m = t.mark;
3341                                        var pos = Math.round(axis.u2p(t.value)) + 0.5;
3342                                        switch (m) {
3343                                            case 'outside':
3344                                                b = this._top-s;
3345                                                e = this._top;
3346                                                break;
3347                                            case 'inside':
3348                                                b = this._top;
3349                                                e = this._top+s;
3350                                                break;
3351                                            case 'cross':
3352                                                b = this._top-s;
3353                                                e = this._top+s;
3354                                                break;
3355                                            default:
3356                                                b = this._top-s;
3357                                                e = this._top;
3358                                                break;
3359                                                }
3360                                        // draw the shadow
3361                                        if (this.shadow) {
3362                                            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});
3363                                        }
3364                                        drawLine(pos, b, pos, e);
3365                                    }
3366                                    break;
3367                                case 'y2axis':
3368                                    // draw the grid line
3369                                    if (t.showGridline) {
3370                                        drawLine(this._left, pos, this._right, pos);
3371                                    }
3372                                    // draw the mark
3373                                    if (t.showMark && t.mark) {
3374                                        s = t.markSize;
3375                                        m = t.mark;
3376                                        var pos = Math.round(axis.u2p(t.value)) + 0.5;
3377                                        switch (m) {
3378                                            case 'outside':
3379                                                b = this._right;
3380                                                e = this._right+s;
3381                                                break;
3382                                            case 'inside':
3383                                                b = this._right-s;
3384                                                e = this._right;
3385                                                break;
3386                                            case 'cross':
3387                                                b = this._right-s;
3388                                                e = this._right+s;
3389                                                break;
3390                                            default:
3391                                                b = this._right;
3392                                                e = this._right+s;
3393                                                break;
3394                                                }
3395                                        // draw the shadow
3396                                        if (this.shadow) {
3397                                            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});
3398                                        }
3399                                        drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
3400                                    }
3401                                    break;
3402                                default:
3403                                    break;
3404                            }
3405                        }
3406                    }
3407                }
3408            }
3409            // Now draw grid lines for additional y axes
3410            ax = ['y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis'];
3411            for (var i=7; i>0; i--) {
3412                var axis = axes[ax[i-1]];
3413                var ticks = axis._ticks;
3414                if (axis.show) {
3415                    var tn = ticks[axis.numberTicks-1];
3416                    var t0 = ticks[0];
3417                    var left = axis.getLeft();
3418                    var points = [[left, tn.getTop() + tn.getHeight()/2], [left, t0.getTop() + t0.getHeight()/2 + 1.0]];
3419                    // draw the shadow
3420                    if (this.shadow) {
3421                        this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', fill:false, closePath:false});
3422                    }
3423                    // draw the line
3424                    drawLine(points[0][0], points[0][1], points[1][0], points[1][1], {lineCap:'butt', strokeStyle:axis.borderColor, lineWidth:axis.borderWidth});
3425                    // draw the tick marks
3426                    for (var j=ticks.length; j>0; j--) {
3427                        var t = ticks[j-1];
3428                        s = t.markSize;
3429                        m = t.mark;
3430                        var pos = Math.round(axis.u2p(t.value)) + 0.5;
3431                        if (t.showMark && t.mark) {
3432                            switch (m) {
3433                                case 'outside':
3434                                    b = left;
3435                                    e = left+s;
3436                                    break;
3437                                case 'inside':
3438                                    b = left-s;
3439                                    e = left;
3440                                    break;
3441                                case 'cross':
3442                                    b = left-s;
3443                                    e = left+s;
3444                                    break;
3445                                default:
3446                                    b = left;
3447                                    e = left+s;
3448                                    break;
3449                            }
3450                            points = [[b,pos], [e,pos]];
3451                            // draw the shadow
3452                            if (this.shadow) {
3453                                this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false});
3454                            }
3455                            // draw the line
3456                            drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
3457                        }
3458                    }
3459                }
3460            }
3461            
3462            ctx.restore();
3463        }
3464        
3465        function drawLine(bx, by, ex, ey, opts) {
3466            ctx.save();
3467            opts = opts || {};
3468            if (opts.lineWidth == null || opts.lineWidth != 0){
3469                $.extend(true, ctx, opts);
3470                ctx.beginPath();
3471                ctx.moveTo(bx, by);
3472                ctx.lineTo(ex, ey);
3473                ctx.stroke();
3474                ctx.restore();
3475            }
3476        }
3477        
3478        if (this.shadow) {
3479            var points = [[this._left, this._bottom], [this._right, this._bottom], [this._right, this._top]];
3480            this.renderer.shadowRenderer.draw(ctx, points);
3481        }
3482        // Now draw border around grid.  Use axis border definitions. start at
3483        // upper left and go clockwise.
3484        if (this.borderWidth != 0 && this.drawBorder) {
3485            drawLine (this._left, this._top, this._right, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth});
3486            drawLine (this._right, this._top, this._right, this._bottom, {lineCap:'round', strokeStyle:axes.y2axis.borderColor, lineWidth:axes.y2axis.borderWidth});
3487            drawLine (this._right, this._bottom, this._left, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth});
3488            drawLine (this._left, this._bottom, this._left, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth});
3489        }
3490        // ctx.lineWidth = this.borderWidth;
3491        // ctx.strokeStyle = this.borderColor;
3492        // ctx.strokeRect(this._left, this._top, this._width, this._height);
3493        
3494    
3495        ctx.restore();
3496    };
3497   
3498    /**
3499     * Date instance methods
3500     *
3501     * @author Ken Snyder (ken d snyder at gmail dot com)
3502     * @date 2008-09-10
3503     * @version 2.0.2 (http://kendsnyder.com/sandbox/date/)     
3504     * @license Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/)
3505     *
3506     * @contributions Chris Leonello
3507     * @comment Bug fix to 12 hour time and additions to handle milliseconds and 
3508     * @comment 24 hour time without am/pm suffix
3509     *
3510     */
3511 
3512    // begin by creating a scope for utility variables
3513    
3514    //
3515    // pre-calculate the number of milliseconds in a day
3516    //  
3517    
3518    var day = 24 * 60 * 60 * 1000;
3519    //
3520    // function to add leading zeros
3521    //
3522    var zeroPad = function(number, digits) {
3523        number = String(number);
3524        while (number.length < digits) {
3525            number = '0' + number;
3526        }
3527        return number;
3528    };
3529    //
3530    // set up integers and functions for adding to a date or subtracting two dates
3531    //
3532    var multipliers = {
3533        millisecond: 1,
3534        second: 1000,
3535        minute: 60 * 1000,
3536        hour: 60 * 60 * 1000,
3537        day: day,
3538        week: 7 * day,
3539        month: {
3540            // add a number of months
3541            add: function(d, number) {
3542                // add any years needed (increments of 12)
3543                multipliers.year.add(d, Math[number > 0 ? 'floor' : 'ceil'](number / 12));
3544                // ensure that we properly wrap betwen December and January
3545                var prevMonth = d.getMonth() + (number % 12);
3546                if (prevMonth == 12) {
3547                    prevMonth = 0;
3548                    d.setYear(d.getFullYear() + 1);
3549                } else if (prevMonth == -1) {
3550                    prevMonth = 11;
3551                    d.setYear(d.getFullYear() - 1);
3552                }
3553                d.setMonth(prevMonth);
3554            },
3555            // get the number of months between two Date objects (decimal to the nearest day)
3556            diff: function(d1, d2) {
3557                // get the number of years
3558                var diffYears = d1.getFullYear() - d2.getFullYear();
3559                // get the number of remaining months
3560                var diffMonths = d1.getMonth() - d2.getMonth() + (diffYears * 12);
3561                // get the number of remaining days
3562                var diffDays = d1.getDate() - d2.getDate();
3563                // return the month difference with the days difference as a decimal
3564                return diffMonths + (diffDays / 30);
3565            }
3566        },
3567        year: {
3568            // add a number of years
3569            add: function(d, number) {
3570                d.setYear(d.getFullYear() + Math[number > 0 ? 'floor' : 'ceil'](number));
3571            },
3572            // get the number of years between two Date objects (decimal to the nearest day)
3573            diff: function(d1, d2) {
3574                return multipliers.month.diff(d1, d2) / 12;
3575            }
3576        }        
3577    };
3578    //
3579    // alias each multiplier with an 's' to allow 'year' and 'years' for example
3580    //
3581    for (var unit in multipliers) {
3582        if (unit.substring(unit.length - 1) != 's') { // IE will iterate newly added properties :|
3583            multipliers[unit + 's'] = multipliers[unit];
3584        }
3585    }
3586    //
3587    // take a date instance and a format code and return the formatted value
3588    //
3589    var format = function(d, code) {
3590            if (Date.prototype.strftime.formatShortcuts[code]) {
3591                    // process any shortcuts recursively
3592                    return d.strftime(Date.prototype.strftime.formatShortcuts[code]);
3593            } else {
3594                    // get the format code function and toPaddedString() argument
3595                    var getter = (Date.prototype.strftime.formatCodes[code] || '').split('.');
3596                    var nbr = d['get' + getter[0]] ? d['get' + getter[0]]() : '';
3597                    // run toPaddedString() if specified
3598                    if (getter[1]) {
3599                        nbr = zeroPad(nbr, getter[1]);
3600                    }
3601                    // prepend the leading character
3602                    return nbr;
3603            }       
3604    };
3605    //
3606    // Add methods to Date instances
3607    //
3608    var instanceMethods = {
3609        //
3610        // Return a date one day ahead (or any other unit)
3611        //
3612        // @param string unit
3613        // units: year | month | day | week | hour | minute | second | millisecond
3614        // @return object Date
3615        //
3616        succ: function(unit) {
3617            return this.clone().add(1, unit);
3618        },
3619        //
3620        // Add an arbitrary amount to the currently stored date
3621        //
3622        // @param integer/float number      
3623        // @param string unit
3624        // @return object Date (chainable)      
3625        //
3626        add: function(number, unit) {
3627            var factor = multipliers[unit] || multipliers.day;
3628            if (typeof factor == 'number') {
3629                this.setTime(this.getTime() + (factor * number));
3630            } else {
3631                factor.add(this, number);
3632            }
3633            return this;
3634        },
3635        //
3636        // Find the difference between the current and another date
3637        //
3638        // @param string/object dateObj
3639        // @param string unit
3640        // @param boolean allowDecimal
3641        // @return integer/float
3642        //
3643        diff: function(dateObj, unit, allowDecimal) {
3644            // ensure we have a Date object
3645            dateObj = Date.create(dateObj);
3646            if (dateObj === null) {
3647                return null;
3648            }
3649            // get the multiplying factor integer or factor function
3650            var factor = multipliers[unit] || multipliers.day;
3651            if (typeof factor == 'number') {
3652                // multiply
3653                var unitDiff = (this.getTime() - dateObj.getTime()) / factor;
3654            } else {
3655                // run function
3656                var unitDiff = factor.diff(this, dateObj);
3657            }
3658            // if decimals are not allowed, round toward zero
3659            return (allowDecimal ? unitDiff : Math[unitDiff > 0 ? 'floor' : 'ceil'](unitDiff));          
3660        },
3661        //
3662        // Convert a date to a string using traditional strftime format codes
3663        //
3664        // @param string formatStr
3665        // @return string
3666        //
3667        strftime: function(formatStr) {
3668            // default the format string to year-month-day
3669            var source = formatStr || '%Y-%m-%d', result = '', match;
3670            // Account for display of time in local time or as UTC time
3671            // var val = ($.jqplot.comfig.convertUTCtoLocaltime) ? this : 
3672            // replace each format code
3673            while (source.length > 0) {
3674                if (match = source.match(Date.prototype.strftime.formatCodes.matcher)) {
3675                    result += source.slice(0, match.index);
3676                    result += (match[1] || '') + format(this, match[2]);
3677                    source = source.slice(match.index + match[0].length);
3678                } else {
3679                    result += source;
3680                    source = '';
3681                }
3682            }
3683            return result;
3684        },
3685        //
3686        // Return a proper two-digit year integer
3687        //
3688        // @return integer
3689        //
3690        getShortYear: function() {
3691            return this.getYear() % 100;
3692        },
3693        //
3694        // Get the number of the current month, 1-12
3695        //
3696        // @return integer
3697        //
3698        getMonthNumber: function() {
3699            return this.getMonth() + 1;
3700        },
3701        //
3702        // Get the name of the current month
3703        //
3704        // @return string
3705        //
3706        getMonthName: function() {
3707            return Date.MONTHNAMES[this.getMonth()];
3708        },
3709        //
3710        // Get the abbreviated name of the current month
3711        //
3712        // @return string
3713        //
3714        getAbbrMonthName: function() {
3715            return Date.ABBR_MONTHNAMES[this.getMonth()];
3716        },
3717        //
3718        // Get the name of the current week day
3719        //
3720        // @return string
3721        //      
3722        getDayName: function() {
3723            return Date.DAYNAMES[this.getDay()];
3724        },
3725        //
3726        // Get the abbreviated name of the current week day
3727        //
3728        // @return string
3729        //      
3730        getAbbrDayName: function() {
3731            return Date.ABBR_DAYNAMES[this.getDay()];
3732        },
3733        //
3734        // Get the ordinal string associated with the day of the month (i.e. st, nd, rd, th)
3735        //
3736        // @return string
3737        //      
3738        getDayOrdinal: function() {
3739            return Date.ORDINALNAMES[this.getDate() % 10];
3740        },
3741        //
3742        // Get the current hour on a 12-hour scheme
3743        //
3744        // @return integer
3745        //
3746        getHours12: function() {
3747            var hours = this.getHours();
3748            return hours > 12 ? hours - 12 : (hours == 0 ? 12 : hours);
3749        },
3750        //
3751        // Get the AM or PM for the current time
3752        //
3753        // @return string
3754        //
3755        getAmPm: function() {
3756            return this.getHours() >= 12 ? 'PM' : 'AM';
3757        },
3758        //
3759        // Get the current date as a Unix timestamp
3760        //
3761        // @return integer
3762        //
3763        getUnix: function() {
3764            return Math.round(this.getTime() / 1000, 0);
3765        },
3766        //
3767        // Get the GMT offset in hours and minutes (e.g. +06:30)
3768        //
3769        // @return string
3770        //
3771        getGmtOffset: function() {
3772            // divide the minutes offset by 60
3773            var hours = this.getTimezoneOffset() / 60;
3774            // decide if we are ahead of or behind GMT
3775            var prefix = hours < 0 ? '+' : '-';
3776            // remove the negative sign if any
3777            hours = Math.abs(hours);
3778            // add the +/- to the padded number of hours to : to the padded minutes
3779            return prefix + zeroPad(Math.floor(hours), 2) + ':' + zeroPad((hours % 1) * 60, 2);
3780        },
3781        //
3782        // Get the browser-reported name for the current timezone (e.g. MDT, Mountain Daylight Time)
3783        //
3784        // @return string
3785        //
3786        getTimezoneName: function() {
3787            var match = /(?:\((.+)\)$| ([A-Z]{3}) )/.exec(this.toString());
3788            return match[1] || match[2] || 'GMT' + this.getGmtOffset();
3789        },
3790        //
3791        // Convert the current date to an 8-digit integer (%Y%m%d)
3792        //
3793        // @return int
3794        //
3795        toYmdInt: function() {
3796            return (this.getFullYear() * 10000) + (this.getMonthNumber() * 100) + this.getDate();
3797        },  
3798        //
3799        // Create a copy of a date object
3800        //
3801        // @return object
3802        //       
3803        clone: function() {
3804                return new Date(this.getTime());
3805        }
3806    };
3807    for (var name in instanceMethods) {
3808        Date.prototype[name] = instanceMethods[name];
3809    }
3810    //
3811    // Add static methods to the date object
3812    //
3813    var staticMethods = {
3814        //
3815        // The heart of the date functionality: returns a date object if given a convertable value
3816        //
3817        // @param string/object/integer date
3818        // @return object Date
3819        //
3820        create: function(date) {
3821            // If the passed value is already a date object, return it
3822            if (date instanceof Date) {
3823                return date;
3824            }
3825            // if (typeof date == 'number') return new Date(date);
3826            // If the passed value is an integer, interpret it as a javascript timestamp
3827            if (typeof date == 'number') {
3828                return new Date(date);
3829            }
3830            // If the passed value is a string, attempt to parse it using Date.parse()
3831            var parsable = String(date).replace(/^\s*(.+)\s*$/, '$1'), i = 0, length = Date.create.patterns.length, pattern;
3832            var current = parsable;
3833            while (i < length) {
3834                ms = Date.parse(current);
3835                if (!isNaN(ms)) {
3836                    return new Date(ms);
3837                }
3838                pattern = Date.create.patterns[i];
3839                if (typeof pattern == 'function') {
3840                    obj = pattern(current);
3841                    if (obj instanceof Date) {
3842                        return obj;
3843                    }
3844                } else {
3845                    current = parsable.replace(pattern[0], pattern[1]);
3846                }
3847                i++;
3848            }
3849            return NaN;
3850        },
3851        //
3852        // constants representing month names, day names, and ordinal names
3853        // (same names as Ruby Date constants)
3854        //
3855        MONTHNAMES          : 'January February March April May June July August September October November December'.split(' '),
3856        ABBR_MONTHNAMES : 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' '),
3857        DAYNAMES                : 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' '),
3858        ABBR_DAYNAMES       : 'Sun Mon Tue Wed Thu Fri Sat'.split(' '),
3859        ORDINALNAMES        : 'th st nd rd th th th th th th'.split(' '),
3860        //
3861        // Shortcut for full ISO-8601 date conversion
3862        //
3863        ISO: '%Y-%m-%dT%H:%M:%S.%N%G',
3864        //
3865        // Shortcut for SQL-type formatting
3866        //
3867        SQL: '%Y-%m-%d %H:%M:%S',
3868        //
3869        // Setter method for month, day, and ordinal names for i18n
3870        //
3871        // @param object newNames
3872        //
3873        daysInMonth: function(year, month) {
3874            if (month == 2) {
3875                return new Date(year, 1, 29).getDate() == 29 ? 29 : 28;
3876            }
3877            return [undefined,31,undefined,31,30,31,30,31,31,30,31,30,31][month];
3878        }
3879    };
3880    for (var name in staticMethods) {
3881        Date[name] = staticMethods[name];
3882    }
3883    //
3884    // format codes for strftime
3885    //
3886    // each code must be an array where the first member is the name of a Date.prototype function
3887    // and optionally a second member indicating the number to pass to Number#toPaddedString()
3888    //
3889    Date.prototype.strftime.formatCodes = {
3890        //
3891        // 2-part regex matcher for format codes
3892        //
3893        // first match must be the character before the code (to account for escaping)
3894        // second match must be the format code character(s)
3895        //
3896        matcher: /()%(#?(%|[a-z]))/i,
3897        // year
3898        Y: 'FullYear',
3899        y: 'ShortYear.2',
3900        // month
3901        m: 'MonthNumber.2',
3902        '#m': 'MonthNumber',
3903        B: 'MonthName',
3904        b: 'AbbrMonthName',
3905        // day
3906        d: 'Date.2',
3907        '#d': 'Date',
3908        e: 'Date',
3909        A: 'DayName',
3910        a: 'AbbrDayName',
3911        w: 'Day',
3912        o: 'DayOrdinal',
3913        // hours
3914        H: 'Hours.2',
3915        '#H': 'Hours',
3916        I: 'Hours12.2',
3917        '#I': 'Hours12',
3918        p: 'AmPm',
3919        // minutes
3920        M: 'Minutes.2',
3921        '#M': 'Minutes',
3922        // seconds
3923        S: 'Seconds.2',
3924        '#S': 'Seconds',
3925        s: 'Unix',
3926        // milliseconds
3927        N: 'Milliseconds.3',
3928        '#N': 'Milliseconds',
3929        // timezone
3930        O: 'TimezoneOffset',
3931        Z: 'TimezoneName',
3932        G: 'GmtOffset'  
3933    };
3934    //
3935    // shortcuts that will be translated into their longer version
3936    //
3937    // be sure that format shortcuts do not refer to themselves: this will cause an infinite loop
3938    //
3939    Date.prototype.strftime.formatShortcuts = {
3940        // date
3941        F: '%Y-%m-%d',
3942        // time
3943        T: '%H:%M:%S',
3944        X: '%H:%M:%S',
3945        // local format date
3946        x: '%m/%d/%y',
3947        D: '%m/%d/%y',
3948        // local format extended
3949        '#c': '%a %b %e %H:%M:%S %Y',
3950        // local format short
3951        v: '%e-%b-%Y',
3952        R: '%H:%M',
3953        r: '%I:%M:%S %p',
3954        // tab and newline
3955        t: '\t',
3956        n: '\n',
3957        '%': '%'
3958    };
3959    //
3960    // A list of conversion patterns (array arguments sent directly to gsub)
3961    // Add, remove or splice a patterns to customize date parsing ability
3962    //
3963    Date.create.patterns = [
3964        [/-/g, '/'], // US-style time with dashes => Parsable US-style time
3965        [/st|nd|rd|th/g, ''], // remove st, nd, rd and th        
3966        [/(3[01]|[0-2]\d)\s*\.\s*(1[0-2]|0\d)\s*\.\s*([1-9]\d{3})/, '$2/$1/$3'], // World time => Parsable US-style time
3967        [/([1-9]\d{3})\s*-\s*(1[0-2]|0\d)\s*-\s*(3[01]|[0-2]\d)/, '$2/$3/$1'], // ISO-style time => Parsable US-style time
3968        function(str) { // 12-hour or 24 hour time with milliseconds
3969            // var match = str.match(/^(?:(.+)\s+)?([1-9]|1[012])(?:\s*\:\s*(\d\d))?(?:\s*\:\s*(\d\d))?\s*(am|pm)\s*$/i);
3970            var match = str.match(/^(?:(.+)\s+)?([012]?\d)(?:\s*\:\s*(\d\d))?(?:\s*\:\s*(\d\d(\.\d*)?))?\s*(am|pm)?\s*$/i);
3971            //                   opt. date      hour       opt. minute     opt. second       opt. msec   opt. am or pm
3972            if (match) {
3973                if (match[1]) {
3974                    var d = Date.create(match[1]);
3975                    if (isNaN(d)) {
3976                        return;
3977                    }
3978                } else {
3979                    var d = new Date();
3980                    d.setMilliseconds(0);
3981                }
3982                var hour = parseFloat(match[2]);
3983                if (match[6]) {
3984                    hour = match[6].toLowerCase() == 'am' ? (hour == 12 ? 0 : hour) : (hour == 12 ? 12 : hour + 12);
3985                }
3986                d.setHours(hour, parseInt(match[3] || 0, 10), parseInt(match[4] || 0, 10), ((parseFloat(match[5] || 0)) || 0)*1000);
3987                return d;
3988            }
3989            else {
3990                return str;
3991            }
3992        },
3993        function(str) { // ISO timestamp with time zone.
3994            var match = str.match(/^(?:(.+))[T|\s+]([012]\d)(?:\:(\d\d))(?:\:(\d\d))(?:\.\d+)([\+\-]\d\d\:\d\d)$/i);
3995            if (match) {
3996                if (match[1]) {
3997                    var d = Date.create(match[1]);
3998                    if (isNaN(d)) {
3999                        return;
4000                    }
4001                } else {
4002                    var d = new Date();
4003                    d.setMilliseconds(0);
4004                }
4005                var hour = parseFloat(match[2]);
4006                d.setHours(hour, parseInt(match[3], 10), parseInt(match[4], 10), parseFloat(match[5])*1000);
4007                return d;
4008            }
4009            else {
4010                    return str;
4011            }
4012        },
4013        function(str) {
4014            var match = str.match(/^([0-3]?\d)\s*[-\/.\s]{1}\s*([a-zA-Z]{3,9})\s*[-\/.\s]{1}\s*([0-3]?\d)$/);
4015            if (match) {
4016                var d = new Date();
4017                var y = parseFloat(String(d.getFullYear()).slice(2,4));
4018                var cent = parseInt(String(d.getFullYear())/100, 10)*100;
4019                var centoffset = 1;
4020                var m1 = parseFloat(match[1]);
4021                var m3 = parseFloat(match[3]);
4022                var ny, nd, nm;
4023                if (m1 > 31) { // first number is a year
4024                    nd = match[3];
4025                    if (m1 < y+centoffset) { // if less than 1 year out, assume it is this century.
4026                        ny = cent + m1;
4027                    }
4028                    else {
4029                        ny = cent - 100 + m1;
4030                    }
4031                }
4032                
4033                else { // last number is the year
4034                    nd = match[1];
4035                    if (m3 < y+centoffset) { // if less than 1 year out, assume it is this century.
4036                        ny = cent + m3;
4037                    }
4038                    else {
4039                        ny = cent - 100 + m3;
4040                    }
4041                }
4042                
4043                var nm = $.inArray(match[2], Date.ABBR_MONTHNAMES);
4044                
4045                if (nm == -1) {
4046                    nm = $.inArray(match[2], Date.MONTHNAMES);
4047                }
4048            
4049                d.setFullYear(ny, nm, nd);
4050                d.setHours(0,0,0,0);
4051                return d;
4052            }
4053            
4054            else {
4055                return str;
4056            }
4057        }        
4058    ];
4059    
4060    if ($.jqplot.config.debug) {
4061        $.date = Date.create;
4062    }
4063
4064    // Class: $.jqplot.DivTitleRenderer
4065    // The default title renderer for jqPlot.  This class has no options beyond the <Title> class. 
4066    $.jqplot.DivTitleRenderer = function() {
4067    };
4068    
4069    $.jqplot.DivTitleRenderer.prototype.init = function(options) {
4070        $.extend(true, this, options);
4071    };
4072    
4073    $.jqplot.DivTitleRenderer.prototype.draw = function() {
4074        var r = this.renderer;
4075        if (!this.text) {
4076            this.show = false;
4077            this._elem = $('<div class="jqplot-title" style="height:0px;width:0px;"></div>');
4078        }
4079        else if (this.text) {
4080            var color;
4081            if (this.color) {
4082                color = this.color;
4083            }
4084            else if (this.textColor) {
4085                color = this.textColor;
4086            }
4087            // don't trust that a stylesheet is present, set the position.
4088            var styletext = 'position:absolute;top:0px;left:0px;';
4089            styletext += (this._plotWidth) ? 'width:'+this._plotWidth+'px;' : '';
4090            styletext += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
4091            styletext += (this.textAlign) ? 'text-align:'+this.textAlign+';' : 'text-align:center;';
4092            styletext += (color) ? 'color:'+color+';' : '';
4093            styletext += (this.paddingBottom) ? 'padding-bottom:'+this.paddingBottom+';' : '';
4094            this._elem = $('<div class="jqplot-title" style="'+styletext+'">'+this.text+'</div>');
4095            if (this.fontFamily) {
4096                this._elem.css('font-family', this.fontFamily);
4097            }
4098        }
4099        
4100        return this._elem;
4101    };
4102    
4103    $.jqplot.DivTitleRenderer.prototype.pack = function() {
4104        // nothing to do here
4105    };
4106  
4107    // Class: $.jqplot.LineRenderer
4108    // The default line renderer for jqPlot, this class has no options beyond the <Series> class.
4109    // Draws series as a line.
4110    $.jqplot.LineRenderer = function(){
4111        this.shapeRenderer = new $.jqplot.ShapeRenderer();
4112        this.shadowRenderer = new $.jqplot.ShadowRenderer();
4113    };
4114    
4115    // called with scope of series.
4116    $.jqplot.LineRenderer.prototype.init = function(options, plot) {
4117        options = options || {};
4118        var lopts = {highlightMouseOver: options.highlightMouseOver, highlightMouseDown: options.highlightMouseDown, highlightColor: options.highlightColor};
4119        
4120        delete (options.highlightMouseOver);
4121        delete (options.highlightMouseDown);
4122        delete (options.highlightColor);
4123        
4124        $.extend(true, this.renderer, options);
4125        // set the shape renderer options
4126        var opts = {lineJoin:'round', lineCap:'round', fill:this.fill, isarc:false, strokeStyle:this.color, fillStyle:this.fillColor, lineWidth:this.lineWidth, closePath:this.fill};
4127        this.renderer.shapeRenderer.init(opts);
4128        // set the shadow renderer options
4129        // scale the shadowOffset to the width of the line.
4130        if (this.lineWidth > 2.5) {
4131            var shadow_offset = this.shadowOffset* (1 + (Math.atan((this.lineWidth/2.5))/0.785398163 - 1)*0.6);
4132            // var shadow_offset = this.shadowOffset;
4133        }
4134        // for skinny lines, don't make such a big shadow.
4135        else {
4136            var shadow_offset = this.shadowOffset*Math.atan((this.lineWidth/2.5))/0.785398163;
4137        }
4138        var sopts = {lineJoin:'round', lineCap:'round', fill:this.fill, isarc:false, angle:this.shadowAngle, offset:shadow_offset, alpha:this.shadowAlpha, depth:this.shadowDepth, lineWidth:this.lineWidth, closePath:this.fill};
4139        this.renderer.shadowRenderer.init(sopts);
4140        this._areaPoints = [];
4141        this._boundingBox = [[],[]];
4142        
4143        if (!this.isTrendline && this.fill) {
4144        
4145            // prop: highlightMouseOver
4146            // True to highlight slice when moused over.
4147            // This must be false to enable highlightMouseDown to highlight when clicking on an area on a filled plot.
4148            this.highlightMouseOver = true;
4149            // prop: highlightMouseDown
4150            // True to highlight when a mouse button is pressed over a area on a filled plot.
4151            // This will be disabled if highlightMouseOver is true.
4152            this.highlightMouseDown = false;
4153            // prop: highlightColor
4154            // color to use when highlighting an area on a filled plot.
4155            this.highlightColor = null;
4156            // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
4157            if (lopts.highlightMouseDown && lopts.highlightMouseOver == null) {
4158                lopts.highlightMouseOver = false;
4159            }
4160        
4161            $.extend(true, this, {highlightMouseOver: lopts.highlightMouseOver, highlightMouseDown: lopts.highlightMouseDown, highlightColor: lopts.highlightColor});
4162            
4163            if (!this.highlightColor) {
4164                this.highlightColor = $.jqplot.computeHighlightColors(this.fillColor);
4165            }
4166            // turn off traditional highlighter
4167            if (this.highlighter) {
4168                this.highlighter.show = false;
4169            }
4170            plot.postInitHooks.addOnce(postInit);
4171            plot.postDrawHooks.addOnce(postPlotDraw);
4172            plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
4173            plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown);
4174            plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp);
4175            plot.eventListenerHooks.addOnce('jqplotClick', handleClick);
4176            plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick);
4177        }
4178
4179    };
4180    
4181    // Method: setGridData
4182    // converts the user data values to grid coordinates and stores them
4183    // in the gridData array.
4184    // Called with scope of a series.
4185    $.jqplot.LineRenderer.prototype.setGridData = function(plot) {
4186        // recalculate the grid data
4187        var xp = this._xaxis.series_u2p;
4188        var yp = this._yaxis.series_u2p;
4189        var data = this._plotData;
4190        var pdata = this._prevPlotData;
4191        this.gridData = [];
4192        this._prevGridData = [];
4193        for (var i=0; i<this.data.length; i++) {
4194            // if not a line series or if no nulls in data, push the converted point onto the array.
4195            if (data[i][0] != null && data[i][1] != null) {
4196                this.gridData.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]);
4197            }
4198            // else if there is a null, preserve it.
4199            else if (data[i][0] == null) {
4200                this.gridData.push([null, yp.call(this._yaxis, data[i][1])]);
4201            }
4202            else if (data[i][1] == null) {
4203                this.gridData.push([xp.call(this._xaxis, data[i][0]), null]);
4204            }
4205            // if not a line series or if no nulls in data, push the converted point onto the array.
4206            if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] != null) {
4207                this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), yp.call(this._yaxis, pdata[i][1])]);
4208            }
4209            // else if there is a null, preserve it.
4210            else if (pdata[i] != null && pdata[i][0] == null) {
4211                this._prevGridData.push([null, yp.call(this._yaxis, pdata[i][1])]);
4212            }  
4213            else if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] == null) {
4214                this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), null]);
4215            }
4216        }
4217    };
4218    
4219    // Method: makeGridData
4220    // converts any arbitrary data values to grid coordinates and
4221    // returns them.  This method exists so that plugins can use a series'
4222    // linerenderer to generate grid data points without overwriting the
4223    // grid data associated with that series.
4224    // Called with scope of a series.
4225    $.jqplot.LineRenderer.prototype.makeGridData = function(data, plot) {
4226        // recalculate the grid data
4227        var xp = this._xaxis.series_u2p;
4228        var yp = this._yaxis.series_u2p;
4229        var gd = [];
4230        var pgd = [];
4231        for (var i=0; i<data.length; i++) {
4232            // if not a line series or if no nulls in data, push the converted point onto the array.
4233            if (data[i][0] != null && data[i][1] != null) {
4234                gd.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]);
4235            }
4236            // else if there is a null, preserve it.
4237            else if (data[i][0] == null) {
4238                gd.push([null, yp.call(this._yaxis, data[i][1])]);
4239            }
4240            else if (data[i][1] == null) {
4241                gd.push([xp.call(this._xaxis, data[i][0]), null]);
4242            }
4243        }
4244        return gd;
4245    };
4246    
4247
4248    // called within scope of series.
4249    $.jqplot.LineRenderer.prototype.draw = function(ctx, gd, options) {
4250        var i;
4251        var opts = (options != undefined) ? options : {};
4252        var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
4253        var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
4254        var fill = (opts.fill != undefined) ? opts.fill : this.fill;
4255        var fillAndStroke = (opts.fillAndStroke != undefined) ? opts.fillAndStroke : this.fillAndStroke;
4256        var xmin, ymin, xmax, ymax;
4257        ctx.save();
4258        if (gd.length) {
4259            if (showLine) {
4260                // if we fill, we'll have to add points to close the curve.
4261                if (fill) {
4262                    if (this.fillToZero) {
4263                        // have to break line up into shapes at axis crossings
4264                        var negativeColors = new $.jqplot.ColorGenerator(this.negativeSeriesColors);
4265                        var negativeColor = negativeColors.get(this.index);
4266                        if (! this.useNegativeColors) {
4267                            negativeColor = opts.fillStyle;
4268                        }
4269                        var isnegative = false;
4270                        var posfs = opts.fillStyle;
4271                    
4272                        // if stoking line as well as filling, get a copy of line data.
4273                        if (fillAndStroke) {
4274                            var fasgd = gd.slice(0);
4275                        }
4276                        // if not stacked, fill down to axis
4277                        if (this.index == 0 || !this._stack) {
4278                        
4279                            var tempgd = [];
4280                            this._areaPoints = [];
4281                            var pyzero = this._yaxis.series_u2p(this.fillToValue);
4282                            var pxzero = this._xaxis.series_u2p(this.fillToValue);
4283                            
4284                            if (this.fillAxis == 'y') {
4285                                tempgd.push([gd[0][0], pyzero]);
4286                                this._areaPoints.push([gd[0][0], pyzero]);
4287                                
4288                                for (var i=0; i<gd.length-1; i++) {
4289                                    tempgd.push(gd[i]);
4290                                    this._areaPoints.push(gd[i]);
4291                                    // do we have an axis crossing?
4292                                    if (this._plotData[i][1] * this._plotData[i+1][1] < 0) {
4293                                        if (this._plotData[i][1] < 0) {
4294                                            isnegative = true;
4295                                            opts.fillStyle = negativeColor;
4296                                        }
4297                                        else {
4298                                            isnegative = false;
4299                                            opts.fillStyle = posfs;
4300                                        }
4301                                        
4302                                        var xintercept = gd[i][0] + (gd[i+1][0] - gd[i][0]) * (pyzero-gd[i][1])/(gd[i+1][1] - gd[i][1]);
4303                                        tempgd.push([xintercept, pyzero]);
4304                                        this._areaPoints.push([xintercept, pyzero]);
4305                                        // now draw this shape and shadow.
4306                                        if (shadow) {
4307                                            this.renderer.shadowRenderer.draw(ctx, tempgd, opts);
4308                                        }
4309                                        this.renderer.shapeRenderer.draw(ctx, tempgd, opts);
4310                                        // now empty temp array and continue
4311                                        tempgd = [[xintercept, pyzero]];
4312                                        // this._areaPoints = [[xintercept, pyzero]];
4313                                    }   
4314                                }
4315                                if (this._plotData[gd.length-1][1] < 0) {
4316                                    isnegative = true;
4317                                    opts.fillStyle = negativeColor;
4318                                }
4319                                else {
4320                                    isnegative = false;
4321                                    opts.fillStyle = posfs;
4322                                }
4323                                tempgd.push(gd[gd.length-1]);
4324                                this._areaPoints.push(gd[gd.length-1]);
4325                                tempgd.push([gd[gd.length-1][0], pyzero]); 
4326                                this._areaPoints.push([gd[gd.length-1][0], pyzero]); 
4327                            }
4328                            // now draw this shape and shadow.
4329                            if (shadow) {
4330                                this.renderer.shadowRenderer.draw(ctx, tempgd, opts);
4331                            }
4332                            this.renderer.shapeRenderer.draw(ctx, tempgd, opts);
4333                            
4334                            
4335                            // var gridymin = this._yaxis.series_u2p(0);
4336                            // // IE doesn't return new length on unshift
4337                            // gd.unshift([gd[0][0], gridymin]);
4338                            // len = gd.length;
4339                            // gd.push([gd[len - 1][0], gridymin]);                   
4340                        }
4341                        // if stacked, fill to line below 
4342                        else {
4343                            var prev = this._prevGridData;
4344                            for (var i=prev.length; i>0; i--) {
4345                                gd.push(prev[i-1]);
4346                                // this._areaPoints.push(prev[i-1]);
4347                            }
4348                            if (shadow) {
4349                                this.renderer.shadowRenderer.draw(ctx, gd, opts);
4350                            }
4351                            this._areaPoints = gd;
4352                            this.renderer.shapeRenderer.draw(ctx, gd, opts);
4353                        }
4354                    }
4355                    /////////////////////////
4356                    // Not filled to zero
4357                    ////////////////////////
4358                    else {                    
4359                        // if stoking line as well as filling, get a copy of line data.
4360                        if (fillAndStroke) {
4361                            var fasgd = gd.slice(0);
4362                        }
4363                        // if not stacked, fill down to axis
4364                        if (this.index == 0 || !this._stack) {
4365                            // var gridymin = this._yaxis.series_u2p(this._yaxis.min) - this.gridBorderWidth / 2;
4366                            var gridymin = ctx.canvas.height;
4367                            // IE doesn't return new length on unshift
4368                            gd.unshift([gd[0][0], gridymin]);
4369                            len = gd.length;
4370                            gd.push([gd[len - 1][0], gridymin]);                   
4371                        }
4372                        // if stacked, fill to line below 
4373                        else {
4374                            var prev = this._prevGridData;
4375                            for (var i=prev.length; i>0; i--) {
4376                                gd.push(prev[i-1]);
4377                            }
4378                        }
4379                        this._areaPoints = gd;
4380                        
4381                        if (shadow) {
4382                            this.renderer.shadowRenderer.draw(ctx, gd, opts);
4383                        }
4384            
4385                        this.renderer.shapeRenderer.draw(ctx, gd, opts);                        
4386                    }
4387                    if (fillAndStroke) {
4388                        var fasopts = $.extend(true, {}, opts, {fill:false, closePath:false});
4389                        this.renderer.shapeRenderer.draw(ctx, fasgd, fasopts);
4390                        //////////
4391                        // TODO: figure out some way to do shadows nicely
4392                        // if (shadow) {
4393                        //     this.renderer.shadowRenderer.draw(ctx, fasgd, fasopts);
4394                        // }
4395                        // now draw the markers
4396                        if (this.markerRenderer.show) {
4397                            for (i=0; i<fasgd.length; i++) {
4398                                this.markerRenderer.draw(fasgd[i][0], fasgd[i][1], ctx, opts.markerOptions);
4399                            }
4400                        }
4401                    }
4402                }
4403                else {
4404                    if (shadow) {
4405                        this.renderer.shadowRenderer.draw(ctx, gd, opts);
4406                    }
4407    
4408                    this.renderer.shapeRenderer.draw(ctx, gd, opts);
4409                }
4410            }
4411            // calculate the bounding box
4412            var xmin = xmax = ymin = ymax = null;
4413            for (i=0; i<this._areaPoints.length; i++) {
4414                var p = this._areaPoints[i];
4415                if (xmin > p[0] || xmin == null) {
4416                    xmin = p[0];
4417                }
4418                if (ymax < p[1] || ymax == null) {
4419                    ymax = p[1];
4420                }
4421                if (xmax < p[0] || xmax == null) {
4422                    xmax = p[0];
4423                }
4424                if (ymin > p[1] || ymin == null) {
4425                    ymin = p[1];
4426                }
4427            }
4428            this._boundingBox = [[xmin, ymax], [xmax, ymin]];
4429        
4430            // now draw the markers
4431            if (this.markerRenderer.show && !fill) {
4432                for (i=0; i<gd.length; i++) {
4433                    if (gd[i][0] != null && gd[i][1] != null) {
4434                        this.markerRenderer.draw(gd[i][0], gd[i][1], ctx, opts.markerOptions);
4435                    }
4436                }
4437            }
4438        }
4439        
4440        ctx.restore();
4441    };  
4442    
4443    $.jqplot.LineRenderer.prototype.drawShadow = function(ctx, gd, options) {
4444        // This is a no-op, shadows drawn with lines.
4445    };
4446    
4447    // called with scope of plot.
4448    // make sure to not leave anything highlighted.
4449    function postInit(target, data, options) {
4450        for (i=0; i<this.series.length; i++) {
4451            if (this.series[i].renderer.constructor == $.jqplot.LineRenderer) {
4452                // don't allow mouseover and mousedown at same time.
4453                if (this.series[i].highlightMouseOver) {
4454                    this.series[i].highlightMouseDown = false;
4455                }
4456            }
4457        }
4458        this.target.bind('mouseout', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
4459    }  
4460    
4461    // called within context of plot
4462    // create a canvas which we can draw on.
4463    // insert it before the eventCanvas, so eventCanvas will still capture events.
4464    function postPlotDraw() {
4465        this.plugins.lineRenderer = {highlightedSeriesIndex:null};
4466        this.plugins.lineRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
4467        
4468        this.eventCanvas._elem.before(this.plugins.lineRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-lineRenderer-highlight-canvas', this._plotDimensions));
4469        var hctx = this.plugins.lineRenderer.highlightCanvas.setContext();
4470    } 
4471    
4472    function highlight (plot, sidx, pidx, points) {
4473        var s = plot.series[sidx];
4474        var canvas = plot.plugins.lineRenderer.highlightCanvas;
4475        canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height);
4476        s._highlightedPoint = pidx;
4477        plot.plugins.lineRenderer.highlightedSeriesIndex = sidx;
4478        var opts = {fillStyle: s.highlightColor};
4479        s.renderer.shapeRenderer.draw(canvas._ctx, points, opts);
4480    }
4481    
4482    function unhighlight (plot) {
4483        var canvas = plot.plugins.lineRenderer.highlightCanvas;
4484        canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
4485        for (var i=0; i<plot.series.length; i++) {
4486            plot.series[i]._highlightedPoint = null;
4487        }
4488        plot.plugins.lineRenderer.highlightedSeriesIndex = null;
4489        plot.target.trigger('jqplotDataUnhighlight');
4490    }
4491    
4492    
4493    function handleMove(ev, gridpos, datapos, neighbor, plot) {
4494        if (neighbor) {
4495            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
4496            var evt1 = jQuery.Event('jqplotDataMouseOver');
4497            evt1.pageX = ev.pageX;
4498            evt1.pageY = ev.pageY;
4499            plot.target.trigger(evt1, ins);
4500            if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.lineRenderer.highlightedSeriesIndex)) {
4501                var evt = jQuery.Event('jqplotDataHighlight');
4502                evt.pageX = ev.pageX;
4503                evt.pageY = ev.pageY;
4504                plot.target.trigger(evt, ins);
4505                highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points);
4506            }
4507        }
4508        else if (neighbor == null) {
4509            unhighlight (plot);
4510        }
4511    }
4512    
4513    function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
4514        if (neighbor) {
4515            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
4516            if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.lineRenderer.highlightedSeriesIndex)) {
4517                var evt = jQuery.Event('jqplotDataHighlight');
4518                evt.pageX = ev.pageX;
4519                evt.pageY = ev.pageY;
4520                plot.target.trigger(evt, ins);
4521                highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points);
4522            }
4523        }
4524        else if (neighbor == null) {
4525            unhighlight (plot);
4526        }
4527    }
4528    
4529    function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
4530        var idx = plot.plugins.lineRenderer.highlightedSeriesIndex;
4531        if (idx != null && plot.series[idx].highlightMouseDown) {
4532            unhighlight(plot);
4533        }
4534    }
4535    
4536    function handleClick(ev, gridpos, datapos, neighbor, plot) {
4537        if (neighbor) {
4538            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
4539            var evt = jQuery.Event('jqplotDataClick');
4540            evt.pageX = ev.pageX;
4541            evt.pageY = ev.pageY;
4542            plot.target.trigger(evt, ins);
4543        }
4544    }
4545    
4546    function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
4547        if (neighbor) {
4548            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
4549            var idx = plot.plugins.lineRenderer.highlightedSeriesIndex;
4550            if (idx != null && plot.series[idx].highlightMouseDown) {
4551                unhighlight(plot);
4552            }
4553            var evt = jQuery.Event('jqplotDataRightClick');
4554            evt.pageX = ev.pageX;
4555            evt.pageY = ev.pageY;
4556            plot.target.trigger(evt, ins);
4557        }
4558    }
4559    
4560    
4561    // class: $.jqplot.LinearAxisRenderer
4562    // The default jqPlot axis renderer, creating a numeric axis.
4563    // The renderer has no additional options beyond the <Axis> object.
4564    $.jqplot.LinearAxisRenderer = function() {
4565    };
4566    
4567    // called with scope of axis object.
4568    $.jqplot.LinearAxisRenderer.prototype.init = function(options){
4569        $.extend(true, this, options);
4570        var db = this._dataBounds;
4571        // Go through all the series attached to this axis and find
4572        // the min/max bounds for this axis.
4573        for (var i=0; i<this._series.length; i++) {
4574            var s = this._series[i];
4575            var d = s._plotData;
4576            
4577            for (var j=0; j<d.length; j++) { 
4578                if (this.name == 'xaxis' || this.name == 'x2axis') {
4579                    if ((d[j][0] != null && d[j][0] < db.min) || db.min == null) {
4580                        db.min = d[j][0];
4581                    }
4582                    if ((d[j][0] != null && d[j][0] > db.max) || db.max == null) {
4583                        db.max = d[j][0];
4584                    }
4585                }              
4586                else {
4587                    if ((d[j][1] != null && d[j][1] < db.min) || db.min == null) {
4588                        db.min = d[j][1];
4589                    }
4590                    if ((d[j][1] != null && d[j][1] > db.max) || db.max == null) {
4591                        db.max = d[j][1];
4592                    }
4593                }              
4594            }
4595        }
4596    };
4597    
4598    // called with scope of axis
4599    $.jqplot.LinearAxisRenderer.prototype.draw = function(ctx) {
4600        if (this.show) {
4601            // populate the axis label and value properties.
4602            // createTicks is a method on the renderer, but
4603            // call it within the scope of the axis.
4604            this.renderer.createTicks.call(this);
4605            // fill a div with axes labels in the right direction.
4606            // Need to pregenerate each axis to get it's bounds and
4607            // position it and the labels correctly on the plot.
4608            var dim=0;
4609            var temp;
4610            // Added for theming.
4611            if (this._elem) {
4612                this._elem.empty();
4613            }
4614            
4615            this._elem = $('<div class="jqplot-axis jqplot-'+this.name+'" style="position:absolute;"></div>');
4616            
4617            if (this.name == 'xaxis' || this.name == 'x2axis') {
4618                this._elem.width(this._plotDimensions.width);
4619            }
4620            else {
4621                this._elem.height(this._plotDimensions.height);
4622            }
4623            
4624            // create a _label object.
4625            this.labelOptions.axis = this.name;
4626            this._label = new this.labelRenderer(this.labelOptions);
4627            if (this._label.show) {
4628                var elem = this._label.draw(ctx);
4629                elem.appendTo(this._elem);
4630            }
4631    
4632            if (this.showTicks) {
4633                var t = this._ticks;
4634                for (var i=0; i<t.length; i++) {
4635                    var tick = t[i];
4636                    if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
4637                        var elem = tick.draw(ctx);
4638                        elem.appendTo(this._elem);
4639                    }
4640                }
4641            }
4642        }
4643        return this._elem;
4644    };
4645    
4646    // called with scope of an axis
4647    $.jqplot.LinearAxisRenderer.prototype.reset = function() {
4648        this.min = this._min;
4649        this.max = this._max;
4650        this.tickInterval = this._tickInterval;
4651        this.numberTicks = this._numberTicks;
4652        // this._ticks = this.__ticks;
4653    };
4654    
4655    // called with scope of axis
4656    $.jqplot.LinearAxisRenderer.prototype.set = function() { 
4657        var dim = 0;
4658        var temp;
4659        var w = 0;
4660        var h = 0;
4661        var lshow = (this._label == null) ? false : this._label.show;
4662        if (this.show && this.showTicks) {
4663            var t = this._ticks;
4664            for (var i=0; i<t.length; i++) {
4665                var tick = t[i];
4666                if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
4667                    if (this.name == 'xaxis' || this.name == 'x2axis') {
4668                        temp = tick._elem.outerHeight(true);
4669                    }
4670                    else {
4671                        temp = tick._elem.outerWidth(true);
4672                    }
4673                    if (temp > dim) {
4674                        dim = temp;
4675                    }
4676                }
4677            }
4678            
4679            if (lshow) {
4680                w = this._label._elem.outerWidth(true);
4681                h = this._label._elem.outerHeight(true); 
4682            }
4683            if (this.name == 'xaxis') {
4684                dim = dim + h;
4685                this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'});
4686            }
4687            else if (this.name == 'x2axis') {
4688                dim = dim + h;
4689                this._elem.css({'height':dim+'px', left:'0px', top:'0px'});
4690            }
4691            else if (this.name == 'yaxis') {
4692                dim = dim + w;
4693                this._elem.css({'width':dim+'px', left:'0px', top:'0px'});
4694                if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
4695                    this._label._elem.css('width', w+'px');
4696                }
4697            }
4698            else {
4699                dim = dim + w;
4700                this._elem.css({'width':dim+'px', right:'0px', top:'0px'});
4701                if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
4702                    this._label._elem.css('width', w+'px');
4703                }
4704            }
4705        }  
4706    };    
4707    
4708    // called with scope of axis
4709    $.jqplot.LinearAxisRenderer.prototype.createTicks = function() {
4710        // we're are operating on an axis here
4711        var ticks = this._ticks;
4712        var userTicks = this.ticks;
4713        var name = this.name;
4714        // databounds were set on axis initialization.
4715        var db = this._dataBounds;
4716        var dim, interval;
4717        var min, max;
4718        var pos1, pos2;
4719        var tt, i;
4720        // get a copy of user's settings for min/max.
4721        var userMin = this.min;
4722        var userMax = this.max;
4723        var userNT = this.numberTicks;
4724        var userTI = this.tickInterval;
4725        
4726        // if we already have ticks, use them.
4727        // ticks must be in order of increasing value.
4728        
4729        if (userTicks.length) {
4730            // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed
4731            for (i=0; i<userTicks.length; i++){
4732                var ut = userTicks[i];
4733                var t = new this.tickRenderer(this.tickOptions);
4734                if (ut.constructor == Array) {
4735                    t.value = ut[0];
4736                    t.label = ut[1];
4737                    if (!this.showTicks) {
4738                        t.showLabel = false;
4739                        t.showMark = false;
4740                    }
4741                    else if (!this.showTickMarks) {
4742                        t.showMark = false;
4743                    }
4744                    t.setTick(ut[0], this.name);
4745                    this._ticks.push(t);
4746                }
4747                
4748                else {
4749                    t.value = ut;
4750                    if (!this.showTicks) {
4751                        t.showLabel = false;
4752                        t.showMark = false;
4753                    }
4754                    else if (!this.showTickMarks) {
4755                        t.showMark = false;
4756                    }
4757                    t.setTick(ut, this.name);
4758                    this._ticks.push(t);
4759                }
4760            }
4761            this.numberTicks = userTicks.length;
4762            this.min = this._ticks[0].value;
4763            this.max = this._ticks[this.numberTicks-1].value;
4764            this.tickInterval = (this.max - this.min) / (this.numberTicks - 1);
4765        }
4766        
4767        // we don't have any ticks yet, let's make some!
4768        else {
4769            if (name == 'xaxis' || name == 'x2axis') {
4770                dim = this._plotDimensions.width;
4771            }
4772            else {
4773                dim = this._plotDimensions.height;
4774            }
4775            
4776            // if min, max and number of ticks specified, user can't specify interval.
4777            if (!this.autoscale && this.min != null && this.max != null && this.numberTicks != null) {
4778                this.tickInterval = null;
4779            }
4780            
4781            // if max, min, and interval specified and interval won't fit, ignore interval.
4782            // if (this.min != null && this.max != null && this.tickInterval != null) {
4783            //     if (parseInt((this.max-this.min)/this.tickInterval, 10) != (this.max-this.min)/this.tickInterval) {
4784            //         this.tickInterval = null;
4785            //     }
4786            // }
4787        
4788            min = ((this.min != null) ? this.min : db.min);
4789            max = ((this.max != null) ? this.max : db.max);
4790            
4791            // if min and max are same, space them out a bit
4792            if (min == max) {
4793                var adj = 0.05;
4794                if (min > 0) {
4795                    adj = Math.max(Math.log(min)/Math.LN10, 0.05);
4796                }
4797                min -= adj;
4798                max += adj;
4799            }
4800
4801            var range = max - min;
4802            var rmin, rmax;
4803            var temp;
4804            
4805            // autoscale.  Can't autoscale if min or max is supplied.
4806            // Will use numberTicks and tickInterval if supplied.  Ticks
4807            // across multiple axes may not line up depending on how
4808            // bars are to be plotted.
4809            if (this.autoscale && this.min == null && this.max == null) {
4810                var rrange, ti, margin;
4811                var forceMinZero = false;
4812                var forceZeroLine = false;
4813                var intervals = {min:null, max:null, average:null, stddev:null};
4814                // if any series are bars, or if any are fill to zero, and if this
4815                // is the axis to fill toward, check to see if we can start axis at zero.
4816                for (var i=0; i<this._series.length; i++) {
4817                    var s = this._series[i];
4818                    var faname = (s.fillAxis == 'x') ? s._xaxis.name : s._yaxis.name;
4819                    // check to see if this is the fill axis
4820                    if (this.name == faname) {
4821                        var vals = s._plotValues[s.fillAxis];
4822                        var vmin = vals[0];
4823                        var vmax = vals[0];
4824                        for (var j=1; j<vals.length; j++) {
4825                            if (vals[j] < vmin) {
4826                                vmin = vals[j];
4827                            }
4828                            else if (vals[j] > vmax) {
4829                                vmax = vals[j];
4830                            }
4831                        }
4832                        var dp = (vmax - vmin) / vmax;
4833                        // is this sries a bar?
4834                        if (s.renderer.constructor == $.jqplot.BarRenderer) {
4835                            // if no negative values and could also check range.
4836                            if (vmin >= 0 && (s.fillToZero || dp > 0.1)) {
4837                                forceMinZero = true;
4838                            }
4839                            else {
4840                                forceMinZero = false;
4841                                if (s.fill && s.fillToZero && vmin < 0 && vmax > 0) {
4842                                    forceZeroLine = true;
4843                                }
4844                                else {
4845                                    forceZeroLine = false;
4846                                }
4847                            }
4848                        }
4849                        
4850                        // if not a bar and filling, use appropriate method.
4851                        else if (s.fill) {
4852                            if (vmin >= 0 && (s.fillToZero || dp > 0.1)) {
4853                                forceMinZero = true;
4854                            }
4855                            else if (vmin < 0 && vmax > 0 && s.fillToZero) {
4856                                forceMinZero = false;
4857                                forceZeroLine = true;
4858                            }
4859                            else {
4860                                forceMinZero = false;
4861                                forceZeroLine = false;
4862                            }
4863                        }
4864                        
4865                        // if not a bar and not filling, only change existing state
4866                        // if it doesn't make sense
4867                        else if (vmin < 0) {
4868                            forceMinZero = false;
4869                        }
4870                    }
4871                }
4872                
4873                // check if we need make axis min at 0.
4874                if (forceMinZero) {
4875                    // compute number of ticks
4876                    this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
4877                    this.min = 0;
4878                    userMin = 0;
4879                    // what order is this range?
4880                    // what tick interval does that give us?
4881                    ti = max/(this.numberTicks-1);
4882                    temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10)));
4883                    if (ti/temp == parseInt(ti/temp, 10)) {
4884                        ti += temp;
4885                    }
4886                    this.tickInterval = Math.ceil(ti/temp) * temp;
4887                    this.max = this.tickInterval * (this.numberTicks - 1);
4888                }
4889                
4890                // check if we need to make sure there is a tick at 0.
4891                else if (forceZeroLine) {
4892                    // compute number of ticks
4893                    this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
4894                    var ntmin = Math.ceil(Math.abs(min)/range*(this.numberTicks-1));
4895                    var ntmax = this.numberTicks - 1  - ntmin;
4896                    ti = Math.max(Math.abs(min/ntmin), Math.abs(max/ntmax));
4897                    temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10)));
4898                    this.tickInterval = Math.ceil(ti/temp) * temp;
4899                    this.max = this.tickInterval * ntmax;
4900                    this.min = -this.tickInterval * ntmin;                  
4901                }
4902                
4903                // if nothing else, do autoscaling which will try to line up ticks across axes.
4904                else {  
4905                    if (this.numberTicks == null){
4906                        if (this.tickInterval) {
4907                            this.numberTicks = 3 + Math.ceil(range / this.tickInterval);
4908                        }
4909                        else {
4910                            this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
4911                        }
4912                    }
4913            
4914                    if (this.tickInterval == null) {
4915                        // get a tick interval
4916                        ti = range/(this.numberTicks - 1);
4917
4918                        if (ti < 1) {
4919                            temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10)));
4920                        }
4921                        else {
4922                            temp = 1;
4923                        }
4924                        this.tickInterval = Math.ceil(ti*temp*this.pad)/temp;
4925                    }
4926                    else {
4927                        temp = 1 / this.tickInterval;
4928                    }
4929                    
4930                    // try to compute a nicer, more even tick interval
4931                    // temp = Math.pow(10, Math.floor(Math.log(ti)/Math.LN10));
4932                    // this.tickInterval = Math.ceil(ti/temp) * temp;
4933                    rrange = this.tickInterval * (this.numberTicks - 1);
4934                    margin = (rrange - range)/2;
4935       
4936                    if (this.min == null) {
4937                        this.min = Math.floor(temp*(min-margin))/temp;
4938                    }
4939                    if (this.max == null) {
4940                        this.max = this.min + rrange;
4941                    }
4942                }
4943            }
4944            
4945            // Use the default algorithm which pads each axis to make the chart
4946            // centered nicely on the grid.
4947            else {
4948                rmin = (this.min != null) ? this.min : min - range*(this.padMin - 1);
4949                rmax = (this.max != null) ? this.max : max + range*(this.padMax - 1);
4950                this.min = rmin;
4951                this.max = rmax;
4952                range = this.max - this.min;
4953    
4954                if (this.numberTicks == null){
4955                    // if tickInterval is specified by user, we will ignore computed maximum.
4956                    // max will be equal or greater to fit even # of ticks.
4957                    if (this.tickInterval != null) {
4958                        this.numberTicks = Math.ceil((this.max - this.min)/this.tickInterval)+1;
4959                        this.max = this.min + this.tickInterval*(this.numberTicks-1);
4960                    }
4961                    else if (dim > 100) {
4962                        this.numberTicks = parseInt(3+(dim-100)/75, 10);
4963                    }
4964                    else {
4965                        this.numberTicks = 2;
4966                    }
4967                }
4968            
4969                if (this.tickInterval == null) {
4970                    this.tickInterval = range / (this.numberTicks-1);
4971                }
4972            }
4973            
4974            if (this.renderer.constructor == $.jqplot.LinearAxisRenderer) {
4975                // fix for misleading tick display with small range and low precision.
4976                range = this.max - this.min;
4977                // figure out precision
4978                var temptick = new this.tickRenderer(this.tickOptions);
4979                // use the tick formatString or, the default.
4980                var fs = temptick.formatString || $.jqplot.config.defaultTickFormatString; 
4981                var fs = fs.match($.jqplot.sprintf.regex)[0];
4982                var precision = 0;
4983                if (fs) {
4984                    if (fs.search(/[fFeEgGpP]/) > -1) {
4985                        var m = fs.match(/\%\.(\d{0,})?[eEfFgGpP]/);
4986                        if (m) precision = parseInt(m[1], 10);
4987                        else precision = 6;
4988                    }
4989                    else if (fs.search(/[di]/) > -1) {
4990                        precision = 0;
4991                    }
4992                    // fact will be <= 1;
4993                    var fact = Math.pow(10, -precision);
4994                    if (this.tickInterval < fact) {
4995                        // need to correct underrange
4996                        if (userNT == null && userTI == null) {
4997                            this.tickInterval = fact;
4998                            if (userMax == null && userMin == null) {
4999                                // this.min = Math.floor((this._dataBounds.min - this.tickInterval)/fact) * fact;
5000                                this.min = Math.floor(this._dataBounds.min/fact) * fact;
5001                                if (this.min == this._dataBounds.min) {
5002                                    this.min = this._dataBounds.min - this.tickInterval;
5003                                }
5004                                // this.max = Math.ceil((this._dataBounds.max + this.tickInterval)/fact) * fact;
5005                                this.max = Math.ceil(this._dataBounds.max/fact) * fact;
5006                                if (this.max == this._dataBounds.max) {
5007                                    this.max = this._dataBounds.max + this.tickInterval;
5008                                }
5009                                var n = (this.max - this.min)/this.tickInterval;
5010                                n = n.toFixed(11);
5011                                n = Math.ceil(n);
5012                                this.numberTicks = n + 1;
5013                            }
5014                            else if (userMax == null) {
5015                                // add one tick for top of range.
5016                                var n = (this._dataBounds.max - this.min) / this.tickInterval;
5017                                n = n.toFixed(11);
5018                                this.numberTicks = Math.ceil(n) + 2;
5019                                this.max = this.min + this.tickInterval * (this.numberTicks-1);
5020                            }
5021                            else if (userMin == null) {
5022                                // add one tick for bottom of range.
5023                                var n = (this.max - this._dataBounds.min) / this.tickInterval;
5024                                n = n.toFixed(11);
5025                                this.numberTicks = Math.ceil(n) + 2;
5026                                this.min = this.max - this.tickInterval * (this.numberTicks-1);
5027                            }
5028                            else {
5029                                // calculate a number of ticks so max is within axis scale
5030                                this.numberTicks = Math.ceil((userMax - userMin)/this.tickInterval) + 1;
5031                                // if user's min and max don't fit evenly in ticks, adju
5031st.
5032                                // This takes care of cases such as user min set to 0, max set to 3.5 but tick
5033                                // format string set to %d (integer ticks)
5034                                this.min =  Math.floor(userMin*Math.pow(10, precision))/Math.pow(10, precision);
5035                                this.max =  Math.ceil(userMax*Math.pow(10, precision))/Math.pow(10, precision);
5036                                // this.max = this.min + this.tickInterval*(this.numberTicks-1);
5037                                this.numberTicks = Math.ceil((this.max - this.min)/this.tickInterval) + 1;
5038                            }
5039                        }
5040                    }
5041                }
5042            }
5043            
5044            
5045
5046            for (var i=0; i<this.numberTicks; i++){
5047                tt = this.min + i * this.tickInterval;
5048                var t = new this.tickRenderer(this.tickOptions);
5049                // var t = new $.jqplot.AxisTickRenderer(this.tickOptions);
5050                if (!this.showTicks) {
5051                    t.showLabel = false;
5052                    t.showMark = false;
5053                }
5054                else if (!this.showTickMarks) {
5055                    t.showMark = false;
5056                }
5057                t.setTick(tt, this.name);
5058                this._ticks.push(t);
5059            }
5060        }
5061    };
5062    
5063    // called with scope of axis
5064    $.jqplot.LinearAxisRenderer.prototype.pack = function(pos, offsets) {
5065        var ticks = this._ticks;
5066        var max = this.max;
5067        var min = this.min;
5068        var offmax = offsets.max;
5069        var offmin = offsets.min;
5070        var lshow = (this._label == null) ? false : this._label.show;
5071        
5072        for (var p in pos) {
5073            this._elem.css(p, pos[p]);
5074        }
5075        
5076        this._offsets = offsets;
5077        // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left.
5078        var pixellength = offmax - offmin;
5079        var unitlength = max - min;
5080        
5081        // point to unit and unit to point conversions references to Plot DOM element top left corner.
5082        this.p2u = function(p){
5083            return (p - offmin) * unitlength / pixellength + min;
5084        };
5085        
5086        this.u2p = function(u){
5087            return (u - min) * pixellength / unitlength + offmin;
5088        };
5089                
5090        if (this.name == 'xaxis' || this.name == 'x2axis'){
5091            this.series_u2p = function(u){
5092                return (u - min) * pixellength / unitlength;
5093            };
5094            this.series_p2u = function(p){
5095                return p * unitlength / pixellength + min;
5096            };
5097        }
5098        
5099        else {
5100            this.series_u2p = function(u){
5101                return (u - max) * pixellength / unitlength;
5102            };
5103            this.series_p2u = function(p){
5104                return p * unitlength / pixellength + max;
5105            };
5106        }
5107        
5108        if (this.show) {
5109            if (this.name == 'xaxis' || this.name == 'x2axis') {
5110                for (i=0; i<ticks.length; i++) {
5111                    var t = ticks[i];
5112                    if (t.show && t.showLabel) {
5113                        var shim;
5114                        
5115                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
5116                            // will need to adjust auto positioning based on which axis this is.
5117                            var temp = (this.name == 'xaxis') ? 1 : -1;
5118                            switch (t.labelPosition) {
5119                                case 'auto':
5120                                    // position at end
5121                                    if (temp * t.angle < 0) {
5122                                        shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
5123                                    }
5124                                    // position at start
5125                                    else {
5126                                        shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
5127                                    }
5128                                    break;
5129                                case 'end':
5130                                    shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
5131                                    break;
5132                                case 'start':
5133                                    shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
5134                                    break;
5135                                case 'middle':
5136                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
5137                                    break;
5138                                default:
5139                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
5140                                    break;
5141                            }
5142                        }
5143                        else {
5144                            shim = -t.getWidth()/2;
5145                        }
5146                        var val = this.u2p(t.value) + shim + 'px';
5147                        t._elem.css('left', val);
5148                        t.pack();
5149                    }
5150                }
5151                if (lshow) {
5152                    var w = this._label._elem.outerWidth(true);
5153                    this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px');
5154                    if (this.name == 'xaxis') {
5155                        this._label._elem.css('bottom', '0px');
5156                    }
5157                    else {
5158                        this._label._elem.css('top', '0px');
5159                    }
5160                    this._label.pack();
5161                }
5162            }
5163            else {
5164                for (i=0; i<ticks.length; i++) {
5165                    var t = ticks[i];
5166                    if (t.show && t.showLabel) {                        
5167                        var shim;
5168                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
5169                            // will need to adjust auto positioning based on which axis this is.
5170                            var temp = (this.name == 'yaxis') ? 1 : -1;
5171                            switch (t.labelPosition) {
5172                                case 'auto':
5173                                    // position at end
5174                                case 'end':
5175                                    if (temp * t.angle < 0) {
5176                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
5177                                    }
5178                                    else {
5179                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
5180                                    }
5181                                    break;
5182                                case 'start':
5183                                    if (t.angle > 0) {
5184                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
5185                                    }
5186                                    else {
5187                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
5188                                    }
5189                                    break;
5190                                case 'middle':
5191                                    // if (t.angle > 0) {
5192                                    //     shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
5193                                    // }
5194                                    // else {
5195                                    //     shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
5196                                    // }
5197                                    shim = -t.getHeight()/2;
5198                                    break;
5199                                default:
5200                                    shim = -t.getHeight()/2;
5201                                    break;
5202                            }
5203                        }
5204                        else {
5205                            shim = -t.getHeight()/2;
5206                        }
5207                        
5208                        var val = this.u2p(t.value) + shim + 'px';
5209                        t._elem.css('top', val);
5210                        t.pack();
5211                    }
5212                }
5213                if (lshow) {
5214                    var h = this._label._elem.outerHeight(true);
5215                    this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px');
5216                    if (this.name == 'yaxis') {
5217                        this._label._elem.css('left', '0px');
5218                    }
5219                    else {
5220                        this._label._elem.css('right', '0px');
5221                    }   
5222                    this._label.pack();
5223                }
5224            }
5225        }
5226    };
5227
5228
5229    // class: $.jqplot.MarkerRenderer
5230    // The default jqPlot marker renderer, rendering the points on the line.
5231    $.jqplot.MarkerRenderer = function(options){
5232        // Group: Properties
5233        
5234        // prop: show
5235        // wether or not to show the marker.
5236        this.show = true;
5237        // prop: style
5238        // One of diamond, circle, square, x, plus, dash, filledDiamond, filledCircle, filledSquare
5239        this.style = 'filledCircle';
5240        // prop: lineWidth
5241        // size of the line for non-filled markers.
5242        this.lineWidth = 2;
5243        // prop: size
5244        // Size of the marker (diameter or circle, length of edge of square, etc.)
5245        this.size = 9.0;
5246        // prop: color
5247        // color of marker.  Will be set to color of series by default on init.
5248        this.color = '#666666';
5249        // prop: shadow
5250        // wether or not to draw a shadow on the line
5251        this.shadow = true;
5252        // prop: shadowAngle
5253        // Shadow angle in degrees
5254        this.shadowAngle = 45;
5255        // prop: shadowOffset
5256        // Shadow offset from line in pixels
5257        this.shadowOffset = 1;
5258        // prop: shadowDepth
5259        // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
5260        this.shadowDepth = 3;
5261        // prop: shadowAlpha
5262        // Alpha channel transparency of shadow.  0 = transparent.
5263        this.shadowAlpha = '0.07';
5264        // prop: shadowRenderer
5265        // Renderer that will draws the shadows on the marker.
5266        this.shadowRenderer = new $.jqplot.ShadowRenderer();
5267        // prop: shapeRenderer
5268        // Renderer that will draw the marker.
5269        this.shapeRenderer = new $.jqplot.ShapeRenderer();
5270        
5271        $.extend(true, this, options);
5272    };
5273    
5274    $.jqplot.MarkerRenderer.prototype.init = function(options) {
5275        $.extend(true, this, options);
5276        var sdopt = {angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, lineWidth:this.lineWidth, depth:this.shadowDepth, closePath:true};
5277        if (this.style.indexOf('filled') != -1) {
5278            sdopt.fill = true;
5279        }
5280        if (this.style.indexOf('ircle') != -1) {
5281            sdopt.isarc = true;
5282            sdopt.closePath = false;
5283        }
5284        this.shadowRenderer.init(sdopt);
5285        
5286        var shopt = {fill:false, isarc:false, strokeStyle:this.color, fillStyle:this.color, lineWidth:this.lineWidth, closePath:true};
5287        if (this.style.indexOf('filled') != -1) {
5288            shopt.fill = true;
5289        }
5290        if (this.style.indexOf('ircle') != -1) {
5291            shopt.isarc = true;
5292            shopt.closePath = false;
5293        }
5294        this.shapeRenderer.init(shopt);
5295    };
5296    
5297    $.jqplot.MarkerRenderer.prototype.drawDiamond = function(x, y, ctx, fill, options) {
5298        var stretch = 1.2;
5299        var dx = this.size/2/stretch;
5300        var dy = this.size/2*stretch;
5301        var points = [[x-dx, y], [x, y+dy], [x+dx, y], [x, y-dy]];
5302        if (this.shadow) {
5303            this.shadowRenderer.draw(ctx, points);
5304        }
5305        this.shapeRenderer.draw(ctx, points, options);
5306
5307        // ctx.restore();
5308    };
5309    
5310    $.jqplot.MarkerRenderer.prototype.drawPlus = function(x, y, ctx, fill, options) {
5311        var stretch = 1.0;
5312        var dx = this.size/2*stretch;
5313        var dy = this.size/2*stretch;
5314        var points1 = [[x, y-dy], [x, y+dy]];
5315        var points2 = [[x+dx, y], [x-dx, y]];
5316        var opts = $.extend(true, {}, this.options, {closePath:false});
5317        if (this.shadow) {
5318            this.shadowRenderer.draw(ctx, points1, {closePath:false});
5319            this.shadowRenderer.draw(ctx, points2, {closePath:false});
5320        }
5321        this.shapeRenderer.draw(ctx, points1, opts);
5322        this.shapeRenderer.draw(ctx, points2, opts);
5323
5324        // ctx.restore();
5325    };
5326    
5327    $.jqplot.MarkerRenderer.prototype.drawX = function(x, y, ctx, fill, options) {
5328        var stretch = 1.0;
5329        var dx = this.size/2*stretch;
5330        var dy = this.size/2*stretch;
5331        var opts = $.extend(true, {}, this.options, {closePath:false});
5332        var points1 = [[x-dx, y-dy], [x+dx, y+dy]];
5333        var points2 = [[x-dx, y+dy], [x+dx, y-dy]];
5334        if (this.shadow) {
5335            this.shadowRenderer.draw(ctx, points1, {closePath:false});
5336            this.shadowRenderer.draw(ctx, points2, {closePath:false});
5337        }
5338        this.shapeRenderer.draw(ctx, points1, opts);
5339        this.shapeRenderer.draw(ctx, points2, opts);
5340
5341        // ctx.restore();
5342    };
5343    
5344    $.jqplot.MarkerRenderer.prototype.drawDash = function(x, y, ctx, fill, options) {
5345        var stretch = 1.0;
5346        var dx = this.size/2*stretch;
5347        var dy = this.size/2*stretch;
5348        var points = [[x-dx, y], [x+dx, y]];
5349        if (this.shadow) {
5350            this.shadowRenderer.draw(ctx, points);
5351        }
5352        this.shapeRenderer.draw(ctx, points, options);
5353
5354        // ctx.restore();
5355    };
5356    
5357    $.jqplot.MarkerRenderer.prototype.drawSquare = function(x, y, ctx, fill, options) {
5358        var stretch = 1.0;
5359        var dx = this.size/2/stretch;
5360        var dy = this.size/2*stretch;
5361        var points = [[x-dx, y-dy], [x-dx, y+dy], [x+dx, y+dy], [x+dx, y-dy]];
5362        if (this.shadow) {
5363            this.shadowRenderer.draw(ctx, points);
5364        }
5365        this.shapeRenderer.draw(ctx, points, options);
5366
5367        // ctx.restore();
5368    };
5369    
5370    $.jqplot.MarkerRenderer.prototype.drawCircle = function(x, y, ctx, fill, options) {
5371        var radius = this.size/2;
5372        var end = 2*Math.PI;
5373        var points = [x, y, radius, 0, end, true];
5374        if (this.shadow) {
5375            this.shadowRenderer.draw(ctx, points);
5376        }
5377        this.shapeRenderer.draw(ctx, points, options);
5378        
5379        // ctx.restore();
5380    };
5381    
5382    $.jqplot.MarkerRenderer.prototype.draw = function(x, y, ctx, options) {
5383        options = options || {};
5384        // hack here b/c shape renderer uses canvas based color style options
5385        // and marker uses css style names.
5386        if (options.show == null || options.show != false) {
5387            if (options.color && !options.fillStyle) {
5388                options.fillStyle = options.color;
5389            }
5390            if (options.color && !options.strokeStyle) {
5391                options.strokeStyle = options.color;
5392            }
5393            switch (this.style) {
5394                case 'diamond':
5395                    this.drawDiamond(x,y,ctx, false, options);
5396                    break;
5397                case 'filledDiamond':
5398                    this.drawDiamond(x,y,ctx, true, options);
5399                    break;
5400                case 'circle':
5401                    this.drawCircle(x,y,ctx, false, options);
5402                    break;
5403                case 'filledCircle':
5404                    this.drawCircle(x,y,ctx, true, options);
5405                    break;
5406                case 'square':
5407                    this.drawSquare(x,y,ctx, false, options);
5408                    break;
5409                case 'filledSquare':
5410                    this.drawSquare(x,y,ctx, true, options);
5411                    break;
5412                case 'x':
5413                    this.drawX(x,y,ctx, true, options);
5414                    break;
5415                case 'plus':
5416                    this.drawPlus(x,y,ctx, true, options);
5417                    break;
5418                case 'dash':
5419                    this.drawDash(x,y,ctx, true, options);
5420                    break;
5421                default:
5422                    this.drawDiamond(x,y,ctx, false, options);
5423                    break;
5424            }
5425        }
5426    };
5427    
5428    // class: $.jqplot.shadowRenderer
5429    // The default jqPlot shadow renderer, rendering shadows behind shapes.
5430    $.jqplot.ShadowRenderer = function(options){ 
5431        // Group: Properties
5432        
5433        // prop: angle
5434        // Angle of the shadow in degrees.  Measured counter-clockwise from the x axis.
5435        this.angle = 45;
5436        // prop: offset
5437        // Pixel offset at the given shadow angle of each shadow stroke from the last stroke.
5438        this.offset = 1;
5439        // prop: alpha
5440        // alpha transparency of shadow stroke.
5441        this.alpha = 0.07;
5442        // prop: lineWidth
5443        // width of the shadow line stroke.
5444        this.lineWidth = 1.5;
5445        // prop: lineJoin
5446        // How line segments of the shadow are joined.
5447        this.lineJoin = 'miter';
5448        // prop: lineCap
5449        // how ends of the shadow line are rendered.
5450        this.lineCap = 'round';
5451        // prop; closePath
5452        // whether line path segment is closed upon itself.
5453        this.closePath = false;
5454        // prop: fill
5455        // whether to fill the shape.
5456        this.fill = false;
5457        // prop: depth
5458        // how many times the shadow is stroked.  Each stroke will be offset by offset at angle degrees.
5459        this.depth = 3;
5460        this.strokeStyle = 'rgba(0,0,0,0.1)';
5461        // prop: isarc
5462        // wether the shadow is an arc or not.
5463        this.isarc = false;
5464        
5465        $.extend(true, this, options);
5466    };
5467    
5468    $.jqplot.ShadowRenderer.prototype.init = function(options) {
5469        $.extend(true, this, options);
5470    };
5471    
5472    // function: draw
5473    // draws an transparent black (i.e. gray) shadow.
5474    //
5475    // ctx - canvas drawing context
5476    // points - array of points or [x, y, radius, start angle (rad), end angle (rad)]
5477    $.jqplot.ShadowRenderer.prototype.draw = function(ctx, points, options) {
5478        ctx.save();
5479        var opts = (options != null) ? options : {};
5480        var fill = (opts.fill != null) ? opts.fill : this.fill;
5481        var closePath = (opts.closePath != null) ? opts.closePath 
5481: this.closePath;
5482        var offset = (opts.offset != null) ? opts.offset : this.offset;
5483        var alpha = (opts.alpha != null) ? opts.alpha : this.alpha;
5484        var depth = (opts.depth != null) ? opts.depth : this.depth;
5485        var isarc = (opts.isarc != null) ? opts.isarc : this.isarc;
5486        ctx.lineWidth = (opts.lineWidth != null) ? opts.lineWidth : this.lineWidth;
5487        ctx.lineJoin = (opts.lineJoin != null) ? opts.lineJoin : this.lineJoin;
5488        ctx.lineCap = (opts.lineCap != null) ? opts.lineCap : this.lineCap;
5489        ctx.strokeStyle = opts.strokeStyle || this.strokeStyle || 'rgba(0,0,0,'+alpha+')';
5490        ctx.fillStyle = opts.fillStyle || this.fillStyle || 'rgba(0,0,0,'+alpha+')';
5491        for (var j=0; j<depth; j++) {
5492            ctx.translate(Math.cos(this.angle*Math.PI/180)*offset, Math.sin(this.angle*Math.PI/180)*offset);
5493            ctx.beginPath();
5494            if (isarc) {
5495                ctx.arc(points[0], points[1], points[2], points[3], points[4], true);                
5496            }
5497            else {
5498                ctx.moveTo(points[0][0], points[0][1]);
5499                for (var i=1; i<points.length; i++) {
5500                    if (points[i][0] != null && points[i][1] != null) {
5501                        ctx.lineTo(points[i][0], points[i][1]);                        
5502                    }
5503                    // if null value, skip ahead a point and move to it
5504                    else {
5505                        i++;
5506                        if (i < points.length) {
5507                            ctx.moveTo(points[i][0], points[i][1]);
5508                        }
5509                    }
5510                }
5511                
5512            }
5513            if (closePath) {
5514                ctx.closePath();
5515            }
5516            if (fill) {
5517                ctx.fill();
5518            }
5519            else {
5520                ctx.stroke();
5521            }
5522        }
5523        ctx.restore();
5524    };
5525    
5526    // class: $.jqplot.shapeRenderer
5527    // The default jqPlot shape renderer.  Given a set of points will
5528    // plot them and either stroke a line (fill = false) or fill them (fill = true).
5529    // If a filled shape is desired, closePath = true must also be set to close
5530    // the shape.
5531    $.jqplot.ShapeRenderer = function(options){
5532        
5533        this.lineWidth = 1.5;
5534        // prop: lineJoin
5535        // How line segments of the shadow are joined.
5536        this.lineJoin = 'miter';
5537        // prop: lineCap
5538        // how ends of the shadow line are rendered.
5539        this.lineCap = 'round';
5540        // prop; closePath
5541        // whether line path segment is closed upon itself.
5542        this.closePath = false;
5543        // prop: fill
5544        // whether to fill the shape.
5545        this.fill = false;
5546        // prop: isarc
5547        // wether the shadow is an arc or not.
5548        this.isarc = false;
5549        // prop: fillRect
5550        // true to draw shape as a filled rectangle.
5551        this.fillRect = false;
5552        // prop: strokeRect
5553        // true to draw shape as a stroked rectangle.
5554        this.strokeRect = false;
5555        // prop: clearRect
5556        // true to cear a rectangle.
5557        this.clearRect = false;
5558        // prop: strokeStyle
5559        // css color spec for the stoke style
5560        this.strokeStyle = '#999999';
5561        // prop: fillStyle
5562        // css color spec for the fill style.
5563        this.fillStyle = '#999999'; 
5564        
5565        $.extend(true, this, options);
5566    };
5567    
5568    $.jqplot.ShapeRenderer.prototype.init = function(options) {
5569        $.extend(true, this, options);
5570    };
5571    
5572    // function: draw
5573    // draws the shape.
5574    //
5575    // ctx - canvas drawing context
5576    // points - array of points for shapes or 
5577    // [x, y, width, height] for rectangles or
5578    // [x, y, radius, start angle (rad), end angle (rad)] for circles and arcs.
5579    $.jqplot.ShapeRenderer.prototype.draw = function(ctx, points, options) {
5580        ctx.save();
5581        var opts = (options != null) ? options : {};
5582        var fill = (opts.fill != null) ? opts.fill : this.fill;
5583        var closePath = (opts.closePath != null) ? opts.closePath 
5583: this.closePath;
5584        var fillRect = (opts.fillRect != null) ? opts.fillRect : this.fillRect;
5585        var strokeRect = (opts.strokeRect != null) ? opts.strokeRect : this.strokeRect;
5586        var clearRect = (opts.clearRect != null) ? opts.clearRect : this.clearRect;
5587        var isarc = (opts.isarc != null) ? opts.isarc : this.isarc;
5588        ctx.lineWidth = opts.lineWidth || this.lineWidth;
5589        ctx.lineJoin = opts.lineJoing || this.lineJoin;
5590        ctx.lineCap = opts.lineCap || this.lineCap;
5591        ctx.strokeStyle = (opts.strokeStyle || opts.color) || this.strokeStyle;
5592        ctx.fillStyle = opts.fillStyle || this.fillStyle;
5593        ctx.beginPath();
5594        if (isarc) {
5595            ctx.arc(points[0], points[1], points[2], points[3], points[4], true);   
5596            if (closePath) {
5597                ctx.closePath();
5598            }
5599            if (fill) {
5600                ctx.fill();
5601            }
5602            else {
5603                ctx.stroke();
5604            }
5605            ctx.restore();
5606            return;
5607        }
5608        else if (clearRect) {
5609            ctx.clearRect(points[0], points[1], points[2], points[3]);
5610            ctx.restore();
5611            return;
5612        }
5613        else if (fillRect || strokeRect) {
5614            if (fillRect) {
5615                ctx.fillRect(points[0], points[1], points[2], points[3]);
5616            }
5617            if (strokeRect) {
5618                ctx.strokeRect(points[0], points[1], points[2], points[3]);
5619                ctx.restore();
5620                return;
5621            }
5622        }
5623        else {
5624            ctx.moveTo(points[0][0], points[0][1]);
5625            for (var i=1; i<points.length; i++) {
5626                if (points[i][0] != null && points[i][1] != null) {
5627                    ctx.lineTo(points[i][0], points[i][1]);                        
5628                }
5629                // if null value, skip ahead a point and move to it
5630                else {
5631                    i++;
5632                    if (i < points.length) {
5633                        ctx.moveTo(points[i][0], points[i][1]);
5634                    }
5635                }
5636            }
5637            if (closePath) {
5638                ctx.closePath();
5639            }
5640            if (fill) {
5641                ctx.fill();
5642            }
5643            else {
5644                ctx.stroke();
5645            }
5646        }
5647        ctx.restore();
5648    };
5649    
5650    // class $.jqplot.TableLegendRenderer
5651    // The default legend renderer for jqPlot.
5652    $.jqplot.TableLegendRenderer = function(){
5653        //
5654    };
5655    
5656    $.jqplot.TableLegendRenderer.prototype.init = function(options) {
5657        $.extend(true, this, options);
5658    };
5659        
5660    $.jqplot.TableLegendRenderer.prototype.addrow = function (label, color, pad, reverse) {
5661        var rs = (pad) ? this.rowSpacing : '0';
5662        if (reverse){
5663            var tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem);
5664        }
5665        else{
5666            var tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem);
5667        }
5668        if (this.showSwatches) {
5669            $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+
5670            '<div><div class="jqplot-table-legend-swatch" style="background-color:'+color+';border-color:'+color+';"></div>'+
5671            '</div></td>').appendTo(tr);
5672        }
5673        if (this.showLabels) {
5674            var elem = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
5675            elem.appendTo(tr);
5676            if (this.escapeHtml) {
5677                elem.text(label);
5678            }
5679            else {
5680                elem.html(label);
5681            }
5682        }
5683    };
5684    
5685    // called with scope of legend
5686    $.jqplot.TableLegendRenderer.prototype.draw = function() {
5687        var legend = this;
5688        if (this.show) {
5689            var series = this._series;
5690            // make a table.  one line label per row.
5691            var ss = 'position:absolute;';
5692            ss += (this.background) ? 'background:'+this.background+';' : '';
5693            ss += (this.border) ? 'border:'+this.border+';' : '';
5694            ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
5695            ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : '';
5696            ss += (this.textColor) ? 'color:'+this.textColor+';' : '';
5697            ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : '';
5698            ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : '';
5699            ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : '';
5700            ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : '';
5701            this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>');
5702        
5703            var pad = false, 
5704                reverse = false;
5705            for (var i = 0; i< series.length; i++) {
5706                s = series[i];
5707                if (s._stack || s.renderer.constructor == $.jqplot.BezierCurveRenderer){
5708                    reverse = true;
5709                }
5710                if (s.show && s.showLabel) {
5711                    var lt = this.labels[i] || s.label.toString();
5712                    if (lt) {
5713                        var color = s.color;
5714                        if (reverse && i < series.length - 1){
5715                            pad = true;
5716                        }
5717                        else if (reverse && i == series.length - 1){
5718                            pad = false;
5719                        }
5720                        this.renderer.addrow.call(this, lt, color, pad, reverse);
5721                        pad = true;
5722                    }
5723                    // let plugins add more rows to legend.  Used by trend line plugin.
5724                    for (var j=0; j<$.jqplot.addLegendRowHooks.length; j++) {
5725                        var item = $.jqplot.addLegendRowHooks[j].call(this, s);
5726                        if (item) {
5727                            this.renderer.addrow.call(this, item.label, item.color, pad);
5728                            pad = true;
5729                        } 
5730                    }
5731                }
5732            }
5733        }
5734        return this._elem;
5735    };
5736    
5737    $.jqplot.TableLegendRenderer.prototype.pack = function(offsets) {
5738        if (this.show) {       
5739            if (this.placement == 'insideGrid') {
5740                switch (this.location) {
5741                    case 'nw':
5742                        var a = offsets.left;
5743                        var b = offsets.top;
5744                        this._elem.css('left', a);
5745                        this._elem.css('top', b);
5746                        break;
5747                    case 'n':
5748                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
5749                        var b = offsets.top;
5750                        this._elem.css('left', a);
5751                        this._elem.css('top', b);
5752                        break;
5753                    case 'ne':
5754                        var a = offsets.right;
5755                        var b = offsets.top;
5756                        this._elem.css({right:a, top:b});
5757                        break;
5758                    case 'e':
5759                        var a = offsets.right;
5760                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
5761                        this._elem.css({right:a, top:b});
5762                        break;
5763                    case 'se':
5764                        var a = offsets.right;
5765                        var b = offsets.bottom;
5766                        this._elem.css({right:a, bottom:b});
5767                        break;
5768                    case 's':
5769                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
5770                        var b = offsets.bottom;
5771                        this._elem.css({left:a, bottom:b});
5772                        break;
5773                    case 'sw':
5774                        var a = offsets.left;
5775                        var b = offsets.bottom;
5776                        this._elem.css({left:a, bottom:b});
5777                        break;
5778                    case 'w':
5779                        var a = offsets.left;
5780                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
5781                        this._elem.css({left:a, top:b});
5782                        break;
5783                    default:  // same as 'se'
5784                        var a = offsets.right;
5785                        var b = offsets.bottom;
5786                        this._elem.css({right:a, bottom:b});
5787                        break;
5788                }
5789                
5790            }
5791            else if (this.placement == 'outside'){
5792                switch (this.location) {
5793                    case 'nw':
5794                        var a = this._plotDimensions.width - offsets.left;
5795                        var b = offsets.top;
5796                        this._elem.css('right', a);
5797                        this._elem.css('top', b);
5798                        break;
5799                    case 'n':
5800                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
5801                        var b = this._plotDimensions.height - offsets.top;
5802                        this._elem.css('left', a);
5803                        this._elem.css('bottom', b);
5804                        break;
5805                    case 'ne':
5806                        var a = this._plotDimensions.width - offsets.right;
5807                        var b = offsets.top;
5808                        this._elem.css({left:a, top:b});
5809                        break;
5810                    case 'e':
5811                        var a = this._plotDimensions.width - offsets.right;
5812                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
5813                        this._elem.css({left:a, top:b});
5814                        break;
5815                    case 'se':
5816                        var a = this._plotDimensions.width - offsets.right;
5817                        var b = offsets.bottom;
5818                        this._elem.css({left:a, bottom:b});
5819                        break;
5820                    case 's':
5821                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
5822                        var b = this._plotDimensions.height - offsets.bottom;
5823                        this._elem.css({left:a, top:b});
5824                        break;
5825                    case 'sw':
5826                        var a = this._plotDimensions.width - offsets.left;
5827                        var b = offsets.bottom;
5828                        this._elem.css({right:a, bottom:b});
5829                        break;
5830                    case 'w':
5831                        var a = this._plotDimensions.width - offsets.left;
5832                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
5833                        this._elem.css({right:a, top:b});
5834                        break;
5835                    default:  // same as 'se'
5836                        var a = offsets.right;
5837                        var b = offsets.bottom;
5838                        this._elem.css({right:a, bottom:b});
5839                        break;
5840                }
5841            }
5842            else {
5843                switch (this.location) {
5844                    case 'nw':
5845                        this._elem.css({left:0, top:offsets.top});
5846                        break;
5847                    case 'n':
5848                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
5849                        this._elem.css({left: a, top:offsets.top});
5850                        break;
5851                    case 'ne':
5852                        this._elem.css({right:0, top:offsets.top});
5853                        break;
5854                    case 'e':
5855                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
5856                        this._elem.css({right:offsets.right, top:b});
5857                        break;
5858                    case 'se':
5859                        this._elem.css({right:offsets.right, bottom:offsets.bottom});
5860                        break;
5861                    case 's':
5862                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
5863                        this._elem.css({left: a, bottom:offsets.bottom});
5864                        break;
5865                    case 'sw':
5866                        this._elem.css({left:offsets.left, bottom:offsets.bottom});
5867                        break;
5868                    case 'w':
5869                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
5870                        this._elem.css({left:offsets.left, top:b});
5871                        break;
5872                    default:  // same as 'se'
5873                        this._elem.css({right:offsets.right, bottom:offsets.bottom});
5874                        break;
5875                }
5876            }
5877        } 
5878    };
5879
5880    /**
5881     * Class: $.jqplot.ThemeEngine
5882     * Theme Engine provides a programatic way to change some of the  more
5883     * common jqplot styling options such as fonts, colors and grid options.
5884     * A theme engine instance is created with each plot.  The theme engine
5885     * manages a collection of themes which can be modified, added to, or 
5886     * applied to the plot.
5887     * 
5888     * The themeEngine class is not instantiated directly.
5889     * When a plot is initialized, the current plot options are scanned
5890     * an a default theme named "Default" is created.  This theme is
5891     * used as the basis for other themes added to the theme engine and
5892     * is always available.
5893     * 
5894     * A theme is a simple javascript object with styling parameters for
5895     * various entities of the plot.  A theme has the form:
5896     * 
5897     * 
5898     * > {
5899     * >     _name:f "Default",
5900     * >     target: {
5901     * >         backgroundColor: "transparent"
5902     * >     },
5903     * >     legend: {
5904     * >         textColor: null,
5905     * >         fontFamily: null,
5906     * >         fontSize: null,
5907     * >         border: null,
5908     * >         background: null
5909     * >     },
5910     * >     title: {
5911     * >         textColor: "rgb(102, 102, 102)",
5912     * >         fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif",
5913     * >         fontSize: "19.2px",
5914     * >         textAlign: "center"
5915     * >     },
5916     * >     seriesStyles: {},
5917     * >     series: [{
5918     * >         color: "#4bb2c5",
5919     * >         lineWidth: 2.5,
5920     * >         shadow: true,
5921     * >         fillColor: "#4bb2c5",
5922     * >         showMarker: true,
5923     * >         markerOptions: {
5924     * >             color: "#4bb2c5",
5925     * >             show: true,
5926     * >             style: 'filledCircle',
5927     * >             lineWidth: 1.5,
5928     * >             size: 4,
5929     * >             shadow: true
5930     * >         }
5931     * >     }],
5932     * >     grid: {
5933     * >
5933         drawGridlines: true,
5934     * >         gridLineColor: "#cccccc",
5935     * >         gridLineWidth: 1,
5936     * >         backgroundColor: "#fffdf6",
5937     * >         borderColor: "#999999",
5938     * >         borderWidth: 2,
5939     * >         shadow: true
5940     * >     },
5941     * >     axesStyles: {
5942     * >         label: {},
5943     * >         ticks: {}
5944     * >     },
5945     * >     axes: {
5946     * >         xaxis: {
5947     * >             borderColor: "#999999",
5948     * >             borderWidth: 2,
5949     * >             ticks: {
5950     * >                 show: true,
5951     * >                 showGridline: true,
5952     * >                 showLabel: true,
5953     * >                 showMark: true,
5954     * >                 size: 4,
5955     * >                 textColor: "",
5956     * >                 whiteSpace: "nowrap",
5957     * >                 fontSize: "12px",
5958     * >                 fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif"
5959     * >             },
5960     * >             label: {
5961     * >                 textColor: "rgb(102, 102, 102)",
5962     * >                 whiteSpace: "normal",
5963     * >                 fontSize: "14.6667px",
5964     * >                 fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif",
5965     * >                 fontWeight: "400"
5966     * >             }
5967     * >         },
5968     * >         yaxis: {
5969     * >             borderColor: "#999999",
5970     * >             borderWidth: 2,
5971     * >             ticks: {
5972     * >                 show: true,
5973     * >                 showGridline: true,
5974     * >                 showLabel: true,
5975     * >                 showMark: true,
5976     * >                 size: 4,
5977     * >                 textColor: "",
5978     * >                 whiteSpace: "nowrap",
5979     * >                 fontSize: "12px",
5980     * >                 fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif"
5981     * >             },
5982     * >             label: {
5983     * >                 textColor: null,
5984     * >                 whiteSpace: null,
5985     * >                 fontSize: null,
5986     * >                 fontFamily: null,
5987     * >                 fontWeight: null
5988     * >             }
5989     * >         },
5990     * >         x2axis: {...
5991     * >         },
5992     * >         ...
5993     * >         y9axis: {...
5994     * >         }
5995     * >     }
5996     * > }
5997     * 
5998     * "seriesStyles" is a style object that will be applied to all series in the plot.
5999     * It will forcibly override any styles applied on the individual series.  "axesStyles" is
6000     * a style object that will be applied to all axes in the plot.  It will also forcibly
6001     * override any styles on the individual axes.
6002     * 
6003     * The example shown above has series options for a line series.  Options for other
6004     * series types are shown below:
6005     * 
6006     * Bar Series:
6007     * 
6008     * > {
6009     * >     color: "#4bb2c5",
6010     * >     seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
6011     * >     lineWidth: 2.5,
6012     * >     shadow: true,
6013     * >     barPadding: 2,
6014     * >     barMargin: 10,
6015     * >     barWidth: 15.09375,
6016     * >     highlightColors: ["rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)"]
6017     * > }
6018     * 
6019     * Pie Series:
6020     * 
6021     * > {
6022     * >     seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
6023     * >     padding: 20,
6024     * >     sliceMargin: 0,
6025     * >     fill: true,
6026     * >     shadow: true,
6027     * >     startAngle: 0,
6028     * >     lineWidth: 2.5,
6029     * >     highlightColors: ["rgb(129,201,214)", "rgb(240,189,104)", "rgb(214,202,165)", "rgb(137,180,158)", "rgb(168,180,137)", "rgb(180,174,89)", "rgb(180,113,161)", "rgb(129,141,236)", "rgb(227,205,120)", "rgb(255,138,76)", "rgb(76,169,219)", "rgb(215,126,190)", "rgb(220,232,135)", "rgb(200,167,96)", "rgb(103,202,235)", "rgb(208,154,215)"]
6030     * > }
6031     * 
6032     * Funnel Series:
6033     * 
6034     * > {
6035     * >     color: "#4bb2c5",
6036     * >     lineWidth: 2,
6037     * >     shadow: true,
6038     * >     padding: {
6039     * >         top: 20,
6040     * >         right: 20,
6041     * >         bottom: 20,
6042     * >         left: 20
6043     * >     },
6044     * >     sectionMargin: 6,
6045     * >     seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
6046     * >     highlightColors: ["rgb(147,208,220)", "rgb(242,199,126)", "rgb(220,210,178)", "rgb(154,191,172)", "rgb(180,191,154)", "rgb(191,186,112)", "rgb(191,133,174)", "rgb(147,157,238)", "rgb(231,212,139)", "rgb(255,154,102)", "rgb(102,181,224)", "rgb(221,144,199)", "rgb(225,235,152)", "rgb(200,167,96)", "rgb(124,210,238)", "rgb(215,169,221)"]
6047     * > }
6048     * 
6049     */
6050    $.jqplot.ThemeEngine = function(){
6051        // Group: Properties
6052        //
6053        // prop: themes
6054        // hash of themes managed by the theme engine.  
6055        // Indexed by theme name.
6056        this.themes = {};
6057        // prop: activeTheme
6058        // Pointer to currently active theme
6059        this.activeTheme=null;
6060        
6061    };
6062    
6063    // called with scope of plot
6064    $.jqplot.ThemeEngine.prototype.init = function() {
6065        // get the Default theme from the current plot settings.
6066        var th = new $.jqplot.Theme({_name:'Default'});
6067        var n, i;
6068        
6069        for (n in th.target) {
6070            if (n == "textColor") {
6071                th.target[n] = this.target.css('color');
6072            }
6073            else {
6074                th.target[n] = this.target.css(n);
6075            }
6076        }
6077        
6078        if (this.title.show && this.title._elem) {
6079            for (n in th.title) {
6080                if (n == "textColor") {
6081                    th.title[n] = this.title._elem.css('color');
6082                }
6083                else {
6084                    th.title[n] = this.title._elem.css(n);
6085                }
6086            }
6087        }
6088        
6089        for (n in th.grid) {
6090            th.grid[n] = this.grid[n];
6091        }
6092        if (th.grid.backgroundColor == null && this.grid.background != null) {
6093            th.grid.backgroundColor = this.grid.background;
6094        }
6095        if (this.legend.show && this.legend._elem) {
6096            for (n in th.legend) {
6097                if (n == 'textColor') {
6098                    th.legend[n] = this.legend._elem.css('color');
6099                }
6100                else {
6101                    th.legend[n] = this.legend._elem.css(n);
6102                }
6103            }
6104        }
6105        var s;
6106        
6107        for (i=0; i<this.series.length; i++) {
6108            s = this.series[i];
6109            if (s.renderer.constructor == $.jqplot.LineRenderer) {
6110                th.series.push(new LineSeriesProperties());
6111            }
6112            else if (s.renderer.constructor == $.jqplot.BarRenderer) {
6113                th.series.push(new BarSeriesProperties());
6114            }
6115            else if (s.renderer.constructor == $.jqplot.PieRenderer) {
6116                th.series.push(new PieSeriesProperties());
6117            }
6118            else if (s.renderer.constructor == $.jqplot.DonutRenderer) {
6119                th.series.push(new DonutSeriesProperties());
6120            }
6121            else if (s.renderer.constructor == $.jqplot.FunnelRenderer) {
6122                th.series.push(new FunnelSeriesProperties());
6123            }
6124            else if (s.renderer.constructor == $.jqplot.MeterGaugeRenderer) {
6125                th.series.push(new MeterSeriesProperties());
6126            }
6127            else {
6128                th.series.push({});
6129            }
6130            for (n in th.series[i]) {
6131                th.series[i][n] = s[n];
6132            }
6133        }
6134        var a, ax;
6135        for (n in this.axes) {
6136            ax = this.axes[n];
6137            a = th.axes[n] = new AxisProperties();
6138            a.borderColor = ax.borderColor;
6139            a.borderWidth = ax.borderWidth;
6140            if (ax._ticks && ax._ticks[0]) {
6141                for (nn in a.ticks) {
6142                    if (ax._ticks[0].hasOwnProperty(nn)) {
6143                        a.ticks[nn] = ax._ticks[0][nn];
6144                    }
6145                    else if (ax._ticks[0]._elem){
6146                        a.ticks[nn] = ax._ticks[0]._elem.css(nn);
6147                    }
6148                }
6149            }
6150            if (ax._label && ax._label.show) {
6151                for (nn in a.label) {
6152                    // a.label[nn] = ax._label._elem.css(nn);
6153                    if (ax._label[nn]) {
6154                        a.label[nn] = ax._label[nn];
6155                    }
6156                    else if (ax._label._elem){
6157                        if (nn == 'textColor') {
6158                            a.label[nn] = ax._label._elem.css('color');
6159                        }
6160                        else {
6161                            a.label[nn] = ax._label._elem.css(nn);
6162                        }
6163                    }
6164                }
6165            }
6166        }
6167        this.themeEngine._add(th);
6168        this.themeEngine.activeTheme  = this.themeEngine.themes[th._name];
6169    };
6170    /**
6171     * Group: methods
6172     * 
6173     * method: get
6174     * 
6175     * Get and return the named theme or the active theme if no name given.
6176     * 
6177     * parameter:
6178     * 
6179     * name - name of theme to get.
6180     * 
6181     * returns:
6182     * 
6183     * Theme instance of given name.
6184     */   
6185    $.jqplot.ThemeEngine.prototype.get = function(name) {
6186        if (!name) {
6187            // return the active theme
6188            return this.activeTheme;
6189        }
6190        else {
6191            return this.themes[name];
6192        }
6193    };
6194    
6195    function numericalOrder(a,b) { return a-b; }
6196    
6197    /**
6198     * method: getThemeNames
6199     * 
6200     * Return the list of theme names in this manager in alpha-numerical order.
6201     * 
6202     * parameter:
6203     * 
6204     * None
6205     * 
6206     * returns:
6207     * 
6208     * A the list of theme names in this manager in alpha-numerical order.
6209     */       
6210    $.jqplot.ThemeEngine.prototype.getThemeNames = function() {
6211        var tn = [];
6212        for (var n in this.themes) {
6213            tn.push(n);
6214        }
6215        return tn.sort(numericalOrder);
6216    };
6217
6218    /**
6219     * method: getThemes
6220     * 
6221     * Return a list of themes in alpha-numerical order by name.
6222     * 
6223     * parameter:
6224     * 
6225     * None
6226     * 
6227     * returns:
6228     * 
6229     * A list of themes in alpha-numerical order by name.
6230     */ 
6231    $.jqplot.ThemeEngine.prototype.getThemes = function() {
6232        var tn = [];
6233        var themes = [];
6234        for (var n in this.themes) {
6235            tn.push(n);
6236        }
6237        tn.sort(numericalOrder);
6238        for (var i=0; i<tn.length; i++) {
6239            themes.push(this.themes[tn[i]]);
6240        }
6241        return themes;
6242    };
6243    
6244    $.jqplot.ThemeEngine.prototype.activate = function(plot, name) {
6245        // sometimes need to redraw whole plot.
6246        var redrawPlot = false;
6247        if (!name && this.activeTheme && this.activeTheme._name) {
6248            name = this.activeTheme._name;
6249        }
6250        if (!this.themes.hasOwnProperty(name)) {
6251            throw new Error("No theme of that name");
6252        }
6253        else {
6254            var th = this.themes[name];
6255            this.activeTheme = th;
6256            var val, checkBorderColor = false, checkBorderWidth = false;
6257            var arr = ['xaxis', 'x2axis', 'yaxis', 'y2axis'];
6258            
6259            for (i=0; i<arr.length; i++) {
6260                var ax = arr[i];
6261                if (th.axesStyles.borderColor != null) {
6262                    plot.axes[ax].borderColor = th.axesStyles.borderColor;
6263                }
6264                if (th.axesStyles.borderWidth != null) {
6265                    plot.axes[ax].borderWidth = th.axesStyles.borderWidth;
6266                }
6267            }
6268            
6269            for (axname in plot.axes) {
6270                var axis = plot.axes[axname];
6271                if (axis.show) {
6272                    var thaxis = th.axes[axname] || {};
6273                    var thaxstyle = th.axesStyles;
6274                    var thax = $.jqplot.extend(true, {}, thaxis, thaxstyle);
6275                    val = (th.axesStyles.borderColor != null) ? th.axesStyles.borderColor : thax.borderColor;
6276                    if (thax.borderColor != null) {
6277                        axis.borderColor = thax.borderColor;
6278                        redrawPlot = true;
6279                    }
6280                    val = (th.axesStyles.borderWidth != null) ? th.axesStyles.borderWidth : thax.borderWidth;
6281                    if (thax.borderWidth != null) {
6282                        axis.borderWidth = thax.borderWidth;
6283                        redrawPlot = true;
6284                    }
6285                    if (axis._ticks && axis._ticks[0]) {
6286                        for (nn in thax.ticks) {
6287                            // val = null;
6288                            // if (th.axesStyles.ticks && th.axesStyles.ticks[nn] != null) {
6289                            //     val = th.axesStyles.ticks[nn];
6290                            // }
6291                            // else if (thax.ticks[nn] != null){
6292                            //     val = thax.ticks[nn]
6293                            // }
6294                            val = thax.ticks[nn];
6295                            if (val != null) {
6296                                axis.tickOptions[nn] = val;
6297                                axis._ticks = [];
6298                                redrawPlot = true;
6299                            }
6300                        }
6301                    }
6302                    if (axis._label && axis._label.show) {
6303                        for (nn in thax.label) {
6304                            // val = null;
6305                            // if (th.axesStyles.label && th.axesStyles.label[nn] != null) {
6306                            //     val = th.axesStyles.label[nn];
6307                            // }
6308                            // else if (thax.label && thax.label[nn] != null){
6309                            //     val = thax.label[nn]
6310                            // }
6311                            val = thax.label[nn];
6312                            if (val != null) {
6313                                axis.labelOptions[nn] = val;
6314                                redrawPlot = true;
6315                            }
6316                        }
6317                    }
6318                    
6319                }
6320            }            
6321            
6322            for (var n in th.grid) {
6323                if (th.grid[n] != null) {
6324                    plot.grid[n] = th.grid[n];
6325                }
6326            }
6327            if (!redrawPlot) {
6328                plot.grid.draw();
6329            }
6330            
6331            if (plot.legend.show) { 
6332                for (n in th.legend) {
6333                    if (th.legend[n] != null) {
6334                        plot.legend[n] = th.legend[n];
6335                    }
6336                }
6337            }
6338            if (plot.title.show) {
6339                for (n in th.title) {
6340                    if (th.title[n] != null) {
6341                        plot.title[n] = th.title[n];
6342                    }
6343                }
6344            }
6345            
6346            var i;
6347            for (i=0; i<th.series.length; i++) {
6348                var opts = {};
6349                var redrawSeries = false;
6350                for (n in th.series[i]) {
6351                    val = (th.seriesStyles[n] != null) ? th.seriesStyles[n] : th.series[i][n];
6352                    if (val != null) {
6353                        opts[n] = val;
6354                        if (n == 'color') {
6355                            plot.series[i].renderer.shapeRenderer.fillStyle = val;
6356                            plot.series[i].renderer.shapeRenderer.strokeStyle = val;
6357                            plot.series[i][n] = val;
6358                        }
6359                        else if (n == 'lineWidth') {
6360                            plot.series[i].renderer.shapeRenderer.lineWidth = val;
6361                            plot.series[i][n] = val;
6362                        }
6363                        else if (n == 'markerOptions') {
6364                            merge (plot.series[i].markerOptions, val);
6365                            merge (plot.series[i].markerRenderer, val);
6366                        }
6367                        else {
6368                            plot.series[i][n] = val;
6369                        }
6370                        redrawPlot = true;
6371                    }
6372                }
6373            }
6374            
6375            if (redrawPlot) {
6376                plot.target.empty();
6377                plot.draw();
6378            }
6379            
6380            for (n in th.target) {
6381                if (th.target[n] != null) {
6382                    plot.target.css(n, th.target[n]);
6383                }
6384            }
6385        }
6386        
6387    };
6388    
6389    $.jqplot.ThemeEngine.prototype._add = function(theme, name) {
6390        if (name) {
6391            theme._name = name;
6392        }
6393        if (!theme._name) {
6394            theme._name = Date.parse(new Date());
6395        }
6396        if (!this.themes.hasOwnProperty(theme._name)) {
6397            this.themes[theme._name] = theme;
6398        }
6399        else {
6400            throw new Error("jqplot.ThemeEngine Error: Theme already in use");
6401        }
6402    };
6403    
6404    // method remove
6405    // Delete the named theme, return true on success, false on failure.
6406    
6407
6408    /**
6409     * method: remove
6410     * 
6411     * Remove the given theme from the themeEngine.
6412     * 
6413     * parameters:
6414     * 
6415     * name - name of the theme to remove.
6416     * 
6417     * returns:
6418     * 
6419     * true on success, false on failure.
6420     */
6421    $.jqplot.ThemeEngine.prototype.remove = function(name) {
6422        if (name == 'Default') {
6423            return false;
6424        }
6425        return delete this.themes[name];
6426    };
6427
6428    /**
6429     * method: newTheme
6430     * 
6431     * Create a new theme based on the default theme, adding it the themeEngine.
6432     * 
6433     * parameters:
6434     * 
6435     * name - name of the new theme.
6436     * obj - optional object of styles to be applied to this new theme.
6437     * 
6438     * returns:
6439     * 
6440     * new Theme object.
6441     */
6442    $.jqplot.ThemeEngine.prototype.newTheme = function(name, obj) {
6443        if (typeof(name) == 'object') {
6444            obj = obj || name;
6445            name = null;
6446        }
6447        if (obj && obj._name) {
6448            name = obj._name;
6449        }
6450        else {
6451            name = name || Date.parse(new Date());
6452        }
6453        // var th = new $.jqplot.Theme(name);
6454        var th = this.copy(this.themes['Default']._name, name);
6455        $.jqplot.extend(th, obj);
6456        return th;
6457    };
6458    
6459    // function clone(obj) {
6460    //     return eval(obj.toSource());
6461    // }
6462    
6463    function clone(obj){
6464        if(obj == null || typeof(obj) != 'object'){
6465            return obj;
6466        }
6467    
6468        var temp = new obj.constructor();
6469        for(var key in obj){
6470            temp[key] = clone(obj[key]);
6471        }   
6472        return temp;
6473    }
6474    
6475    $.jqplot.clone = clone;
6476    
6477    function merge(obj1, obj2) {
6478        if (obj2 ==  null || typeof(obj2) != 'object') {
6479            return;
6480        }
6481        for (var key in obj2) {
6482            if (key == 'highlightColors') {
6483                obj1[key] = clone(obj2[key]);
6484            }
6485            if (obj2[key] != null && typeof(obj2[key]) == 'object') {
6486                if (!obj1.hasOwnProperty(key)) {
6487                    obj1[key] = {};
6488                }
6489                merge(obj1[key], obj2[key]);
6490            }
6491            else {
6492                obj1[key] = obj2[key];
6493            }
6494        }
6495    }
6496    
6497    $.jqplot.merge = merge;
6498    
6499        // Use the jQuery 1.3.2 extend function since behaviour in jQuery 1.4 seems problematic
6500    $.jqplot.extend = function() {
6501    	// copy reference to target object
6502    	var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
6503
6504    	// Handle a deep copy situation
6505    	if ( typeof target === "boolean" ) {
6506    		deep = target;
6507    		target = arguments[1] || {};
6508    		// skip the boolean and the target
6509    		i = 2;
6510    	}
6511
6512    	// Handle case when target is a string or something (possible in deep copy)
6513    	if ( typeof target !== "object" && !toString.call(target) === "[object Function]" ) {
6514    	    target = {};
6515    	}
6516
6517    	for ( ; i < length; i++ ){
6518    		// Only deal with non-null/undefined values
6519    		if ( (options = arguments[ i ]) != null ) {
6520    			// Extend the base object
6521    			for ( var name in options ) {
6522    				var src = target[ name ], copy = options[ name ];
6523
6524    				// Prevent never-ending loop
6525    				if ( target === copy ) {
6526    					continue;
6527    				}
6528
6529    				// Recurse if we're merging object values
6530    				if ( deep && copy && typeof copy === "object" && !copy.nodeType ) {
6531    					target[ name ] = $.jqplot.extend( deep, 
6532    						// Never move original objects, clone them
6533    						src || ( copy.length != null ? [ ] : { } )
6534    					, copy );
6535                    }
6536    				// Don't bring in undefined values
6537    				else if ( copy !== undefined ) {
6538    					target[ name ] = copy;
6539    				}
6540    			}
6541    		}
6542        }
6543    	// Return the modified object
6544    	return target;
6545    };
6546
6547    /**
6548     * method: rename
6549     * 
6550     * Rename a theme.
6551     * 
6552     * parameters:
6553     * 
6554     * oldName - current name of the theme.
6555     * newName - desired name of the theme.
6556     * 
6557     * returns:
6558     * 
6559     * new Theme object.
6560     */
6561    $.jqplot.ThemeEngine.prototype.rename = function (oldName, newName) {
6562        if (oldName == 'Default' || newName == 'Default') {
6563            throw new Error ("jqplot.ThemeEngine Error: Cannot rename from/to Default");
6564        }
6565        if (this.themes.hasOwnProperty(newName)) {
6566            throw new Error ("jqplot.ThemeEngine Error: New name already in use.");
6567        }
6568        else if (this.themes.hasOwnProperty(oldName)) {
6569            var th = this.copy (oldName, newName);
6570            this.remove(oldName);
6571            return th;
6572        }
6573        throw new Error("jqplot.ThemeEngine Error: Old name or new name invalid");
6574    };
6575
6576    /**
6577     * method: copy
6578     * 
6579     * Create a copy of an existing theme in the themeEngine, adding it the themeEngine.
6580     * 
6581     * parameters:
6582     * 
6583     * sourceName - name of the existing theme.
6584     * targetName - name of the copy.
6585     * obj - optional object of style parameter to apply to the new theme.
6586     * 
6587     * returns:
6588     * 
6589     * new Theme object.
6590     */
6591    $.jqplot.ThemeEngine.prototype.copy = function (sourceName, targetName, obj) {
6592        if (targetName == 'Default') {
6593            throw new Error ("jqplot.ThemeEngine Error: Cannot copy over Default theme");
6594        }
6595        if (!this.themes.hasOwnProperty(sourceName)) {
6596            var s = "jqplot.ThemeEngine Error: Source name invalid";
6597            throw new Error(s);
6598        }
6599        if (this.themes.hasOwnProperty(targetName)) {
6600            var s = "jqplot.ThemeEngine Error: Target name invalid";
6601            throw new Error(s);
6602        }
6603        else {
6604            var th = clone(this.themes[sourceName]);
6605            th._name = targetName;
6606            $.jqplot.extend(true, th, obj);
6607            this._add(th);
6608            return th;
6609        }
6610    };
6611    
6612    
6613    $.jqplot.Theme = function(name, obj) {
6614        if (typeof(name) == 'object') {
6615            obj = obj || name;
6616            name = null;
6617        }
6618        name = name || Date.parse(new Date());
6619        this._name = name;
6620        this.target = {
6621            backgroundColor: null
6622        };
6623        this.legend = {
6624            textColor: null,
6625            fontFamily: null,
6626            fontSize: null,
6627            border: null,
6628            background: null
6629        };
6630        this.title = {
6631            textColor: null,
6632            fontFamily: null,
6633            fontSize: null,
6634            textAlign: null
6635        };
6636        this.seriesStyles = {};
6637        this.series = [];
6638        this.grid = {
6639            drawGridlines: null,
6640            gridLineColor: null,
6641            gridLineWidth: null,
6642            backgroundColor: null,
6643            borderColor: null,
6644            borderWidth: null,
6645            shadow: null
6646        };
6647        this.axesStyles = {label:{}, ticks:{}};
6648        this.axes = {};
6649        if (typeof(obj) == 'string') {
6650            this._name = obj;
6651        }
6652        else if(typeof(obj) == 'object') {
6653            $.jqplot.extend(true, this, obj);
6654        }
6655    };
6656    
6657    var AxisProperties = function() {
6658        this.borderColor = null;
6659        this.borderWidth = null;
6660        this.ticks = new AxisTicks();
6661        this.label = new AxisLabel();
6662    };
6663    
6664    var AxisTicks = function() {
6665        this.show = null;
6666        this.showGridline = null;
6667        this.showLabel = null;
6668        this.showMark = null;
6669        this.size = null;
6670        this.textColor = null;
6671        this.whiteSpace = null;
6672        this.fontSize = null;
6673        this.fontFamily = null;
6674    };
6675    
6676    var AxisLabel = function() {
6677        this.textColor = null;
6678        this.whiteSpace = null;
6679        this.fontSize = null;
6680        this.fontFamily = null;
6681        this.fontWeight = null;
6682    };
6683    
6684    var LineSeriesProperties = function() {
6685        this.color=null;
6686        this.lineWidth=null;
6687        this.shadow=null;
6688        this.fillColor=null;
6689        this.showMarker=null;
6690        this.markerOptions = new MarkerOptions();
6691    };
6692    
6693    var MarkerOptions = function() {
6694        this.show = null;
6695        this.style = null;
6696        this.lineWidth = null;
6697        this.size = null;
6698        this.color = null;
6699        this.shadow = null;
6700    };
6701    
6702    var BarSeriesProperties = function() {
6703        this.color=null;
6704        this.seriesColors=null;
6705        this.lineWidth=null;
6706        this.shadow=null;
6707        this.barPadding=null;
6708        this.barMargin=null;
6709        this.barWidth=null;
6710        this.highlightColors=null;
6711    };
6712    
6713    var PieSeriesProperties = function() {
6714        this.seriesColors=null;
6715        this.padding=null;
6716        this.sliceMargin=null;
6717        this.fill=null;
6718        this.shadow=null;
6719        this.startAngle=null;
6720        this.lineWidth=null;
6721        this.highlightColors=null;
6722    };
6723    
6724    var DonutSeriesProperties = function() {
6725        this.seriesColors=null;
6726        this.padding=null;
6727        this.sliceMargin=null;
6728        this.fill=null;
6729        this.shadow=null;
6730        this.startAngle=null;
6731        this.lineWidth=null;
6732        this.innerDiameter=null;
6733        this.thickness=null;
6734        this.ringMargin=null;
6735        this.highlightColors=null;
6736    };
6737    
6738    var FunnelSeriesProperties = function() {
6739        this.color=null;
6740        this.lineWidth=null;
6741        this.shadow=null;
6742        this.padding=null;
6743        this.sectionMargin=null;
6744        this.seriesColors=null;
6745        this.highlightColors=null;
6746    };
6747    
6748    var MeterSeriesProperties = function() {
6749        this.padding=null;
6750        this.backgroundColor=null;
6751        this.ringColor=null;
6752        this.tickColor=null;
6753        this.ringWidth=null;
6754        this.intervalColors=null;
6755        this.intervalInnerRadius=null;
6756        this.intervalOuterRadius=null;
6757        this.hubRadius=null;
6758        this.needleThickness=null;
6759        this.needlePad=null;
6760    };
6761        
6762
6763      
6764    /**
6765     * JavaScript printf/sprintf functions.
6766     * 
6767     * This code has been adapted from the publicly available sprintf methods
6768     * by Ash Searle. His original header follows:
6769     *
6770     *     This code is unrestricted: you are free to use it however you like.
6771     *     
6772     *     The functions should work as expected, performing left or right alignment,
6773     *     truncating strings, outputting numbers with a required precision etc.
6774     *
6775     *     For complex cases, these functions follow the Perl implementations of
6776     *     (s)printf, allowing arguments to be passed out-of-order, and to set the
6777     *     precision or length of the output based on arguments instead of fixed
6778     *     numbers.
6779     *
6780     *     See http://perldoc.perl.org/functions/sprintf.html for more information.
6781     *
6782     *     Implemented:
6783     *     - zero and space-padding
6784     *     - right and left-alignment,
6785     *     - base X prefix (binary, octal and hex)
6786     *     - positive number prefix
6787     *     - (minimum) width
6788     *     - precision / truncation / maximum width
6789     *     - out of order arguments
6790     *
6791     *     Not implemented (yet):
6792     *     - vector flag
6793     *     - size (bytes, words, long-words etc.)
6794     *     
6795     *     Will not implement:
6796     *     - %n or %p (no pass-by-reference in JavaScript)
6797     *
6798     *     @version 2007.04.27
6799     *     @author Ash Searle 
6800     * 
6801     * You can see the original work and comments on his blog:
6802     * http://hexmen.com/blog/2007/03/printf-sprintf/
6803     * http://hexmen.com/js/sprintf.js
6804     */
6805     
6806     /**
6807      * @Modifications 2009.05.26
6808      * @author Chris Leonello
6809      * 
6810      * Added %p %P specifier
6811      * Acts like %g or %G but will not add more significant digits to the output than present in the input.
6812      * Example:
6813      * Format: '%.3p', Input: 0.012, Output: 0.012
6814      * Format: '%.3g', Input: 0.012, Output: 0.0120
6815      * Format: '%.4p', Input: 12.0, Output: 12.0
6816      * Format: '%.4g', Input: 12.0, Output: 12.00
6817      * Format: '%.4p', Input: 4.321e-5, Output: 4.321e-5
6818      * Format: '%.4g', Input: 4.321e-5, Output: 4.3210e-5
6819      */
6820    $.jqplot.sprintf = function() {
6821        function pad(str, len, chr, leftJustify) {
6822            var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr);
6823            return leftJustify ? str + padding : padding + str;
6824
6825        }
6826
6827        function justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace) {
6828            var diff = minWidth - value.length;
6829            if (diff > 0) {
6830                var spchar = ' ';
6831                if (htmlSpace) { spchar = '&nbsp;'; }
6832                if (leftJustify || !zeroPad) {
6833                    value = pad(value, minWidth, spchar, leftJustify);
6834                } else {
6835                    value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length);
6836                }
6837            }
6838            return value;
6839        }
6840
6841        function formatBaseX(value, base, prefix, leftJustify, minWidth, precision, zeroPad, htmlSpace) {
6842            // Note: casts negative numbers to positive ones
6843            var number = value >>> 0;
6844            prefix = prefix && number && {'2': '0b', '8': '0', '16': '0x'}[base] || '';
6845            value = prefix + pad(number.toString(base), precision || 0, '0', false);
6846            return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace);
6847        }
6848
6849        function formatString(value, leftJustify, minWidth, precision, zeroPad, htmlSpace) {
6850            if (precision != null) {
6851                value = value.slice(0, precision);
6852            }
6853            return justify(value, '', leftJustify, minWidth, zeroPad, htmlSpace);
6854        }
6855
6856        var a = arguments, i = 0, format = a[i++];
6857
6858        return format.replace($.jqplot.sprintf.regex, function(substring, valueIndex, flags, minWidth, _, precision, type) {
6859            if (substring == '%%') { return '%'; }
6860
6861            // parse flags
6862            var leftJustify = false, positivePrefix = '', zeroPad = false, prefixBaseX = false, htmlSpace = false;
6863                for (var j = 0; flags && j < flags.length; j++) switch (flags.charAt(j)) {
6864                case ' ': positivePrefix = ' '; break;
6865                case '+': positivePrefix = '+'; break;
6866                case '-': leftJustify = true; break;
6867                case '0': zeroPad = true; break;
6868                case '#': prefixBaseX = true; break;
6869                case '&': htmlSpace = true; break;
6870            }
6871
6872            // parameters may be null, undefined, empty-string or real valued
6873            // we want to ignore null, undefined and empty-string values
6874
6875            if (!minWidth) {
6876                minWidth = 0;
6877            } 
6878            else if (minWidth == '*') {
6879                minWidth = +a[i++];
6880            } 
6881            else if (minWidth.charAt(0) == '*') {
6882                minWidth = +a[minWidth.slice(1, -1)];
6883            } 
6884            else {
6885                minWidth = +minWidth;
6886            }
6887
6888            // Note: undocumented perl feature:
6889            if (minWidth < 0) {
6890                minWidth = -minWidth;
6891                leftJustify = true;
6892            }
6893
6894            if (!isFinite(minWidth)) {
6895                throw new Error('$.jqplot.sprintf: (minimum-)width must be finite');
6896            }
6897
6898            if (!precision) {
6899                precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : void(0);
6900            } 
6901            else if (precision == '*') {
6902                precision = +a[i++];
6903            } 
6904            else if (precision.charAt(0) == '*') {
6905                precision = +a[precision.slice(1, -1)];
6906            } 
6907            else {
6908                precision = +precision;
6909            }
6910
6911            // grab value using valueIndex if required?
6912            var value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++];
6913
6914            switch (type) {
6915            case 's': {
6916                if (value == null) {
6917                    return '';
6918                }
6919                return formatString(String(value), leftJustify, minWidth, precision, zeroPad, htmlSpace);
6920            }
6921            case 'c': return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad, htmlSpace);
6922            case 'b': return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad,htmlSpace);
6923            case 'o': return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace);
6924            case 'x': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace);
6925            case 'X': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace).toUpperCase();
6926            case 'u': return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace);
6927            case 'i': {
6928              var number = parseInt(+value, 10);
6929              if (isNaN(number)) {
6930                return '';
6931              }
6932              var prefix = number < 0 ? '-' : positivePrefix;
6933              value = prefix + pad(String(Math.abs(number)), precision, '0', false);
6934              return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace);
6935                  }
6936            case 'd': {
6937              var number = Math.round(+value);
6938              if (isNaN(number)) {
6939                return '';
6940              }
6941              var prefix = number < 0 ? '-' : positivePrefix;
6942              value = prefix + pad(String(Math.abs(number)), precision, '0', false);
6943              return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace);
6944                  }
6945            case 'e':
6946            case 'E':
6947            case 'f':
6948            case 'F':
6949            case 'g':
6950            case 'G':
6951                      {
6952                      var number = +value;
6953                      if (isNaN(number)) {
6954                          return '';
6955                      }
6956                      var prefix = number < 0 ? '-' : positivePrefix;
6957                      var method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())];
6958                      var textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2];
6959                      value = prefix + Math.abs(number)[method](precision);
6960                      return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace)[textTransform]();
6961                  }
6962            case 'p':
6963            case 'P':
6964            {
6965                // make sure number is a number
6966                var number = +value;
6967                if (isNaN(number)) {
6968                    return '';
6969                }
6970                var prefix = number < 0 ? '-' : positivePrefix;
6971
6972                var parts = String(Number(Math.abs(number)).toExponential()).split(/e|E/);
6973                var sd = (parts[0].indexOf('.') != -1) ? parts[0].length - 1 : parts[0].length;
6974                var zeros = (parts[1] < 0) ? -parts[1] - 1 : 0;
6975                
6976                if (Math.abs(number) < 1) {
6977                    if (sd + zeros  <= precision) {
6978                        value = prefix + Math.abs(number).toPrecision(sd);
6979                    }
6980                    else {
6981                        if (sd  <= precision - 1) {
6982                            value = prefix + Math.abs(number).toExponential(sd-1);
6983                        }
6984                        else {
6985                            value = prefix + Math.abs(number).toExponential(precision-1);
6986                        }
6987                    }
6988                }
6989                else {
6990                    var prec = (sd <= precision) ? sd : precision;
6991                    value = prefix + Math.abs(number).toPrecision(prec);
6992                }
6993                var textTransform = ['toString', 'toUpperCase']['pP'.indexOf(type) % 2];
6994                return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace)[textTransform]();
6995            }
6996            case 'n': return '';
6997            default: return substring;
6998            }
6999        });
7000    };
7001    
7002    $.jqplot.sprintf.regex = /%%|%(\d+\$)?([-+#0& ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([nAscboxXuidfegpEGP])/g;
7003
7004})(jQuery);  

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.