PageSourceSearch

https://www.myownmusic.de/extern/plot/jquery.jqplot.js

js myownmusic.de collected 2026-10-02 04:46:30 UTC 466,621 bytes, 11,411 lines download raw bytes

1/**
2 * Title: jqPlot Charts
3 * 
4 * Pure JavaScript plotting plugin for jQuery.
5 * 
6 * About: Version
7 * 
8 * version: 1.0.8 
9 * revision: 1250
10 * 
11 * About: Copyright & License
12 * 
13 * Copyright (c) 2009-2013 Chris Leonello
14 * jqPlot is currently available for use in all personal or commercial projects 
15 * under both the MIT and GPL version 2.0 licenses. This means that you can 
16 * choose the license that best suits your project and use it accordingly.
17 * 
18 * See <GPL Version 2> and <MIT License> contained within this distribution for further information. 
19 *
20 * The author would appreciate an email letting him know of any substantial
21 * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
22 * or see http://www.jqplot.com/info.php.  This is, of course, not required.
23 *
24 * If you are feeling kind and generous, consider supporting the project by
25 * making a donation at: http://www.jqplot.com/donate.php.
26 *
27 * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
28 * 
29 *     version 2007.04.27
30 *     author Ash Searle
31 *     http://hexmen.com/blog/2007/03/printf-sprintf/
32 *     http://hexmen.com/js/sprintf.js
33 *     The author (Ash Searle) has placed this code in the public domain:
34 *     "This code is unrestricted: you are free to use it however you like."
35 * 
36 * 
37 * About: Introduction
38 * 
39 * jqPlot requires jQuery (1.4+ required for certain features). jQuery 1.4.2 is included in the distribution.  
40 * To use jqPlot include jQuery, the jqPlot jQuery plugin, the jqPlot css file and optionally 
41 * the excanvas script for IE support in your web page:
42 * 
43 * > <!--[if lt IE 9]><script language="javascript" type="text/javascript" src="excanvas.js"></script><![endif]-->
44 * > <script language="javascript" type="text/javascript" src="jquery-1.4.4.min.js"></script>
45 * > <script language="javascript" type="text/javascript" src="jquery.jqplot.min.js"></script>
46 * > <link rel="stylesheet" type="text/css" href="jquery.jqplot.css" />
47 * 
48 * jqPlot can be customized by overriding the defaults of any of the objects which make
49 * up the plot. The general usage of jqplot is:
50 * 
51 * > chart = $.jqplot('targetElemId', [dataArray,...], {optionsObject});
52 * 
53 * The options available to jqplot are detailed in <jqPlot Options> in the jqPlotOptions.txt file.
54 * 
55 * An actual call to $.jqplot() may look like the 
56 * examples below:
57 * 
58 * > chart = $.jqplot('chartdiv',  [[[1, 2],[3,5.12],[5,13.1],[7,33.6],[9,85.9],[11,219.9]]]);
59 * 
60 * or
61 * 
62 * > dataArray = [34,12,43,55,77];
63 * > chart = $.jqplot('targetElemId', [dataArray, ...], {title:'My Plot', axes:{yaxis:{min:20, max:100}}});
64 * 
65 * For more inforrmation, see <jqPlot Usage>.
66 * 
67 * About: Usage
68 * 
69 * See <jqPlot Usage>
70 * 
71 * About: Available Options 
72 * 
73 * See <jqPlot Options> for a list of options available thorugh the options object (not complete yet!)
74 * 
75 * About: Options Usage
76 * 
77 * See <Options Tutorial>
78 * 
79 * About: Changes
80 * 
81 * See <Change Log>
82 * 
83 */
84
85(function($) {
86    // make sure undefined is undefined
87    var undefined;
88    
89    $.fn.emptyForce = function() {
90      for ( var i = 0, elem; (elem = $(this)[i]) != null; i++ ) {
91        // Remove element nodes and prevent memory leaks
92        if ( elem.nodeType === 1 ) {
93          $.cleanData( elem.getElementsByTagName("*") );
94        }
95  
96        // Remove any remaining nodes
97        if ($.jqplot.use_excanvas) {
98          elem.outerHTML = "";
99        }
100        else {
101          while ( elem.firstChild ) {
102            elem.removeChild( elem.firstChild );
103          }
104        }
105
106        elem = null;
107      }
108  
109      return $(this);
110    };
111  
112    $.fn.removeChildForce = function(parent) {
113      while ( parent.firstChild ) {
114        this.removeChildForce( parent.firstChild );
115        parent.removeChild( parent.firstChild );
116      }
117    };
118
119    $.fn.jqplot = function() {
120        var datas = [];
121        var options = [];
122        // see how many data arrays we have
123        for (var i=0, l=arguments.length; i<l; i++) {
124            if ($.isArray(arguments[i])) {
125                datas.push(arguments[i]);
126            }
127            else if ($.isPlainObject(arguments[i])) {
128                options.push(arguments[i]);
129            }
130        }
131
132        return this.each(function(index) {
133            var tid, 
134                plot, 
135                $this = $(this),
136                dl = datas.length,
137                ol = options.length,
138                data, 
139                opts;
140
141            if (index < dl) {
142                data = datas[index];
143            }
144            else {
145                data = dl ? datas[dl-1] : null;
146            }
147
148            if (index < ol) {
149                opts = options[index];
150            }
151            else {
152                opts = ol ? options[ol-1] : null;
153            }
154
155            // does el have an id?
156            // if not assign it one.
157            tid = $this.attr('id');
158            if (tid === undefined) {
159                tid = 'jqplot_target_' + $.jqplot.targetCounter++;
160                $this.attr('id', tid);
161            }
162
163            plot = $.jqplot(tid, data, opts);
164
165            $this.data('jqplot', plot);
166        });
167    };
168
169
170    /**
171     * Namespace: $.jqplot
172     * jQuery function called by the user to create a plot.
173     *  
174     * Parameters:
175     * target - ID of target element to render the plot into.
176     * data - an array of data series.
177     * options - user defined options object.  See the individual classes for available options.
178     * 
179     * Properties:
180     * config - object to hold configuration information for jqPlot plot object.
181     * 
182     * attributes:
183     * enablePlugins - False to disable plugins by default.  Plugins must then be explicitly 
184     *   enabled in the individual plot options.  Default: false.
185     *   This property sets the "show" property of certain plugins to true or false.
186     *   Only plugins that can be immediately active upon loading are affected.  This includes
187     *   non-renderer plugins like cursor, dragable, highlighter, and trendline.
188     * defaultHeight - Default height for plots where no css height specification exists.  This
189     *   is a jqplot wide default.
190     * defaultWidth - Default height for plots where no css height specification exists.  This
191     *   is a jqplot wide default.
192     */
193
194    $.jqplot = function(target, data, options) {
195        var _data = null, _options = null;
196
197        if (arguments.length === 3) {
198            _data = data;
199            _options = options;
200        }
201
202        else if (arguments.length === 2) {
203            if ($.isArray(data)) {
204                _data = data;
205            }
206
207            else if ($.isPlainObject(data)) {
208                _options = data;
209            }
210        }
211
212        if (_data === null && _options !== null && _options.data) {
213            _data = _options.data;
214        }
215
216        var plot = new jqPlot();
217        // remove any error class that may be stuck on target.
218        $('#'+target).removeClass('jqplot-error');
219        
220        if ($.jqplot.config.catchErrors) {
221            try {
222                plot.init(target, _data, _options);
223                plot.draw();
224                plot.themeEngine.init.call(plot);
225                return plot;
226            }
227            catch(e) {
228                var msg = $.jqplot.config.errorMessage || e.message;
229                $('#'+target).append('<div class="jqplot-error-message">'+msg+'</div>');
230                $('#'+target).addClass('jqplot-error');
231                document.getElementById(target).style.background = $.jqplot.config.errorBackgroun
231d;
232                document.getElementById(target).style.border = $.jqplot.config.errorBorder;
233                document.getElementById(target).style.fontFamily = $.jqplot.config.errorFontFamily;
234                document.getElementById(target).style.fontSize = $.jqplot.config.errorFontSize;
235                document.getElementById(target).style.fontStyle = $.jqplot.config.errorFontStyle;
236                document.getElementById(target).style.fontWeight = $.jqplot.config.errorFontWeight;
237            }
238        }
239        else {        
240            plot.init(target, _data, _options);
241            plot.draw();
242            plot.themeEngine.init.call(plot);
243            return plot;
244        }
245    };
246
247    $.jqplot.version = "1.0.8";
248    $.jqplot.revision = "1250";
249
250    $.jqplot.targetCounter = 1;
251
252    // canvas manager to reuse canvases on the plot.
253    // Should help solve problem of canvases not being freed and
254    // problem of waiting forever for firefox to decide to free memory.
255    $.jqplot.CanvasManager = function() {
256        // canvases are managed globally so that they can be reused
257        // across plots after they have been freed
258        if (typeof $.jqplot.CanvasManager.canvases == 'undefined') {
259            $.jqplot.CanvasManager.canvases = [];
260            $.jqplot.CanvasManager.free = [];
261        }
262        
263        var myCanvases = [];
264        
265        this.getCanvas = function() {
266            var canvas;
267            var makeNew = true;
268            
269            if (!$.jqplot.use_excanvas) {
270                for (var i = 0, l = $.jqplot.CanvasManager.canvases.length; i < l; i++) {
271                    if ($.jqplot.CanvasManager.free[i] === true) {
272                        makeNew = false;
273                        canvas = $.jqplot.CanvasManager.canvases[i];
274                        // $(canvas).removeClass('jqplot-canvasManager-free').addClass('jqplot-canvasManager-inuse');
275                        $.jqplot.CanvasManager.free[i] = false;
276                        myCanvases.push(i);
277                        break;
278                    }
279                }
280            }
281
282            if (makeNew) {
283                canvas = document.createElement('canvas');
284                myCanvases.push($.jqplot.CanvasManager.canvases.length);
285                $.jqplot.CanvasManager.canvases.push(canvas);
286                $.jqplot.CanvasManager.free.push(false);
287            }   
288            
289            return canvas;
290        };
291        
292        // this method has to be used after settings the dimesions
293        // on the element returned by getCanvas()
294        this.initCanvas = function(canvas) {
295            if ($.jqplot.use_excanvas) {
296                return window.G_vmlCanvasManager.initElement(canvas);
297            }
298            return canvas;
299        };
300
301        this.freeAllCanvases = function() {
302            for (var i = 0, l=myCanvases.length; i < l; i++) {
303                this.freeCanvas(myCanvases[i]);
304            }
305            myCanvases = [];
306        };
307
308        this.freeCanvas = function(idx) {
309            if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) {
310                // excanvas can't be reused, but properly unset
311                window.G_vmlCanvasManager.uninitElement($.jqplot.CanvasManager.canvases[idx]);
312                $.jqplot.CanvasManager.canvases[idx] = null;
313            } 
314            else {
315                var canvas = $.jqplot.CanvasManager.canvases[idx];
316                canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
317                $(canvas).unbind().removeAttr('class').removeAttr('style');
318                // Style attributes seemed to be still hanging around.  wierd.  Some ticks
319                // still retained a left: 0px attribute after reusing a canvas.
320                $(canvas).css({left: '', top: '', position: ''});
321                // setting size to 0 may save memory of unused canvases?
322                canvas.width = 0;
323                canvas.height = 0;
324                $.jqplot.CanvasManager.free[idx] = true;
325            }
326        };
327        
328    };
329
330            
331    // Convienence function that won't hang IE or FF without FireBug.
332    $.jqplot.log = function() {
333        if (window.console) {
334            window.console.log.apply(window.console, arguments);
335        }
336    };
337        
338    $.jqplot.config = {
339        addDomReference: false,
340        enablePlugins:false,
341        defaultHeight:300,
342        defaultWidth:400,
343        UTCAdjust:false,
344        timezoneOffset: new Date(new Date().getTimezoneOffset() * 60000),
345        errorMessage: '',
346        errorBackground: '',
347        errorBorder: '',
348        errorFontFamily: '',
349        errorFontSize: '',
350        errorFontStyle: '',
351        errorFontWeight: '',
352        catchErrors: false,
353        defaultTickFormatString: "%.1f",
354        defaultColors: [ "#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
355        defaultNegativeColors: [ "#498991", "#C08840", "#9F9274", "#546D61", "#646C4A", "#6F6621", "#6E3F5F", "#4F64B0", "#A89050", "#C45923", "#187399", "#945381", "#959E5C", "#C7AF7B", "#478396", "#907294"],
356        dashLength: 4,
357        gapLength: 4,
358        dotGapLength: 2.5,
359        srcLocation: 'jqplot/src/',
360        pluginLocation: 'jqplot/src/plugins/'
361    };
362    
363    
364    $.jqplot.arrayMax = function( array ){
365        return Math.max.apply( Math, array );
366    };
367    
368    $.jqplot.arrayMin = function( array ){
369        return Math.min.apply( Math, array );
370    };
371    
372    $.jqplot.enablePlugins = $.jqplot.config.enablePlugins;
373    
374    // canvas related tests taken from modernizer:
375    // Copyright (c) 2009 - 2010 Faruk Ates.
376    // http://www.modernizr.com
377    
378    $.jqplot.support_canvas = function() {
379        if (typeof $.jqplot.support_canvas.result == 'undefined') {
380            $.jqplot.support_canvas.result = !!document.createElement('canvas').getContext; 
381        }
382        return $.jqplot.support_canvas.result;
383    };
384            
385    $.jqplot.support_canvas_text = function() {
386        if (typeof $.jqplot.support_canvas_text.result == 'undefined') {
387            if (window.G_vmlCanvasManager !== undefined && window.G_vmlCanvasManager._version > 887) {
388                $.jqplot.support_canvas_text.result = true;
389            }
390            else {
391                $.jqplot.support_canvas_text.result = !!(document.createElement('canvas').getContext && typeof document.createElement('canvas').getContext('2d').fillText == 'function');
392            }
393             
394        }
395        return $.jqplot.support_canvas_text.result;
396    };
397    
398    $.jqplot.use_excanvas = ((!$.support.boxModel || !$.support.objectAll || !$support.leadingWhitespace) && !$.jqplot.support_canvas()) ? true : false;
399    
400    /**
401     * 
402     * Hooks: jqPlot Pugin Hooks
403     * 
404     * $.jqplot.preInitHooks - called before initialization.
405     * $.jqplot.postInitHooks - called after initialization.
406     * $.jqplot.preParseOptionsHooks - called before user options are parsed.
407     * $.jqplot.postParseOptionsHooks - called after user options are parsed.
408     * $.jqplot.preDrawHooks - called before plot draw.
409     * $.jqplot.postDrawHooks - called after plot draw.
410     * $.jqplot.preDrawSeriesHooks - called before each series is drawn.
411     * $.jqplot.postDrawSeriesHooks - called after each series is drawn.
412     * $.jqplot.preDrawLegendHooks - called before the legend is drawn.
413     * $.jqplot.addLegendRowHooks - called at the end of legend draw, so plugins
414     *     can add rows to the legend table.
415     * $.jqplot.preSeriesInitHooks - called before series is initialized.
416     * $.jqplot.postSeriesInitHooks - called after series is initialized.
417     * $.jqplot.preParseSeriesOptionsHooks - called before series related options
418     *     are parsed.
419     * $.jqplot.postParseSeriesOptionsHooks - called after series related options
420     *     are parsed.
421     * $.jqplot.eventListenerHooks - called at the end of plot drawing, binds
422     *     listeners to the event canvas which lays on top of the grid area.
423     * $.jqplot.preDrawSeriesShadowHooks - called before series shadows are drawn.
424     * $.jqplot.postDrawSeriesShadowHooks - called after series shadows are drawn.
425     * 
426     */
427    
428    $.jqplot.preInitHooks = [];
429    $.jqplot.postInitHooks = [];
430    $.jqplot.preParseOptionsHooks = [];
431    $.jqplot.postParseOptionsHooks = [];
432    $.jqplot.preDrawHooks = [];
433    $.jqplot.postDrawHooks = [];
434    $.jqplot.preDrawSeriesHooks = [];
435    $.jqplot.postDrawSeriesHooks = [];
436    $.jqplot.preDrawLegendHooks = [];
437    $.jqplot.addLegendRowHooks = [];
438    $.jqplot.preSeriesInitHooks = [];
439    $.jqplot.postSeriesInitHooks = [];
440    $.jqplot.preParseSeriesOptionsHooks = [];
441    $.jqplot.postParseSeriesOptionsHooks = [];
442    $.jqplot.eventListenerHooks = [];
443    $.jqplot.preDrawSeriesShadowHooks = [];
444    $.jqplot.postDrawSeriesShadowHooks = [];
445
446    // A superclass holding some common properties and methods.
447    $.jqplot.ElemContainer = function() {
448        this._elem;
449        this._plotWidth;
450        this._plotHeight;
451        this._plotDimensions = {height:null, width:null};
452    };
453    
454    $.jqplot.ElemContainer.prototype.createElement = function(el, offsets, clss, cssopts, attrib) {
455        this._offsets = offsets;
456        var klass = clss || 'jqplot';
457        var elem = document.createElement(el);
458        this._elem = $(elem);
459        this._elem.addClass(klass);
460        this._elem.css(cssopts);
461        this._elem.attr(attrib);
462        // avoid memory leak;
463        elem = null;
464        return this._elem;
465    };
466    
467    $.jqplot.ElemContainer.prototype.getWidth = function() {
468        if (this._elem) {
469            return this._elem.outerWidth(true);
470        }
471        else {
472            return null;
473        }
474    };
475    
476    $.jqplot.ElemContainer.prototype.getHeight = function() {
477        if (this._elem) {
478            return this._elem.outerHeight(true);
479        }
480        else {
481            return null;
482        }
483    };
484    
485    $.jqplot.ElemContainer.prototype.getPosition = function() {
486        if (this._elem) {
487            return this._elem.position();
488        }
489        else {
490            return {top:null, left:null, bottom:null, right:null};
491        }
492    };
493    
494    $.jqplot.ElemContainer.prototype.getTop = function() {
495        return this.getPosition().top;
496    };
497    
498    $.jqplot.ElemContainer.prototype.getLeft = function() {
499        return this.getPosition().left;
500    };
501    
502    $.jqplot.ElemContainer.prototype.getBottom = function() {
503        return this._elem.css('bottom');
504    };
505    
506    $.jqplot.ElemContainer.prototype.getRight = function() {
507        return this._elem.css('right');
508    };
509    
510
511    /**
512     * Class: Axis
513     * An individual axis object.  Cannot be instantiated directly, but created
514     * by the Plot object.  Axis properties can be set or overridden by the 
515     * options passed in from the user.
516     * 
517     */
518    function Axis(name) {
519        $.jqplot.ElemContainer.call(this);
520        // Group: Properties
521        //
522        // Axes options are specified within an axes object at the top level of the 
523        // plot options like so:
524        // > {
525        // >    axes: {
526        // >        xaxis: {min: 5},
527        // >        yaxis: {min: 2, max: 8, numberTicks:4},
528        // >        x2axis: {pad: 1.5},
529        // >        y2axis: {ticks:[22, 44, 66, 88]}
530        // >        }
531        // > }
532        // There are 2 x axes, 'xaxis' and 'x2axis', and 
533        // 9 yaxes, 'yaxis', 'y2axis'. 'y3axis', ...  Any or all of which may be specified.
534        this.name = name;
535        this._series = [];
536        // prop: show
537        // Wether to display the axis on the graph.
538        this.show = false;
539        // prop: tickRenderer
540        // A class of a rendering engine for creating the ticks labels displayed on the plot, 
541        // See <$.jqplot.AxisTickRenderer>.
542        this.tickRenderer = $.jqplot.AxisTickRenderer;
543        // prop: tickOptions
544        // Options that will be passed to the tickRenderer, see <$.jqplot.AxisTickRenderer> options.
545        this.tickOptions = {};
546        // prop: labelRenderer
547        // A class of a rendering engine for creating an axis label.
548        this.labelRenderer = $.jqplot.AxisLabelRenderer;
549        // prop: labelOptions
550        // Options passed to the label renderer.
551        this.labelOptions = {};
552        // prop: label
553        // Label for the axis
554        this.label = null;
555        // prop: showLabel
556        // true to show the axis label.
557        this.showLabel = true;
558        // prop: min
559        // minimum value of the axis (in data units, not pixels).
560        this.min = null;
561        // prop: max
562        // maximum value of the axis (in data units, not pixels).
563        this.max = null;
564        // prop: autoscale
565        // DEPRECATED
566        // the default scaling algorithm produces superior results.
567        this.autoscale = false;
568        // prop: pad
569        // Padding to extend the range above and below the data bounds.
570        // The data range is multiplied by this factor to determine minimum and maximum axis bounds.
571        // A value of 0 will be interpreted to mean no padding, and pad will be set to 1.0.
572        this.pad = 1.2;
573        // prop: padMax
574        // Padding to extend the range above data bounds.
575        // The top of the data range is multiplied by this factor to determine maximum axis bounds.
576        // A value of 0 will be interpreted to mean no padding, and padMax will be set to 1.0.
577        this.padMax = null;
578        // prop: padMin
579        // Padding to extend the range below data bounds.
580        // The bottom of the data range is multiplied by this factor to determine minimum axis bounds.
581        // A value of 0 will be interpreted to mean no padding, and padMin will be set to 1.0.
582        this.padMin = null;
583        // prop: ticks
584        // 1D [val, val, ...] or 2D [[val, label], [val, label], ...] array of ticks for the axis.
585        // If no label is specified, the value is formatted into an appropriate label.
586        this.ticks = [];
587        // prop: numberTicks
588        // Desired number of ticks.  Default is to compute automatically.
589        this.numberTicks;
590        // prop: tickInterval
591        // number of units between ticks.  Mutually exclusive with numberTicks.
592        this.tickInterval;
593        // prop: renderer
594        // A class of a rendering engine that handles tick generation, 
595        // scaling input data to pixel grid units and drawing the axis element.
596        this.renderer = $.jqplot.LinearAxisRenderer;
597        // prop: rendererOptions
598        // renderer specific options.  See <$.jqplot.LinearAxisRenderer> for options.
599        this.rendererOptions = {};
600        // prop: showTicks
601        // Wether to show the ticks (both marks and labels) or not.
602        // Will not override showMark and showLabel options if specified on the ticks themselves.
603        this.showTicks = true;
604        // prop: showTickMarks
605        // Wether to show the tick marks (line crossing grid) or not.
606        // Overridden by showTicks and showMark option of tick itself.
607        this.showTickMarks = true;
608        // prop: showMinorTicks
609        // Wether or not to show minor ticks.  This is renderer dependent.
610        this.showMinorTicks = true;
611        // prop: drawMajorGridlines
612        // True to draw gridlines for major axis ticks.
613        this.drawMajorGridlines = true;
614        // prop: drawMinorGridlines
615        // True to draw gridlines for minor ticks.
616        this.drawMinorGridlines = false;
617        // prop: drawMajorTickMarks
618        // True to draw tick marks for major axis ticks.
619        this.drawMajorTickMarks = true;
620        // prop: drawMinorTickMarks
621        // True to draw tick marks for minor ticks.  This is renderer dependent.
622        this.drawMinorTickMarks = true;
623        // prop: useSeriesColor
624        // Use the color of the first series associated with this axis for the
625        // tick marks and line bordering this axis.
626        this.useSeriesColor = false;
627        // prop: borderWidth
628        // width of line stroked at the border of the axis.  Defaults
629        // to the width of the grid boarder.
630        this.borderWidth = null;
631        // prop: borderColor
632        // color of the border adjacent to the axis.  Defaults to grid border color.
633        this.borderColor = null;
634        // prop: scaleToHiddenSeries
635        // True to include hidden series when computing axes bounds and scaling.
636        this.scaleToHiddenSeries = false;
637        // minimum and maximum values on the axis.
638        this._dataBounds = {min:null, max:null};
639        // statistics (min, max, mean) as well as actual data intervals for each series attached to axis.
640        // holds collection of {intervals:[], min:, max:, mean: } objects for each series on axis.
641        this._intervalStats = [];
642        // pixel position from the top left of the min value and max value on the axis.
643        this._offsets = {min:null, max:null};
644        this._ticks=[];
645        this._label = null;
646        // prop: syncTicks
647        // true to try and synchronize tick spacing across multiple axes so that ticks and
648        // grid lines line up.  This has an impact on autoscaling algorithm, however.
649        // In general, autoscaling an individual axis will work better if it does not
650        // have to sync ticks.
651        this.syncTicks = null;
652        // prop: tickSpacing
653        // Approximate pixel spacing between ticks on graph.  Used during autoscaling.
654        // This number will be an upper bound, actual spacing will be less.
655        this.tickSpacing = 75;
656        // Properties to hold the original values for min, max, ticks, tickInterval and numberTicks
657        // so they can be restored if altered by plugins.
658        this._min = null;
659        this._max = null;
660        this._tickInterval = null;
661        this._numberTicks = null;
662        this.__ticks = null;
663        // hold original user options.
664        this._options = {};
665    }
666    
667    Axis.prototype = new $.jqplot.ElemContainer();
668    Axis.prototype.constructor = Axis;
669    
670    Axis.prototype.init = function() {
671        if ($.isFunction(this.renderer)) {
672            this.renderer = new this.renderer();  
673        }
674        // set the axis name
675        this.tickOptions.axis = this.name;
676        // if showMark or showLabel tick options not specified, use value of axis option.
677        // showTicks overrides showTickMarks.
678        if (this.tickOptions.showMark == null) {
679            this.tickOptions.showMark = this.showTicks;
680        }
681        if (this.tickOptions.showMark == null) {
682            this.tickOptions.showMark = this.showTickMarks;
683        }
684        if (this.tickOptions.showLabel == null) {
685            this.tickOptions.showLabel = this.showTicks;
686        }
687        
688        if (this.label == null || this.label == '') {
689            this.showLabel = false;
690        }
691        else {
692            this.labelOptions.label = this.label;
693        }
694        if (this.showLabel == false) {
695            this.labelOptions.show = false;
696        }
697        // set the default padMax, padMin if not specified
698        // special check, if no padding desired, padding
699        // should be set to 1.0
700        if (this.pad == 0) {
701            this.pad = 1.0;
702        }
703        if (this.padMax == 0) {
704            this.padMax = 1.0;
705        }
706        if (this.padMin == 0) {
707            this.padMin = 1.0;
708        }
709        if (this.padMax == null) {
710            this.padMax = (this.pad-1)/2 + 1;
711        }
712        if (this.padMin == null) {
713            this.padMin = (this.pad-1)/2 + 1;
714        }
715        // now that padMin and padMax are correctly set, reset pad in case user has supplied 
716        // padMin and/or padMax
717        this.pad = this.padMax + this.padMin - 1;
718        if (this.min != null || this.max != null) {
719            this.autoscale = false;
720        }
721        // if not set, sync ticks for y axes but not x by default.
722        if (this.syncTicks == null && this.name.indexOf('y') > -1) {
723            this.syncTicks = true;
724        }
725        else if (this.syncTicks == null){
726            this.syncTicks = false;
727        }
728        this.renderer.init.call(this, this.rendererOptions);
729        
730    };
731    
732    Axis.prototype.draw = function(ctx, plot) {
733        // Memory Leaks patch
734        if (this.__ticks) {
735          this.__ticks = null;
736        }
737
738        return this.renderer.draw.call(this, ctx, plot);
739        
740    };
741    
742    Axis.prototype.set = function() {
743        this.renderer.set.call(this);
744    };
745    
746    Axis.prototype.pack = function(pos, offsets) {
747        if (this.show) {
748            this.renderer.pack.call(this, pos, offsets);
749        }
750        // these properties should all be available now.
751        if (this._min == null) {
752            this._min = this.min;
753            this._max = this.max;
754            this._tickInterval = this.tickInterval;
755            this._numberTicks = this.numberTicks;
756            this.__ticks = this._ticks;
757        }
758    };
759    
760    // reset the axis back to original values if it has been scaled, zoomed, etc.
761    Axis.prototype.reset = function() {
762        this.renderer.reset.call(this);
763    };
764    
765    Axis.prototype.resetScale = function(opts) {
766        $.extend(true, this, {min: null, max: null, numberTicks: null, tickInterval: null, _ticks: [], ticks: []}, opts);
767        this.resetDataBounds();
768    };
769    
770    Axis.prototype.resetDataBounds = function() {
771        // Go through all the series attached to this axis and find
772        // the min/max bounds for this axis.
773        var db = this._dataBounds;
774        db.min = null;
775        db.max = null;
776        var l, s, d;
777        // check for when to force min 0 on bar series plots.
778        var doforce = (this.show) ? true : false;
779        for (var i=0; i<this._series.length; i++) {
780            s = this._series[i];
781            if (s.show || this.scaleToHiddenSeries) {
782                d = s._plotData;
783                if (s._type === 'line' && s.renderer.bands.show && this.name.charAt(0) !== 'x') {
784                    d = [[0, s.renderer.bands._min], [1, s.renderer.bands._max]];
785                }
786
787                var minyidx = 1, maxyidx = 1;
788
789                if (s._type != null && s._type == 'ohlc') {
790                    minyidx = 3;
791                    maxyidx = 2;
792                }
793                
794                for (var j=0, l=d.length; j<l; j++) { 
795                    if (this.name == 'xaxis' || this.name == 'x2axis') {
796                        if ((d[j][0] != null && d[j][0] < db.min) || db.min == null) {
797                            db.min = d[j][0];
798                        }
799                        if ((d[j][0] != null && d[j][0] > db.max) || db.max == null) {
800                            db.max = d[j][0];
801                        }
802                    }              
803                    else {
804                        if ((d[j][minyidx] != null && d[j][minyidx] < db.min) || db.min == null) {
805                            db.min = d[j][minyidx];
806                        }
807                        if ((d[j][maxyidx] != null && d[j][maxyidx] > db.max) || db.max == null) {
808                            db.max = d[j][maxyidx];
809                        }
810                    }              
811                }
812
813                // Hack to not pad out bottom of bar plots unless user has specified a padding.
814                // every series will have a chance to set doforce to false.  once it is set to 
815                // false, it cannot be reset to true.
816                // If any series attached to axis is not a bar, wont force 0.
817                if (doforce && s.renderer.constructor !== $.jqplot.BarRenderer) {
818                    doforce = false;
819                }
820
821                else if (doforce && this._options.hasOwnProperty('forceTickAt0') && this._options.forceTickAt0 == false) {
822                    doforce = false;
823                }
824
825                else if (doforce && s.renderer.constructor === $.jqplot.BarRenderer) {
826                    if (s.barDirection == 'vertical' && this.name != 'xaxis' && this.name != 'x2axis') { 
827                        if (this._options.pad != null || this._options.padMin != null) {
828                            doforce = false;
829                        }
830                    }
831
832                    else if (s.barDirection == 'horizontal' && (this.name == 'xaxis' || this.name == 'x2axis')) {
833                        if (this._options.pad != null || this._options.padMin != null) {
834                            doforce = false;
835                        }
836                    }
837
838                }
839            }
840        }
841
842        if (doforce && this.renderer.constructor === $.jqplot.LinearAxisRenderer && db.min >= 0) {
843            this.padMin = 1.0;
844            this.forceTickAt0 = true;
845        }
846    };
847
848    /**
849     * Class: Legend
850     * Legend object.  Cannot be instantiated directly, but created
851     * by the Plot object.  Legend properties can be set or overridden by the 
852     * options passed in from the user.
853     */
854    function Legend(options) {
855        $.jqplot.ElemContainer.call(this);
856        // Group: Properties
857        
858        // prop: show
859        // Wether to display the legend on the graph.
860        this.show = false;
861        // prop: location
862        // Placement of the legend.  one of the compass directions: nw, n, ne, e, se, s, sw, w
863        this.location = 'ne';
864        // prop: labels
865        // Array of labels to use.  By default the renderer will look for labels on the series.
866        // Labels specified in this array will override labels specified on the series.
867        this.labels = [];
868        // prop: showLabels
869        // true to show the label text on the  legend.
870        this.showLabels = true;
871        // prop: showSwatch
872        // true to show the color swatches on the legend.
873        this.showSwatches = true;
874        // prop: placement
875        // "insideGrid" places legend inside the grid area of the plot.
876        // "outsideGrid" places the legend outside the grid but inside the plot container, 
877        // shrinking the grid to accomodate the legend.
878        // "inside" synonym for "insideGrid", 
879        // "outside" places the legend ouside the grid area, but does not shrink the grid which
880        // can cause the legend to overflow the plot container.
881        this.placement = "insideGrid";
882        // prop: xoffset
883        // DEPRECATED.  Set the margins on the legend using the marginTop, marginLeft, etc. 
884        // properties or via CSS margin styling of the .jqplot-table-legend class.
885        this.xoffset = 0;
886        // prop: yoffset
887        // DEPRECATED.  Set the margins on the legend using the marginTop, marginLeft, etc. 
888        // properties or via CSS margin styling of the .jqplot-table-legend class.
889        this.yoffset = 0;
890        // prop: border
891        // css spec for the border around the legend box.
892        this.border;
893        // prop: background
894        // css spec for the background of the legend box.
895        this.background;
896        // prop: textColor
897        // css color spec for the legend text.
898        this.textColor;
899        // prop: fontFamily
900        // css font-family spec for the legend text.
901        this.fontFamily; 
902        // prop: fontSize
903        // css font-size spec for the legend text.
904        this.fontSize ;
905        // prop: rowSpacing
906        // css padding-top spec for the rows in the legend.
907        this.rowSpacing = '0.5em';
908        // renderer
909        // A class that will create a DOM object for the legend,
910        // see <$.jqplot.TableLegendRenderer>.
911        this.renderer = $.jqplot.TableLegendRenderer;
912        // prop: rendererOptions
913        // renderer specific options passed to the renderer.
914        this.rendererOptions = {};
915        // prop: predraw
916        // Wether to draw the legend before the series or not.
917        // Used with series specific legend renderers for pie, donut, mekko charts, etc.
918        this.preDraw = false;
919        // prop: marginTop
920        // CSS margin for the legend DOM element. This will set an element 
921        // CSS style for the margin which will override any style sheet setting.
922        // The default will be taken from the stylesheet.
923        this.marginTop = null;
924        // prop: marginRight
925        // CSS margin for the legend DOM element. This will set an element 
926        // CSS style for the margin which will override any style sheet setting.
927        // The default will be taken from the stylesheet.
928        this.marginRight = null;
929        // prop: marginBottom
930        // CSS margin for the legend DOM element. This will set an element 
931        // CSS style for the margin which will override any style sheet setting.
932        // The default will be taken from the stylesheet.
933        this.marginBottom = null;
934        // prop: marginLeft
935        // CSS margin for the legend DOM element. This will set an element 
936        // CSS style for the margin which will override any style sheet setting.
937        // The default will be taken from the stylesheet.
938        this.marginLeft = null;
939        // prop: escapeHtml
940        // True to escape special characters with their html entity equivalents
941        // in legend text.  "<" becomes &lt; and so on, so html tags are not rendered.
942        this.escapeHtml = false;
943        this._series = [];
944        
945        $.extend(true, this, options);
946    }
947    
948    Legend.prototype = new $.jqplot.ElemContainer();
949    Legend.prototype.constructor = Legend;
950    
951    Legend.prototype.setOptions = function(options) {
952        $.extend(true, this, options);
953        
954        // Try to emulate deprecated behaviour
955        // if user has specified xoffset or yoffset, copy these to
956        // the margin properties.
957        
958        if (this.placement ==  'inside') {
959            this.placement = 'insideGrid';
960        }
961        
962        if (this.xoffset >0) {
963            if (this.placement == 'insideGrid') {
964                switch (this.location) {
965                    case 'nw':
966                    case 'w':
967                    case 'sw':
968                        if (this.marginLeft == null) {
969                            this.marginLeft = this.xoffset + 'px';
970                        }
971                        this.marginRight = '0px';
972                        break;
973                    case 'ne':
974                    case 'e':
975                    case 'se':
976                    default:
977                        if (this.marginRight == null) {
978                            this.marginRight = this.xoffset + 'px';
979                        }
980                        this.marginLeft = '0px';
981                        break;
982                }
983            }
984            else if (this.placement == 'outside') {
985                switch (this.location) {
986                    case 'nw':
987                    case 'w':
988                    case 'sw':
989                        if (this.marginRight == null) {
990                            this.marginRight = this.xoffset + 'px';
991                        }
992                        this.marginLeft = '0px';
993                        break;
994                    case 'ne':
995                    case 'e':
996                    case 'se':
997                    default:
998                        if (this.marginLeft == null) {
999                            this.marginLeft = this.xoffset + 'px';
1000                        }
1001                        this.marginRight = '0px';
1002                        break;
1003                }
1004            }
1005            this.xoffset = 0;
1006        }
1007        
1008        if (this.yoffset >0) {
1009            if (this.placement == 'outside') {
1010                switch (this.location) {
1011                    case 'sw':
1012                    case 's':
1013                    case 'se':
1014                        if (this.marginTop == null) {
1015                            this.marginTop = this.yoffset + 'px';
1016                        }
1017                        this.marginBottom = '0px';
1018                        break;
1019                    case 'ne':
1020                    case 'n':
1021                    case 'nw':
1022                    default:
1023                        if (this.marginBottom == null) {
1024                            this.marginBottom = this.yoffset + 'px';
1025                        }
1026                        this.marginTop = '0px';
1027                        break;
1028                }
1029            }
1030            else if (this.placement == 'insideGrid') {
1031                switch (this.location) {
1032                    case 'sw':
1033                    case 's':
1034                    case 'se':
1035                        if (this.marginBottom == null) {
1036                            this.marginBottom = this.yoffset + 'px';
1037                        }
1038                        this.marginTop = '0px';
1039                        break;
1040                    case 'ne':
1041                    case 'n':
1042                    case 'nw':
1043                    default:
1044                        if (this.marginTop == null) {
1045                            this.marginTop = this.yoffset + 'px';
1046                        }
1047                        this.marginBottom = '0px';
1048                        break;
1049                }
1050            }
1051            this.yoffset = 0;
1052        }
1053        
1054        // TO-DO:
1055        // Handle case where offsets are < 0.
1056        //
1057    };
1058    
1059    Legend.prototype.init = function() {
1060        if ($.isFunction(this.renderer)) {
1061            this.renderer = new this.renderer();  
1062        }
1063        this.renderer.init.call(this, this.rendererOptions);
1064    };
1065    
1066    Legend.prototype.draw = function(offsets, plot) {
1067        for (var i=0; i<$.jqplot.preDrawLegendHooks.length; i++){
1068            $.jqplot.preDrawLegendHooks[i].call(this, offsets);
1069        }
1070        return this.renderer.draw.call(this, offsets, plot);
1071    };
1072    
1073    Legend.prototype.pack = function(offsets) {
1074        this.renderer.pack.call(this, offsets);
1075    };
1076
1077    /**
1078     * Class: Title
1079     * Plot Title object.  Cannot be instantiated directly, but created
1080     * by the Plot object.  Title properties can be set or overridden by the 
1081     * options passed in from the user.
1082     * 
1083     * Parameters:
1084     * text - text of the title.
1085     */
1086    function Title(text) {
1087        $.jqplot.ElemContainer.call(this);
1088        // Group: Properties
1089        
1090        // prop: text
1091        // text of the title;
1092        this.text = text;
1093        // prop: show
1094        // whether or not to show the title
1095        this.show = true;
1096        // prop: fontFamily
1097        // css font-family spec for the text.
1098        this.fontFamily;
1099        // prop: fontSize
1100        // css font-size spec for the text.
1101        this.fontSize ;
1102        // prop: textAlign
1103        // css text-align spec for the text.
1104        this.textAlign;
1105        // prop: textColor
1106        // css color spec for the text.
1107        this.textColor;
1108        // prop: renderer
1109        // A class for creating a DOM element for the title,
1110        // see <$.jqplot.DivTitleRenderer>.
1111        this.renderer = $.jqplot.DivTitleRenderer;
1112        // prop: rendererOptions
1113        // renderer specific options passed to the renderer.
1114        this.rendererOptions = {};   
1115        // prop: escapeHtml
1116        // True to escape special characters with their html entity equivalents
1117        // in title text.  "<" becomes &lt; and so on, so html tags are not rendered.
1118        this.escapeHtml = false;
1119    }
1120    
1121    Title.prototype = new $.jqplot.ElemContainer();
1122    Title.prototype.constructor = Title;
1123    
1124    Title.prototype.init = function() {
1125        if ($.isFunction(this.renderer)) {
1126            this.renderer = new this.renderer();  
1127        }
1128        this.renderer.init.call(this, this.rendererOptions);
1129    };
1130    
1131    Title.prototype.draw = function(width) {
1132        return this.renderer.draw.call(this, width);
1133    };
1134    
1135    Title.prototype.pack = function() {
1136        this.renderer.pack.call(this);
1137    };
1138
1139
1140    /**
1141     * Class: Series
1142     * An individual data series object.  Cannot be instantiated directly, but created
1143     * by the Plot object.  Series properties can be set or overridden by the 
1144     * options passed in from the user.
1145     */
1146    function Series(options) {
1147        options = options || {};
1148        $.jqplot.ElemContainer.call(this);
1149        // Group: Properties
1150        // Properties will be assigned from a series array at the top level of the
1151        // options.  If you had two series and wanted to change the color and line
1152        // width of the first and set the second to use the secondary y axis with
1153        // no shadow and supply custom labels for each:
1154        // > {
1155        // >    series:[
1156        // >        {color: '#ff4466', lineWidth: 5, label:'good line'},
1157        // >        {yaxis: 'y2axis', shadow: false, label:'bad line'}
1158        // >    ]
1159        // > }
1160
1161        // prop: show
1162        // whether or not to draw the series.
1163        this.show = true;
1164        // prop: xaxis
1165        // which x axis to use with this series, either 'xaxis' or 'x2axis'.
1166        this.xaxis = 'xaxis';
1167        this._xaxis;
1168        // prop: yaxis
1169        // which y axis to use with this series, either 'yaxis' or 'y2axis'.
1170        this.yaxis = 'yaxis';
1171        this._yaxis;
1172        this.gridBorderWidth = 2.0;
1173        // prop: renderer
1174        // A class of a renderer which will draw the series, 
1175        // see <$.jqplot.LineRenderer>.
1176        this.renderer = $.jqplot.LineRenderer;
1177        // prop: rendererOptions
1178        // Options to pass on to the renderer.
1179        this.rendererOptions = {};
1180        this.data = [];
1181        this.gridData = [];
1182        // prop: label
1183        // Line label to use in the legend.
1184        this.label = '';
1185        // prop: showLabel
1186        // true to show label for this series in the legend.
1187        this.showLabel = true;
1188        // prop: color
1189        // css color spec for the series
1190        this.color;
1191        // prop: negativeColor
1192        // css color spec used for filled (area) plots that are filled to zero and
1193        // the "useNegativeColors" option is true.
1194        this.negativeColor;
1195        // prop: lineWidth
1196        // width of the line in pixels.  May have different meanings depending on renderer.
1197        this.lineWidth = 2.5;
1198        // prop: lineJoin
1199        // Canvas lineJoin style between segments of series.
1200        this.lineJoin = 'round';
1201        // prop: lineCap
1202        // Canvas lineCap style at ends of line.
1203        this.lineCap = 'round';
1204        // prop: linePattern
1205        // line pattern 'dashed', 'dotted', 'solid', some combination
1206        // of '-' and '.' characters such as '.-.' or a numerical array like 
1207        // [draw, skip, draw, skip, ...] such as [1, 10] to draw a dotted line, 
1208        // [1, 10, 20, 10] to draw a dot-dash line, and so on.
1209        this.linePattern = 'solid';
1210        this.shadow = true;
1211        // prop: shadowAngle
1212        // Shadow angle in degrees
1213        this.shadowAngle = 45;
1214        // prop: shadowOffset
1215        // Shadow offset from line in pixels
1216        this.shadowOffset = 1.25;
1217        // prop: shadowDepth
1218        // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
1219        this.shadowDepth = 3;
1220        // prop: shadowAlpha
1221        // Alpha channel transparency of shadow.  0 = transparent.
1222        this.shadowAlpha = '0.1';
1223        // prop: breakOnNull
1224        // Wether line segments should be be broken at null value.
1225        // False will join point on either side of line.
1226        this.breakOnNull = false;
1227        // prop: markerRenderer
1228        // A class of a renderer which will draw marker (e.g. circle, square, ...) at the data points,
1229        // see <$.jqplot.MarkerRenderer>.
1230        this.markerRenderer = $.jqplot.MarkerRenderer;
1231        // prop: markerOptions
1232        // renderer specific options to pass to the markerRenderer,
1233        // see <$.jqplot.MarkerRenderer>.
1234        this.markerOptions = {};
1235        // prop: showLine
1236        // whether to actually draw the line or not.  Series will still be renderered, even if no line is drawn.
1237        this.showLine = true;
1238        // prop: showMarker
1239        // whether or not to show the markers at the data points.
1240        this.showMarker = true;
1241        // prop: index
1242        // 0 based index of this series in the plot series array.
1243        this.index;
1244        // prop: fill
1245        // true or false, whether to fill under lines or in bars.
1246        // May not be implemented in all renderers.
1247        this.fill = false;
1248        // prop: fillColor
1249        // CSS color spec to use for fill under line.  Defaults to line color.
1250        this.fillColor;
1251        // prop: fillAlpha
1252        // Alpha transparency to apply to the fill under the line.
1253        // Use this to adjust alpha separate from fill color.
1254        this.fillAlpha;
1255        // prop: fillAndStroke
1256        // If true will stroke the line (with color this.color) as well as fill under it.
1257        // Applies only when fill is true.
1258        this.fillAndStroke = false;
1259        // prop: disableStack
1260        // true to not stack this series with other series in the plot.
1261        // To render properly, non-stacked series must come after any stacked series
1262        // in the plot's data series array.  So, the plot's data series array would look like:
1263        // > [stackedSeries1, stackedSeries2, ..., nonStackedSeries1, nonStackedSeries2, ...]
1264        // disableStack will put a gap in the stacking order of series, and subsequent
1265        // stacked series will not fill down through the non-stacked series and will
1266        // most likely not stack properly on top of the non-stacked series.
1267        this.disableStack = false;
1268        // _stack is set by the Plot if the plot is a stacked chart.
1269        // will stack lines or bars on top of one another to build a "mountain" style chart.
1270        // May not be implemented in all renderers.
1271        this._stack = false;
1272        // prop: neighborThreshold
1273        // how close or far (in pixels) the cursor must be from a point marker to detect the point.
1274        this.neighborThreshold = 4;
1275        // prop: fillToZero
1276        // true will force bar and filled series to fill toward zero on the fill Axis.
1277        this.fillToZero = false;
1278        // prop: fillToValue
1279        // fill a filled series to this value on the fill axis.
1280        // Works in conjunction with fillToZero, so that must be true.
1281        this.fillToValue = 0;
1282        // prop: fillAxis
1283        // Either 'x' or 'y'.  Which axis to fill the line toward if fillToZero is true.
1284        // 'y' means fill up/down to 0 on the y axis for this series.
1285        this.fillAxis = 'y';
1286        // prop: useNegativeColors
1287        // true to color negative values differently in filled and bar charts.
1288        this.useNegativeColors = true;
1289        this._stackData = [];
1290        // _plotData accounts for stacking.  If plots not stacked, _plotData and data are same.  If
1291        // stacked, _plotData is accumulation of stacking data.
1292        this._plotData = [];
1293        // _plotValues hold the individual x and y values that will be plotted for this series.
1294        this._plotValues = {x:[], y:[]};
1295        // statistics about the intervals between data points.  Used for auto scaling.
1296        this._intervals = {x:{}, y:{}};
1297        // data from the previous series, for stacked charts.
1298        this._prevPlotData = [];
1299        this._prevGridData = [];
1300        this._stackAxis = 'y';
1301        this._primaryAxis = '_xaxis';
1302        // give each series a canvas to draw on.  This should allow for redrawing speedups.
1303        this.canvas = new $.jqplot.GenericCanvas();
1304        this.shadowCanvas = new $.jqplot.GenericCanvas();
1305        this.plugins = {};
1306        // sum of y values in this series.
1307        this._sumy = 0;
1308        this._sumx = 0;
1309        this._type = '';
1310    }
1311    
1312    Series.prototype = new $.jqplot.ElemContainer();
1313    Series.prototype.constructor = Series;
1314    
1315    Series.prototype.init = function(index, gridbw, plot) {
1316        // weed out any null values in the data.
1317        this.index = index;
1318        this.gridBorderWidth = gridbw;
1319        var d = this.data;
1320        var temp = [], i, l;
1321        for (i=0, l=d.length; i<l; i++) {
1322            if (! this.breakOnNull) {
1323                if (d[i] == null || d[i][0] == null || d[i][1] == null) {
1324                    continue;
1325                }
1326                else {
1327                    temp.push(d[i]);
1328                }
1329            }
1330            else {
1331                // TODO: figure out what to do with null values
1332                // probably involve keeping nulls in data array
1333                // and then updating renderers to break line
1334                // when it hits null value.
1335                // For now, just keep value.
1336                temp.push(d[i]);
1337            }
1338        }
1339        this.data = temp;
1340
1341        // parse the renderer options and apply default colors if not provided
1342        // Set color even if not shown, so series don't change colors when other
1343        // series on plot shown/hidden.
1344        if (!this.color) {
1345            this.color = plot.colorGenerator.get(this.index);
1346        }
1347        if (!this.negativeColor) {
1348            this.negativeColor = plot.negativeColorGenerator.get(this.index);
1349        }
1350
1351
1352        if (!this.fillColor) {
1353            this.fillColor = this.color;
1354        }
1355        if (this.fillAlpha) {
1356            var comp = $.jqplot.normalize2rgb(this.fillColor);
1357            var comp = $.jqplot.getColorComponents(comp);
1358            this.fillColor = 'rgba('+comp[0]+','+comp[1]+','+comp[2]+','+this.fillAlpha+')';
1359        }
1360        if ($.isFunction(this.renderer)) {
1361            this.renderer = new this.renderer();  
1362        }
1363        this.renderer.init.call(this, this.rendererOptions, plot);
1364        this.markerRenderer = new this.markerRenderer();
1365        if (!this.markerOptions.color) {
1366            this.markerOptions.color = this.color;
1367        }
1368        if (this.markerOptions.show == null) {
1369            this.markerOptions.show = this.showMarker;
1370        }
1371        this.showMarker = this.markerOptions.show;
1372        // the markerRenderer is called within its own scope, don't want to overwrite series options!!
1373        this.markerRenderer.init(this.markerOptions);
1374    };
1375    
1376    // data - optional data point array to draw using this series renderer
1377    // gridData - optional grid data point array to draw using this series renderer
1378    // stackData - array of cumulative data for stacked plots.
1379    Series.prototype.draw = function(sctx, opts, plot) {
1380        var options = (opts == undefined) ? {} : opts;
1381        sctx = (sctx == undefined) ? this.canvas._ctx : sctx;
1382        
1383        var j, data, gridData;
1384        
1385        // hooks get called even if series not shown
1386        // we don't clear canvas here, it would wipe out all other series as well.
1387        for (j=0; j<$.jqplot.preDrawSeriesHooks.length; j++) {
1388            $.jqplot.preDrawSeriesHooks[j].call(this, sctx, options);
1389        }
1390        if (this.show) {
1391            this.renderer.setGridData.call(this, plot);
1392            if (!options.preventJqPlotSeriesDrawTrigger) {
1393                $(sctx.canvas).trigger('jqplotSeriesDraw', [this.data, this.gridData]);
1394            }
1395            data = [];
1396            if (options.data) {
1397                data = options.data;
1398            }
1399            else if (!this._stack) {
1400                data = this.data;
1401            }
1402            else {
1403                data = this._plotData;
1404            }
1405            gridData = options.gridData || this.renderer.makeGridData.call(this, data, plot);
1406
1407            if (this._type === 'line' && this.renderer.smooth && this.renderer._smoothedData.length) {
1408                gridData = this.renderer._smoothedData;
1409            }
1410
1411            this.renderer.draw.call(this, sctx, gridData, options, plot);
1412        }
1413        
1414        for (j=0; j<$.jqplot.postDrawSeriesHooks.length; j++) {
1415            $.jqplot.postDrawSeriesHooks[j].call(this, sctx, options, plot);
1416        }
1417        
1418        sctx = opts = plot = j = data = gridData = null;
1419    };
1420    
1421    Series.prototype.drawShadow = function(sctx, opts, plot) {
1422        var options = (opts == undefined) ? {} : opts;
1423        sctx = (sctx == undefined) ? this.shadowCanvas._ctx : sctx;
1424        
1425        var j, data, gridData;
1426        
1427        // hooks get called even if series not shown
1428        // we don't clear canvas here, it would wipe out all other series as well.
1429        for (j=0; j<$.jqplot.preDrawSeriesShadowHooks.length; j++) {
1430            $.jqplot.preDrawSeriesShadowHooks[j].call(this, sctx, options);
1431        }
1432        if (this.shadow) {
1433            this.renderer.setGridData.call(this, plot);
1434
1435            data = [];
1436            if (options.data) {
1437                data = options.data;
1438            }
1439            else if (!this._stack) {
1440                data = this.data;
1441            }
1442            else {
1443                data = this._plotData;
1444            }
1445            gridData = options.gridData || this.renderer.makeGridData.call(this, data, plot);
1446        
1447            this.renderer.drawShadow.call(this, sctx, gridData, options, plot);
1448        }
1449        
1450        for (j=0; j<$.jqplot.postDrawSeriesShadowHooks.length; j++) {
1451            $.jqplot.postDrawSeriesShadowHooks[j].call(this, sctx, options);
1452        }
1453        
1454        sctx = opts = plot = j = data = gridData = null;
1455        
1456    };
1457    
1458    // toggles series display on plot, e.g. show/hide series
1459    Series.prototype.toggleDisplay = function(ev, callback) {
1460        var s, speed;
1461        if (ev.data.series) {
1462            s = ev.data.series;
1463        }
1464        else {
1465            s = this;
1466        }
1467
1468        if (ev.data.speed) {
1469            speed = ev.data.speed;
1470        }
1471        if (speed) {
1472            // this can be tricky because series may not have a canvas element if replotting.
1473            if (s.canvas._elem.is(':hidden') || !s.show) {
1474                s.show = true;
1475
1476                s.canvas._elem.removeClass('jqplot-series-hidden');
1477                if (s.shadowCanvas._elem) {
1478                    s.shadowCanvas._elem.fadeIn(speed);
1479                }
1480                s.canvas._elem.fadeIn(speed, callback);
1481                s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).fadeIn(speed);
1482            }
1483            else {
1484                s.show = false;
1485
1486                s.canvas._elem.addClass('jqplot-series-hidden');
1487                if (s.shadowCanvas._elem) {
1488                    s.shadowCanvas._elem.fadeOut(speed);
1489                }
1490                s.canvas._elem.fadeOut(speed, callback);
1491                s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).fadeOut(speed);
1492            }
1493        }
1494        else {
1495            // this can be tricky because series may not have a canvas element if replotting.
1496            if (s.canvas._elem.is(':hidden') || !s.show) {
1497                s.show = true;
1498
1499                s.canvas._elem.removeClass('jqplot-series-hidden');
1500                if (s.shadowCanvas._elem) {
1501                    s.shadowCanvas._elem.show();
1502                }
1503                s.canvas._elem.show(0, callback);
1504                s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).show();
1505            }
1506            else {
1507                s.show = false;
1508
1509                s.canvas._elem.addClass('jqplot-series-hidden');
1510                if (s.shadowCanvas._elem) {
1511                    s.shadowCanvas._elem.hide();
1512                }
1513                s.canvas._elem.hide(0, callback);
1514                s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).hide();
1515            }
1516        }
1517    };
1518    
1519
1520
1521    /**
1522     * Class: Grid
1523     * 
1524     * Object representing the grid on which the plot is drawn.  The grid in this
1525     * context is the area bounded by the axes, the area which will contain the series.
1526     * Note, the series are drawn on their own canvas.
1527     * The Grid object cannot be instantiated directly, but is created by the Plot object.  
1528     * Grid properties can be set or overridden by the options passed in from the user.
1529     */
1530    function Grid() {
1531        $.jqplot.ElemContainer.call(this);
1532        // Group: Properties
1533        
1534        // prop: drawGridlines
1535        // whether to draw the gridlines on the plot.
1536        this.drawGridlines = true;
1537        // prop: gridLineColor
1538        // color of the grid lines.
1539        this.gridLineColor = '#cccccc';
1540        // prop: gridLineWidth
1541        // width of the grid lines.
1542        this.gridLineWidth = 1.0;
1543        // prop: background
1544        // css spec for the background color.
1545        this.background = '#fffdf6';
1546        // prop: borderColor
1547        // css spec for the color of the grid border.
1548        this.borderColor = '#999999';
1549        // prop: borderWidth
1550        // width of the border in pixels.
1551        this.borderWidth = 2.0;
1552        // prop: drawBorder
1553        // True to draw border around grid.
1554        this.drawBorder = true;
1555        // prop: shadow
1556        // whether to show a shadow behind the grid.
1557        this.shadow = true;
1558        // prop: shadowAngle
1559        // shadow angle in degrees
1560        this.shadowAngle = 45;
1561        // prop: shadowOffset
1562        // Offset of each shadow stroke from the border in pixels
1563        this.shadowOffset = 1.5;
1564        // prop: shadowWidth
1565        // width of the stoke for the shadow
1566        this.shadowWidth = 3;
1567        // prop: shadowDepth
1568        // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
1569        this.shadowDepth = 3;
1570        // prop: shadowColor
1571        // an optional css color spec for the shadow in 'rgba(n, n, n, n)' form
1572        this.shadowColor = null;
1573        // prop: shadowAlpha
1574        // Alpha channel transparency of shadow.  0 = transparent.
1575        this.shadowAlpha = '0.07';
1576        this._left;
1577        this._top;
1578        this._right;
1579        this._bottom;
1580        this._width;
1581        this._height;
1582        this._axes = [];
1583        // prop: renderer
1584        // Instance of a renderer which will actually render the grid,
1585        // see <$.jqplot.CanvasGridRenderer>.
1586        this.renderer = $.jqplot.CanvasGridRenderer;
1587        // prop: rendererOptions
1588        // Options to pass on to the renderer,
1589        // see <$.jqplot.CanvasGridRenderer>.
1590        this.rendererOptions = {};
1591        this._offsets = {top:null, bottom:null, left:null, right:null};
1592    }
1593    
1594    Grid.prototype = new $.jqplot.ElemContainer();
1595    Grid.prototype.constructor = Grid;
1596    
1597    Grid.prototype.init = function() {
1598        if ($.isFunction(this.renderer)) {
1599            this.renderer = new this.renderer();  
1600        }
1601        this.renderer.init.call(this, this.rendererOptions);
1602    };
1603    
1604    Grid.prototype.createElement = function(offsets,plot) {
1605        this._offsets = offsets;
1606        return this.renderer.createElement.call(this, plot);
1607    };
1608    
1609    Grid.prototype.draw = function() {
1610        this.renderer.draw.call(this);
1611    };
1612    
1613    $.jqplot.GenericCanvas = function() {
1614        $.jqplot.ElemContainer.call(this);
1615        this._ctx;  
1616    };
1617    
1618    $.jqplot.GenericCanvas.prototype = new $.jqplot.ElemContainer();
1619    $.jqplot.GenericCanvas.prototype.constructor = $.jqplot.GenericCanvas;
1620    
1621    $.jqplot.GenericCanvas.prototype.createElement = function(offsets, clss, plotDimensions, plot) {
1622        this._offsets = offsets;
1623        var klass = 'jqplot';
1624        if (clss != undefined) {
1625            klass = clss;
1626        }
1627        var elem;
1628
1629        elem = plot.canvasManager.getCanvas();
1630        
1631        // if new plotDimensions supplied, use them.
1632        if (plotDimensions != null) {
1633            this._plotDimensions = plotDimensions;
1634        }
1635        
1636        elem.width = this._plotDimensions.width - this._offsets.left - this._offsets.right;
1637        elem.height = this._plotDimensions.height - this._offsets.top - this._offsets.bottom;
1638        this._elem = $(elem);
1639        this._elem.css({ position: 'absolute', left: this._offsets.left, top: this._offsets.top });
1640        
1641        this._elem.addClass(klass);
1642        
1643        elem = plot.canvasManager.initCanvas(elem);
1644        
1645        elem = null;
1646        return this._elem;
1647    };
1648    
1649    $.jqplot.GenericCanvas.prototype.setContext = function() {
1650        this._ctx = this._elem.get(0).getContext("2d");
1651        return this._ctx;
1652    };
1653    
1654    // Memory Leaks patch
1655    $.jqplot.GenericCanvas.prototype.resetCanvas = function() {
1656      if (this._elem) {
1657        if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) {
1658           window.G_vmlCanvasManager.uninitElement(this._elem.get(0));
1659        }
1660        
1661        //this._elem.remove();
1662        this._elem.emptyForce();
1663      }
1664      
1665      this._ctx = null;
1666    };
1667    
1668    $.jqplot.HooksManager = function () {
1669        this.hooks =[];
1670        this.args = [];
1671    };
1672    
1673    $.jqplot.HooksManager.prototype.addOnce = function(fn, args) {
1674        args = args || [];
1675        var havehook = false;
1676        for (var i=0, l=this.hooks.length; i<l; i++) {
1677            if (this.hooks[i] == fn) {
1678                havehook = true;
1679            }
1680        }
1681        if (!havehook) {
1682            this.hooks.push(fn);
1683            this.args.push(args);
1684        }
1685    };
1686    
1687    $.jqplot.HooksManager.prototype.add = function(fn, args) {
1688        args = args || [];
1689        this.hooks.push(fn);
1690        this.args.push(args);
1691    };
1692    
1693    $.jqplot.EventListenerManager = function () {
1694        this.hooks =[];
1695    };
1696    
1697    $.jqplot.EventListenerManager.prototype.addOnce = function(ev, fn) {
1698        var havehook = false, h, i;
1699        for (var i=0, l=this.hooks.length; i<l; i++) {
1700            h = this.hooks[i];
1701            if (h[0] == ev && h[1] == fn) {
1702                havehook = true;
1703            }
1704        }
1705        if (!havehook) {
1706            this.hooks.push([ev, fn]);
1707        }
1708    };
1709    
1710    $.jqplot.EventListenerManager.prototype.add = function(ev, fn) {
1711        this.hooks.push([ev, fn]);
1712    };
1713
1714
1715    var _axisNames = ['yMidAxis', 'xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis'];
1716
1717    /**
1718     * Class: jqPlot
1719     * Plot object returned by call to $.jqplot.  Handles parsing user options,
1720     * creating sub objects (Axes, legend, title, series) and rendering the plot.
1721     */
1722    function jqPlot() {
1723        // Group: Properties
1724        // These properties are specified at the top of the options object
1725        // like so:
1726        // > {
1727        // >     axesDefaults:{min:0},
1728        // >     series:[{color:'#6633dd'}],
1729        // >     title: 'A Plot'
1730        // > }
1731        //
1732
1733        // prop: animate
1734        // True to animate the series on initial plot draw (renderer dependent).
1735        // Actual animation functionality must be supported in the renderer.
1736        this.animate = false;
1737        // prop: animateReplot
1738        // True to animate series after a call to the replot() method.
1739        // Use with caution!  Replots can happen very frequently under
1740        // certain circumstances (e.g. resizing, dragging points) and
1741        // animation in these situations can cause problems.
1742        this.animateReplot = false;
1743        // prop: axes
1744        // up to 4 axes are supported, each with its own options, 
1745        // See <Axis> for axis specific options.
1746        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'), yMidAxis: new Axis('yMidAxis')};
1747        this.baseCanvas = new $.jqplot.GenericCanvas();
1748        // true to intercept right click events and fire a 'jqplotRightClick' event.
1749        // this will also block the context menu.
1750        this.captureRightClick = false;
1751        // prop: data
1752        // user's data.  Data should *NOT* be specified in the options object,
1753        // but be passed in as the second argument to the $.jqplot() function.
1754        // The data property is described here soley for reference. 
1755        // The data should be in the form of an array of 2D or 1D arrays like
1756        // > [ [[x1, y1], [x2, y2],...], [y1, y2, ...] ].
1757        this.data = [];
1758        // prop: dataRenderer
1759        // A callable which can be used to preprocess data passed into the plot.
1760        // Will be called with 3 arguments: the plot data, a reference to the plot,
1761        // and the value of dataRendererOptions.
1762        this.dataRenderer;
1763        // prop: dataRendererOptions
1764        // Options that will be passed to the dataRenderer.
1765        // Can be of any type.
1766        this.dataRendererOptions;
1767        this.defaults = {
1768            // prop: axesDefaults
1769            // default options that will be applied to all axes.
1770            // see <Axis> for axes options.
1771            axesDefaults: {},
1772            axes: {xaxis:{}, yaxis:{}, x2axis:{}, y2axis:{}, y3axis:{}, y4axis:{}, y5axis:{}, y6axis:{}, y7axis:{}, y8axis:{}, y9axis:{}, yMidAxis:{}},
1773            // prop: seriesDefaults
1774            // default options that will be applied to all series.
1775            // see <Series> for series options.
1776            seriesDefaults: {},
1777            series:[]
1778        };
1779        // prop: defaultAxisStart
1780        // 1-D data series are internally converted into 2-D [x,y] data point arrays
1781        // by jqPlot.  This is the default starting value for the missing x or y value.
1782        // The added data will be a monotonically increasing series (e.g. [1, 2, 3, ...])
1783        // starting at this value.
1784        this.defaultAxisStart = 1;
1785        // this.doCustomEventBinding = true;
1786        // prop: drawIfHidden
1787        // True to execute the draw method even if the plot target is hidden.
1788        // Generally, this should be false.  Most plot elements will not be sized/
1789        // positioned correclty if renderered into a hidden container.  To render into
1790        // a hidden container, call the replot method when the container is shown.
1791        this.drawIfHidden = false;
1792        this.eventCanvas = new $.jqplot.GenericCanvas();
1793        // prop: fillBetween
1794        // Fill between 2 line series in a plot.
1795        // Options object:
1796        // {
1797        //    series1: first index (0 based) of series in fill
1798        //    series2: second index (0 based) of series in fill
1799        //    color: color of fill [default fillColor of series1]
1800        //    baseSeries:  fill will be drawn below this series (0 based index)
1801        //    fill: false to turn off fill [default true].
1802        //  }
1803        this.fillBetween = {
1804            series1: null,
1805            series2: null,
1806            color: null,
1807            baseSeries: 0,
1808            fill: true
1809        };
1810        // prop; fontFamily
1811        // css spec for the font-family attribute.  Default for the entire plot.
1812        this.fontFamily;
1813        // prop: fontSize
1814        // css spec for the font-size attribute.  Default for the entire plot.
1815        this.fontSize;
1816        // prop: grid
1817        // See <Grid> for grid specific options.
1818        this.grid = new Grid();
1819        // prop: legend
1820        // see <$.jqplot.TableLegendRenderer>
1821        this.legend = new Legend();
1822        // prop: noDataIndicator
1823        // Options to set up a mock plot with a data loading indicator if no data is specified.
1824        this.noDataIndicator = {    
1825            show: false,
1826            indicator: 'Loading Data...',
1827            axes: {
1828                xaxis: {
1829                    min: 0,
1830                    max: 10,
1831                    tickInterval: 2,
1832                    show: true
1833                },
1834                yaxis: {
1835                    min: 0,
1836                    max: 12,
1837                    tickInterval: 3,
1838                    show: true
1839                }
1840            }
1841        };
1842        // prop: negativeSeriesColors 
1843        // colors to use for portions of the line below zero.
1844        this.negativeSeriesColors = $.jqplot.config.defaultNegativeColors;
1845        // container to hold all of the merged options.  Convienence for plugins.
1846        this.options = {};
1847        this.previousSeriesStack = [];
1848        // Namespace to hold plugins.  Generally non-renderer plugins add themselves to here.
1849        this.plugins = {};
1850        // prop: series
1851        // Array of series object options.
1852        // see <Series> for series specific options.
1853        this.series = [];
1854        // array of series indices. Keep track of order
1855        // which series canvases are displayed, lowest
1856        // to highest, back to front.
1857        this.seriesStack = [];
1858        // prop: seriesColors
1859        // Ann array of CSS color specifications that will be applied, in order,
1860        // to the series in the plot.  Colors will wrap around so, if their
1861        // are more series than colors, colors will be reused starting at the
1862        // beginning.  For pie charts, this specifies the colors of the slices.
1863        this.seriesColors = $.jqplot.config.defaultColors;
1864        // prop: sortData
1865        // false to not sort the data passed in by the user.
1866        // Many bar, stacked and other graphs as well as many plugins depend on
1867        // having sorted data.
1868        this.sortData = true;
1869        // prop: stackSeries
1870        // true or false, creates a stack or "mountain" plot.
1871        // Not all series renderers may implement this option.
1872        this.stackSeries = false;
1873        // a shortcut for axis syncTicks options.  Not implemented yet.
1874        this.syncXTicks = true;
1875        // a shortcut for axis syncTicks options.  Not implemented yet.
1876        this.syncYTicks = true;
1877        // the jquery object for the dom target.
1878        this.target = null; 
1879        // The id of the dom element to render the plot into
1880        this.targetId = null;
1881        // prop textColor
1882        // css spec for the css color attribute.  Default for the entire plot.
1883        this.textColor;
1884        // prop: title
1885        // Title object.  See <Title> for specific options.  As a shortcut, you
1886        // can specify the title option as just a string like: title: 'My Plot'
1887        // and this will create a new title object with the specified text.
1888        this.title = new Title();
1889        // Count how many times the draw method has been called while the plot is visible.
1890        // Mostly used to test if plot has never been dran (=0), has been successfully drawn
1891        // into a visible container once (=1) or draw more than once into a visible container.
1892        // Can use this in tests to see if plot has been visibly drawn at least one time.
1893        // After plot has been visibly drawn once, it generally doesn't need redrawing if its
1894        // container is hidden and shown.
1895        this._drawCount = 0;
1896        // sum of y values for all series in plot.
1897        // used in mekko chart.
1898        this._sumy = 0;
1899        this._sumx = 0;
1900        // array to hold the cumulative stacked series data.
1901        // used to ajust the individual series data, which won't have access to other
1902        // series data.
1903        this._stackData = [];
1904        // array that holds the data to be plotted. This will be the series data
1905        // merged with the the appropriate data from _stackData according to the stackAxis.
1906        this._plotData = [];
1907        this._width = null;
1908        this._height = null; 
1909        this._plotDimensions = {height:null, width:null};
1910        this._gridPadding = {top:null, right:null, bottom:null, left:null};
1911        this._defaultGridPadding = {top:10, right:10, bottom:23, left:10};
1912
1913        this._addDomReference = $.jqplot.config.addDomReference;
1914
1915        this.preInitHooks = new $.jqplot.HooksManager();
1916        this.postInitHooks = new $.jqplot.HooksManager();
1917        this.preParseOptionsHooks = new $.jqplot.HooksManager();
1918        this.postParseOptionsHooks = new $.jqplot.HooksManager();
1919        this.preDrawHooks = new $.jqplot.HooksManager();
1920        this.postDrawHooks = new $.jqplot.HooksManager();
1921        this.preDrawSeriesHooks = new $.jqplot.HooksManager();
1922        this.postDrawSeriesHooks = new $.jqplot.HooksManager();
1923        this.preDrawLegendHooks = new $.jqplot.HooksManager();
1924        this.addLegendRowHooks = new $.jqplot.HooksManager();
1925        this.preSeriesInitHooks = new $.jqplot.HooksManager();
1926        this.postSeriesInitHooks = new $.jqplot.HooksManager();
1927        this.preParseSeriesOptionsHooks = new $.jqplot.HooksManager();
1928        this.postParseSeriesOptionsHooks = new $.jqplot.HooksManager();
1929        this.eventListenerHooks = new $.jqplot.EventListenerManager();
1930        this.preDrawSeriesShadowHooks = new $.jqplot.HooksManager();
1931        this.postDrawSeriesShadowHooks = new $.jqplot.HooksManager();
1932        
1933        this.colorGenerator = new $.jqplot.ColorGenerator();
1934        this.negativeColorGenerator = new $.jqplot.ColorGenerator();
1935
1936        this.canvasManager = new $.jqplot.CanvasManager();
1937
1938        this.themeEngine = new $.jqplot.ThemeEngine();
1939        
1940        var seriesColorsIndex = 0;
1941
1942        // Group: methods
1943        //
1944        // method: init
1945        // sets the plot target, checks data and applies user
1946        // options to plot.
1947        this.init = function(target, data, options) {
1948            options = options || {};
1949            for (var i=0; i<$.jqplot.preInitHooks.length; i++) {
1950                $.jqplot.preInitHooks[i].call(this, target, data, options);
1951            }
1952
1953            for (var i=0; i<this.preInitHooks.hooks.length; i++) {
1954                this.preInitHooks.hooks[i].call(this, target, data, options);
1955            }
1956            
1957            this.targetId = '#'+target;
1958            this.target = $('#'+target);
1959
1960            //////
1961            // Add a reference to plot
1962            //////
1963            if (this._addDomReference) {
1964                this.target.data('jqplot', this);
1965            }
1966            // remove any error class that may be stuck on target.
1967            this.target.removeClass('jqplot-error');
1968            if (!this.target.get(0)) {
1969                throw new Error("No plot target specified");
1970            }
1971            
1972            // make sure the target is positioned by some means and set css
1973            if (this.target.css('position') == 'static') {
1974                this.target.css('position', 'relative');
1975            }
1976            if (!this.target.hasClass('jqplot-target')) {
1977                this.target.addClass('jqplot-target');
1978            }
1979            
1980            // if no height or width specified, use a default.
1981            if (!this.target.height()) {
1982                var h;
1983                if (options && options.height) {
1984                    h = parseInt(options.height, 10);
1985                }
1986                else if (this.target.attr('data-height')) {
1987                    h = parseInt(this.target.attr('data-height'), 10);
1988                }
1989                else {
1990                    h = parseInt($.jqplot.config.defaultHeight, 10);
1991                }
1992                this._height = h;
1993                this.target.css('height', h+'px');
1994            }
1995            else {
1996                this._height = h = this.target.height();
1997            }
1998            if (!this.target.width()) {
1999                var w;
2000                if (options && options.width) {
2001                    w = parseInt(options.width, 10);
2002                }
2003                else if (this.target.attr('data-width')) {
2004                    w = parseInt(this.target.attr('data-width'), 10);
2005                }
2006                else {
2007                    w = parseInt($.jqplot.config.defaultWidth, 10);
2008                }
2009                this._width = w;
2010                this.target.css('width', w+'px');
2011            }
2012            else {
2013                this._width = w = this.target.width();
2014            }
2015
2016            for (var i=0, l=_axisNames.length; i<l; i++) {
2017                this.axes[_axisNames[i]] = new Axis(_axisNames[i]);
2018            }
2019            
2020            this._plotDimensions.height = this._height;
2021            this._plotDimensions.width = this._width;
2022            this.grid._plotDimensions = this._plotDimensions;
2023            this.title._plotDimensions = this._plotDimensions;
2024            this.baseCanvas._plotDimensions = this._plotDimensions;
2025            this.eventCanvas._plotDimensions = this._plotDimensions;
2026            this.legend._plotDimensions = this._plotDimensions;
2027            if (this._height <=0 || this._width <=0 || !this._height || !this._width) {
2028                throw new Error("Canvas dimension not set");
2029            }
2030            
2031            if (options.dataRenderer && $.isFunction(options.dataRenderer)) {
2032                if (options.dataRendererOptions) {
2033                    this.dataRendererOptions = options.dataRendererOptions;
2034                }
2035                this.dataRenderer = options.dataRenderer;
2036                data = this.dataRenderer(data, this, this.dataRendererOptions);
2037            }
2038            
2039            if (options.noDataIndicator && $.isPlainObject(options.noDataIndicator)) {
2040                $.extend(true, this.noDataIndicator, options.noDataIndicator);
2041            }
2042            
2043            if (data == null || $.isArray(data) == false || data.length == 0 || $.isArray(data[0]) == false || data[0].length == 0) {
2044                
2045                if (this.noDataIndicator.show == false) {
2046                    throw new Error("No data specified");
2047                }
2048                
2049                else {
2050                    // have to be descructive here in order for plot to not try and render series.
2051                    // This means that $.jqplot() will have to be called again when there is data.
2052                    //delete options.series;
2053                    
2054                    for (var ax in this.noDataIndicator.axes) {
2055                        for (var prop in this.noDataIndicator.axes[ax]) {
2056                            this.axes[ax][prop] = this.noDataIndicator.axes[ax][prop];
2057                        }
2058                    }
2059                    
2060                    this.postDrawHooks.add(function() {
2061                        var eh = this.eventCanvas.getHeight();
2062                        var ew = this.eventCanvas.getWidth();
2063                        var temp = $('<div class="jqplot-noData-container" style="position:absolute;"></div>');
2064                        this.target.append(temp);
2065                        temp.height(eh);
2066                        temp.width(ew);
2067                        temp.css('top', this.eventCanvas._offsets.top);
2068                        temp.css('left', this.eventCanvas._offsets.left);
2069                        
2070                        var temp2 = $('<div class="jqplot-noData-contents" style="text-align:center; position:relative; margin-left:auto; margin-right:auto;"></div>');
2071                        temp.append(temp2);
2072                        temp2.html(this.noDataIndicator.indicator);
2073                        var th = temp2.height();
2074                        var tw = temp2.width();
2075                        temp2.height(th);
2076                        temp2.width(tw);
2077                        temp2.css('top', (eh - th)/2 + 'px');
2078                    });
2079
2080                }
2081            }
2082            
2083            // make a copy of the data
2084            this.data = $.extend(true, [], data);
2085            
2086            this.parseOptions(options);
2087            
2088            if (this.textColor) {
2089                this.target.css('color', this.textColor);
2090            }
2091            if (this.fontFamily) {
2092                this.target.css('font-family', this.fontFamily);
2093            }
2094            if (this.fontSize) {
2095                this.target.css('font-size', this.fontSize);
2096            }
2097            
2098            this.title.init();
2099            this.legend.init();
2100            this._sumy = 0;
2101            this._sumx = 0;
2102            this.computePlotData();
2103            for (var i=0; i<this.series.length; i++) {
2104                // set default stacking order for series canvases
2105                this.seriesStack.push(i);
2106                this.previousSeriesStack.push(i);
2107                this.series[i].shadowCanvas._plotDimensions = this._plotDimensions;
2108                this.series[i].canvas._plotDimensions = this._plotDimensions;
2109                for (var j=0; j<$.jqplot.preSeriesInitHooks.length; j++) {
2110                    $.jqplot.preSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
2111                }
2112                for (var j=0; j<this.preSeriesInitHooks.hooks.length; j++) {
2113                    this.preSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
2114                }
2115                // this.populatePlotData(this.series[i], i);
2116                this.series[i]._plotDimensions = this._plotDimensions;
2117                this.series[i].init(i, this.grid.borderWidth, this);
2118                for (var j=0; j<$.jqplot.postSeriesInitHooks.length; j++) {
2119                    $.jqplot.postSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
2120                }
2121                for (var j=0; j<this.postSeriesInitHooks.hooks.length; j++) {
2122                    this.postSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
2123                }
2124                this._sumy += this.series[i]._sumy;
2125                this._sumx += this.series[i]._sumx;
2126            }
2127
2128            var name,
2129                axis;
2130            for (var i=0, l=_axisNames.length; i<l; i++) {
2131                name = _axisNames[i];
2132                axis = this.axes[name];
2133                axis._plotDimensions = this._plotDimensions;
2134                axis.init();
2135                if (this.axes[name].borderColor == null) {
2136                    if (name.charAt(0) !== 'x' && axis.useSeriesColor === true && axis.show) {
2137                        axis.borderColor = axis._series[0].color;
2138                    }
2139                    else {
2140                        axis.borderColor = this.grid.borderColor;
2141                    }
2142                }
2143            }
2144            
2145            if (this.sortData) {
2146                sortData(this.series);
2147            }
2148            this.grid.init();
2149            this.grid._axes = this.axes;
2150            
2151            this.legend._series = this.series;
2152
2153            for (var i=0; i<$.jqplot.postInitHooks.length; i++) {
2154                $.jqplot.postInitHooks[i].call(this, target, this.data, options);
2155            }
2156
2157            for (var i=0; i<this.postInitHooks.hooks.length; i++) {
2158                this.postInitHooks.hooks[i].call(this, target, this.data, options);
2159            }
2160        };  
2161        
2162        // method: resetAxesScale
2163        // Reset the specified axes min, max, numberTicks and tickInterval properties to null
2164        // or reset these properties on all axes if no list of axes is provided.
2165        //
2166        // Parameters:
2167        // axes - Boolean to reset or not reset all axes or an array or object of axis names to reset.
2168        this.resetAxesScale = function(axes, options) {
2169            var opts = options || {};
2170            var ax = axes || this.axes;
2171            if (ax === true) {
2172                ax = this.axes;
2173            }
2174            if ($.isArray(ax)) {
2175                for (var i = 0; i < ax.length; i++) {
2176                    this.axes[ax[i]].resetScale(opts[ax[i]]);
2177                }
2178            }
2179            else if (typeof(ax) === 'object') {
2180                for (var name in ax) {
2181                    this.axes[name].resetScale(opts[name]);
2182                }
2183            }
2184        };
2185        // method: reInitialize
2186        // reinitialize plot for replotting.
2187        // not called directly.
2188        this.reInitialize = function (data, opts) {
2189            // Plot should be visible and have a height and width.
2190            // If plot doesn't have height and width for some
2191            // reason, set it by other means.  Plot must not have
2192            // a display:none attribute, however.
2193
2194            var options = $.extend(true, {}, this.options, opts);
2195
2196            var target = this.targetId.substr(1);
2197            var tdata = (data == null) ? this.data : data;
2198
2199            for (var i=0; i<$.jqplot.preInitHooks.length; i++) {
2200                $.jqplot.preInitHooks[i].call(this, target, tdata, options);
2201            }
2202
2203            for (var i=0; i<this.preInitHooks.hooks.length; i++) {
2204                this.preInitHooks.hooks[i].call(this, target, tdata, options);
2205            }
2206            
2207            this._height = this.target.height();
2208            this._width = this.target.width();
2209            
2210            if (this._height <=0 || this._width <=0 || !this._height || !this._width) {
2211                throw new Error("Target dimension not set");
2212            }
2213            
2214            this._plotDimensions.height = this._height;
2215            this._plotDimensions.width = this._width;
2216            this.grid._plotDimensions = this._plotDimensions;
2217            this.title._plotDimensions = this._plotDimensions;
2218            this.baseCanvas._plotDimensions = this._plotDimensions;
2219            this.eventCanvas._plotDimensions = this._plotDimensions;
2220            this.legend._plotDimensions = this._plotDimensions;
2221
2222            var name,
2223                t, 
2224                j, 
2225                axis;
2226
2227            for (var i=0, l=_axisNames.length; i<l; i++) {
2228                name = _axisNames[i];
2229                axis = this.axes[name];
2230
2231                // Memory Leaks patch : clear ticks elements
2232                t = axis._ticks;
2233                for (var j = 0, tlen = t.length; j < tlen; j++) {
2234                  var el = t[j]._elem;
2235                  if (el) {
2236                    // if canvas renderer
2237                    if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) {
2238                      window.G_vmlCanvasManager.uninitElement(el.get(0));
2239                    }
2240                    el.emptyForce();
2241                    el = null;
2242                    t._elem = null;
2243                  }
2244                }
2245                t = null;
2246
2247                delete axis.ticks;
2248                delete axis._ticks;
2249                this.axes[name] = new Axis(name);
2250                this.axes[name]._plotWidth = this._width;
2251                this.axes[name]._plotHeight = this._height;
2252            }
2253            
2254            if (data) {
2255                if (options.dataRenderer && $.isFunction(options.dataRenderer)) {
2256                    if (options.dataRendererOptions) {
2257                        this.dataRendererOptions = options.dataRendererOptions;
2258                    }
2259                    this.dataRenderer = options.dataRenderer;
2260                    data = this.dataRenderer(data, this, this.dataRendererOptions);
2261                }
2262                
2263                // make a copy of the data
2264                this.data = $.extend(true, [], data);
2265            }
2266
2267            if (opts) {
2268                this.parseOptions(options);
2269            }
2270            
2271            this.title._plotWidth = this._width;
2272            
2273            if (this.textColor) {
2274                this.target.css('color', this.textColor);
2275            }
2276            if (this.fontFamily) {
2277                this.target.css('font-family', this.fontFamily);
2278            }
2279            if (this.fontSize) {
2280                this.target.css('font-size', this.fontSize);
2281            }
2282
2283            this.title.init();
2284            this.legend.init();
2285            this._sumy = 0;
2286            this._sumx = 0;
2287
2288            this.seriesStack = [];
2289            this.previousSeriesStack = [];
2290
2291            this.computePlotData();
2292            for (var i=0, l=this.series.length; i<l; i++) {
2293                // set default stacking order for series canvases
2294                this.seriesStack.push(i);
2295                this.previousSeriesStack.push(i);
2296                this.series[i].shadowCanvas._plotDimensions = this._plotDimensions;
2297                this.series[i].canvas._plotDimensions = this._plotDimensions;
2298                for (var j=0; j<$.jqplot.preSeriesInitHooks.length; j++) {
2299                    $.jqplot.preSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
2300                }
2301                for (var j=0; j<this.preSeriesInitHooks.hooks.length; j++) {
2302                    this.preSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
2303                }
2304                // this.populatePlotData(this.series[i], i);
2305                this.series[i]._plotDimensions = this._plotDimensions;
2306                this.series[i].init(i, this.grid.borderWidth, this);
2307                for (var j=0; j<$.jqplot.postSeriesInitHooks.length; j++) {
2308                    $.jqplot.postSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
2309                }
2310                for (var j=0; j<this.postSeriesInitHooks.hooks.length; j++) {
2311                    this.postSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
2312                }
2313                this._sumy += this.series[i]._sumy;
2314                this._sumx += this.series[i]._sumx;
2315            }
2316
2317            for (var i=0, l=_axisNames.length; i<l; i++) {
2318                name = _axisNames[i];
2319                axis = this.axes[name];
2320
2321                axis._plotDimensions = this._plotDimensions;
2322                axis.init();
2323                if (axis.borderColor == null) {
2324                    if (name.charAt(0) !== 'x' && axis.useSeriesColor === true && axis.show) {
2325                        axis.borderColor = axis._series[0].color;
2326                    }
2327                    else {
2328                        axis.borderColor = this.grid.borderColor;
2329                    }
2330                }
2331            }
2332            
2333            if (this.sortData) {
2334                sortData(this.series);
2335            }
2336            this.grid.init();
2337            this.grid._axes = this.axes;
2338            
2339            this.legend._series = this.series;
2340
2341            for (var i=0, l=$.jqplot.postInitHooks.length; i<l; i++) {
2342                $.jqplot.postInitHooks[i].call(this, target, this.data, options);
2343            }
2344
2345            for (var i=0, l=this.postInitHooks.hooks.length; i<l; i++) {
2346                this.postInitHooks.hooks[i].call(this, target, this.data, options);
2347            }
2348        };
2349
2350
2351
2352        // method: quickInit
2353        // 
2354        // Quick reinitialization plot for replotting.
2355        // Does not parse options ore recreate axes and series.
2356        // not called directly.
2357        this.quickInit = function () {
2358            // Plot should be visible and have a height and width.
2359            // If plot doesn't have height and width for some
2360            // reason, set it by other means.  Plot must not have
2361            // a display:none attribute, however.
2362            
2363            this._height = this.target.height();
2364            this._width = this.target.width();
2365            
2366            if (this._height <=0 || this._width <=0 || !this._height || !this._width) {
2367                throw new Error("Target dimension not set");
2368            }
2369            
2370            this._plotDimensions.height = this._height;
2371            this._plotDimensions.width = this._width;
2372            this.grid._plotDimensions = this._plotDimensions;
2373            this.title._plotDimensions = this._plotDimensions;
2374            this.baseCanvas._plotDimensions = this._plotDimensions;
2375            this.eventCanvas._plotDimensions = this._plotDimensions;
2376            this.legend._plotDimensions = this._plotDimensions;
2377            
2378            for (var n in this.axes) {
2379                this.axes[n]._plotWidth = this._width;
2380                this.axes[n]._plotHeight = this._height;
2381            }
2382            
2383            this.title._plotWidth = this._width;
2384            
2385            if (this.textColor) {
2386                this.target.css('color', this.textColor);
2387            }
2388            if (this.fontFamily) {
2389                this.target.css('font-family', this.fontFamily);
2390            }
2391            if (this.fontSize) {
2392                this.target.css('font-size', this.fontSize);
2393            }
2394            
2395            this._sumy = 0;
2396            this._sumx = 0;
2397            this.computePlotData();
2398            for (var i=0; i<this.series.length; i++) {
2399                // this.populatePlotData(this.series[i], i);
2400                if (this.series[i]._type === 'line' && this.series[i].renderer.bands.show) {
2401                    this.series[i].renderer.initBands.call(this.series[i], this.series[i].renderer.options, this);
2402                }
2403                this.series[i]._plotDimensions = this._plotDimensions;
2404                this.series[i].canvas._plotDimensions = this._plotDimensions;
2405                //this.series[i].init(i, this.grid.borderWidth);
2406                this._sumy += this.series[i]._sumy;
2407                this._sumx += this.series[i]._sumx;
2408            }
2409
2410            var name;
2411            
2412            for (var j=0; j<12; j++) {
2413                name = _axisNames[j];
2414                // Memory Leaks patch : clear ticks elements
2415                var t = this.axes[name]._ticks;
2416                for (var i = 0; i < t.length; i++) {
2417                  var el = t[i]._elem;
2418                  if (el) {
2419                    // if canvas renderer
2420                    if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) {
2421                      window.G_vmlCanvasManager.uninitElement(el.get(0));
2422                    }
2423                    el.emptyForce();
2424                    el = null;
2425                    t._elem = null;
2426                  }
2427                }
2428                t = null;
2429                
2430                this.axes[name]._plotDimensions = this._plotDimensions;
2431                this.axes[name]._ticks = [];
2432                // this.axes[name].renderer.init.call(this.axes[name], {});
2433            }
2434            
2435            if (this.sortData) {
2436                sortData(this.series);
2437            }
2438            
2439            this.grid._axes = this.axes;
2440            
2441            this.legend._series = this.series;
2442        };
2443        
2444        // sort the series data in increasing order.
2445        function sortData(series) {
2446            var d, sd, pd, ppd, ret;
2447            for (var i=0; i<series.length; i++) {
2448                var check;
2449                var bat = [series[i].data, series[i]._stackData, series[i]._plotData, series[i]._prevPlotData];
2450                for (var n=0; n<4; n++) {
2451                    check = true;
2452                    d = bat[n];
2453                    if (series[i]._stackAxis == 'x') {
2454                        for (var j = 0; j < d.length; j++) {
2455                            if (typeof(d[j][1]) != "number") {
2456                                check = false;
2457                                break;
2458                            }
2459                        }
2460                        if (check) {
2461                            d.sort(function(a,b) { return a[1] - b[1]; });
2462                        }
2463                    }
2464                    else {
2465                        for (var j = 0; j < d.length; j++) {
2466                            if (typeof(d[j][0]) != "number") {
2467                                check = false;
2468                                break;
2469                            }
2470                        }
2471                        if (check) {
2472                            d.sort(function(a,b) { return a[0] - b[0]; });
2473                        }
2474                    }
2475                }
2476               
2477            }
2478        }
2479
2480        this.computePlotData = function() {
2481            this._plotData = [];
2482            this._stackData = [];
2483            var series,
2484                index,
2485                l;
2486
2487
2488            for (index=0, l=this.series.length; index<l; index++) {
2489                series = this.series[index];
2490                this._plotData.push([]);
2491                this._stackData.push([]);
2492                var cd = series.data;
2493                this._plotData[index] = $.extend(true, [], cd);
2494                this._stackData[index] = $.extend(true, [], cd);
2495                series._plotData = this._plotData[index];
2496                series._stackData = this._stackData[index];
2497                var plotValues = {x:[], y:[]};
2498
2499                if (this.stackSeries && !series.disableStack) {
2500                    series._stack = true;
2501                    ///////////////////////////
2502                    // have to check for nulls
2503                    ///////////////////////////
2504                    var sidx = (series._stackAxis === 'x') ? 0 : 1;
2505
2506                    for (var k=0, cdl=cd.length; k<cdl; k++) {
2507                        var temp = cd[k][sidx];
2508                        if (temp == null) {
2509                            temp = 0;
2510                        }
2511                        this._plotData[index][k][sidx] = temp;
2512                        this._stackData[index][k][sidx] = temp;
2513
2514                        if (index > 0) {
2515                            for (var j=index; j--;) {
2516                                var prevval = this._plotData[j][k][sidx];
2517                                // only need to sum up the stack axis column of data
2518                                // and only sum if it is of same sign.
2519                                // if previous series isn't same sign, keep looking
2520                                // at earlier series untill we find one of same sign.
2521                                if (temp * prevval >= 0) {
2522                                    this._plotData[index][k][sidx] += prevval;
2523                                    this._stackData[index][k][sidx] += prevval;
2524                                    break;
2525                                } 
2526                            }
2527                        }
2528                    }
2529
2530                }
2531                else {
2532                    for (var i=0; i<series.data.length; i++) {
2533                        plotValues.x.push(series.data[i][0]);
2534                        plotValues.y.push(series.data[i][1]);
2535                    }
2536                    this._stackData.push(series.data);
2537                    this.series[index]._stackData = series.data;
2538                    this._plotData.push(series.data);
2539                    series._plotData = series.data;
2540                    series._plotValues = plotValues;
2541                }
2542                if (index>0) {
2543                    series._prevPlotData = this.series[index-1]._plotData;
2544                }
2545                series._sumy = 0;
2546                series._sumx = 0;
2547                for (i=series.data.length-1; i>-1; i--) {
2548                    series._sumy += series.data[i][1];
2549                    series._sumx += series.data[i][0];
2550                }
2551            }
2552
2553        };
2554        
2555        // populate the _stackData and _plotData arrays for the plot and the series.
2556        this.populatePlotData = function(series, index) {
2557            // if a stacked chart, compute the stacked data
2558            this._plotData = [];
2559            this._stackData = [];
2560            series._stackData = [];
2561            series._plotData = [];
2562            var plotValues = {x:[], y:[]};
2563            if (this.stackSeries && !series.disableStack) {
2564                series._stack = true;
2565                var sidx = (series._stackAxis === 'x') ? 0 : 1;
2566                // var idx = sidx ? 0 : 1;
2567                // push the current data into stackData
2568                //this._stackData.push(this.series[i].data);
2569                var temp = $.extend(true, [], series.data);
2570                // create the data that will be plotted for this series
2571                var plotdata = $.extend(true, [], series.data);
2572                var tempx, tempy, dval, stackval, comparator;
2573                // for first series, nothing to add to stackData.
2574                for (var j=0; j<index; j++) {
2575                    var cd = this.series[j].data;
2576                    for (var k=0; k<cd.length; k++) {
2577                        dval = cd[k];
2578                        tempx = (dval[0] != null) ? dval[0] : 0;
2579                        tempy = (dval[1] != null) ? dval[1] : 0;
2580                        temp[k][0] += tempx;
2581                        temp[k][1] += tempy;
2582                        stackval = (sidx) ? tempy : tempx;
2583                        // only need to sum up the stack axis column of data
2584                        // and only sum if it is of same sign.
2585                        if (series.data[k][sidx] * stackval >= 0) {
2586                            plotdata[k][sidx] += stackval;
2587                        }
2588                    }
2589                }
2590                for (var i=0; i<plotdata.length; i++) {
2591                    plotValues.x.push(plotdata[i][0]);
2592                    plotValues.y.push(plotdata[i][1]);
2593                }
2594                this._plotData.push(plotdata);
2595                this._stackData.push(temp);
2596                series._stackData = temp;
2597                series._plotData = plotdata;
2598                series._plotValues = plotValues;
2599            }
2600            else {
2601                for (var i=0; i<series.data.length; i++) {
2602                    plotValues.x.push(series.data[i][0]);
2603                    plotValues.y.push(series.data[i][1]);
2604                }
2605                this._stackData.push(series.data);
2606                this.series[index]._stackData = series.data;
2607                this._plotData.push(series.data);
2608                series._plotData = series.data;
2609                series._plotValues = plotValues;
2610            }
2611            if (index>0) {
2612                series._prevPlotData = this.series[index-1]._plotData;
2613            }
2614            series._sumy = 0;
2615            series._sumx = 0;
2616            for (i=series.data.length-1; i>-1; i--) {
2617                series._sumy += series.data[i][1];
2618                series._sumx += series.data[i][0];
2619            }
2620        };
2621        
2622        // function to safely return colors from the color array and wrap around at the end.
2623        this.getNextSeriesColor = (function(t) {
2624            var idx = 0;
2625            var sc = t.seriesColors;
2626            
2627            return function () { 
2628                if (idx < sc.length) {
2629                    return sc[idx++];
2630                }
2631                else {
2632                    idx = 0;
2633                    return sc[idx++];
2634                }
2635            };
2636        })(this);
2637    
2638        this.parseOptions = function(options){
2639            for (var i=0; i<this.preParseOptionsHooks.hooks.length; i++) {
2640                this.preParseOptionsHooks.hooks[i].call(this, options);
2641            }
2642            for (var i=0; i<$.jqplot.preParseOptionsHooks.length; i++) {
2643                $.jqplot.preParseOptionsHooks[i].call(this, options);
2644            }
2645            this.options = $.extend(true, {}, this.defaults, options);
2646            var opts = this.options;
2647            this.animate = opts.animate;
2648            this.animateReplot = opts.animateReplot;
2649            this.stackSeries = opts.stackSeries;
2650            if ($.isPlainObject(opts.fillBetween)) {
2651
2652                var temp = ['series1', 'series2', 'color', 'baseSeries', 'fill'], 
2653                    tempi;
2654
2655                for (var i=0, l=temp.length; i<l; i++) {
2656                    tempi = temp[i];
2657                    if (opts.fillBetween[tempi] != null) {
2658                        this.fillBetween[tempi] = opts.fillBetween[tempi];
2659                    }
2660                }
2661            }
2662
2663            if (opts.seriesColors) {
2664                this.seriesColors = opts.seriesColors;
2665            }
2666            if (opts.negativeSeriesColors) {
2667                this.negativeSeriesColors = opts.negativeSeriesColors;
2668            }
2669            if (opts.captureRightClick) {
2670                this.captureRightClick = opts.captureRightClick;
2671            }
2672            this.defaultAxisStart = (options && options.defaultAxisStart != null) ? options.defaultAxisStart : this.defaultAxisStart;
2673            this.colorGenerator.setColors(this.seriesColors);
2674            this.negativeColorGenerator.setColors(this.negativeSeriesColors);
2675            // var cg = new this.colorGenerator(this.seriesColors);
2676            // var ncg = new this.colorGenerator(this.negativeSeriesColors);
2677            // this._gridPadding = this.options.gridPadding;
2678            $.extend(true, this._gridPadding, opts.gridPadding);
2679            this.sortData = (opts.sortData != null) ? opts.sortData : this.sortData;
2680            for (var i=0; i<12; i++) {
2681                var n = _axisNames[i];
2682                var axis = this.axes[n];
2683                axis._options = $.extend(true, {}, opts.axesDefaults, opts.axes[n]);
2684                $.extend(true, axis, opts.axesDefaults, opts.axes[n]);
2685                axis._plotWidth = this._width;
2686                axis._plotHeight = this._height;
2687            }
2688            // if (this.data.length == 0) {
2689            //     this.data = [];
2690            //     for (var i=0; i<this.options.series.length; i++) {
2691            //         this.data.push(this.options.series.data);
2692            //     }    
2693            // }
2694                
2695            var normalizeData = function(data, dir, start) {
2696                // return data as an array of point arrays,
2697                // in form [[x1,y1...], [x2,y2...], ...]
2698                var temp = [];
2699                var i, l;
2700                dir = dir || 'vertical';
2701                if (!$.isArray(data[0])) {
2702                    // we have a series of scalars.  One line with just y values.
2703                    // turn the scalar list of data into a data array of form:
2704                    // [[1, data[0]], [2, data[1]], ...]
2705                    for (i=0, l=data.length; i<l; i++) {
2706                        if (dir == 'vertical') {
2707                            temp.push([start + i, data[i]]);   
2708                        }
2709                        else {
2710                            temp.push([data[i], start+i]);
2711                        }
2712                    }
2713                }            
2714                else {
2715                    // we have a properly formatted data series, copy it.
2716                    $.extend(true, temp, data);
2717                }
2718                return temp;
2719            };
2720
2721            var colorIndex = 0;
2722            this.series = [];
2723            for (var i=0; i<this.data.length; i++) {
2724                var sopts = $.extend(true, {index: i}, {seriesColors:this.seriesColors, negativeSeriesColors:this.negativeSeriesColors}, this.options.seriesDefaults, this.options.series[i], {rendererOptions:{animation:{show: this.animate}}});
2725                // pass in options in case something needs set prior to initialization.
2726                var temp = new Series(sopts);
2727                for (var j=0; j<$.jqplot.preParseSeriesOptionsHooks.length; j++) {
2728                    $.jqplot.preParseSeriesOptionsHooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]);
2729                }
2730                for (var j=0; j<this.preParseSeriesOptionsHooks.hooks.length; j++) {
2731                    this.preParseSeriesOptionsHooks.hooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]);
2732                }
2733                // Now go back and apply the options to the series.  Really should just do this during initializaiton, but don't want to
2734                // mess up preParseSeriesOptionsHooks at this point.
2735                $.extend(true, temp, sopts);
2736                var dir = 'vertical';
2737                if (temp.renderer === $.jqplot.BarRenderer && temp.rendererOptions && temp.rendererOptions.barDirection == 'horizontal') {
2738                    dir = 'horizontal';
2739                    temp._stackAxis = 'x';
2740                    temp._primaryAxis = '_yaxis';
2741                }
2742                temp.data = normalizeData(this.data[i], dir, this.defaultAxisStart);
2743                switch (temp.xaxis) {
2744                    case 'xaxis':
2745                        temp._xaxis = this.axes.xaxis;
2746                        break;
2747                    case 'x2axis':
2748                        temp._xaxis = this.axes.x2axis;
2749                        break;
2750                    default:
2751                        break;
2752                }
2753                temp._yaxis = this.axes[temp.yaxis];
2754                temp._xaxis._series.push(temp);
2755                temp._yaxis._series.push(temp);
2756                if (temp.show) {
2757                    temp._xaxis.show = true;
2758                    temp._yaxis.show = true;
2759                }
2760                else {
2761                    if (temp._xaxis.scaleToHiddenSeries) {
2762                        temp._xaxis.show = true;
2763                    }
2764                    if (temp._yaxis.scaleToHiddenSeries) {
2765                        temp._yaxis.show = true;
2766                    }
2767                }
2768
2769                // // parse the renderer options and apply default colors if not provided
2770                // if (!temp.color && temp.show != false) {
2771                //     temp.color = cg.next();
2772                //     colorIndex = cg.getIndex() - 1;;
2773                // }
2774                // if (!temp.negativeColor && temp.show != false) {
2775                //     temp.negativeColor = ncg.get(colorIndex);
2776                //     ncg.setIndex(colorIndex);
2777                // }
2778                if (!temp.label) {
2779                    temp.label = 'Series '+ (i+1).toString();
2780                }
2781                // temp.rendererOptions.show = temp.show;
2782                // $.extend(true, temp.renderer, {color:this.seriesColors[i]}, this.rendererOptions);
2783                this.series.push(temp);  
2784                for (var j=0; j<$.jqplot.postParseSeriesOptionsHooks.length; j++) {
2785                    $.jqplot.postParseSeriesOptionsHooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]);
2786                }
2787                for (var j=0; j<this.postParseSeriesOptionsHooks.hooks.length; j++) {
2788                    this.postParseSeriesOptionsHooks.hooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]);
2789                }
2790            }
2791            
2792            // copy the grid and title options into this object.
2793            $.extend(true, this.grid, this.options.grid);
2794            // if axis border properties aren't set, set default.
2795            for (var i=0, l=_axisNames.length; i<l; i++) {
2796                var n = _axisNames[i];
2797                var axis = this.axes[n];
2798                if (axis.borderWidth == null) {
2799                    axis.borderWidth =this.grid.borderWidth;
2800                }
2801            }
2802            
2803            if (typeof this.options.title == 'string') {
2804                this.title.text = this.options.title;
2805            }
2806            else if (typeof this.options.title == 'object') {
2807                $.extend(true, this.title, this.options.title);
2808            }
2809            this.title._plotWidth = this._width;
2810            this.legend.setOptions(this.options.legend);
2811            
2812            for (var i=0; i<$.jqplot.postParseOptionsHooks.length; i++) {
2813                $.jqplot.postParseOptionsHooks[i].call(this, options);
2814            }
2815            for (var i=0; i<this.postParseOptionsHooks.hooks.length; i++) {
2816                this.postParseOptionsHooks.hooks[i].call(this, options);
2817            }
2818        };
2819        
2820        // method: destroy
2821        // Releases all resources occupied by the plot
2822        this.destroy = function() {
2823            this.canvasManager.freeAllCanvases();
2824            if (this.eventCanvas && this.eventCanvas._elem) {
2825                this.eventCanvas._elem.unbind();
2826            }
2827            // Couple of posts on Stack Overflow indicate that empty() doesn't
2828            // always cear up the dom and release memory.  Sometimes setting
2829            // innerHTML property to null is needed.  Particularly on IE, may 
2830            // have to directly set it to null, bypassing $.
2831            this.target.empty();
2832
2833            this.target[0].innerHTML = '';
2834        };
2835        
2836        // method: replot
2837        // Does a reinitialization of the plot followed by
2838        // a redraw.  Method could be used to interactively
2839        // change plot characteristics and then replot.
2840        //
2841        // Parameters:
2842        // options - Options used for replotting.
2843        //
2844        // Properties:
2845        // clear - false to not clear (empty) the plot container before replotting (default: true).
2846        // resetAxes - true to reset all axes min, max, numberTicks and tickInterval setting so axes will rescale themselves.
2847        //             optionally pass in list of axes to reset (e.g. ['xaxis', 'y2axis']) (default: false).
2848        this.replot = function(options) {
2849            var opts =  options || {};
2850            var data = opts.data || null;
2851            var clear = (opts.clear === false) ? false : true;
2852            var resetAxes = opts.resetAxes || false;
2853            delete opts.data;
2854            delete opts.clear;
2855            delete opts.resetAxes;
2856
2857            this.target.trigger('jqplotPreReplot');
2858            
2859            if (clear) {
2860                this.destroy();
2861            }
2862            // if have data or other options, full reinit.
2863            // otherwise, quickinit.
2864            if (data || !$.isEmptyObject(opts)) {
2865                this.reInitialize(data, opts);
2866            }
2867            else {
2868                this.quickInit();
2869            }
2870
2871            if (resetAxes) {
2872                this.resetAxesScale(resetAxes, opts.axes);
2873            }
2874            this.draw();
2875            this.target.trigger('jqplotPostReplot');
2876        };
2877        
2878        // method: redraw
2879        // Empties the plot target div and redraws the plot.
2880        // This enables plot data and properties to be changed
2881        // and then to comletely clear the plot and redraw.
2882        // redraw *will not* reinitialize any plot elements.
2883        // That is, axes will not be autoscaled and defaults
2884        // will not be reapplied to any plot elements.  redraw
2885        // is used primarily with zooming. 
2886        //
2887        // Parameters:
2888        // clear - false to not clear (empty) the plot container before redrawing (default: true).
2889        this.redraw = function(clear) {
2890            clear = (clear != null) ? clear : true;
2891            this.target.trigger('jqplotPreRedraw');
2892            if (clear) {
2893                this.canvasManager.freeAllCanvases();
2894                this.eventCanvas._elem.unbind();
2895                // Dont think I bind any events to the target, this shouldn't be necessary.
2896                // It will remove user's events.
2897                // this.target.unbind();
2898                this.target.empty();
2899            }
2900             for (var ax in this.axes) {
2901                this.axes[ax]._ticks = [];
2902            }
2903            this.computePlotData();
2904            // for (var i=0; i<this.series.length; i++) {
2905            //     this.populatePlotData(this.series[i], i);
2906            // }
2907            this._sumy = 0;
2908            this._sumx = 0;
2909            for (var i=0, tsl = this.series.length; i<tsl; i++) {
2910                this._sumy += this.series[i]._sumy;
2911                this._sumx += this.series[i]._sumx;
2912            }
2913            this.draw();
2914            this.target.trigger('jqplotPostRedraw');
2915        };
2916        
2917        // method: draw
2918        // Draws all elements of the plot into the container.
2919        // Does not clear the container before drawing.
2920        this.draw = function(){
2921            if (this.drawIfHidden || this.target.is(':visible')) {
2922                this.target.trigger('jqplotPreDraw');
2923                var i,
2924                    j,
2925                    l,
2926                    tempseries;
2927                for (i=0, l=$.jqplot.preDrawHooks.length; i<l; i++) {
2928                    $.jqplot.preDrawHooks[i].call(this);
2929                }
2930                for (i=0, l=this.preDrawHooks.hooks.length; i<l; i++) {
2931                    this.preDrawHooks.hooks[i].apply(this, this.preDrawSeriesHooks.args[i]);
2932                }
2933                // create an underlying canvas to be used for special features.
2934                this.target.append(this.baseCanvas.createElement({left:0, right:0, top:0, bottom:0}, 'jqplot-base-canvas', null, this));
2935                this.baseCanvas.setContext();
2936                this.target.append(this.title.draw());
2937                this.title.pack({top:0, left:0});
2938                
2939                // make room  for the legend between the grid and the edge.
2940                // pass a dummy offsets object and a reference to the plot.
2941                var legendElem = this.legend.draw({}, this);
2942                
2943                var gridPadding = {top:0, left:0, bottom:0, right:0};
2944                
2945                if (this.legend.placement == "outsideGrid") {
2946                    // temporarily append the legend to get dimensions
2947                    this.target.append(legendElem);
2948                    switch (this.legend.location) {
2949                        case 'n':
2950                            gridPadding.top += this.legend.getHeight();
2951                            break;
2952                        case 's':
2953                            gridPadding.bottom += this.legend.getHeight();
2954                            break;
2955                        case 'ne':
2956                        case 'e':
2957                        case 'se':
2958                            gridPadding.right += this.legend.getWidth();
2959                            break;
2960                        case 'nw':
2961                        case 'w':
2962                        case 'sw':
2963                            gridPadding.left += this.legend.getWidth();
2964                            break;
2965                        default:  // same as 'ne'
2966                            gridPadding.right += this.legend.getWidth();
2967                            break;
2968                    }
2969                    legendElem = legendElem.detach();
2970                }
2971                
2972                var ax = this.axes;
2973                var name;
2974                // draw the yMidAxis first, so xaxis of pyramid chart can adjust itself if needed.
2975                for (i=0; i<12; i++) {
2976                    name = _axisNames[i];
2977                    this.target.append(ax[name].draw(this.baseCanvas._ctx, this));
2978                    ax[name].set();
2979                }
2980                if (ax.yaxis.show) {
2981                    gridPadding.left += ax.yaxis.getWidth();
2982                }
2983                var ra = ['y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis'];
2984                var rapad = [0, 0, 0, 0, 0, 0, 0, 0];
2985                var gpr = 0;
2986                var n;
2987                for (n=0; n<8; n++) {
2988                    if (ax[ra[n]].show) {
2989                        gpr += ax[ra[n]].getWidth();
2990                        rapad[n] = gpr;
2991                    }
2992                }
2993                gridPadding.right += gpr;
2994                if (ax.x2axis.show) {
2995                    gridPadding.top += ax.x2axis.getHeight();
2996                }
2997                if (this.title.show) {
2998                    gridPadding.top += this.title.getHeight();
2999                }
3000                if (ax.xaxis.show) {
3001                    gridPadding.bottom += ax.xaxis.getHeight();
3002                }
3003                
3004                // end of gridPadding adjustments.
3005
3006                // if user passed in gridDimensions option, check against calculated gridPadding
3007                if (this.options.gridDimensions && $.isPlainObject(this.options.gridDimensions)) {
3008                    var gdw = parseInt(this.options.gridDimensions.width, 10) || 0;
3009                    var gdh = parseInt(this.options.gridDimensions.height, 10) || 0;
3010                    var widthAdj = (this._width - gridPadding.left - gridPadding.right - gdw)/2;
3011                    var heightAdj = (this._height - gridPadding.top - gridPadding.bottom - gdh)/2;
3012
3013                    if (heightAdj >= 0 && widthAdj >= 0) {
3014                        gridPadding.top += heightAdj;
3015                        gridPadding.bottom += heightAdj;
3016                        gridPadding.left += widthAdj;
3017                        gridPadding.right += widthAdj;
3018                    }
3019                }
3020                var arr = ['top', 'bottom', 'left', 'right'];
3021                for (var n in arr) {
3022                    if (this._gridPadding[arr[n]] == null && gridPadding[arr[n]] > 0) {
3023                        this._gridPadding[arr[n]] = gridPadding[arr[n]];
3024                    }
3025                    else if (this._gridPadding[arr[n]] == null) {
3026                        this._gridPadding[arr[n]] = this._defaultGridPadding[arr[n]];
3027                    }
3028                }
3029                
3030                var legendPadding = this._gridPadding;
3031                
3032                if (this.legend.placement === 'outsideGrid') {
3033                    legendPadding = {top:this.title.getHeight(), left: 0, right: 0, bottom: 0};
3034                    if (this.legend.location === 's') {
3035                        legendPadding.left = this._gridPadding.left;
3036                        legendPadding.right = this._gridPadding.right;
3037                    }
3038                }
3039                
3040                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});
3041                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});
3042                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});
3043                for (i=8; i>0; i--) {
3044                    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});
3045                }
3046                var ltemp = (this._width - this._gridPadding.left - this._gridPadding.right)/2.0 + this._gridPadding.left - ax.yMidAxis.getWidth()/2.0;
3047                ax.yMidAxis.pack({position:'absolute', top:0, left:ltemp, zIndex:9, textAlign: 'center'}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top});
3048            
3049                this.target.append(this.grid.createElement(this._gridPadding, this));
3050                this.grid.draw();
3051                
3052                var series = this.series;
3053                var seriesLength = series.length;
3054                // put the shadow canvases behind the series canvases so shadows don't overlap on stacked bars.
3055                for (i=0, l=seriesLength; i<l; i++) {
3056                    // draw series in order of stacking.  This affects only
3057                    // order in which canvases are added to dom.
3058                    j = this.seriesStack[i];
3059                    this.target.append(series[j].shadowCanvas.createElement(this._gridPadding, 'jqplot-series-shadowCanvas', null, this));
3060                    series[j].shadowCanvas.setContext();
3061                    series[j].shadowCanvas._elem.data('seriesIndex', j);
3062                }
3063                
3064                for (i=0, l=seriesLength; i<l; i++) {
3065                    // draw series in order of stacking.  This affects only
3066                    // order in which canvases are added to dom.
3067                    j = this.seriesStack[i];
3068                    this.target.append(series[j].canvas.createElement(this._gridPadding, 'jqplot-series-canvas', null, this));
3069                    series[j].canvas.setContext();
3070                    series[j].canvas._elem.data('seriesIndex', j);
3071                }
3072                // Need to use filled canvas to capture events in IE.
3073                // Also, canvas seems to block selection of other elements in document on FF.
3074                this.target.append(this.eventCanvas.createElement(this._gridPadding, 'jqplot-event-canvas', null, this));
3075                this.eventCanvas.setContext();
3076                this.eventCanvas._ctx.fillStyle = 'rgba(0,0,0,0)';
3077                this.eventCanvas._ctx.fillRect(0,0,this.eventCanvas._ctx.canvas.width, this.eventCanvas._ctx.canvas.height);
3078            
3079                // bind custom event handlers to regular events.
3080                this.bindCustomEvents();
3081            
3082                // draw legend before series if the series needs to know the legend dimensions.
3083                if (this.legend.preDraw) {  
3084                    this.eventCanvas._elem.before(legendElem);
3085                    this.legend.pack(legendPadding);
3086                    if (this.legend._elem) {
3087                        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}});
3088                    }
3089                    else {
3090                        this.drawSeries();
3091                    }
3092                }
3093                else {  // draw series before legend
3094                    this.drawSeries();
3095                    if (seriesLength) {
3096                        $(series[seriesLength-1].canvas._elem).after(legendElem);
3097                    }
3098                    this.legend.pack(legendPadding);                
3099                }
3100            
3101                // register event listeners on the overlay canvas
3102                for (var i=0, l=$.jqplot.eventListenerHooks.length; i<l; i++) {
3103                    // in the handler, this will refer to the eventCanvas dom element.
3104                    // make sure there are references back into plot objects.
3105                    this.eventCanvas._elem.bind($.jqplot.eventListenerHooks[i][0], {plot:this}, $.jqplot.eventListenerHooks[i][1]);
3106                }
3107            
3108                // register event listeners on the overlay canvas
3109                for (var i=0, l=this.eventListenerHooks.hooks.length; i<l; i++) {
3110                    // in the handler, this will refer to the eventCanvas dom element.
3111                    // make sure there are references back into plot objects.
3112                    this.eventCanvas._elem.bind(this.eventListenerHooks.hooks[i][0], {plot:this}, this.eventListenerHooks.hooks[i][1]);
3113                }
3114
3115                var fb = this.fillBetween;
3116                if (fb.fill && fb.series1 !== fb.series2 && fb.series1 < seriesLength && fb.series2 < seriesLength && series[fb.series1]._type === 'line' && series[fb.series2]._type === 'line') {
3117                    this.doFillBetweenLines();
3118                }
3119
3120                for (var i=0, l=$.jqplot.postDrawHooks.length; i<l; i++) {
3121                    $.jqplot.postDrawHooks[i].call(this);
3122                }
3123
3124                for (var i=0, l=this.postDrawHooks.hooks.length; i<l; i++) {
3125                    this.postDrawHooks.hooks[i].apply(this, this.postDrawHooks.args[i]);
3126                }
3127            
3128                if (this.target.is(':visible')) {
3129                    this._drawCount += 1;
3130                }
3131
3132                var temps, 
3133                    tempr,
3134                    sel,
3135                    _els;
3136                // ughh.  ideally would hide all series then show them.
3137                for (i=0, l=seriesLength; i<l; i++) {
3138                    temps = series[i];
3139                    tempr = temps.renderer;
3140                    sel = '.jqplot-point-label.jqplot-series-'+i;
3141                    if (tempr.animation && tempr.animation._supported && tempr.animation.show && (this._drawCount < 2 || this.animateReplot)) {
3142                        _els = this.target.find(sel);
3143                        _els.stop(true, true).hide();
3144                        temps.canvas._elem.stop(true, true).hide();
3145                        temps.shadowCanvas._elem.stop(true, true).hide();
3146                        temps.canvas._elem.jqplotEffect('blind', {mode: '
3146show', direction: tempr.animation.direction}, tempr.animation.speed);
3147                        temps.shadowCanvas._elem.jqplotEffect('blind', {mode: 'show', direction: tempr.animation.direction}, tempr.animation.speed);
3148                        _els.fadeIn(tempr.animation.speed*0.8);
3149                    }
3150                }
3151                _els = null;
3152            
3153                this.target.trigger('jqplotPostDraw', [this]);
3154            }
3155        };
3156
3157        jqPlot.prototype.doFillBetweenLines = function () {
3158            var fb = this.fillBetween;
3159            var sid1 = fb.series1;
3160            var sid2 = fb.series2;
3161            // first series should always be lowest index
3162            var id1 = (sid1 < sid2) ? sid1 : sid2;
3163            var id2 = (sid2 >  sid1) ? sid2 : sid1;
3164
3165            var series1 = this.series[id1];
3166            var series2 = this.series[id2];
3167
3168            if (series2.renderer.smooth) {
3169                var tempgd = series2.renderer._smoothedData.slice(0).reverse();
3170            }
3171            else {
3172                var tempgd = series2.gridData.slice(0).reverse();
3173            }
3174
3175            if (series1.renderer.smooth) {
3176                var gd = series1.renderer._smoothedData.concat(tempgd);
3177            }
3178            else {
3179                var gd = series1.gridData.concat(tempgd);
3180            }
3181
3182            var color = (fb.color !== null) ? fb.color : this.series[sid1].fillColor;
3183            var baseSeries = (fb.baseSeries !== null) ? fb.baseSeries : id1;
3184
3185            // now apply a fill to the shape on the lower series shadow canvas,
3186            // so it is behind both series.
3187            var sr = this.series[baseSeries].renderer.shapeRenderer;
3188            var opts = {fillStyle: color, fill: true, closePath: true};
3189            sr.draw(series1.shadowCanvas._ctx, gd, opts);
3190        };
3191        
3192        this.bindCustomEvents = function() {
3193            this.eventCanvas._elem.bind('click', {plot:this}, this.onClick);
3194            this.eventCanvas._elem.bind('dblclick', {plot:this}, this.onDblClick);
3195            this.eventCanvas._elem.bind('mousedown', {plot:this}, this.onMouseDown);
3196            this.eventCanvas._elem.bind('mousemove', {plot:this}, this.onMouseMove);
3197            this.eventCanvas._elem.bind('mouseenter', {plot:this}, this.onMouseEnter);
3198            this.eventCanvas._elem.bind('mouseleave', {plot:this}, this.onMouseLeave);
3199            if (this.captureRightClick) {
3200                this.eventCanvas._elem.bind('mouseup', {plot:this}, this.onRightClick);
3201                this.eventCanvas._elem.get(0).oncontextmenu = function() {
3202                    return false;
3203                };
3204            }
3205            else {
3206                this.eventCanvas._elem.bind('mouseup', {plot:this}, this.onMouseUp);
3207            }
3208        };
3209        
3210        function getEventPosition(ev) {
3211            var plot = ev.data.plot;
3212            var go = plot.eventCanvas._elem.offset();
3213            var gridPos = {x:ev.pageX - go.left, y:ev.pageY - go.top};
3214            var dataPos = {xaxis:null, yaxis:null, x2axis:null, y2axis:null, y3axis:null, y4axis:null, y5axis:null, y6axis:null, y7axis:null, y8axis:null, y9axis:null, yMidAxis:null};
3215            var an = ['xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis'];
3216            var ax = plot.axes;
3217            var n, axis;
3218            for (n=11; n>0; n--) {
3219                axis = an[n-1];
3220                if (ax[axis].show) {
3221                    dataPos[axis] = ax[axis].series_p2u(gridPos[axis.charAt(0)]);
3222                }
3223            }
3224
3225            return {offsets:go, gridPos:gridPos, dataPos:dataPos};
3226        }
3227        
3228        
3229        // function to check if event location is over a area area
3230        function checkIntersection(gridpos, plot) {
3231            var series = plot.series;
3232            var i, j, k, s, r, x, y, theta, sm, sa, minang, maxang;
3233            var d0, d, p, pp, points, bw, hp;
3234            var threshold, t;
3235            for (k=plot.seriesStack.length-1; k>=0; k--) {
3236                i = plot.seriesStack[k];
3237                s = series[i];
3238                hp = s._highlightThreshold;
3239                switch (s.renderer.constructor) {
3240                    case $.jqplot.BarRenderer:
3241                        x = gridpos.x;
3242                        y = gridpos.y;
3243                        for (j=0; j<s._barPoints.length; j++) {
3244                            points = s._barPoints[j];
3245                            p = s.gridData[j];
3246                            if (x>points[0][0] && x<points[2][0] && y>points[2][1] && y<points[0][1]) {
3247                                return {seriesIndex:s.index, pointIndex:j, gridData:p, data:s.data[j], points:s._barPoints[j]};
3248                            }
3249                        }
3250                        break;
3251                    case $.jqplot.PyramidRenderer:
3252                        x = gridpos.x;
3253                        y = gridpos.y;
3254                        for (j=0; j<s._barPoints.length; j++) {
3255                            points = s._barPoints[j];
3256                            p = s.gridData[j];
3257                            if (x > points[0][0] + hp[0][0] && x < points[2][0] + hp[2][0] && y > points[2][1] && y < points[0][1]) {
3258                                return {seriesIndex:s.index, pointIndex:j, gridData:p, data:s.data[j], points:s._barPoints[j]};
3259                            }
3260                        }
3261                        break;
3262                    
3263                    case $.jqplot.DonutRenderer:
3264                        sa = s.startAngle/180*Math.PI;
3265                        x = gridpos.x - s._center[0];
3266                        y = gridpos.y - s._center[1];
3267                        r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
3268                        if (x > 0 && -y >= 0) {
3269                            theta = 2*Math.PI - Math.atan(-y/x);
3270                        }
3271                        else if (x > 0 && -y < 0) {
3272                            theta = -Math.atan(-y/x);
3273                        }
3274                        else if (x < 0) {
3275                            theta = Math.PI - Math.atan(-y/x);
3276                        }
3277                        else if (x == 0 && -y > 0) {
3278                            theta = 3*Math.PI/2;
3279                        }
3280                        else if (x == 0 && -y < 0) {
3281                            theta = Math.PI/2;
3282                        }
3283                        else if (x == 0 && y == 0) {
3284                            theta = 0;
3285                        }
3286                        if (sa) {
3287                            theta -= sa;
3288                            if (theta < 0) {
3289                                theta += 2*Math.PI;
3290                            }
3291                            else if (theta > 2*Math.PI) {
3292                                theta -= 2*Math.PI;
3293                            }
3294                        }
3295            
3296                        sm = s.sliceMargin/180*Math.PI;
3297                        if (r < s._radius && r > s._innerRadius) {
3298                            for (j=0; j<s.gridData.length; j++) {
3299                                minang = (j>0) ? s.gridData[j-1][1]+sm : sm;
3300                                maxang = s.gridData[j][1];
3301                                if (theta > minang && theta < maxang) {
3302                                    return {seriesIndex:s.index, pointIndex:j, gridData:[gridpos.x,gridpos.y], data:s.data[j]};
3303                                }
3304                            }
3305                        }
3306                        break;
3307                        
3308                    case $.jqplot.PieRenderer:
3309                        sa = s.startAngle/180*Math.PI;
3310                        x = gridpos.x - s._center[0];
3311                        y = gridpos.y - s._center[1];
3312                        r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
3313                        if (x > 0 && -y >= 0) {
3314                            theta = 2*Math.PI - Math.atan(-y/x);
3315                        }
3316                        else if (x > 0 && -y < 0) {
3317                            theta = -Math.atan(-y/x);
3318                        }
3319                        else if (x < 0) {
3320                            theta = Math.PI - Math.atan(-y/x);
3321                        }
3322                        else if (x == 0 && -y > 0) {
3323                            theta = 3*Math.PI/2;
3324                        }
3325                        else if (x == 0 && -y < 0) {
3326                            theta = Math.PI/2;
3327                        }
3328                        else if (x == 0 && y == 0) {
3329                            theta = 0;
3330                        }
3331                        if (sa) {
3332                            theta -= sa;
3333                            if (theta < 0) {
3334                                theta += 2*Math.PI;
3335                            }
3336                            else if (theta > 2*Math.PI) {
3337                                theta -= 2*Math.PI;
3338                            }
3339                        }
3340            
3341                        sm = s.sliceMargin/180*Math.PI;
3342                        if (r < s._radius) {
3343                            for (j=0; j<s.gridData.length; j++) {
3344                                minang = (j>0) ? s.gridData[j-1][1]+sm : sm;
3345                                maxang = s.gridData[j][1];
3346                                if (theta > minang && theta < maxang) {
3347                                    return {seriesIndex:s.index, pointIndex:j, gridData:[gridpos.x,gridpos.y], data:s.data[j]};
3348                                }
3349                            }
3350                        }
3351                        break;
3352                        
3353                    case $.jqplot.BubbleRenderer:
3354                        x = gridpos.x;
3355                        y = gridpos.y;
3356                        var ret = null;
3357                        
3358                        if (s.show) {
3359                            for (var j=0; j<s.gridData.length; j++) {
3360                                p = s.gridData[j];
3361                                d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-
3361p[1]) * (y-p[1]) );
3362                                if (d <= p[2] && (d <= d0 || d0 == null)) {
3363                                   d0 = d;
3364                                   ret = {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
3365                                }
3366                            }
3367                            if (ret != null) {
3368                                return ret;
3369                            }
3370                        }
3371                        break;
3372                        
3373                    case $.jqplot.FunnelRenderer:
3374                        x = gridpos.x;
3375                        y = gridpos.y;
3376                        var v = s._vertices,
3377                            vfirst = v[0],
3378                            vlast = v[v.length-1],
3379                            lex,
3380                            rex,
3381                            cv;
3382    
3383                        // equations of right and left sides, returns x, y values given height of section (y value and 2 points)
3384    
3385                        function findedge (l, p1 , p2) {
3386                            var m = (p1[1] - p2[1])/(p1[0] - p2[0]);
3387                            var b = p1[1] - m*p1[0];
3388                            var y = l + p1[1];
3389        
3390                            return [(y - b)/m, y];
3391                        }
3392    
3393                        // check each section
3394                        lex = findedge(y, vfirst[0], vlast[3]);
3395                        rex = findedge(y, vfirst[1], vlast[2]);
3396                        for (j=0; j<v.length; j++) {
3397                            cv = v[j];
3398                            if (y >= cv[0][1] && y <= cv[3][1] && x >= lex[0] && x <= rex[0]) {
3399                                return {seriesIndex:s.index, pointIndex:j, gridData:null, data:s.data[j]};
3400                            }
3401                        }         
3402                        break;           
3403                    
3404                    case $.jqplot.LineRenderer:
3405                        x = gridpos.x;
3406                        y = gridpos.y;
3407                        r = s.renderer;
3408                        if (s.show) {
3409                            if ((s.fill || (s.renderer.bands.show && s.renderer.bands.fill)) && (!plot.plugins.highlighter || !plot.plugins.highlighter.show)) {
3410                                // first check if it is in bounding box
3411                                var inside = false;
3412                                if (x>s._boundingBox[0][0] && x<s._boundingBox[1][0] && y>s._boundingBox[1][1] && y<s._boundingBox[0][1]) { 
3413                                    // now check the crossing number   
3414                                    
3415                                    var numPoints = s._areaPoints.length;
3416                                    var ii;
3417                                    var j = numPoints-1;
3418
3419                                    for(var ii=0; ii < numPoints; ii++) { 
3420                                        var vertex1 = [s._areaPoints[ii][0], s._areaPoints[ii][1]];
3421                                        var vertex2 = [s._areaPoints[j][0], s._areaPoints[j][1]];
3422
3423                                        if (vertex1[1] < y && vertex2[1] >= y || vertex2[1] < y && vertex1[1] >= y)     {
3424                                            if (vertex1[0] + (y - vertex1[1]) / (vertex2[1] - vertex1[1]) * (vertex2[0] - vertex1[0]) < x) {
3425                                                inside = !inside;
3426                                            }
3427                                        }
3428
3429                                        j = ii;
3430                                    }        
3431                                }
3432                                if (inside) {
3433                                    return {seriesIndex:i, pointIndex:null, gridData:s.gridData, data:s.data, points:s._areaPoints};
3434                                }
3435                                break;
3436                                
3437                            }
3438
3439                            else {
3440                                t = s.markerRenderer.size/2+s.neighborThreshold;
3441                                threshold = (t > 0) ? t : 0;
3442                                for (var j=0; j<s.gridData.length; j++) {
3443                                    p = s.gridData[j];
3444                                    // neighbor looks different to OHLC chart.
3445                                    if (r.constructor == $.jqplot.OHLCRenderer) {
3446                                        if (r.candleStick) {
3447                                            var yp = s._yaxis.series_u2p;
3448                                            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])) {
3449                                                return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
3450                                            }
3451                                        }
3452                                        // if an open hi low close chart
3453                                        else if (!r.hlc){
3454                                            var yp = s._yaxis.series_u2p;
3455                                            if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) {
3456                                                return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
3457                                            }
3458                                        }
3459                                        // a hi low close chart
3460                                        else {
3461                                            var yp = s._yaxis.series_u2p;
3462                                            if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) {
3463                                                return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
3464                                            }
3465                                        }
3466                            
3467                                    }
3468                                    else if (p[0] != null && p[1] != null){
3469                                        d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) );
3470                                        if (d <= threshold && (d <= d0 || d0 == null)) {
3471                                           d0 = d;
3472                                           return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
3473                                        }
3474                                    }
3475                                } 
3476                            }
3477                        }
3478                        break;
3479                        
3480                    default:
3481                        x = gridpos.x;
3482                        y = gridpos.y;
3483                        r = s.renderer;
3484                        if (s.show) {
3485                            t = s.markerRenderer.size/2+s.neighborThreshold;
3486                            threshold = (t > 0) ? t : 0;
3487                            for (var j=0; j<s.gridData.length; j++) {
3488                                p = s.gridData[j];
3489                                // neighbor looks different to OHLC chart.
3490                                if (r.constructor == $.jqplot.OHLCRenderer) {
3491                                    if (r.candleStick) {
3492                                        var yp = s._yaxis.series_u2p;
3493                                        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])) {
3494                                            return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
3495                                        }
3496                                    }
3497                                    // if an open hi low close chart
3498                                    else if (!r.hlc){
3499                                        var yp = s._yaxis.series_u2p;
3500                                        if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) {
3501                                            return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
3502                                        }
3503                                    }
3504                                    // a hi low close chart
3505                                    else {
3506                                        var yp = s._yaxis.series_u2p;
3507                                        if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) {
3508                                            return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
3509                                        }
3510                                    }
3511                            
3512                                }
3513                                else {
3514                                    d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-
3514p[1]) * (y-p[1]) );
3515                                    if (d <= threshold && (d <= d0 || d0 == null)) {
3516                                       d0 = d;
3517                                       return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
3518                                    }
3519                                }
3520                            } 
3521                        }
3522                        break;
3523                }
3524            }
3525            
3526            return null;
3527        }
3528        
3529        
3530        
3531        this.onClick = function(ev) {
3532            // Event passed in is normalized and will have data attribute.
3533            // Event passed out is unnormalized.
3534            var positions = getEventPosition(ev);
3535            var p = ev.data.plot;
3536            var neighbor = checkIntersection(positions.gridPos, p);
3537            var evt = $.Event('jqplotClick');
3538            evt.pageX = ev.pageX;
3539            evt.pageY = ev.pageY;
3540            $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
3541        };
3542        
3543        this.onDblClick = function(ev) {
3544            // Event passed in is normalized and will have data attribute.
3545            // Event passed out is unnormalized.
3546            var positions = getEventPosition(ev);
3547            var p = ev.data.plot;
3548            var neighbor = checkIntersection(positions.gridPos, p);
3549            var evt = $.Event('jqplotDblClick');
3550            evt.pageX = ev.pageX;
3551            evt.pageY = ev.pageY;
3552            $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
3553        };
3554        
3555        this.onMouseDown = function(ev) {
3556            var positions = getEventPosition(ev);
3557            var p = ev.data.plot;
3558            var neighbor = checkIntersection(positions.gridPos, p);
3559            var evt = $.Event('jqplotMouseDown');
3560            evt.pageX = ev.pageX;
3561            evt.pageY = ev.pageY;
3562            $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
3563        };
3564        
3565        this.onMouseUp = function(ev) {
3566            var positions = getEventPosition(ev);
3567            var evt = $.Event('jqplotMouseUp');
3568            evt.pageX = ev.pageX;
3569            evt.pageY = ev.pageY;
3570            $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, ev.data.plot]);
3571        };
3572        
3573        this.onRightClick = function(ev) {
3574            var positions = getEventPosition(ev);
3575            var p = ev.data.plot;
3576            var neighbor = checkIntersection(positions.gridPos, p);
3577            if (p.captureRightClick) {
3578                if (ev.which == 3) {
3579                var evt = $.Event('jqplotRightClick');
3580                evt.pageX = ev.pageX;
3581                evt.pageY = ev.pageY;
3582                    $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
3583                }
3584                else {
3585                var evt = $.Event('jqplotMouseUp');
3586                evt.pageX = ev.pageX;
3587                evt.pageY = ev.pageY;
3588                    $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
3589                }
3590            }
3591        };
3592        
3593        this.onMouseMove = function(ev) {
3594            var positions = getEventPosition(ev);
3595            var p = ev.data.plot;
3596            var neighbor = checkIntersection(positions.gridPos, p);
3597            var evt = $.Event('jqplotMouseMove');
3598            evt.pageX = ev.pageX;
3599            evt.pageY = ev.pageY;
3600            $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
3601        };
3602        
3603        this.onMouseEnter = function(ev) {
3604            var positions = getEventPosition(ev);
3605            var p = ev.data.plot;
3606            var evt = $.Event('jqplotMouseEnter');
3607            evt.pageX = ev.pageX;
3608            evt.pageY = ev.pageY;
3609            evt.relatedTarget = ev.relatedTarget;
3610            $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, p]);
3611        };
3612        
3613        this.onMouseLeave = function(ev) {
3614            var positions = getEventPosition(ev);
3615            var p = ev.data.plot;
3616            var evt = $.Event('jqplotMouseLeave');
3617            evt.pageX = ev.pageX;
3618            evt.pageY = ev.pageY;
3619            evt.relatedTarget = ev.relatedTarget;
3620            $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, p]);
3621        };
3622        
3623        // method: drawSeries
3624        // Redraws all or just one series on the plot.  No axis scaling
3625        // is performed and no other elements on the plot are redrawn.
3626        // options is an options object to pass on to the series renderers.
3627        // It can be an empty object {}.  idx is the series index
3628        // to redraw if only one series is to be redrawn.
3629        this.drawSeries = function(options, idx){
3630            var i, series, ctx;
3631            // if only one argument passed in and it is a number, use it ad idx.
3632            idx = (typeof(options) === "number" && idx == null) ? options : idx;
3633            options = (typeof(options) === "object") ? options : {};
3634            // draw specified series
3635            if (idx != undefined) {
3636                series = this.series[idx];
3637                ctx = series.shadowCanvas._ctx;
3638                ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
3639                series.drawShadow(ctx, options, this);
3640                ctx = series.canvas._ctx;
3641                ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
3642                series.draw(ctx, options, this);
3643                if (series.renderer.constructor == $.jqplot.BezierCurveRenderer) {
3644                    if (idx < this.series.length - 1) {
3645                        this.drawSeries(idx+1); 
3646                    }
3647                }
3648            }
3649            
3650            else {
3651                // if call series drawShadow method first, in case all series shadows
3652                // should be drawn before any series.  This will ensure, like for 
3653                // stacked bar plots, that shadows don't overlap series.
3654                for (i=0; i<this.series.length; i++) {
3655                    // first clear the canvas
3656                    series = this.series[i];
3657                    ctx = series.shadowCanvas._ctx;
3658                    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
3659                    series.drawShadow(ctx, options, this);
3660                    ctx = series.canvas._ctx;
3661                    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
3662                    series.draw(ctx, options, this);
3663                }
3664            }
3665            options = idx = i = series = ctx = null;
3666        };
3667        
3668        // method: moveSeriesToFront
3669        // This method requires jQuery 1.4+
3670        // Moves the specified series canvas in front of all other series canvases.
3671        // This effectively "draws" the specified series on top of all other series,
3672        // although it is performed through DOM manipulation, no redrawing is performed.
3673        //
3674        // Parameters:
3675        // idx - 0 based index of the series to move.  This will be the index of the series
3676        // as it was first passed into the jqplot function.
3677        this.moveSeriesToFront = function (idx) { 
3678            idx = parseInt(idx, 10);
3679            var stackIndex = $.inArray(idx, this.seriesStack);
3680            // if already in front, return
3681            if (stackIndex == -1) {
3682                return;
3683            }
3684            if (stackIndex == this.seriesStack.length -1) {
3685                this.previousSeriesStack = this.seriesStack.slice(0);
3686                return;
3687            }
3688            var opidx = this.seriesStack[this.seriesStack.length -1];
3689            var serelem = this.series[idx].canvas._elem.detach();
3690            var shadelem = this.series[idx].shadowCanvas._elem.detach();
3691            this.series[opidx].shadowCanvas._elem.after(shadelem);
3692            this.series[opidx].canvas._elem.after(serelem);
3693            this.previousSeriesStack = this.seriesStack.slice(0);
3694            this.seriesStack.splice(stackIndex, 1);
3695            this.seriesStack.push(idx);
3696        };
3697        
3698        // method: moveSeriesToBack
3699        // This method requires jQuery 1.4+
3700        // Moves the specified series canvas behind all other series canvases.
3701        //
3702        // Parameters:
3703        // idx - 0 based index of the series to move.  This will be the index of the series
3704        // as it was first passed into the jqplot function.
3705        this.moveSeriesToBack = function (idx) {
3706            idx = parseInt(idx, 10);
3707            var stackIndex = $.inArray(idx, this.seriesStack);
3708            // if already in back, return
3709            if (stackIndex == 0 || stackIndex == -1) {
3710                return;
3711            }
3712            var opidx = this.seriesStack[0];
3713            var serelem = this.series[idx].canvas._elem.detach();
3714            var shadelem = this.series[idx].shadowCanvas._elem.detach();
3715            this.series[opidx].shadowCanvas._elem.before(shadelem);
3716            this.series[opidx].canvas._elem.before(serelem);
3717            this.previousSeriesStack = this.seriesStack.slice(0);
3718            this.seriesStack.splice(stackIndex, 1);
3719            this.seriesStack.unshift(idx);
3720        };
3721        
3722        // method: restorePreviousSeriesOrder
3723        // This method requires jQuery 1.4+
3724        // Restore the series canvas order to its previous state.
3725        // Useful to put a series back where it belongs after moving
3726        // it to the front.
3727        this.restorePreviousSeriesOrder = function () {
3728            var i, j, serelem, shadelem, temp, move, keep;
3729            // if no change, return.
3730            if (this.seriesStack == this.previousSeriesStack) {
3731                return;
3732            }
3733            for (i=1; i<this.previousSeriesStack.length; i++) {
3734                move = this.previousSeriesStack[i];
3735                keep = this.previousSeriesStack[i-1];
3736                serelem = this.series[move].canvas._elem.detach();
3737                shadelem = this.series[move].shadowCanvas._elem.detach();
3738                this.series[keep].shadowCanvas._elem.after(shadelem);
3739                this.series[keep].canvas._elem.after(serelem);
3740            }
3741            temp = this.seriesStack.slice(0);
3742            this.seriesStack = this.previousSeriesStack.slice(0);
3743            this.previousSeriesStack = temp;
3744        };
3745        
3746        // method: restoreOriginalSeriesOrder
3747        // This method requires jQuery 1.4+
3748        // Restore the series canvas order to its original order
3749        // when the plot was created.
3750        this.restoreOriginalSeriesOrder = function () {
3751            var i, j, arr=[], serelem, shadelem;
3752            for (i=0; i<this.series.length; i++) {
3753                arr.push(i);
3754            }
3755            if (this.seriesStack == arr) {
3756                return;
3757            }
3758            this.previousSeriesStack = this.seriesStack.slice(0);
3759            this.seriesStack = arr;
3760            for (i=1; i<this.seriesStack.length; i++) {
3761                serelem = this.series[i].canvas._elem.detach();
3762                shadelem = this.series[i].shadowCanvas._elem.detach();
3763                this.series[i-1].shadowCanvas._elem.after(shadelem);
3764                this.series[i-1].canvas._elem.after(serelem);
3765            }
3766        };
3767        
3768        this.activateTheme = function (name) {
3769            this.themeEngine.activate(this, name);
3770        };
3771    }
3772    
3773    
3774    // conpute a highlight color or array of highlight colors from given colors.
3775    $.jqplot.computeHighlightColors  = function(colors) {
3776        var ret;
3777        if ($.isArray(colors)) {
3778            ret = [];
3779            for (var i=0; i<colors.length; i++){
3780                var rgba = $.jqplot.getColorComponents(colors[i]);
3781                var newrgb = [rgba[0], rgba[1], rgba[2]];
3782                var sum = newrgb[0] + newrgb[1] + newrgb[2];
3783                for (var j=0; j<3; j++) {
3784                    // when darkening, lowest color component can be is 60.
3785                    newrgb[j] = (sum > 660) ?  newrgb[j] * 0.85 : 0.73 * newrgb[j] + 90;
3786                    newrgb[j] = parseInt(newrgb[j], 10);
3787                    (newrgb[j] > 255) ? 255 : newrgb[j];
3788                }
3789                // newrgb[3] = (rgba[3] > 0.4) ? rgba[3] * 0.4 : rgba[3] * 1.5;
3790                // newrgb[3] = (rgba[3] > 0.5) ? 0.8 * rgba[3] - .1 : rgba[3] + 0.2;
3791                newrgb[3] = 0.3 + 0.35 * rgba[3];
3792                ret.push('rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+newrgb[3]+')');
3793            }
3794        }
3795        else {
3796            var rgba = $.jqplot.getColorComponents(colors);
3797            var newrgb = [rgba[0], rgba[1], rgba[2]];
3798            var sum = newrgb[0] + newrgb[1] + newrgb[2];
3799            for (var j=0; j<3; j++) {
3800                // when darkening, lowest color component can be is 60.
3801                // newrgb[j] = (sum > 570) ?  newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]);
3802                // newrgb[j] = parseInt(newrgb[j], 10);
3803                newrgb[j] = (sum > 660) ?  newrgb[j] * 0.85 : 0.73 * newrgb[j] + 90;
3804                newrgb[j] = parseInt(newrgb[j], 10);
3805                (newrgb[j] > 255) ? 255 : newrgb[j];
3806            }
3807            // newrgb[3] = (rgba[3] > 0.4) ? rgba[3] * 0.4 : rgba[3] * 1.5;
3808            // newrgb[3] = (rgba[3] > 0.5) ? 0.8 * rgba[3] - .1 : rgba[3] + 0.2;
3809            newrgb[3] = 0.3 + 0.35 * rgba[3];
3810            ret = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+newrgb[3]+')';
3811        }
3812        return ret;
3813    };
3814        
3815   $.jqplot.ColorGenerator = function(colors) {
3816        colors = colors || $.jqplot.config.defaultColors;
3817        var idx = 0;
3818        
3819        this.next = function () { 
3820            if (idx < colors.length) {
3821                return colors[idx++];
3822            }
3823            else {
3824                idx = 0;
3825                return colors[idx++];
3826            }
3827        };
3828        
3829        this.previous = function () { 
3830            if (idx > 0) {
3831                return colors[idx--];
3832            }
3833            else {
3834                idx = colors.length-1;
3835                return colors[idx];
3836            }
3837        };
3838        
3839        // get a color by index without advancing pointer.
3840        this.get = function(i) {
3841            var idx = i - colors.length * Math.floor(i/colors.length);
3842            return colors[idx];
3843        };
3844        
3845        this.setColors = function(c) {
3846            colors = c;
3847        };
3848        
3849        this.reset = function() {
3850            idx = 0;
3851        };
3852
3853        this.getIndex = function() {
3854            return idx;
3855        };
3856
3857        this.setIndex = function(index) {
3858            idx = index;
3859        };
3860    };
3861
3862    // convert a hex color string to rgb string.
3863    // h - 3 or 6 character hex string, with or without leading #
3864    // a - optional alpha
3865    $.jqplot.hex2rgb = function(h, a) {
3866        h = h.replace('#', '');
3867        if (h.length == 3) {
3868            h = h.charAt(0)+h.charAt(0)+h.charAt(1)+h.charAt(1)+h.charAt(2)+h.charAt(2);
3869        }
3870        var rgb;
3871        rgb = 'rgba('+parseInt(h.slice(0,2), 16)+', '+parseInt(h.slice(2,4), 16)+', '+parseInt(h.slice(4,6), 16);
3872        if (a) {
3873            rgb += ', '+a;
3874        }
3875        rgb += ')';
3876        return rgb;
3877    };
3878    
3879    // convert an rgb color spec to a hex spec.  ignore any alpha specification.
3880    $.jqplot.rgb2hex = function(s) {
3881        var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *(?:, *[0-9.]*)?\)/;
3882        var m = s.match(pat);
3883        var h = '#';
3884        for (var i=1; i<4; i++) {
3885            var temp;
3886            if (m[i].search(/%/) != -1) {
3887                temp = parseInt(255*m[i]/100, 10).toString(16);
3888                if (temp.length == 1) {
3889                    temp = '0'+temp;
3890                }
3891            }
3892            else {
3893                temp = parseInt(m[i], 10).toString(16);
3894                if (temp.length == 1) {
3895                    temp = '0'+temp;
3896                }
3897            }
3898            h += temp;
3899        }
3900        return h;
3901    };
3902    
3903    // given a css color spec, return an rgb css color spec
3904    $.jqplot.normalize2rgb = function(s, a) {
3905        if (s.search(/^ *rgba?\(/) != -1) {
3906            return s; 
3907        }
3908        else if (s.search(/^ *#?[0-9a-fA-F]?[0-9a-fA-F]/) != -1) {
3909            return $.jqplot.hex2rgb(s, a);
3910        }
3911        else {
3912            throw new Error('Invalid color spec');
3913        }
3914    };
3915    
3916    // extract the r, g, b, a color components out of a css color spec.
3917    $.jqplot.getColorComponents = function(s) {
3918        // check to see if a color keyword.
3919        s = $.jqplot.colorKeywordMap[s] || s;
3920        var rgb = $.jqplot.normalize2rgb(s);
3921        var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *,? *([0-9.]* *)?\)/;
3922        var m = rgb.match(pat);
3923        var ret = [];
3924        for (var i=1; i<4; i++) {
3925            if (m[i].search(/%/) != -1) {
3926                ret[i-1] = parseInt(255*m[i]/100, 10);
3927            }
3928            else {
3929                ret[i-1] = parseInt(m[i], 10);
3930            }
3931        }
3932        ret[3] = parseFloat(m[4]) ? parseFloat(m[4]) : 1.0;
3933        return ret;
3934    };
3935    
3936    $.jqplot.colorKeywordMap = {
3937        aliceblue: 'rgb(240, 248, 255)',
3938        antiquewhite: 'rgb(250, 235, 215)',
3939        aqua: 'rgb( 0, 255, 255)',
3940        aquamarine: 'rgb(127, 255, 212)',
3941        azure: 'rgb(240, 255, 255)',
3942        beige: 'rgb(245, 245, 220)',
3943        bisque: 'rgb(255, 228, 196)',
3944        black: 'rgb( 0, 0, 0)',
3945        blanchedalmond: 'rgb(255, 235, 205)',
3946        blue: 'rgb( 0, 0, 255)',
3947        blueviolet: 'rgb(138, 43, 226)',
3948        brown: 'rgb(165, 42, 42)',
3949        burlywood: 'rgb(222, 184, 135)',
3950        cadetblue: 'rgb( 95, 158, 160)',
3951        chartreuse: 'rgb(127, 255, 0)',
3952        chocolate: 'rgb(210, 105, 30)',
3953        coral: 'rgb(255, 127, 80)',
3954        cornflowerblue: 'rgb(100, 149, 237)',
3955        cornsilk: 'rgb(255, 248, 220)',
3956        crimson: 'rgb(220, 20, 60)',
3957        cyan: 'rgb( 0, 255, 255)',
3958        darkblue: 'rgb( 0, 0, 139)',
3959        darkcyan: 'rgb( 0, 139, 139)',
3960        darkgoldenrod: 'rgb(184, 134, 11)',
3961        darkgray: 'rgb(169, 169, 169)',
3962        darkgreen: 'rgb( 0, 100, 0)',
3963        darkgrey: 'rgb(169, 169, 169)',
3964        darkkhaki: 'rgb(189, 183, 107)',
3965        darkmagenta: 'rgb(139, 0, 139)',
3966        darkolivegreen: 'rgb( 85, 107, 47)',
3967        darkorange: 'rgb(255, 140, 0)',
3968        darkorchid: 'rgb(153, 50, 204)',
3969        darkred: 'rgb(139, 0, 0)',
3970        darksalmon: 'rgb(233, 150, 122)',
3971        darkseagreen: 'rgb(143, 188, 143)',
3972        darkslateblue: 'rgb( 72, 61, 139)',
3973        darkslategray: 'rgb( 47, 79, 79)',
3974        darkslategrey: 'rgb( 47, 79, 79)',
3975        darkturquoise: 'rgb( 0, 206, 209)',
3976        darkviolet: 'rgb(148, 0, 211)',
3977        deeppink: 'rgb(255, 20, 147)',
3978        deepskyblue: 'rgb( 0, 191, 255)',
3979        dimgray: 'rgb(105, 105, 105)',
3980        dimgrey: 'rgb(105, 105, 105)',
3981        dodgerblue: 'rgb( 30, 144, 255)',
3982        firebrick: 'rgb(178, 34, 34)',
3983        floralwhite: 'rgb(255, 250, 240)',
3984        forestgreen: 'rgb( 34, 139, 34)',
3985        fuchsia: 'rgb(255, 0, 255)',
3986        gainsboro: 'rgb(220, 220, 220)',
3987        ghostwhite: 'rgb(248, 248, 255)',
3988        gold: 'rgb(255, 215, 0)',
3989        goldenrod: 'rgb(218, 165, 32)',
3990        gray: 'rgb(128, 128, 128)',
3991        grey: 'rgb(128, 128, 128)',
3992        green: 'rgb( 0, 128, 0)',
3993        greenyellow: 'rgb(173, 255, 47)',
3994        honeydew: 'rgb(240, 255, 240)',
3995        hotpink: 'rgb(255, 105, 180)',
3996        indianred: 'rgb(205, 92, 92)',
3997        indigo: 'rgb( 75, 0, 130)',
3998        ivory: 'rgb(255, 255, 240)',
3999        khaki: 'rgb(240, 230, 140)',
4000        lavender: 'rgb(230, 230, 250)',
4001        lavenderblush: 'rgb(255, 240, 245)',
4002        lawngreen: 'rgb(124, 252, 0)',
4003        lemonchiffon: 'rgb(255, 250, 205)',
4004        lightblue: 'rgb(173, 216, 230)',
4005        lightcoral: 'rgb(240, 128, 128)',
4006        lightcyan: 'rgb(224, 255, 255)',
4007        lightgoldenrodyellow: 'rgb(250, 250, 210)',
4008        lightgray: 'rgb(211, 211, 211)',
4009        lightgreen: 'rgb(144, 238, 144)',
4010        lightgrey: 'rgb(211, 211, 211)',
4011        lightpink: 'rgb(255, 182, 193)',
4012        lightsalmon: 'rgb(255, 160, 122)',
4013        lightseagreen: 'rgb( 32, 178, 170)',
4014        lightskyblue: 'rgb(135, 206, 250)',
4015        lightslategray: 'rgb(119, 136, 153)',
4016        lightslategrey: 'rgb(119, 136, 153)',
4017        lightsteelblue: 'rgb(176, 196, 222)',
4018        lightyellow: 'rgb(255, 255, 224)',
4019        lime: 'rgb( 0, 255, 0)',
4020        limegreen: 'rgb( 50, 205, 50)',
4021        linen: 'rgb(250, 240, 230)',
4022        magenta: 'rgb(255, 0, 255)',
4023        maroon: 'rgb(128, 0, 0)',
4024        mediumaquamarine: 'rgb(102, 205, 170)',
4025        mediumblue: 'rgb( 0, 0, 205)',
4026        mediumorchid: 'rgb(186, 85, 211)',
4027        mediumpurple: 'rgb(147, 112, 219)',
4028        mediumseagreen: 'rgb( 60, 179, 113)',
4029        mediumslateblue: 'rgb(123, 104, 238)',
4030        mediumspringgreen: 'rgb( 0, 250, 154)',
4031        mediumturquoise: 'rgb( 72, 209, 204)',
4032        mediumvioletred: 'rgb(199, 21, 133)',
4033        midnightblue: 'rgb( 25, 25, 112)',
4034        mintcream: 'rgb(245, 255, 250)',
4035        mistyrose: 'rgb(255, 228, 225)',
4036        moccasin: 'rgb(255, 228, 181)',
4037        navajowhite: 'rgb(255, 222, 173)',
4038        navy: 'rgb( 0, 0, 128)',
4039        oldlace: 'rgb(253, 245, 230)',
4040        olive: 'rgb(128, 128, 0)',
4041        olivedrab: 'rgb(107, 142, 35)',
4042        orange: 'rgb(255, 165, 0)',
4043        orangered: 'rgb(255, 69, 0)',
4044        orchid: 'rgb(218, 112, 214)',
4045        palegoldenrod: 'rgb(238, 232, 170)',
4046        palegreen: 'rgb(152, 251, 152)',
4047        paleturquoise: 'rgb(175, 238, 238)',
4048        palevioletred: 'rgb(219, 112, 147)',
4049        papayawhip: 'rgb(255, 239, 213)',
4050        peachpuff: 'rgb(255, 218, 185)',
4051        peru: 'rgb(205, 133, 63)',
4052        pink: 'rgb(255, 192, 203)',
4053        plum: 'rgb(221, 160, 221)',
4054        powderblue: 'rgb(176, 224, 230)',
4055        purple: 'rgb(128, 0, 128)',
4056        red: 'rgb(255, 0, 0)',
4057        rosybrown: 'rgb(188, 143, 143)',
4058        royalblue: 'rgb( 65, 105, 225)',
4059        saddlebrown: 'rgb(139, 69, 19)',
4060        salmon: 'rgb(250, 128, 114)',
4061        sandybrown: 'rgb(244, 164, 96)',
4062        seagreen: 'rgb( 46, 139, 87)',
4063        seashell: 'rgb(255, 245, 238)',
4064        sienna: 'rgb(160, 82, 45)',
4065        silver: 'rgb(192, 192, 192)',
4066        skyblue: 'rgb(135, 206, 235)',
4067        slateblue: 'rgb(106, 90, 205)',
4068        slategray: 'rgb(112, 128, 144)',
4069        slategrey: 'rgb(112, 128, 144)',
4070        snow: 'rgb(255, 250, 250)',
4071        springgreen: 'rgb( 0, 255, 127)',
4072        steelblue: 'rgb( 70, 130, 180)',
4073        tan: 'rgb(210, 180, 140)',
4074        teal: 'rgb( 0, 128, 128)',
4075        thistle: 'rgb(216, 191, 216)',
4076        tomato: 'rgb(255, 99, 71)',
4077        turquoise: 'rgb( 64, 224, 208)',
4078        violet: 'rgb(238, 130, 238)',
4079        wheat: 'rgb(245, 222, 179)',
4080        white: 'rgb(255, 255, 255)',
4081        whitesmoke: 'rgb(245, 245, 245)',
4082        yellow: 'rgb(255, 255, 0)',
4083        yellowgreen: 'rgb(154, 205, 50)'
4084    };
4085
4086    
4087
4088
4089    // class: $.jqplot.AxisLabelRenderer
4090    // Renderer to place labels on the axes.
4091    $.jqplot.AxisLabelRenderer = function(options) {
4092        // Group: Properties
4093        $.jqplot.ElemContainer.call(this);
4094        // name of the axis associated with this tick
4095        this.axis;
4096        // prop: show
4097        // whether or not to show the tick (mark and label).
4098        this.show = true;
4099        // prop: label
4100        // The text or html for the label.
4101        this.label = '';
4102        this.fontFamily = null;
4103        this.fontSize = null;
4104        this.textColor = null;
4105        this._elem;
4106        // prop: escapeHTML
4107        // true to escape HTML entities in the label.
4108        this.escapeHTML = false;
4109        
4110        $.extend(true, this, options);
4111    };
4112    
4113    $.jqplot.AxisLabelRenderer.prototype = new $.jqplot.ElemContainer();
4114    $.jqplot.AxisLabelRenderer.prototype.constructor = $.jqplot.AxisLabelRenderer;
4115    
4116    $.jqplot.AxisLabelRenderer.prototype.init = function(options) {
4117        $.extend(true, this, options);
4118    };
4119    
4120    $.jqplot.AxisLabelRenderer.prototype.draw = function(ctx, plot) {
4121        // Memory Leaks patch
4122        if (this._elem) {
4123            this._elem.emptyForce();
4124            this._elem = null;
4125        }
4126
4127        this._elem = $('<div style="position:absolute;" class="jqplot-'+this.axis+'-label"></div>');
4128        
4129        if (Number(this.label)) {
4130            this._elem.css('white-space', 'nowrap');
4131        }
4132        
4133        if (!this.escapeHTML) {
4134            this._elem.html(this.label);
4135        }
4136        else {
4137            this._elem.text(this.label);
4138        }
4139        if (this.fontFamily) {
4140            this._elem.css('font-family', this.fontFamily);
4141        }
4142        if (this.fontSize) {
4143            this._elem.css('font-size', this.fontSize);
4144        }
4145        if (this.textColor) {
4146            this._elem.css('color', this.textColor);
4147        }
4148        
4149        return this._elem;
4150    };
4151    
4152    $.jqplot.AxisLabelRenderer.prototype.pack = function() {
4153    };
4154
4155    // class: $.jqplot.AxisTickRenderer
4156    // A "tick" object showing the value of a tick/gridline on the plot.
4157    $.jqplot.AxisTickRenderer = function(options) {
4158        // Group: Properties
4159        $.jqplot.ElemContainer.call(this);
4160        // prop: mark
4161        // tick mark on the axis.  One of 'inside', 'outside', 'cross', '' or null.
4162        this.mark = 'outside';
4163        // name of the axis associated with this tick
4164        this.axis;
4165        // prop: showMark
4166        // whether or not to show the mark on the axis.
4167        this.showMark = true;
4168        // prop: showGridline
4169        // whether or not to draw the gridline on the grid at this tick.
4170        this.showGridline = true;
4171        // prop: isMinorTick
4172        // if this is a minor tick.
4173        this.isMinorTick = false;
4174        // prop: size
4175        // Length of the tick beyond the grid in pixels.
4176        // DEPRECATED: This has been superceeded by markSize
4177        this.size = 4;
4178        // prop:  markSize
4179        // Length of the tick marks in pixels.  For 'cross' style, length
4180        // will be stoked above and below axis, so total length will be twice this.
4181        this.markSize = 6;
4182        // prop: show
4183        // whether or not to show the tick (mark and label).
4184        // Setting this to false requires more testing.  It is recommended
4185        // to set showLabel and showMark to false instead.
4186        this.show = true;
4187        // prop: showLabel
4188        // whether or not to show the label.
4189        this.showLabel = true;
4190        this.label = null;
4191        this.value = null;
4192        this._styles = {};
4193        // prop: formatter
4194        // A class of a formatter for the tick text.  sprintf by default.
4195        this.formatter = $.jqplot.DefaultTickFormatter;
4196        // prop: prefix
4197        // String to prepend to the tick label.
4198        // Prefix is prepended to the formatted tick label.
4199        this.prefix = '';
4200        // prop: suffix
4201        // String to append to the tick label.
4202        // Suffix is appended to the formatted tick label.
4203        this.suffix = '';
4204        // prop: formatString
4205        // string passed to the formatter.
4206        this.formatString = '';
4207        // prop: fontFamily
4208        // css spec for the font-family css attribute.
4209        this.fontFamily;
4210        // prop: fontSize
4211        // css spec for the font-size css attribute.
4212        this.fontSize;
4213        // prop: textColor
4214        // css spec for the color attribute.
4215        this.textColor;
4216        // prop: escapeHTML
4217        // true to escape HTML entities in the label.
4218        this.escapeHTML = false;
4219        this._elem;
4220        this._breakTick = false;
4221        
4222        $.extend(true, this, options);
4223    };
4224    
4225    $.jqplot.AxisTickRenderer.prototype.init = function(options) {
4226        $.extend(true, this, options);
4227    };
4228    
4229    $.jqplot.AxisTickRenderer.prototype = new $.jqplot.ElemContainer();
4230    $.jqplot.AxisTickRenderer.prototype.constructor = $.jqplot.AxisTickRenderer;
4231    
4232    $.jqplot.AxisTickRenderer.prototype.setTick = function(value, axisName, isMinor) {
4233        this.value = value;
4234        this.axis = axisName;
4235        if (isMinor) {
4236            this.isMinorTick = true;
4237        }
4238        return this;
4239    };
4240    
4241    $.jqplot.AxisTickRenderer.prototype.draw = function() {
4242        if (this.label === null) {
4243            this.label = this.prefix + this.formatter(this.formatString, this.value) + this.suffix;
4244        }
4245        var style = {position: 'absolute'};
4246        if (Number(this.label)) {
4247            style['whitSpace'] = 'nowrap';
4248        }
4249        
4250        // Memory Leaks patch
4251        if (this._elem) {
4252            this._elem.emptyForce();
4253            this._elem = null;
4254        }
4255
4256        this._elem = $(document.createElement('div'));
4257        this._elem.addClass("jqplot-"+this.axis+"-tick");
4258        
4259        if (!this.escapeHTML) {
4260            this._elem.html(this.label);
4261        }
4262        else {
4263            this._elem.text(this.label);
4264        }
4265        
4266        this._elem.css(style);
4267
4268        for (var s in this._styles) {
4269            this._elem.css(s, this._styles[s]);
4270        }
4271        if (this.fontFamily) {
4272            this._elem.css('font-family', this.fontFamily);
4273        }
4274        if (this.fontSize) {
4275            this._elem.css('font-size', this.fontSize);
4276        }
4277        if (this.textColor) {
4278            this._elem.css('color', this.textColor);
4279        }
4280        if (this._breakTick) {
4281          this._elem.addClass('jqplot-breakTick');
4282        }
4283        
4284        return this._elem;
4285    };
4286        
4287    $.jqplot.DefaultTickFormatter = function (format, val) {
4288        if (typeof val == 'number') {
4289            if (!format) {
4290                format = $.jqplot.config.defaultTickFormatString;
4291            }
4292            return $.jqplot.sprintf(format, val);
4293        }
4294        else {
4295            return String(val);
4296        }
4297    };
4298        
4299    $.jqplot.PercentTickFormatter = function (format, val) {
4300        if (typeof val == 'number') {
4301            val = 100 * val;
4302            if (!format) {
4303                format = $.jqplot.config.defaultTickFormatString;
4304            }
4305            return $.jqplot.sprintf(format, val);
4306        }
4307        else {
4308            return String(val);
4309        }
4310    };
4311    
4312    $.jqplot.AxisTickRenderer.prototype.pack = function() {
4313    };
4314     
4315    // Class: $.jqplot.CanvasGridRenderer
4316    // The default jqPlot grid renderer, creating a grid on a canvas element.
4317    // The renderer has no additional options beyond the <Grid> class.
4318    $.jqplot.CanvasGridRenderer = function(){
4319        this.shadowRenderer = new $.jqplot.ShadowRenderer();
4320    };
4321    
4322    // called with context of Grid object
4323    $.jqplot.CanvasGridRenderer.prototype.init = function(options) {
4324        this._ctx;
4325        $.extend(true, this, options);
4326        // set the shadow renderer options
4327        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};
4328        this.renderer.shadowRenderer.init(sopts);
4329    };
4330    
4331    // called with context of Grid.
4332    $.jqplot.CanvasGridRenderer.prototype.createElement = function(plot) {
4333        var elem;
4334        // Memory Leaks patch
4335        if (this._elem) {
4336          if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) {
4337            elem = this._elem.get(0);
4338            window.G_vmlCanvasManager.uninitElement(elem);
4339            elem = null;
4340          }
4341          
4342          this._elem.emptyForce();
4343          this._elem = null;
4344        }
4345      
4346        elem = plot.canvasManager.getCanvas();
4347
4348        var w = this._plotDimensions.width;
4349        var h = this._plotDimensions.height;
4350        elem.width = w;
4351        elem.height = h;
4352        this._elem = $(elem);
4353        this._elem.addClass('jqplot-grid-canvas');
4354        this._elem.css({ position: 'absolute', left: 0, top: 0 });
4355        
4356        elem = plot.canvasManager.initCanvas(elem);
4357
4358        this._top = this._offsets.top;
4359        this._bottom = h - this._offsets.bottom;
4360        this._left = this._offsets.left;
4361        this._right = w - this._offsets.right;
4362        this._width = this._right - this._left;
4363        this._height = this._bottom - this._top;
4364        // avoid memory leak
4365        elem = null;
4366        return this._elem;
4367    };
4368    
4369    $.jqplot.CanvasGridRenderer.prototype.draw = function() {
4370        this._ctx = this._elem.get(0).getContext("2d");
4371        var ctx = this._ctx;
4372        var axes = this._axes;
4373        // Add the grid onto the grid canvas.  This is the bottom most layer.
4374        ctx.save();
4375        ctx.clearRect(0, 0, this._plotDimensions.width, this._plotDimensions.height);
4376        ctx.fillStyle = this.backgroundColor || this.background;
4377        ctx.fillRect(this._left, this._top, this._width, this._height);
4378        
4379        ctx.save();
4380        ctx.lineJoin = 'miter';
4381        ctx.lineCap = 'butt';
4382        ctx.lineWidth = this.gridLineWidth;
4383        ctx.strokeStyle = this.gridLineColor;
4384        var b, e, s, m;
4385        var ax = ['xaxis', 'yaxis', 'x2axis', 'y2axis'];
4386        for (var i=4; i>0; i--) {
4387            var name = ax[i-1];
4388            var axis = axes[name];
4389            var ticks = axis._ticks;
4390            var numticks = ticks.length;
4391            if (axis.show) {
4392                if (axis.drawBaseline) {
4393                    var bopts = {};
4394                    if (axis.baselineWidth !== null) {
4395                        bopts.lineWidth = axis.baselineWidth;
4396                    }
4397                    if (axis.baselineColor !== null) {
4398                        bopts.strokeStyle = axis.baselineColor;
4399                    }
4400                    switch (name) {
4401                        case 'xaxis':
4402                            drawLine (this._left, this._bottom, this._right, this._bottom, bopts);
4403                            break;
4404                        case 'yaxis':
4405                            drawLine (this._left, this._bottom, this._left, this._top, bopts);
4406                            break;
4407                        case 'x2axis':
4408                            drawLine (this._left, this._bottom, this._right, this._bottom, bopts);
4409                            break;
4410                        case 'y2axis':
4411                            drawLine (this._right, this._bottom, this._right, this._top, bopts);
4412                            break;
4413                    }
4414                }
4415                for (var j=numticks; j>0; j--) {
4416                    var t = ticks[j-1];
4417                    if (t.show) {
4418                        var pos = Math.round(axis.u2p(t.value)) + 0.5;
4419                        switch (name) {
4420                            case 'xaxis':
4421                                // draw the grid line if we should
4422                                if (t.showGridline && this.drawGri
4422dlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) {
4423                                    drawLine(pos, this._top, pos, this._bottom);
4424                                }
4425                                // draw the mark
4426                                if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) {
4427                                    s = t.markSize;
4428                                    m = t.mark;
4429                                    var pos = Math.round(axis.u2p(t.value)) + 0.5;
4430                                    switch (m) {
4431                                        case 'outside':
4432                                            b = this._bottom;
4433                                            e = this._bottom+s;
4434                                            break;
4435                                        case 'inside':
4436                                            b = this._bottom-s;
4437                                            e = this._bottom;
4438                                            break;
4439                                        case 'cross':
4440                                            b = this._bottom-s;
4441                                            e = this._bottom+s;
4442                                            break;
4443                                        default:
4444                                            b = this._bottom;
4445                                            e = this._bottom+s;
4446                                            break;
4447                                    }
4448                                    // draw the shadow
4449                                    if (this.shadow) {
4450                                        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});
4451                                    }
4452                                    // draw the line
4453                                    drawLine(pos, b, pos, e);
4454                                }
4455                                break;
4456                            case 'yaxis':
4457                                // draw the grid line
4458                                if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) {
4459                                    drawLine(this._right, pos, this._left, pos);
4460                                }
4461                                // draw the mark
4462                                if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) {
4463                                    s = t.markSize;
4464                                    m = t.mark;
4465                                    var pos = Math.round(axis.u2p(t.value)) + 0.5;
4466                                    switch (m) {
4467                                        case 'outside':
4468                                            b = this._left-s;
4469                                            e = this._left;
4470                                            break;
4471                                        case 'inside':
4472                                            b = this._left;
4473                                            e = this._left+s;
4474                                            break;
4475                                        case 'cross':
4476                                            b = this._left-s;
4477                                            e = this._left+s;
4478                                            break;
4479                                        default:
4480                                            b = this._left-s;
4481                                            e = this._left;
4482                                            break;
4483                                            }
4484                                    // draw the shadow
4485                                    if (this.shadow) {
4486                                        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});
4487                                    }
4488                                    drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
4489                                }
4490                                break;
4491                            case 'x2axis':
4492                                // draw the grid line
4493                                if (t.showGridline && this.drawGri
4493dlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) {
4494                                    drawLine(pos, this._bottom, pos, this._top);
4495                                }
4496                                // draw the mark
4497                                if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) {
4498                                    s = t.markSize;
4499                                    m = t.mark;
4500                                    var pos = Math.round(axis.u2p(t.value)) + 0.5;
4501                                    switch (m) {
4502                                        case 'outside':
4503                                            b = this._top-s;
4504                                            e = this._top;
4505                                            break;
4506                                        case 'inside':
4507                                            b = this._top;
4508                                            e = this._top+s;
4509                                            break;
4510                                        case 'cross':
4511                                            b = this._top-s;
4512                                            e = this._top+s;
4513                                            break;
4514                                        default:
4515                                            b = this._top-s;
4516                                            e = this._top;
4517                                            break;
4518                                            }
4519                                    // draw the shadow
4520                                    if (this.shadow) {
4521                                        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});
4522                                    }
4523                                    drawLine(pos, b, pos, e);
4524                                }
4525                                break;
4526                            case 'y2axis':
4527                                // draw the grid line
4528                                if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) {
4529                                    drawLine(this._left, pos, this._right, pos);
4530                                }
4531                                // draw the mark
4532                                if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) {
4533                                    s = t.markSize;
4534                                    m = t.mark;
4535                                    var pos = Math.round(axis.u2p(t.value)) + 0.5;
4536                                    switch (m) {
4537                                        case 'outside':
4538                                            b = this._right;
4539                                            e = this._right+s;
4540                                            break;
4541                                        case 'inside':
4542                                            b = this._right-s;
4543                                            e = this._right;
4544                                            break;
4545                                        case 'cross':
4546                                            b = this._right-s;
4547                                            e = this._right+s;
4548                                            break;
4549                                        default:
4550                                            b = this._right;
4551                                            e = this._right+s;
4552                                            break;
4553                                            }
4554                                    // draw the shadow
4555                                    if (this.shadow) {
4556                                        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});
4557                                    }
4558                                    drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
4559                                }
4560                                break;
4561                            default:
4562                                break;
4563                        }
4564                    }
4565                }
4566                t = null;
4567            }
4568            axis = null;
4569            ticks = null;
4570        }
4571        // Now draw grid lines for additional y axes
4572        //////
4573        // TO DO: handle yMidAxis
4574        //////
4575        ax = ['y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis'];
4576        for (var i=7; i>0; i--) {
4577            var axis = axes[ax[i-1]];
4578            var ticks = axis._ticks;
4579            if (axis.show) {
4580                var tn = ticks[axis.numberTicks-1];
4581                var t0 = ticks[0];
4582                var left = axis.getLeft();
4583                var points = [[left, tn.getTop() + tn.getHeight()/2], [left, t0.getTop() + t0.getHeight()/2 + 1.0]];
4584                // draw the shadow
4585                if (this.shadow) {
4586                    this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', fill:false, closePath:false});
4587                }
4588                // draw the line
4589                drawLine(points[0][0], points[0][1], points[1][0], points[1][1], {lineCap:'butt', strokeStyle:axis.borderColor, lineWidth:axis.borderWidth});
4590                // draw the tick marks
4591                for (var j=ticks.length; j>0; j--) {
4592                    var t = ticks[j-1];
4593                    s = t.markSize;
4594                    m = t.mark;
4595                    var pos = Math.round(axis.u2p(t.value)) + 0.5;
4596                    if (t.showMark && t.mark) {
4597                        switch (m) {
4598                            case 'outside':
4599                                b = left;
4600                                e = left+s;
4601                                break;
4602                            case 'inside':
4603                                b = left-s;
4604                                e = left;
4605                                break;
4606                            case 'cross':
4607                                b = left-s;
4608                                e = left+s;
4609                                break;
4610                            default:
4611                                b = left;
4612                                e = left+s;
4613                                break;
4614                        }
4615                        points = [[b,pos], [e,pos]];
4616                        // draw the shadow
4617                        if (this.shadow) {
4618                            this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false});
4619                        }
4620                        // draw the line
4621                        drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
4622                    }
4623                    t = null;
4624                }
4625                t0 = null;
4626            }
4627            axis = null;
4628            ticks =  null;
4629        }
4630        
4631        ctx.restore();
4632        
4633        function drawLine(bx, by, ex, ey, opts) {
4634            ctx.save();
4635            opts = opts || {};
4636            if (opts.lineWidth == null || opts.lineWidth != 0){
4637                $.extend(true, ctx, opts);
4638                ctx.beginPath();
4639                ctx.moveTo(bx, by);
4640                ctx.lineTo(ex, ey);
4641                ctx.stroke();
4642                ctx.restore();
4643            }
4644        }
4645        
4646        if (this.shadow) {
4647            var points = [[this._left, this._bottom], [this._right, this._bottom], [this._right, this._top]];
4648            this.renderer.shadowRenderer.draw(ctx, points);
4649        }
4650        // Now draw border around grid.  Use axis border definitions. start at
4651        // upper left and go clockwise.
4652        if (this.borderWidth != 0 && this.drawBorder) {
4653            drawLine (this._left, this._top, this._right, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth});
4654            drawLine (this._right, this._top, this._right, this._bottom, {lineCap:'round', strokeStyle:axes.y2axis.borderColor, lineWidth:axes.y2axis.borderWidth});
4655            drawLine (this._right, this._bottom, this._left, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth});
4656            drawLine (this._left, this._bottom, this._left, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth});
4657        }
4658        // ctx.lineWidth = this.borderWidth;
4659        // ctx.strokeStyle = this.borderColor;
4660        // ctx.strokeRect(this._left, this._top, this._width, this._height);
4661        
4662        ctx.restore();
4663        ctx =  null;
4664        axes = null;
4665    };
4666 
4667    // Class: $.jqplot.DivTitleRenderer
4668    // The default title renderer for jqPlot.  This class has no options beyond the <Title> class. 
4669    $.jqplot.DivTitleRenderer = function() {
4670    };
4671    
4672    $.jqplot.DivTitleRenderer.prototype.init = function(options) {
4673        $.extend(true, this, options);
4674    };
4675    
4676    $.jqplot.DivTitleRenderer.prototype.draw = function() {
4677        // Memory Leaks patch
4678        if (this._elem) {
4679            this._elem.emptyForce();
4680            this._elem = null;
4681        }
4682
4683        var r = this.renderer;
4684        var elem = document.createElement('div');
4685        this._elem = $(elem);
4686        this._elem.addClass('jqplot-title');
4687
4688        if (!this.text) {
4689            this.show = false;
4690            this._elem.height(0);
4691            this._elem.width(0);
4692        }
4693        else if (this.text) {
4694            var color;
4695            if (this.color) {
4696                color = this.color;
4697            }
4698            else if (this.textColor) {
4699                color = this.textColor;
4700            }
4701
4702            // don't trust that a stylesheet is present, set the position.
4703            var styles = {position:'absolute', top:'0px', left:'0px'};
4704
4705            if (this._plotWidth) {
4706                styles['width'] = this._plotWidth+'px';
4707            }
4708            if (this.fontSize) {
4709                styles['fontSize'] = this.fontSize;
4710            }
4711            if (typeof this.textAlign === 'string') {
4712                styles['textAlign'] = this.textAlign;
4713            }
4714            else {
4715                styles['textAlign'] = 'center';
4716            }
4717            if (color) {
4718                styles['color'] = color;
4719            }
4720            if (this.paddingBottom) {
4721                styles['paddingBottom'] = this.paddingBottom;
4722            }
4723            if (this.fontFamily) {
4724                styles['fontFamily'] = this.fontFamily;
4725            }
4726
4727            this._elem.css(styles);
4728            if (this.escapeHtml) {
4729                this._elem.text(this.text);
4730            }
4731            else {
4732                this._elem.html(this.text);
4733            }
4734
4735
4736            // styletext += (this._plotWidth) ? 'width:'+this._plotWidth+'px;' : '';
4737            // styletext += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
4738            // styletext += (this.textAlign) ? 'text-align:'+this.textAlign+';' : 'text-align:center;';
4739            // styletext += (color) ? 'color:'+color+';' : '';
4740            // styletext += (this.paddingBottom) ? 'padding-bottom:'+this.paddingBottom+';' : '';
4741            // this._elem = $('<div class="jqplot-title" style="'+styletext+'">'+this.text+'</div>');
4742            // if (this.fontFamily) {
4743            //     this._elem.css('font-family', this.fontFamily);
4744            // }
4745        }
4746
4747        elem = null;
4748        
4749        return this._elem;
4750    };
4751    
4752    $.jqplot.DivTitleRenderer.prototype.pack = function() {
4753        // nothing to do here
4754    };
4755  
4756
4757    var dotlen = 0.1;
4758
4759    $.jqplot.LinePattern = function (ctx, pattern) {
4760
4761        var defaultLinePatterns = {
4762            dotted: [ dotlen, $.jqplot.config.dotGapLength ],
4763            dashed: [ $.jqplot.config.dashLength, $.jqplot.config.gapLength ],
4764            solid: null
4765        };
4766
4767        if (typeof pattern === 'string') {
4768            if (pattern[0] === '.' || pattern[0] === '-') {
4769                var s = pattern;
4770                pattern = [];
4771                for (var i=0, imax=s.length; i<imax; i++) {
4772                    if (s[i] === '.') {
4773                        pattern.push( dotlen );
4774                    }
4775                    else if (s[i] === '-') {
4776                        pattern.push( $.jqplot.config.dashLength );
4777                    }
4778                    else {
4779                        continue;
4780                    }
4781                    pattern.push( $.jqplot.config.gapLength );
4782                }
4783            }
4784            else {
4785                pattern = defaultLinePatterns[pattern];
4786            }
4787        }
4788
4789        if (!(pattern && pattern.length)) {
4790            return ctx;
4791        }
4792
4793        var patternIndex = 0;
4794        var patternDistance = pattern[0];
4795        var px = 0;
4796        var py = 0;
4797        var pathx0 = 0;
4798        var pathy0 = 0;
4799
4800        var moveTo = function (x, y) {
4801            ctx.moveTo( x, y );
4802            px = x;
4803            py = y;
4804            pathx0 = x;
4805            pathy0 = y;
4806        };
4807
4808        var lineTo = function (x, y) {
4809            var scale = ctx.lineWidth;
4810            var dx = x - px;
4811            var dy = y - py;
4812            var dist = Math.sqrt(dx*dx+dy*dy);
4813            if ((dist > 0) && (scale > 0)) {
4814                dx /= dist;
4815                dy /= dist;
4816                while (true) {
4817                    var dp = scale * patternDistance;
4818                    if (dp < dist) {
4819                        px += dp * dx;
4820                        py += dp * dy;
4821                        if ((patternIndex & 1) == 0) {
4822                            ctx.lineTo( px, py );
4823                        }
4824                        else {
4825                            ctx.moveTo( px, py );
4826                        }
4827                        dist -= dp;
4828                        patternIndex++;
4829                        if (patternIndex >= pattern.length) {
4830                            patternIndex = 0;
4831                        }
4832                        patternDistance = pattern[patternIndex];
4833                    }
4834                    else {
4835                        px = x;
4836                        py = y;
4837                        if ((patternIndex & 1) == 0) {
4838                            ctx.lineTo( px, py );
4839                        }
4840                        else {
4841                            ctx.moveTo( px, py );
4842                        }
4843                        patternDistance -= dist / scale;
4844                        break;
4845                    }
4846                }
4847            }
4848        };
4849
4850        var beginPath = function () {
4851            ctx.beginPath();
4852        };
4853
4854        var closePath = function () {
4855            lineTo( pathx0, pathy0 );
4856        };
4857
4858        return {
4859            moveTo: moveTo,
4860            lineTo: lineTo,
4861            beginPath: beginPath,
4862            closePath: closePath
4863        };
4864    };
4865
4866    // Class: $.jqplot.LineRenderer
4867    // The default line renderer for jqPlot, this class has no options beyond the <Series> class.
4868    // Draws series as a line.
4869    $.jqplot.LineRenderer = function(){
4870        this.shapeRenderer = new $.jqplot.ShapeRenderer();
4871        this.shadowRenderer = new $.jqplot.ShadowRenderer();
4872    };
4873    
4874    // called with scope of series.
4875    $.jqplot.LineRenderer.prototype.init = function(options, plot) {
4876        // Group: Properties
4877        //
4878        options = options || {};
4879        this._type='line';
4880        this.renderer.animation = {
4881            show: false,
4882            direction: 'left',
4883            speed: 2500,
4884            _supported: true
4885        };
4886        // prop: smooth
4887        // True to draw a smoothed (interpolated) line through the data points
4888        // with automatically computed number of smoothing points.
4889        // Set to an integer number > 2 to specify number of smoothing points
4890        // to use between each data point.
4891        this.renderer.smooth = false;  // true or a number > 2 for smoothing.
4892        this.renderer.tension = null; // null to auto compute or a number typically > 6.  Fewer points requires higher tension.
4893        // prop: constrainSmoothing
4894        // True to use a more accurate smoothing algorithm that will
4895        // not overshoot any data points.  False to allow overshoot but
4896        // produce a smoother looking line.
4897        this.renderer.constrainSmoothing = true;
4898        // this is smoothed data in grid coordinates, like gridData
4899        this.renderer._smoothedData = [];
4900        // this is smoothed data in plot units (plot coordinates), like plotData.
4901        this.renderer._smoothedPlotData = [];
4902        this.renderer._hiBandGridData = [];
4903        this.renderer._lowBandGridData = [];
4904        this.renderer._hiBandSmoothedData = [];
4905        this.renderer._lowBandSmoothedData = [];
4906
4907        // prop: bandData
4908        // Data used to draw error bands or confidence intervals above/below a line.
4909        //
4910        // bandData can be input in 3 forms.  jqPlot will figure out which is the
4911        // low band line and which is the high band line for all forms:
4912        // 
4913        // A 2 dimensional array like [[yl1, yl2, ...], [yu1, yu2, ...]] where
4914        // [yl1, yl2, ...] are y values of the lower line and
4915        // [yu1, yu2, ...] are y values of the upper line.
4916        // In this case there must be the same number of y data points as data points
4917        // in the series and the bands will inherit the x values of the series.
4918        //
4919        // A 2 dimensional array like [[[xl1, yl1], [xl2, yl2], ...], [[xh1, yh1], [xh2, yh2], ...]]
4920        // where [xl1, yl1] are x,y data points for the lower line and
4921        // [xh1, yh1] are x,y data points for the high line.
4922        // x values do not have to correspond to the x values of the series and can
4923        // be of any arbitrary length.
4924        //
4925        // Can be of form [[yl1, yu1], [yl2, yu2], [yl3, yu3], ...] where
4926        // there must be 3 or more arrays and there must be the same number of arrays
4927        // as there are data points in the series.  In this case, 
4928        // [yl1, yu1] specifies the lower and upper y values for the 1st
4929        // data point and so on.  The bands will inherit the x
4930        // values from the series.
4931        this.renderer.bandData = [];
4932
4933        // Group: bands
4934        // Banding around line, e.g error bands or confidence intervals.
4935        this.renderer.bands = {
4936            // prop: show
4937            // true to show the bands.  If bandData or interval is
4938            // supplied, show will be set to true by default.
4939            show: false,
4940            hiData: [],
4941            lowData: [],
4942            // prop: color
4943            // color of lines at top and bottom of bands [default: series color].
4944            color: this.color,
4945            // prop: showLines
4946            // True to show lines at top and bottom of bands [default: false].
4947            showLines: false,
4948            // prop: fill
4949            // True to fill area between bands [default: true].
4950            fill: true,
4951            // prop: fillColor
4952            // css color spec for filled area.  [default: series color].
4953            fillColor: null,
4954            _min: null,
4955            _max: null,
4956            // prop: interval
4957            // User specified interval above and below line for bands [default: '3%''].
4958            // Can be a value like 3 or a string like '3%' 
4959            // or an upper/lower array like [1, -2] or ['2%', '-1.5%']
4960            interval: '3%'
4961        };
4962
4963
4964        var lopts = {highlightMouseOver: options.highlightMouseOver, highlightMouseDown: options.highlightMouseDown, highlightColor: options.highlightColor};
4965        
4966        delete (options.highlightMouseOver);
4967        delete (options.highlightMouseDown);
4968        delete (options.highlightColor);
4969        
4970        $.extend(true, this.renderer, options);
4971
4972        this.renderer.options = options;
4973
4974        // if we are given some band data, and bands aren't explicity set to false in options, turn them on.
4975        if (this.renderer.bandData.length > 1 && (!options.bands || options.bands.show == null)) {
4976            this.renderer.bands.show = true;
4977        }
4978
4979        // if we are given an interval, and bands aren't explicity set to false in options, turn them on.
4980        else if (options.bands && options.bands.show == null && options.bands.interval != null) {
4981            this.renderer.bands.show = true;
4982        }
4983
4984        // if plot is filled, turn off bands.
4985        if (this.fill) {
4986            this.renderer.bands.show = false;
4987        }
4988
4989        if (this.renderer.bands.show) {
4990            this.renderer.initBands.call(this, this.renderer.options, plot);
4991        }
4992
4993
4994        // smoothing is not compatible with stacked lines, disable
4995        if (this._stack) {
4996            this.renderer.smooth = false;
4997        }
4998
4999        // set the shape renderer options
5000        var opts = {lineJoin:this.lineJoin, lineCap:this.lineCap, fill:this.fill, isarc:false, strokeStyle:this.color, fillStyle:this.fillColor, lineWidth:this.lineWidth, linePattern:this.linePattern, closePath:this.fill};
5001        this.renderer.shapeRenderer.init(opts);
5002
5003        var shadow_offset = options.shadowOffset;
5004        // set the shadow renderer options
5005        if (shadow_offset == null) {
5006            // scale the shadowOffset to the width of the line.
5007            if (this.lineWidth > 2.5) {
5008                shadow_offset = 1.25 * (1 + (Math.atan((this.lineWidth/2.5))/0.785398163 - 1)*0.6);
5009                // var shadow_offset = this.shadowOffset;
5010            }
5011            // for skinny lines, don't make such a big shadow.
5012            else {
5013                shadow_offset = 1.25 * Math.atan((this.lineWidth/2.5))/0.785398163;
5014            }
5015        }
5016        
5017        var sopts = {lineJoin:this.lineJoin, lineCap:this.lineCap, fill:this.fill, isarc:false, angle:this.shadowAngle, offset:shadow_offset, alpha:this.shadowAlpha, depth:this.shadowDepth, lineWidth:this.lineWidth, linePattern:this.linePattern, closePath:this.fill};
5018        this.renderer.shadowRenderer.init(sopts);
5019        this._areaPoints = [];
5020        this._boundingBox = [[],[]];
5021        
5022        if (!this.isTrendline && this.fill || this.renderer.bands.show) {
5023            // Group: Properties
5024            //        
5025            // prop: highlightMouseOver
5026            // True to highlight area on a filled plot when moused over.
5027            // This must be false to enable highlightMouseDown to highlight when clicking on an area on a filled plot.
5028            this.highlightMouseOver = true;
5029            // prop: highlightMouseDown
5030            // True to highlight when a mouse button is pressed over an area on a filled plot.
5031            // This will be disabled if highlightMouseOver is true.
5032            this.highlightMouseDown = false;
5033            // prop: highlightColor
5034            // color to use when highlighting an area on a filled plot.
5035            this.highlightColor = null;
5036            // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
5037            if (lopts.highlightMouseDown && lopts.highlightMouseOver == null) {
5038                lopts.highlightMouseOver = false;
5039            }
5040        
5041            $.extend(true, this, {highlightMouseOver: lopts.highlightMouseOver, highlightMouseDown: lopts.highlightMouseDown, highlightColor: lopts.highlightColor});
5042            
5043            if (!this.highlightColor) {
5044                var fc = (this.renderer.bands.show) ? this.renderer.bands.fillColor : this.fillColor;
5045                this.highlightColor = $.jqplot.computeHighlightColors(fc);
5046            }
5047            // turn off (disable) the highlighter plugin
5048            if (this.highlighter) {
5049                this.highlighter.show = false;
5050            }
5051        }
5052        
5053        if (!this.isTrendline && plot) {
5054            plot.plugins.lineRenderer = {};
5055            plot.postInitHooks.addOnce(postInit);
5056            plot.postDrawHooks.addOnce(postPlotDraw);
5057            plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
5058            plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown);
5059            plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp);
5060            plot.eventListenerHooks.addOnce('jqplotClick', handleClick);
5061            plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick);
5062        }
5063
5064    };
5065
5066    $.jqplot.LineRenderer.prototype.initBands = function(options, plot) {
5067        // use bandData if no data specified in bands option
5068        //var bd = this.renderer.bandData;
5069        var bd = options.bandData || [];
5070        var bands = this.renderer.bands;
5071        bands.hiData = [];
5072        bands.lowData = [];
5073        var data = this.data;
5074        bands._max = null;
5075        bands._min = null;
5076        // If 2 arrays, and each array greater than 2 elements, assume it is hi and low data bands of y values.
5077        if (bd.length == 2) {
5078            // Do we have an array of x,y values?
5079            // like [[[1,1], [2,4], [3,3]], [[1,3], [2,6], [3,5]]]
5080            if ($.isArray(bd[0][0])) {
5081                // since an arbitrary array of points, spin through all of them to determine max and min lines.
5082
5083                var p;
5084                var bdminidx = 0, bdmaxidx = 0;
5085                for (var i = 0, l = bd[0].length; i<l; i++) {
5086                    p = bd[0][i];
5087                    if ((p[1] != null && p[1] > bands._max) || bands._max == null) {
5088                        bands._max = p[1];
5089                    }
5090                    if ((p[1] != null && p[1] < bands._min) || bands._min == null) {
5091                        bands._min = p[1];
5092                    }
5093                }
5094                for (var i = 0, l = bd[1].length; i<l; i++) {
5095                    p = bd[1][i];
5096                    if ((p[1] != null && p[1] > bands._max) || bands._max == null) {
5097                        bands._max = p[1];
5098                        bdmaxidx = 1;
5099                    }
5100                    if ((p[1] != null && p[1] < bands._min) || bands._min == null) {
5101                        bands._min = p[1];
5102                        bdminidx = 1;
5103                    }
5104                }
5105
5106                if (bdmaxidx === bdminidx) {
5107                    bands.show = false;
5108                }
5109
5110                bands.hiData = bd[bdmaxidx];
5111                bands.lowData = bd[bdminidx];
5112            }
5113            // else data is arrays of y values
5114            // like [[1,4,3], [3,6,5]]
5115            // must have same number of band data points as points in series
5116            else if (bd[0].length === data.length && bd[1].length === data.length) {
5117                var hi = (bd[0][0] > bd[1][0]) ? 0 : 1;
5118                var low = (hi) ? 0 : 1;
5119                for (var i=0, l=data.length; i < l; i++) {
5120                    bands.hiData.push([data[i][0], bd[hi][i]]);
5121                    bands.lowData.push([data[i][0], bd[low][i]]);
5122                }
5123            }
5124
5125            // we don't have proper data array, don't show bands.
5126            else {
5127                bands.show = false;
5128            }
5129        }
5130
5131        // if more than 2 arrays, have arrays of [ylow, yhi] values.
5132        // note, can't distinguish case of [[ylow, yhi], [ylow, yhi]] from [[ylow, ylow], [yhi, yhi]]
5133        // this is assumed to be of the latter form.
5134        else if (bd.length > 2 && !$.isArray(bd[0][0])) {
5135            var hi = (bd[0][0] > bd[0][1]) ? 0 : 1;
5136            var low = (hi) ? 0 : 1;
5137            for (var i=0, l=bd.length; i<l; i++) {
5138                bands.hiData.push([data[i][0], bd[i][hi]]);
5139                bands.lowData.push([data[i][0], bd[i][low]]);
5140            }
5141        }
5142
5143        // don't have proper data, auto calculate
5144        else {
5145            var intrv = bands.interval;
5146            var a = null;
5147            var b = null;
5148            var afunc = null;
5149            var bfunc = null;
5150
5151            if ($.isArray(intrv)) {
5152                a = intrv[0];
5153                b = intrv[1];
5154            }
5155            else {
5156                a = intrv;
5157            }
5158
5159            if (isNaN(a)) {
5160                // we have a string
5161                if (a.charAt(a.length - 1) === '%') {
5162                    afunc = 'multiply';
5163                    a = parseFloat(a)/100 + 1;
5164                }
5165            }
5166
5167            else {
5168                a = parseFloat(a);
5169                afunc = 'add';
5170            }
5171
5172            if (b !== null && isNaN(b)) {
5173                // we have a string
5174                if (b.charAt(b.length - 1) === '%') {
5175                    bfunc = 'multiply';
5176                    b = parseFloat(b)/100 + 1;
5177                }
5178            }
5179
5180            else if (b !== null) {
5181                b = parseFloat(b);
5182                bfunc = 'add';
5183            }
5184
5185            if (a !== null) {
5186                if (b === null) {
5187                    b = -a;
5188                    bfunc = afunc;
5189                    if (bfunc === 'multiply') {
5190                        b += 2;
5191                    }
5192                }
5193
5194                // make sure a always applies to hi band.
5195                if (a < b) {
5196                    var temp = a;
5197                    a = b;
5198                    b = temp;
5199                    temp = afunc;
5200                    afunc = bfunc;
5201                    bfunc = temp;
5202                }
5203
5204                for (var i=0, l = data.length; i < l; i++) {
5205                    switch (afunc) {
5206                        case 'add':
5207                            bands.hiData.push([data[i][0], data[i][1] + a]);
5208                            break;
5209                        case 'multiply':
5210                            bands.hiData.push([data[i][0], data[i][1] * a]);
5211                            break;
5212                    }
5213                    switch (bfunc) {
5214                        case 'add':
5215                            bands.lowData.push([data[i][0], data[i][1] + b]);
5216                            break;
5217                        case 'multiply':
5218                            bands.lowData.push([data[i][0], data[i][1] * b]);
5219                            break;
5220                    }
5221                }
5222            }
5223
5224            else {
5225                bands.show = false;
5226            }
5227        }
5228
5229        var hd = bands.hiData;
5230        var ld = bands.lowData;
5231        for (var i = 0, l = hd.length; i<l; i++) {
5232            if ((hd[i][1] != null && hd[i][1] > bands._max) || bands._max == null) {
5233                bands._max = hd[i][1];
5234            }
5235        }
5236        for (var i = 0, l = ld.length; i<l; i++) {
5237            if ((ld[i][1] != null && ld[i][1] < bands._min) || bands._min == null) {
5238                bands._min = ld[i][1];
5239            }
5240        }
5241
5242        // one last check for proper data
5243        // these don't apply any more since allowing arbitrary x,y values
5244        // if (bands.hiData.length != bands.lowData.length) {
5245        //     bands.show = false;
5246        // }
5247
5248        // if (bands.hiData.length != this.data.length) {
5249        //     bands.show = false;
5250        // }
5251
5252        if (bands.fillColor === null) {
5253            var c = $.jqplot.getColorComponents(bands.color);
5254            // now adjust alpha to differentiate fill
5255            c[3] = c[3] * 0.5;
5256            bands.fillColor = 'rgba(' + c[0] +', '+ c[1] +', '+ c[2] +', '+ c[3] + ')';
5257        }
5258    };
5259
5260    function getSteps (d, f) {
5261        return (3.4182054+f) * Math.pow(d, -0.3534992);
5262    }
5263
5264    function computeSteps (d1, d2) {
5265        var s = Math.sqrt(Math.pow((d2[0]- d1[0]), 2) + Math.pow ((d2[1] - d1[1]), 2));
5266        return 5.7648 * Math.log(s) + 7.4456;
5267    }
5268
5269    function tanh (x) {
5270        var a = (Math.exp(2*x) - 1) / (Math.exp(2*x) + 1);
5271        return a;
5272    }
5273
5274    //////////
5275    // computeConstrainedSmoothedData
5276    // An implementation of the constrained cubic spline interpolation
5277    // method as presented in:
5278    //
5279    // Kruger, CJC, Constrained Cubic Spine Interpolation for Chemical Engineering Applications
5280    // http://www.korf.co.uk/spline.pdf
5281    //
5282    // The implementation below borrows heavily from the sample Visual Basic
5283    // implementation by CJC Kruger found in http://www.korf.co.uk/spline.xls
5284    //
5285    /////////
5286
5287    // called with scope of series
5288    function computeConstrainedSmoothedData (gd) {
5289        var smooth = this.renderer.smooth;
5290        var dim = this.canvas.getWidth();
5291        var xp = this._xaxis.series_p2u;
5292        var yp = this._yaxis.series_p2u; 
5293        var steps =null;
5294        var _steps = null;
5295        var dist = gd.length/dim;
5296        var _smoothedData = [];
5297        var _smoothedPlotData = [];
5298
5299        if (!isNaN(parseFloat(smooth))) {
5300            steps = parseFloat(smooth);
5301        }
5302        else {
5303            steps = getSteps(dist, 0.5);
5304        }
5305
5306        var yy = [];
5307        var xx = [];
5308
5309        for (var i=0, l = gd.length; i<l; i++) {
5310            yy.push(gd[i][1]);
5311            xx.push(gd[i][0]);
5312        }
5313
5314        function dxx(x1, x0) {
5315            if (x1 - x0 == 0) {
5316                return Math.pow(10,10);
5317            }
5318            else {
5319                return x1 - x0;
5320            }
5321        }
5322
5323        var A, B, C, D;
5324        // loop through each line segment.  Have # points - 1 line segments.  Nmber segments starting at 1.
5325        var nmax = gd.length - 1;
5326        for (var num = 1, gdl = gd.length; num<gdl; num++) {
5327            var gxx = [];
5328            var ggxx = [];
5329            // point at each end of segment.
5330            for (var j = 0; j < 2; j++) {
5331                var i = num - 1 + j; // point number, 0 to # points.
5332
5333                if (i == 0 || i == nmax) {
5334                    gxx[j] = Math.pow(10, 10);
5335                }
5336                else if (yy[i+1] - yy[i] == 0 || yy[i] - yy[i-1] == 0) {
5337                    gxx[j] = 0;
5338                }
5339                else if (((xx[i+1] - xx[i]) / (yy[i+1] - yy[i]) + (xx[i] - xx[i-1]) / (yy[i] - yy[i-1])) == 0 ) {
5340                    gxx[j] = 0;
5341                }
5342                else if ( (yy[i+1] - yy[i]) * (yy[i] - yy[i-1]) < 0 ) {
5343                    gxx[j] = 0;
5344                }
5345
5346                else {
5347                    gxx[j] = 2 / (dxx(xx[i + 1], xx[i]) / (yy[i + 1] - yy[i]) + dxx(xx[i], xx[i - 1]) / (yy[i] - yy[i - 1]));
5348                }
5349            }
5350
5351            // Reset first derivative (slope) at first and last point
5352            if (num == 1) {
5353                // First point has 0 2nd derivative
5354                gxx[0] = 3 / 2 * (yy[1] - yy[0]) / dxx(xx[1], xx[0]) - gxx[1] / 2;
5355            }
5356            else if (num == nmax) {
5357                // Last point has 0 2nd derivative
5358                gxx[1] = 3 / 2 * (yy[nmax] - yy[nmax - 1]) / dxx(xx[nmax], xx[nmax - 1]) - gxx[0] / 2;
5359            }   
5360
5361            // Calc second derivative at points
5362            ggxx[0] = -2 * (gxx[1] + 2 * gxx[0]) / dxx(xx[num], xx[num - 1]) + 6 * (yy[num] - yy[num - 1]) / Math.pow(dxx(xx[num], xx[num - 1]), 2);
5363            ggxx[1] = 2 * (2 * gxx[1] + gxx[0]) / dxx(xx[num], xx[num - 1]) - 6 * (yy[num] - yy[num - 1]) / Math.pow(dxx(xx[num], xx[num - 1]), 2);
5364
5365            // Calc constants for cubic interpolation
5366            D = 1 / 6 * (ggxx[1] - ggxx[0]) / dxx(xx[num], xx[num - 1]);
5367            C = 1 / 2 * (xx[num] * ggxx[0] - xx[num - 1] * ggxx[1]) / dxx(xx[num], xx[num - 1]);
5368            B = (yy[num] - yy[num - 1] - C * (Math.pow(xx[num], 2) - Math.pow(xx[num - 1], 2)) - D * (Math.pow(xx[num], 3) - Math.pow(xx[num - 1], 3))) / dxx(xx[num], xx[num - 1]);
5369            A = yy[num - 1] - B * xx[num - 1] - C * Math.pow(xx[num - 1], 2) - D * Math.pow(xx[num - 1], 3);
5370
5371            var increment = (xx[num] - xx[num - 1]) / steps;
5372            var temp, tempx;
5373
5374            for (var j = 0, l = steps; j < l; j++) {
5375                temp = [];
5376                tempx = xx[num - 1] + j * increment;
5377                temp.push(tempx);
5378                temp.push(A + B * tempx + C * Math.pow(tempx, 2) + D * Math.pow(tempx, 3));
5379                _smoothedData.push(temp);
5380                _smoothedPlotData.push([xp(temp[0]), yp(temp[1])]);
5381            }
5382        }
5383
5384        _smoothedData.push(gd[i]);
5385        _smoothedPlotData.push([xp(gd[i][0]), yp(gd[i][1])]);
5386
5387        return [_smoothedData, _smoothedPlotData];
5388    }
5389
5390    ///////
5391    // computeHermiteSmoothedData
5392    // A hermite spline smoothing of the plot data.
5393    // This implementation is derived from the one posted
5394    // by krypin on the jqplot-users mailing list:
5395    //
5396    // http://groups.google.com/group/jqplot-users/browse_thread/thread/748be6a445723cea?pli=1
5397    //
5398    // with a blog post:
5399    //
5400    // http://blog.statscollector.com/a-plugin-renderer-for-jqplot-to-draw-a-hermite-spline/
5401    //
5402    // and download of the original plugin:
5403    //
5404    // http://blog.statscollector.com/wp-content/uploads/2010/02/jqplot.hermiteSplineRenderer.js
5405    //////////
5406
5407    // called with scope of series
5408    function computeHermiteSmoothedData (gd) {
5409        var smooth = this.renderer.smooth;
5410        var tension = this.renderer.tension;
5411        var dim = this.canvas.getWidth();
5412        var xp = this._xaxis.series_p2u;
5413        var yp = this._yaxis.series_p2u; 
5414        var steps =null;
5415        var _steps = null;
5416        var a = null;
5417        var a1 = null;
5418        var a2 = null;
5419        var slope = null;
5420        var slope2 = null;
5421        var temp = null;
5422        var t, s, h1, h2, h3, h4;
5423        var TiX, TiY, Ti1X, Ti1Y;
5424        var pX, pY, p;
5425        var sd = [];
5426        var spd = [];
5427        var dist = gd.length/dim;
5428        var min, max, stretch, scale, shift;
5429        var _smoothedData = [];
5430        var _smoothedPlotData = [];
5431        if (!isNaN(parseFloat(smooth))) {
5432            steps = parseFloat(smooth);
5433        }
5434        else {
5435            steps = getSteps(dist, 0.5);
5436        }
5437        if (!isNaN(parseFloat(tension))) {
5438            tension = parseFloat(tension);
5439        }
5440
5441        for (var i=0, l = gd.length-1; i < l; i++) {
5442
5443            if (tension === null) {
5444                slope = Math.abs((gd[i+1][1] - gd[i][1]) / (gd[i+1][0] - gd[i][0]));
5445
5446                min = 0.3;
5447                max = 0.6;
5448                stretch = (max - min)/2.0;
5449                scale = 2.5;
5450                shift = -1.4;
5451
5452                temp = slope/scale + shift;
5453
5454                a1 = stretch * tanh(temp) - stretch * tanh(shift) + min;
5455
5456                // if have both left and right line segments, will use  minimum tension. 
5457                if (i > 0) {
5458                    slope2 = Math.abs((gd[i][1] - gd[i-1][1]) / (gd[i][0] - gd[i-1][0]));
5459                }
5460                temp = slope2/scale + shift;
5461
5462                a2 = stretch * tanh(temp) - stretch * tanh(shift) + min;
5463
5464                a = (a1 + a2)/2.0;
5465
5466            }
5467            else {
5468                a = tension;
5469            }
5470            for (t=0; t < steps; t++) {
5471                s = t / steps;
5472                h1 = (1 + 2*s)*Math.pow((1-s),2);
5473                h2 = s*Math.pow((1-s),2);
5474                h3 = Math.pow(s,2)*(3-2*s);
5475                h4 = Math.pow(s,2)*(s-1);     
5476                
5477                if (gd[i-1]) {  
5478                    TiX = a * (gd[i+1][0] - gd[i-1][0]); 
5479                    TiY = a * (gd[i+1][1] - gd[i-1][1]);
5480                } else {
5481                    TiX = a * (gd[i+1][0] - gd[i][0]); 
5482                    TiY = a * (gd[i+1][1] - gd[i][1]);                                  
5483                }
5484                if (gd[i+2]) {  
5485                    Ti1X = a * (gd[i+2][0] - gd[i][0]); 
5486                    Ti1Y = a * (gd[i+2][1] - gd[i][1]);
5487                } else {
5488                    Ti1X = a * (gd[i+1][0] - gd[i][0]); 
5489                    Ti1Y = a * (gd[i+1][1] - gd[i][1]);                                 
5490                }
5491                
5492                pX = h1*gd[i][0] + h3*gd[i+1][0] + h2*TiX + h4*Ti1X;
5493                pY = h1*gd[i][1] + h3*gd[i+1][1] + h2*TiY + h4*Ti1Y;
5494                p = [pX, pY];
5495
5496                _smoothedData.push(p);
5497                _smoothedPlotData.push([xp(pX), yp(pY)]);
5498            }
5499        }
5500        _smoothedData.push(gd[l]);
5501        _smoothedPlotData.push([xp(gd[l][0]), yp(gd[l][1])]);
5502
5503        return [_smoothedData, _smoothedPlotData];
5504    }
5505    
5506    // setGridData
5507    // converts the user data values to grid coordinates and stores them
5508    // in the gridData array.
5509    // Called with scope of a series.
5510    $.jqplot.LineRenderer.prototype.setGridData = function(plot) {
5511        // recalculate the grid data
5512        var xp = this._xaxis.series_u2p;
5513        var yp = this._yaxis.series_u2p;
5514        var data = this._plotData;
5515        var pdata = this._prevPlotData;
5516        this.gridData = [];
5517        this._prevGridData = [];
5518        this.renderer._smoothedData = [];
5519        this.renderer._smoothedPlotData = [];
5520        this.renderer._hiBandGridData = [];
5521        this.renderer._lowBandGridData = [];
5522        this.renderer._hiBandSmoothedData = [];
5523        this.renderer._lowBandSmoothedData = [];
5524        var bands = this.renderer.bands;
5525        var hasNull = false;
5526        for (var i=0, l=data.length; i < l; i++) {
5527            // if not a line series or if no nulls in data, push the converted point onto the array.
5528            if (data[i][0] != null && data[i][1] != null) {
5529                this.gridData.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]);
5530            }
5531            // else if there is a null, preserve it.
5532            else if (data[i][0] == null) {
5533                hasNull = true;
5534                this.gridData.push([null, yp.call(this._yaxis, data[i][1])]);
5535            }
5536            else if (data[i][1] == null) {
5537                hasNull = true;
5538                this.gridData.push([xp.call(this._xaxis, data[i][0]), null]);
5539            }
5540            // if not a line series or if no nulls in data, push the converted point onto the array.
5541            if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] != null) {
5542                this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), yp.call(this._yaxis, pdata[i][1])]);
5543            }
5544            // else if there is a null, preserve it.
5545            else if (pdata[i] != null && pdata[i][0] == null) {
5546                this._prevGridData.push([null, yp.call(this._yaxis, pdata[i][1])]);
5547            }  
5548            else if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] == null) {
5549                this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), null]);
5550            }
5551        }
5552
5553        // don't do smoothing or bands on broken lines.
5554        if (hasNull) {
5555            this.renderer.smooth = false;
5556            if (this._type === 'line') {
5557                bands.show = false;
5558            }
5559        }
5560
5561        if (this._type === 'line' && bands.show) {
5562            for (var i=0, l=bands.hiData.length; i<l; i++) {
5563                this.renderer._hiBandGridData.push([xp.call(this._xaxis, bands.hiData[i][0]), yp.call(this._yaxis, bands.hiData[i][1])]);
5564            }
5565            for (var i=0, l=bands.lowData.length; i<l; i++) {
5566                this.renderer._lowBandGridData.push([xp.call(this._xaxis, bands.lowData[i][0]), yp.call(this._yaxis, bands.lowData[i][1])]);
5567            }
5568        }
5569
5570        // calculate smoothed data if enough points and no nulls
5571        if (this._type === 'line' && this.renderer.smooth && this.gridData.length > 2) {
5572            var ret;
5573            if (this.renderer.constrainSmoothing) {
5574                ret = computeConstrainedSmoothedData.call(this, this.gridData);
5575                this.renderer._smoothedData = ret[0];
5576                this.renderer._smoothedPlotData = ret[1];
5577
5578                if (bands.show) {
5579                    ret = computeConstrainedSmoothedData.call(this, this.renderer._hiBandGridData);
5580                    this.renderer._hiBandSmoothedData = ret[0];
5581                    ret = computeConstrainedSmoothedData.call(this, this.renderer._lowBandGridData);
5582                    this.renderer._lowBandSmoothedData = ret[0];
5583                }
5584
5585                ret = null;
5586            }
5587            else {
5588                ret = computeHermiteSmoothedData.call(this, this.gridData);
5589                this.renderer._smoothedData = ret[0];
5590                this.renderer._smoothedPlotData = ret[1];
5591
5592                if (bands.show) {
5593                    ret = computeHermiteSmoothedData.call(this, this.renderer._hiBandGridData);
5594                    this.renderer._hiBandSmoothedData = ret[0];
5595                    ret = computeHermiteSmoothedData.call(this, this.renderer._lowBandGridData);
5596                    this.renderer._lowBandSmoothedData = ret[0];
5597                }
5598
5599                ret = null;
5600            }
5601        }
5602    };
5603    
5604    // makeGridData
5605    // converts any arbitrary data values to grid coordinates and
5606    // returns them.  This method exists so that plugins can use a series'
5607    // linerenderer to generate grid data points without overwriting the
5608    // grid data associated with that series.
5609    // Called with scope of a series.
5610    $.jqplot.LineRenderer.prototype.makeGridData = function(data, plot) {
5611        // recalculate the grid data
5612        var xp = this._xaxis.series_u2p;
5613        var yp = this._yaxis.series_u2p;
5614        var gd = [];
5615        var pgd = [];
5616        this.renderer._smoothedData = [];
5617        this.renderer._smoothedPlotData = [];
5618        this.renderer._hiBandGridData = [];
5619        this.renderer._lowBandGridData = [];
5620        this.renderer._hiBandSmoothedData = [];
5621        this.renderer._lowBandSmoothedData = [];
5622        var bands = this.renderer.bands;
5623        var hasNull = false;
5624        for (var i=0; i<data.length; i++) {
5625            // if not a line series or if no nulls in data, push the converted point onto the array.
5626            if (data[i][0] != null && data[i][1] != null) {
5627                gd.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]);
5628            }
5629            // else if there is a null, preserve it.
5630            else if (data[i][0] == null) {
5631                hasNull = true;
5632                gd.push([null, yp.call(this._yaxis, data[i][1])]);
5633            }
5634            else if (data[i][1] == null) {
5635                hasNull = true;
5636                gd.push([xp.call(this._xaxis, data[i][0]), null]);
5637            }
5638        }
5639
5640        // don't do smoothing or bands on broken lines.
5641        if (hasNull) {
5642            this.renderer.smooth = false;
5643            if (this._type === 'line') {
5644                bands.show = false;
5645            }
5646        }
5647
5648        if (this._type === 'line' && bands.show) {
5649            for (var i=0, l=bands.hiData.length; i<l; i++) {
5650                this.renderer._hiBandGridData.push([xp.call(this._xaxis, bands.hiData[i][0]), yp.call(this._yaxis, bands.hiData[i][1])]);
5651            }
5652            for (var i=0, l=bands.lowData.length; i<l; i++) {
5653                this.renderer._lowBandGridData.push([xp.call(this._xaxis, bands.lowData[i][0]), yp.call(this._yaxis, bands.lowData[i][1])]);
5654            }
5655        }
5656
5657        if (this._type === 'line' && this.renderer.smooth && gd.length > 2) {
5658            var ret;
5659            if (this.renderer.constrainSmoothing) {
5660                ret = computeConstrainedSmoothedData.call(this, gd);
5661                this.renderer._smoothedData = ret[0];
5662                this.renderer._smoothedPlotData = ret[1];
5663
5664                if (bands.show) {
5665                    ret = computeConstrainedSmoothedData.call(this, this.renderer._hiBandGridData);
5666                    this.renderer._hiBandSmoothedData = ret[0];
5667                    ret = computeConstrainedSmoothedData.call(this, this.renderer._lowBandGridData);
5668                    this.renderer._lowBandSmoothedData = ret[0];
5669                }
5670
5671                ret = null;
5672            }
5673            else {
5674                ret = computeHermiteSmoothedData.call(this, gd);
5675                this.renderer._smoothedData = ret[0];
5676                this.renderer._smoothedPlotData = ret[1];
5677
5678                if (bands.show) {
5679                    ret = computeHermiteSmoothedData.call(this, this.renderer._hiBandGridData);
5680                    this.renderer._hiBandSmoothedData = ret[0];
5681                    ret = computeHermiteSmoothedData.call(this, this.renderer._lowBandGridData);
5682                    this.renderer._lowBandSmoothedData = ret[0];
5683                }
5684
5685                ret = null;
5686            }
5687        }
5688        return gd;
5689    };
5690    
5691
5692    // called within scope of series.
5693    $.jqplot.LineRenderer.prototype.draw = function(ctx, gd, options, plot) {
5694        var i;
5695        // get a copy of the options, so we don't modify the original object.
5696        var opts = $.extend(true, {}, options);
5697        var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
5698        var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
5699        var fill = (opts.fill != undefined) ? opts.fill : this.fill;
5700        var fillAndStroke = (opts.fillAndStroke != undefined) ? opts.fillAndStroke : this.fillAndStroke;
5701        var xmin, ymin, xmax, ymax;
5702        ctx.save();
5703        if (gd.length) {
5704            if (showLine) {
5705                // if we fill, we'll have to add points to close the curve.
5706                if (fill) {
5707                    if (this.fillToZero) { 
5708                        // have to break line up into shapes at axis crossings
5709                        var negativeColor = this.negativeColor;
5710                        if (! this.useNegativeColors) {
5711                            negativeColor = opts.fillStyle;
5712                        }
5713                        var isnegative = false;
5714                        var posfs = opts.fillStyle;
5715                    
5716                        // if stoking line as well as filling, get a copy of line data.
5717                        if (fillAndStroke) {
5718                            var fasgd = gd.slice(0);
5719                        }
5720                        // if not stacked, fill down to axis
5721                        if (this.index == 0 || !this._stack) {
5722                        
5723                            var tempgd = [];
5724                            var pd = (this.renderer.smooth) ? this.renderer._smoothedPlotData : this._plotData;
5725                            this._areaPoints = [];
5726                            var pyzero = this._yaxis.series_u2p(this.fillToValue);
5727                            var pxzero = this._xaxis.series_u2p(this.fillToValue);
5728
5729                            opts.closePath = true;
5730                            
5731                            if (this.fillAxis == 'y') {
5732                                tempgd.push([gd[0][0], pyzero]);
5733                                this._areaPoints.push([gd[0][0], pyzero]);
5734                                
5735                                for (var i=0; i<gd.length-1; i++) {
5736                                    tempgd.push(gd[i]);
5737                                    this._areaPoints.push(gd[i]);
5738                                    // do we have an axis crossing?
5739                                    if (pd[i][1] * pd[i+1][1] <= 0) {
5740                                        if (pd[i][1] < 0) {
5741                                            isnegative = true;
5742                                            opts.fillStyle = negativeColor;
5743                                        }
5744                                        else {
5745                                            isnegative = false;
5746                                            opts.fillStyle = posfs;
5747                                        }
5748                                        
5749                                        var xintercept = gd[i][0] + (gd[i+1][0] - gd[i][0]) * (pyzero-gd[i][1])/(gd[i+1][1] - gd[i][1]);
5750                                        tempgd.push([xintercept, pyzero]);
5751                                        this._areaPoints.push([xintercept, pyzero]);
5752                                        // now draw this shape and shadow.
5753                                        if (shadow) {
5754                                            this.renderer.shadowRenderer.draw(ctx, tempgd, opts);
5755                                        }
5756                                        this.renderer.shapeRenderer.draw(ctx, tempgd, opts);
5757                                        // now empty temp array and continue
5758                                        tempgd = [[xintercept, pyzero]];
5759                                        // this._areaPoints = [[xintercept, pyzero]];
5760                                    }   
5761                                }
5762                                if (pd[gd.length-1][1] < 0) {
5763                                    isnegative = true;
5764                                    opts.fillStyle = negativeColor;
5765                                }
5766                                else {
5767                                    isnegative = false;
5768                                    opts.fillStyle = posfs;
5769                                }
5770                                tempgd.push(gd[gd.length-1]);
5771                                this._areaPoints.push(gd[gd.length-1]);
5772                                tempgd.push([gd[gd.length-1][0], pyzero]); 
5773                                this._areaPoints.push([gd[gd.length-1][0], pyzero]); 
5774                            }
5775                            // now draw the last area.
5776                            if (shadow) {
5777                                this.renderer.shadowRenderer.draw(ctx, tempgd, opts);
5778                            }
5779                            this.renderer.shapeRenderer.draw(ctx, tempgd, opts);
5780                            
5781                            
5782                            // var gridymin = this._yaxis.series_u2p(0);
5783                            // // IE doesn't return new length on unshift
5784                            // gd.unshift([gd[0][0], gridymin]);
5785                            // len = gd.length;
5786                            // gd.push([gd[len - 1][0], gridymin]);                   
5787                        }
5788                        // if stacked, fill to line below 
5789                        else {
5790                            var prev = this._prevGridData;
5791                            for (var i=prev.length; i>0; i--) {
5792                                gd.push(prev[i-1]);
5793                                // this._areaPoints.push(prev[i-1]);
5794                            }
5795                            if (shadow) {
5796                                this.renderer.shadowRenderer.draw(ctx, gd, opts);
5797                            }
5798                            this._areaPoints = gd;
5799                            this.renderer.shapeRenderer.draw(ctx, gd, opts);
5800                        }
5801                    }
5802                    /////////////////////////
5803                    // Not filled to zero
5804                    ////////////////////////
5805                    else {                    
5806                        // if stoking line as well as filling, get a copy of line data.
5807                        if (fillAndStroke) {
5808                            var fasgd = gd.slice(0);
5809                        }
5810                        // if not stacked, fill down to axis
5811                        if (this.index == 0 || !this._stack) {
5812                            // var gridymin = this._yaxis.series_u2p(this._yaxis.min) - this.gridBorderWidth / 2;
5813                            var gridymin = ctx.canvas.height;
5814                            // IE doesn't return new length on unshift
5815                            gd.unshift([gd[0][0], gridymin]);
5816                            var len = gd.length;
5817                            gd.push([gd[len - 1][0], gridymin]);                   
5818                        }
5819                        // if stacked, fill to line below 
5820                        else {
5821                            var prev = this._prevGridData;
5822                            for (var i=prev.length; i>0; i--) {
5823                                gd.push(prev[i-1]);
5824                            }
5825                        }
5826                        this._areaPoints = gd;
5827                        
5828                        if (shadow) {
5829                            this.renderer.shadowRenderer.draw(ctx, gd, opts);
5830                        }
5831            
5832                        this.renderer.shapeRenderer.draw(ctx, gd, opts);                        
5833                    }
5834                    if (fillAndStroke) {
5835                        var fasopts = $.extend(true, {}, opts, {fill:false, closePath:false});
5836                        this.renderer.shapeRenderer.draw(ctx, fasgd, fasopts);
5837                        //////////
5838                        // TODO: figure out some way to do shadows nicely
5839                        // if (shadow) {
5840                        //     this.renderer.shadowRenderer.draw(ctx, fasgd, fasopts);
5841                        // }
5842                        // now draw the markers
5843                        if (this.markerRenderer.show) {
5844                            if (this.renderer.smooth) {
5845                                fasgd = this.gridData;
5846                            }
5847                            for (i=0; i<fasgd.length; i++) {
5848                                this.markerRenderer.draw(fasgd[i][0], fasgd[i][1], ctx, opts.markerOptions);
5849                            }
5850                        }
5851                    }
5852                }
5853                else {
5854
5855                    if (this.renderer.bands.show) {
5856                        var bdat;
5857                        var bopts = $.extend(true, {}, opts);
5858                        if (this.renderer.bands.showLines) {
5859                            bdat = (this.renderer.smooth) ? this.renderer._hiBandSmoothedData : this.renderer._hiBandGridData;
5860                            this.renderer.shapeRenderer.draw(ctx, bdat, opts);
5861                            bdat = (this.renderer.smooth) ? this.renderer._lowBandSmoothedData : this.renderer._lowBandGridData;
5862                            this.renderer.shapeRenderer.draw(ctx, bdat, bopts);
5863                        }
5864
5865                        if (this.renderer.bands.fill) {
5866                            if (this.renderer.smooth) {
5867                                bdat = this.renderer._hiBandSmoothedData.concat(this.renderer._lowBandSmoothedData.reverse());
5868                            }
5869                            else {
5870                                bdat = this.renderer._hiBandGridData.concat(this.renderer._lowBandGridData.reverse());
5871                            }
5872                            this._areaPoints = bdat;
5873                            bopts.closePath = true;
5874                            bopts.fill = true;
5875                            bopts.fillStyle = this.renderer.bands.fillColor;
5876                            this.renderer.shapeRenderer.draw(ctx, bdat, bopts);
5877                        }
5878                    }
5879
5880                    if (shadow) {
5881                        this.renderer.shadowRenderer.draw(ctx, gd, opts);
5882                    }
5883    
5884                    this.renderer.shapeRenderer.draw(ctx, gd, opts);
5885                }
5886            }
5887            // calculate the bounding box
5888            var xmin = xmax = ymin = ymax = null;
5889            for (i=0; i<this._areaPoints.length; i++) {
5890                var p = this._areaPoints[i];
5891                if (xmin > p[0] || xmin == null) {
5892                    xmin = p[0];
5893                }
5894                if (ymax < p[1] || ymax == null) {
5895                    ymax = p[1];
5896                }
5897                if (xmax < p[0] || xmax == null) {
5898                    xmax = p[0];
5899                }
5900                if (ymin > p[1] || ymin == null) {
5901                    ymin = p[1];
5902                }
5903            }
5904
5905            if (this.type === 'line' && this.renderer.bands.show) {
5906                ymax = this._yaxis.series_u2p(this.renderer.bands._min);
5907                ymin = this._yaxis.series_u2p(this.renderer.bands._max);
5908            }
5909
5910            this._boundingBox = [[xmin, ymax], [xmax, ymin]];
5911        
5912            // now draw the markers
5913            if (this.markerRenderer.show && !fill) {
5914                if (this.renderer.smooth) {
5915                    gd = this.gridData;
5916                }
5917                for (i=0; i<gd.length; i++) {
5918                    if (gd[i][0] != null && gd[i][1] != null) {
5919                        this.markerRenderer.draw(gd[i][0], gd[i][1], ctx, opts.markerOptions);
5920                    }
5921                }
5922            }
5923        }
5924        
5925        ctx.restore();
5926    };  
5927    
5928    $.jqplot.LineRenderer.prototype.drawShadow = function(ctx, gd, options) {
5929        // This is a no-op, shadows drawn with lines.
5930    };
5931    
5932    // called with scope of plot.
5933    // make sure to not leave anything highlighted.
5934    function postInit(target, data, options) {
5935        for (var i=0; i<this.series.length; i++) {
5936            if (this.series[i].renderer.constructor == $.jqplot.LineRenderer) {
5937                // don't allow mouseover and mousedown at same time.
5938                if (this.series[i].highlightMouseOver) {
5939                    this.series[i].highlightMouseDown = false;
5940                }
5941            }
5942        }
5943    }  
5944    
5945    // called within context of plot
5946    // create a canvas which we can draw on.
5947    // insert it before the eventCanvas, so eventCanvas will still capture events.
5948    function postPlotDraw() {
5949        // Memory Leaks patch    
5950        if (this.plugins.lineRenderer && this.plugins.lineRenderer.highlightCanvas) {
5951          this.plugins.lineRenderer.highlightCanvas.resetCanvas();
5952          this.plugins.lineRenderer.highlightCanvas = null;
5953        }
5954        
5955        this.plugins.lineRenderer.highlightedSeriesIndex = null;
5956        this.plugins.lineRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
5957        
5958        this.eventCanvas._elem.before(this.plugins.lineRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-lineRenderer-highlight-canvas', this._plotDimensions, this));
5959        this.plugins.lineRenderer.highlightCanvas.setContext();
5960        this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
5961    } 
5962    
5963    function highlight (plot, sidx, pidx, points) {
5964        var s = plot.series[sidx];
5965        var canvas = plot.plugins.lineRenderer.highlightCanvas;
5966        canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height);
5967        s._highlightedPoint = pidx;
5968        plot.plugins.lineRenderer.highlightedSeriesIndex = sidx;
5969        var opts = {fillStyle: s.highlightColor};
5970        if (s.type === 'line' && s.renderer.bands.show) {
5971            opts.fill = true;
5972            opts.closePath = true;
5973        }
5974        s.renderer.shapeRenderer.draw(canvas._ctx, points, opts);
5975        canvas = null;
5976    }
5977    
5978    function unhighlight (plot) {
5979        var canvas = plot.plugins.lineRenderer.highlightCanvas;
5980        canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
5981        for (var i=0; i<plot.series.length; i++) {
5982            plot.series[i]._highlightedPoint = null;
5983        }
5984        plot.plugins.lineRenderer.highlightedSeriesIndex = null;
5985        plot.target.trigger('jqplotDataUnhighlight');
5986        canvas = null;
5987    }
5988    
5989    
5990    function handleMove(ev, gridpos, datapos, neighbor, plot) {
5991        if (neighbor) {
5992            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
5993            var evt1 = jQuery.Event('jqplotDataMouseOver');
5994            evt1.pageX = ev.pageX;
5995            evt1.pageY = ev.pageY;
5996            plot.target.trigger(evt1, ins);
5997            if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.lineRenderer.highlightedSeriesIndex)) {
5998                var evt = jQuery.Event('jqplotDataHighlight');
5999                evt.which = ev.which;
6000                evt.pageX = ev.pageX;
6001                evt.pageY = ev.pageY;
6002                plot.target.trigger(evt, ins);
6003                highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points);
6004            }
6005        }
6006        else if (neighbor == null) {
6007            unhighlight (plot);
6008        }
6009    }
6010    
6011    function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
6012        if (neighbor) {
6013            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
6014            if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.lineRenderer.highlightedSeriesIndex)) {
6015                var evt = jQuery.Event('jqplotDataHighlight');
6016                evt.which = ev.which;
6017                evt.pageX = ev.pageX;
6018                evt.pageY = ev.pageY;
6019                plot.target.trigger(evt, ins);
6020                highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points);
6021            }
6022        }
6023        else if (neighbor == null) {
6024            unhighlight (plot);
6025        }
6026    }
6027    
6028    function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
6029        var idx = plot.plugins.lineRenderer.highlightedSeriesIndex;
6030        if (idx != null && plot.series[idx].highlightMouseDown) {
6031            unhighlight(plot);
6032        }
6033    }
6034    
6035    function handleClick(ev, gridpos, datapos, neighbor, plot) {
6036        if (neighbor) {
6037            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
6038            var evt = jQuery.Event('jqplotDataClick');
6039            evt.which = ev.which;
6040            evt.pageX = ev.pageX;
6041            evt.pageY = ev.pageY;
6042            plot.target.trigger(evt, ins);
6043        }
6044    }
6045    
6046    function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
6047        if (neighbor) {
6048            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
6049            var idx = plot.plugins.lineRenderer.highlightedSeriesIndex;
6050            if (idx != null && plot.series[idx].highlightMouseDown) {
6051                unhighlight(plot);
6052            }
6053            var evt = jQuery.Event('jqplotDataRightClick');
6054            evt.which = ev.which;
6055            evt.pageX = ev.pageX;
6056            evt.pageY = ev.pageY;
6057            plot.target.trigger(evt, ins);
6058        }
6059    }
6060    
6061    
6062    // class: $.jqplot.LinearAxisRenderer
6063    // The default jqPlot axis renderer, creating a numeric axis.
6064    $.jqplot.LinearAxisRenderer = function() {
6065    };
6066    
6067    // called with scope of axis object.
6068    $.jqplot.LinearAxisRenderer.prototype.init = function(options){
6069        // prop: breakPoints
6070        // EXPERIMENTAL!! Use at your own risk!
6071        // Works only with linear axes and the default tick renderer.
6072        // Array of [start, stop] points to create a broken axis.
6073        // Broken axes have a "jump" in them, which is an immediate 
6074        // transition from a smaller value to a larger value.
6075        // Currently, axis ticks MUST be manually assigned if using breakPoints
6076        // by using the axis ticks array option.
6077        this.breakPoints = null;
6078        // prop: breakTickLabel
6079        // Label to use at the axis break if breakPoints are specified.
6080        this.breakTickLabel = "&asymp;";
6081        // prop: drawBaseline
6082        // True to draw the axis baseline.
6083        this.drawBaseline = true;
6084        // prop: baselineWidth
6085        // width of the baseline in pixels.
6086        this.baselineWidth = null;
6087        // prop: baselineColor
6088        // CSS color spec for the baseline.
6089        this.baselineColor = null;
6090        // prop: forceTickAt0
6091        // This will ensure that there is always a tick mark at 0.
6092        // If data range is strictly positive or negative,
6093        // this will force 0 to be inside the axis bounds unless
6094        // the appropriate axis pad (pad, padMin or padMax) is set
6095        // to 0, then this will force an axis min or max value at 0.
6096        // This has know effect when any of the following options
6097        // are set:  autoscale, min, max, numberTicks or tickInterval.
6098        this.forceTickAt0 = false;
6099        // prop: forceTickAt100
6100        // This will ensure that there is always a tick mark at 100.
6101        // If data range is strictly above or below 100,
6102        // this will force 100 to be inside the axis bounds unless
6103        // the appropriate axis pad (pad, padMin or padMax) is set
6104        // to 0, then this will force an axis min or max value at 100.
6105        // This has know effect when any of the following options
6106        // are set:  autoscale, min, max, numberTicks or tickInterval.
6107        this.forceTickAt100 = false;
6108        // prop: tickInset
6109        // Controls the amount to inset the first and last ticks from 
6110        // the edges of the grid, in multiples of the tick interval.
6111        // 0 is no inset, 0.5 is one half a tick interval, 1 is a full
6112        // tick interval, etc.
6113        this.tickInset = 0;
6114        // prop: minorTicks
6115        // Number of ticks to add between "major" ticks.
6116        // Major ticks are ticks supplied by user or auto computed.
6117        // Minor ticks cannot be created by user.
6118        this.minorTicks = 0;
6119        // prop: alignTicks
6120        // true to align tick marks across opposed axes
6121        // such as from the y2axis to yaxis.
6122        this.alignTicks = false;
6123        this._autoFormatString = '';
6124        this._overrideFormatString = false;
6125        this._scalefact = 1.0;
6126        $.extend(true, this, options);
6127        if (this.breakPoints) {
6128            if (!$.isArray(this.breakPoints)) {
6129                this.breakPoints = null;
6130            }
6131            else if (this.breakPoints.length < 2 || this.breakPoints[1] <= this.breakPoints[0]) {
6132                this.breakPoints = null;
6133            }
6134        }
6135        if (this.numberTicks != null && this.numberTicks < 2) {
6136            this.numberTicks = 2;
6137        }
6138        this.resetDataBounds();
6139    };
6140    
6141    // called with scope of axis
6142    $.jqplot.LinearAxisRenderer.prototype.draw = function(ctx, plot) {
6143        if (this.show) {
6144            // populate the axis label and value properties.
6145            // createTicks is a method on the renderer, but
6146            // call it within the scope of the axis.
6147            this.renderer.createTicks.call(this, plot);
6148            // fill a div with axes labels in the right direction.
6149            // Need to pregenerate each axis to get its bounds and
6150            // position it and the labels correctly on the plot.
6151            var dim=0;
6152            var temp;
6153            // Added for theming.
6154            if (this._elem) {
6155                // Memory Leaks patch
6156                //this._elem.empty();
6157                this._elem.emptyForce();
6158                this._elem = null;
6159            }
6160            
6161            this._elem = $(document.createElement('div'));
6162            this._elem.addClass('jqplot-axis jqplot-'+this.name);
6163            this._elem.css('position', 'absolute');
6164
6165            
6166            if (this.name == 'xaxis' || this.name == 'x2axis') {
6167                this._elem.width(this._plotDimensions.width);
6168            }
6169            else {
6170                this._elem.height(this._plotDimensions.height);
6171            }
6172            
6173            // create a _label object.
6174            this.labelOptions.axis = this.name;
6175            this._label = new this.labelRenderer(this.labelOptions);
6176            if (this._label.show) {
6177                var elem = this._label.draw(ctx, plot);
6178                elem.appendTo(this._elem);
6179                elem = null;
6180            }
6181    
6182            var t = this._ticks;
6183            var tick;
6184            for (var i=0; i<t.length; i++) {
6185                tick = t[i];
6186                if (tick.show && tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
6187                    this._elem.append(tick.draw(ctx, plot));
6188                }
6189            }
6190            tick = null;
6191            t = null;
6192        }
6193        return this._elem;
6194    };
6195    
6196    // called with scope of an axis
6197    $.jqplot.LinearAxisRenderer.prototype.reset = function() {
6198        this.min = this._options.min;
6199        this.max = this._options.max;
6200        this.tickInterval = this._options.tickInterval;
6201        this.numberTicks = this._options.numberTicks;
6202        this._autoFormatString = '';
6203        if (this._overrideFormatString && this.tickOptions && this.tickOptions.formatString) {
6204            this.tickOptions.formatString = '';
6205        }
6206
6207        // this._ticks = this.__ticks;
6208    };
6209    
6210    // called with scope of axis
6211    $.jqplot.LinearAxisRenderer.prototype.set = function() { 
6212        var dim = 0;
6213        var temp;
6214        var w = 0;
6215        var h = 0;
6216        var lshow = (this._label == null) ? false : this._label.show;
6217        if (this.show) {
6218            var t = this._ticks;
6219            var tick;
6220            for (var i=0; i<t.length; i++) {
6221                tick = t[i];
6222                if (!tick._breakTick && tick.show && tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
6223                    if (this.name == 'xaxis' || this.name == 'x2axis') {
6224                        temp = tick._elem.outerHeight(true);
6225                    }
6226                    else {
6227                        temp = tick._elem.outerWidth(true);
6228                    }
6229                    if (temp > dim) {
6230                        dim = temp;
6231                    }
6232                }
6233            }
6234            tick = null;
6235            t = null;
6236            
6237            if (lshow) {
6238                w = this._label._elem.outerWidth(true);
6239                h = this._label._elem.outerHeight(true); 
6240            }
6241            if (this.name == 'xaxis') {
6242                dim = dim + h;
6243                this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'});
6244            }
6245            else if (this.name == 'x2axis') {
6246                dim = dim + h;
6247                this._elem.css({'height':dim+'px', left:'0px', top:'0px'});
6248            }
6249            else if (this.name == 'yaxis') {
6250                dim = dim + w;
6251                this._elem.css({'width':dim+'px', left:'0px', top:'0px'});
6252                if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
6253                    this._label._elem.css('width', w+'px');
6254                }
6255            }
6256            else {
6257                dim = dim + w;
6258                this._elem.css({'width':dim+'px', right:'0px', top:'0px'});
6259                if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
6260                    this._label._elem.css('width', w+'px');
6261                }
6262            }
6263        }  
6264    };    
6265    
6266    // called with scope of axis
6267    $.jqplot.LinearAxisRenderer.prototype.createTicks = function(plot) {
6268        // we're are operating on an axis here
6269        var ticks = this._ticks;
6270        var userTicks = this.ticks;
6271        var name = this.name;
6272        // databounds were set on axis initialization.
6273        var db = this._dataBounds;
6274        var dim = (this.name.charAt(0) === 'x') ? this._plotDimensions.width : this._plotDimensions.height;
6275        var interval;
6276        var min, max;
6277        var pos1, pos2;
6278        var tt, i;
6279        // get a copy of user's settings for min/max.
6280        var userMin = this.min;
6281        var userMax = this.max;
6282        var userNT = this.numberTicks;
6283        var userTI = this.tickInterval;
6284
6285        var threshold = 30;
6286        this._scalefact =  (Math.max(dim, threshold+1) - threshold)/300.0;
6287        
6288        // if we already have ticks, use them.
6289        // ticks must be in order of increasing value.
6290        
6291        if (userTicks.length) {
6292            // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed
6293            for (i=0; i<userTicks.length; i++){
6294                var ut = userTicks[i];
6295                var t = new this.tickRenderer(this.tickOptions);
6296                if ($.isArray(ut)) {
6297                    t.value = ut[0];
6298                    if (this.breakPoints) {
6299                        if (ut[0] == this.breakPoints[0]) {
6300                            t.label = this.breakTickLabel;
6301                            t._breakTick = true;
6302                            t.showGridline = false;
6303                            t.showMark = false;
6304                        }
6305                        else if (ut[0] > this.breakPoints[0] && ut[0] <= this.breakPoints[1]) {
6306                            t.show = false;
6307                            t.showGridline = false;
6308                            t.label = ut[1];
6309                        }
6310                        else {
6311                            t.label = ut[1];
6312                        }
6313                    }
6314                    else {
6315                        t.label = ut[1];
6316                    }
6317                    t.setTick(ut[0], this.name);
6318                    this._ticks.push(t);
6319                }
6320
6321                else if ($.isPlainObject(ut)) {
6322                    $.extend(true, t, ut);
6323                    t.axis = this.name;
6324                    this._ticks.push(t);
6325                }
6326                
6327                else {
6328                    t.value = ut;
6329                    if (this.breakPoints) {
6330                        if (ut == this.breakPoints[0]) {
6331                            t.label = this.breakTickLabel;
6332                            t._breakTick = true;
6333                            t.showGridline = false;
6334                            t.showMark = false;
6335                        }
6336                        else if (ut > this.breakPoints[0] && ut <= this.breakPoints[1]) {
6337                            t.show = false;
6338                            t.showGridline = false;
6339                        }
6340                    }
6341                    t.setTick(ut, this.name);
6342                    this._ticks.push(t);
6343                }
6344            }
6345            this.numberTicks = userTicks.length;
6346            this.min = this._ticks[0].value;
6347            this.max = this._ticks[this.numberTicks-1].value;
6348            this.tickInterval = (this.max - this.min) / (this.numberTicks - 1);
6349        }
6350        
6351        // we don't have any ticks yet, let's make some!
6352        else {
6353            if (name == 'xaxis' || name == 'x2axis') {
6354                dim = this._plotDimensions.width;
6355            }
6356            else {
6357                dim = this._plotDimensions.height;
6358            }
6359
6360            var _numberTicks = this.numberTicks;
6361
6362            // if aligning this axis, use number of ticks from previous axis.
6363            // Do I need to reset somehow if alignTicks is changed and then graph is replotted??
6364            if (this.alignTicks) {
6365                if (this.name === 'x2axis' && plot.axes.xaxis.show) {
6366                    _numberTicks = plot.axes.xaxis.numberTicks;
6367                }
6368                else if (this.name.charAt(0) === 'y' && this.name !== 'yaxis' && this.name !== 'yMidAxis' && plot.axes.yaxis.show) {
6369                    _numberTicks = plot.axes.yaxis.numberTicks;
6370                }
6371            }
6372        
6373            min = ((this.min != null) ? this.min : db.min);
6374            max = ((this.max != null) ? this.max : db.max);
6375
6376            var range = max - min;
6377            var rmin, rmax;
6378            var temp;
6379
6380            if (this.tickOptions == null || !this.tickOptions.formatString) {
6381                this._overrideFormatString = true;
6382            }
6383
6384            // Doing complete autoscaling
6385            if (this.min == null || this.max == null && this.tickInterval == null && !this.autoscale) {
6386                // Check if user must have tick at 0 or 100 and ensure they are in range.
6387                // The autoscaling algorithm will always place ticks at 0 and 100 if they are in range.
6388                if (this.forceTickAt0) {
6389                    if (min > 0) {
6390                        min = 0;
6391                    }
6392                    if (max < 0) {
6393                        max = 0;
6394                    }
6395                }
6396
6397                if (this.forceTickAt100) {
6398                    if (min > 100) {
6399                        min = 100;
6400                    }
6401                    if (max < 100) {
6402                        max = 100;
6403                    }
6404                }
6405
6406                var keepMin = false,
6407                    keepMax = false;
6408
6409                if (this.min != null) {
6410                    keepMin = true;
6411                }
6412
6413                else if (this.max != null) {
6414                    keepMax = true;
6415                }
6416
6417                // var threshold = 30;
6418                // var tdim = Math.max(dim, threshold+1);
6419                // this._scalefact =  (tdim-threshold)/300.0;
6420                var ret = $.jqplot.LinearTickGenerator(min, max, this._scalefact, _numberTicks, keepMin, keepMax); 
6421                // calculate a padded max and min, points should be less than these
6422                // so that they aren't too close to the edges of the plot.
6423                // User can adjust how much padding is allowed with pad, padMin and PadMax options. 
6424                // If min or max is set, don't pad that end of axis.
6425                var tumin = (this.min != null) ? min : min + range*(this.padMin - 1);
6426                var tumax = (this.max != null) ? max : max - range*(this.padMax - 1);
6427
6428                // if they're equal, we shouldn't have to do anything, r
6428ight?
6429                // if (min <=tumin || max >= tumax) {
6430                if (min <tumin || max > tumax) {
6431                    tumin = (this.min != null) ? min : min - range*(this.padMin - 1);
6432                    tumax = (this.max != null) ? max : max + range*(this.padMax - 1);
6433                    ret = $.jqplot.LinearTickGenerator(tumin, tumax, this._scalefact, _numberTicks, keepMin, keepMax);
6434                }
6435
6436                this.min = ret[0];
6437                this.max = ret[1];
6438                // if numberTicks specified, it should return the same.
6439                this.numberTicks = ret[2];
6440                this._autoFormatString = ret[3];
6441                this.tickInterval = ret[4];
6442            }
6443
6444            // User has specified some axis scale related option, can use auto algorithm
6445            else {
6446                
6447                // if min and max are same, space them out a bit
6448                if (min == max) {
6449                    var adj = 0.05;
6450                    if (min > 0) {
6451                        adj = Math.max(Math.log(min)/Math.LN10, 0.05);
6452                    }
6453                    min -= adj;
6454                    max += adj;
6455                }
6456                
6457                // autoscale.  Can't autoscale if min or max is supplied.
6458                // Will use numberTicks and tickInterval if supplied.  Ticks
6459                // across multiple axes may not line up depending on how
6460                // bars are to be plotted.
6461                if (this.autoscale && this.min == null && this.max == null) {
6462                    var rrange, ti, margin;
6463                    var forceMinZero = false;
6464                    var forceZeroLine = false;
6465                    var intervals = {min:null, max:null, average:null, stddev:null};
6466                    // if any series are bars, or if any are fill to zero, and if this
6467                    // is the axis to fill toward, check to see if we can start axis at zero.
6468                    for (var i=0; i<this._series.length; i++) {
6469                        var s = this._series[i];
6470                        var faname = (s.fillAxis == 'x') ? s._xaxis.name : s._yaxis.name;
6471                        // check to see if this is the fill axis
6472                        if (this.name == faname) {
6473                            var vals = s._plotValues[s.fillAxis];
6474                            var vmin = vals[0];
6475                            var vmax = vals[0];
6476                            for (var j=1; j<vals.length; j++) {
6477                                if (vals[j] < vmin) {
6478                                    vmin = vals[j];
6479                                }
6480                                else if (vals[j] > vmax) {
6481                                    vmax = vals[j];
6482                                }
6483                            }
6484                            var dp = (vmax - vmin) / vmax;
6485                            // is this sries a bar?
6486                            if (s.renderer.constructor == $.jqplot.BarRenderer) {
6487                                // if no negative values and could also check range.
6488                                if (vmin >= 0 && (s.fillToZero || dp > 0.1)) {
6489                                    forceMinZero = true;
6490                                }
6491                                else {
6492                                    forceMinZero = false;
6493                                    if (s.fill && s.fillToZero && vmin < 0 && vmax > 0) {
6494                                        forceZeroLine = true;
6495                                    }
6496                                    else {
6497                                        forceZeroLine = false;
6498                                    }
6499                                }
6500                            }
6501                            
6502                            // if not a bar and filling, use appropriate method.
6503                            else if (s.fill) {
6504                                if (vmin >= 0 && (s.fillToZero || dp > 0.1)) {
6505                                    forceMinZero = true;
6506                                }
6507                                else if (vmin < 0 && vmax > 0 && s.fillToZero) {
6508                                    forceMinZero = false;
6509                                    forceZeroLine = true;
6510                                }
6511                                else {
6512                                    forceMinZero = false;
6513                                    forceZeroLine = false;
6514                                }
6515                            }
6516                            
6517                            // if not a bar and not filling, only change existing state
6518                            // if it doesn't make sense
6519                            else if (vmin < 0) {
6520                                forceMinZero = false;
6521                            }
6522                        }
6523                    }
6524                    
6525                    // check if we need make axis min at 0.
6526                    if (forceMinZero) {
6527                        // compute number of ticks
6528                        this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
6529                        this.min = 0;
6530                        userMin = 0;
6531                        // what order is this range?
6532                        // what tick interval does that give us?
6533                        ti = max/(this.numberTicks-1);
6534                        temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10)));
6535                        if (ti/temp == parseInt(ti/temp, 10)) {
6536                            ti += temp;
6537                        }
6538                        this.tickInterval = Math.ceil(ti/temp) * temp;
6539                        this.max = this.tickInterval * (this.numberTicks - 1);
6540                    }
6541                    
6542                    // check if we need to make sure there is a tick at 0.
6543                    else if (forceZeroLine) {
6544                        // compute number of ticks
6545                        this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
6546                        var ntmin = Math.ceil(Math.abs(min)/range*(this.numberTicks-1));
6547                        var ntmax = this.numberTicks - 1  - ntmin;
6548                        ti = Math.max(Math.abs(min/ntmin), Math.abs(max/ntmax));
6549                        temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10)));
6550                        this.tickInterval = Math.ceil(ti/temp) * temp;
6551                        this.max = this.tickInterval * ntmax;
6552                        this.min = -this.tickInterval * ntmin;
6553                    }
6554                    
6555                    // if nothing else, do autoscaling which will try to line up ticks across axes.
6556                    else {  
6557                        if (this.numberTicks == null){
6558                            if (this.tickInterval) {
6559                                this.numberTicks = 3 + Math.ceil(range / this.tickInterval);
6560                            }
6561                            else {
6562                                this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
6563                            }
6564                        }
6565                
6566                        if (this.tickInterval == null) {
6567                            // get a tick interval
6568                            ti = range/(this.numberTicks - 1);
6569
6570                            if (ti < 1) {
6571                                temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10)));
6572                            }
6573                            else {
6574                                temp = 1;
6575                            }
6576                            this.tickInterval = Math.ceil(ti*temp*this.pad)/temp;
6577                        }
6578                        else {
6579                            temp = 1 / this.tickInterval;
6580                        }
6581                        
6582                        // try to compute a nicer, more even tick interval
6583                        // temp = Math.pow(10, Math.floor(Math.log(ti)/Math.LN10));
6584                        // this.tickInterval = Math.ceil(ti/temp) * temp;
6585                        rrange = this.tickInterval * (this.numberTicks - 1);
6586                        margin = (rrange - range)/2;
6587           
6588                        if (this.min == null) {
6589                            this.min = Math.floor(temp*(min-margin))/temp;
6590                        }
6591                        if (this.max == null) {
6592                            this.max = this.min + rrange;
6593                        }
6594                    }
6595
6596                    // Compute a somewhat decent format string if it is needed.
6597                    // get precision of interval and determine a format string.
6598                    var sf = $.jqplot.getSignificantFigures(this.tickInterval);
6599
6600                    var fstr;
6601
6602                    // if we have only a whole number, use integer formatting
6603                    if (sf.digitsLeft >= sf.significantDigits) {
6604                        fstr = '%d';
6605                    }
6606
6607                    else {
6608                        var temp = Math.max(0, 5 - sf.digitsLeft);
6609                        temp = Math.min(temp, sf.digitsRight);
6610                        fstr = '%.'+ temp + 'f';
6611                    }
6612
6613                    this._autoFormatString = fstr;
6614                }
6615                
6616                // Use the default algorithm which pads each axis to make the chart
6617                // centered nicely on the grid.
6618                else {
6619
6620                    rmin = (this.min != null) ? this.min : min - range*(this.padMin - 1);
6621                    rmax = (this.max != null) ? this.max : max + range*(this.padMax - 1);
6622                    range = rmax - rmin;
6623        
6624                    if (this.numberTicks == null){
6625                        // if tickInterval is specified by user, we will ignore computed maximum.
6626                        // max will be equal or greater to fit even # of ticks.
6627                        if (this.tickInterval != null) {
6628                            this.numberTicks = Math.ceil((rmax - rmin)/this.tickInterval)+1;
6629                        }
6630                        else if (dim > 100) {
6631                            this.numberTicks = parseInt(3+(dim-100)/75, 10);
6632                        }
6633                        else {
6634                            this.numberTicks = 2;
6635                        }
6636                    }
6637                
6638                    if (this.tickInterval == null) {
6639                        this.tickInterval = range / (this.numberTicks-1);
6640                    }
6641                    
6642                    if (this.max == null) {
6643                        rmax = rmin + this.tickInterval*(this.numberTicks - 1);
6644                    }        
6645                    if (this.min == null) {
6646                        rmin = rmax - this.tickInterval*(this.numberTicks - 1);
6647                    }
6648
6649                    // get precision of interval and determine a format string.
6650                    var sf = $.jqplot.getSignificantFigures(this.tickInterval);
6651
6652                    var fstr;
6653
6654                    // if we have only a whole number, use integer formatting
6655                    if (sf.digitsLeft >= sf.significantDigits) {
6656                        fstr = '%d';
6657                    }
6658
6659                    else {
6660                        var temp = Math.max(0, 5 - sf.digitsLeft);
6661                        temp = Math.min(temp, sf.digitsRight);
6662                        fstr = '%.'+ temp + 'f';
6663                    }
6664
6665
6666                    this._autoFormatString = fstr;
6667
6668                    this.min = rmin;
6669                    this.max = rmax;
6670                }
6671                
6672                if (this.renderer.constructor == $.jqplot.LinearAxisRenderer && this._autoFormatString == '') {
6673                    // fix for misleading tick display with small range and low precision.
6674                    range = this.max - this.min;
6675                    // figure out precision
6676                    var temptick = new this.tickRenderer(this.tickOptions);
6677                    // use the tick formatString or, the default.
6678                    var fs = temptick.formatString || $.jqplot.config.defaultTickFormatString; 
6679                    var fs = fs.match($.jqplot.sprintf.regex)[0];
6680                    var precision = 0;
6681                    if (fs) {
6682                        if (fs.search(/[fFeEgGpP]/) > -1) {
6683                            var m = fs.match(/\%\.(\d{0,})?[eEfFgGpP]/);
6684                            if (m) {
6685                                precision = parseInt(m[1], 10);
6686                            }
6687                            else {
6688                                precision = 6;
6689                            }
6690                        }
6691                        else if (fs.search(/[di]/) > -1) {
6692                            precision = 0;
6693                        }
6694                        // fact will be <= 1;
6695                        var fact = Math.pow(10, -precision);
6696                        if (this.tickInterval < fact) {
6697                            // need to correct underrange
6698                            if (userNT == null && userTI == null) {
6699                                this.tickInterval = fact;
6700                                if (userMax == null && userMin == null) {
6701                                    // this.min = Math.floor((this._dataBounds.min - this.tickInterval)/fact) * fact;
6702                                    this.min = Math.floor(this._dataBounds.min/fact) * fact;
6703                                    if (this.min == this._dataBounds.min) {
6704                                        this.min = this._dataBounds.min - this.tickInterval;
6705                                    }
6706                                    // this.max = Math.ceil((this._dataBounds.max + this.tickInterval)/fact) * fact;
6707                                    this.max = Math.ceil(this._dataBounds.max/fact) * fact;
6708                                    if (this.max == this._dataBounds.max) {
6709                                        this.max = this._dataBounds.max + this.tickInterval;
6710                                    }
6711                                    var n = (this.max - this.min)/this.tickInterval;
6712                                    n = n.toFixed(11);
6713                                    n = Math.ceil(n);
6714                                    this.numberTicks = n + 1;
6715                                }
6716                                else if (userMax == null) {
6717                                    // add one tick for top of range.
6718                                    var n = (this._dataBounds.max - this.min) / this.tickInterval;
6719                                    n = n.toFixed(11);
6720                                    this.numberTicks = Math.ceil(n) + 2;
6721                                    this.max = this.min + this.tickInterval * (this.numberTicks-1);
6722                                }
6723                                else if (userMin == null) {
6724                                    // add one tick for bottom of range.
6725                                    var n = (this.max - this._dataBounds.min) / this.tickInterval;
6726                                    n = n.toFixed(11);
6727                                    this.numberTicks = Math.ceil(n) + 2;
6728                                    this.min = this.max - this.tickInterval * (this.numberTicks-1);
6729                                }
6730                                else {
6731                                    // calculate a number of ticks so max is within axis scale
6732                                    this.numberTicks = Math.ceil((userMax - userMin)/this.tickInterval) + 1;
6733                                    // if user's min and max don't fit evenly in ticks, adju
6733st.
6734                                    // This takes care of cases such as user min set to 0, max set to 3.5 but tick
6735                                    // format string set to %d (integer ticks)
6736                                    this.min =  Math.floor(userMin*Math.pow(10, precision))/Math.pow(10, precision);
6737                                    this.max =  Math.ceil(userMax*Math.pow(10, precision))/Math.pow(10, precision);
6738                                    // this.max = this.min + this.tickInterval*(this.numberTicks-1);
6739                                    this.numberTicks = Math.ceil((this.max - this.min)/this.tickInterval) + 1;
6740                                }
6741                            }
6742                        }
6743                    }
6744                }
6745                
6746            }
6747            
6748            if (this._overrideFormatString && this._autoFormatString != '') {
6749                this.tickOptions = this.tickOptions || {};
6750                this.tickOptions.formatString = this._autoFormatString;
6751            }
6752
6753            var t, to;
6754            for (var i=0; i<this.numberTicks; i++){
6755                tt = this.min + i * this.tickInterval;
6756                t = new this.tickRenderer(this.tickOptions);
6757                // var t = new $.jqplot.AxisTickRenderer(this.tickOptions);
6758
6759                t.setTick(tt, this.name);
6760                this._ticks.push(t);
6761
6762                if (i < this.numberTicks - 1) {
6763                    for (var j=0; j<this.minorTicks; j++) {
6764                        tt += this.tickInterval/(this.minorTicks+1);
6765                        to = $.extend(true, {}, this.tickOptions, {name:this.name, value:tt, label:'', isMinorTick:true});
6766                        t = new this.tickRenderer(to);
6767                        this._ticks.push(t);
6768                    }
6769                }
6770                t = null;
6771            }
6772        }
6773
6774        if (this.tickInset) {
6775            this.min = this.min - this.tickInset * this.tickInterval;
6776            this.max = this.max + this.tickInset * this.tickInterval;
6777        }
6778
6779        ticks = null;
6780    };
6781    
6782    // Used to reset just the values of the ticks and then repack, which will
6783    // recalculate the positioning functions.  It is assuemd that the 
6784    // number of ticks is the same and the values of the new array are at the
6785    // proper interval.
6786    // This method needs to be called with the scope of an axis object, like:
6787    //
6788    // > plot.axes.yaxis.renderer.resetTickValues.call(plot.axes.yaxis, yarr);
6789    //
6790    $.jqplot.LinearAxisRenderer.prototype.resetTickValues = function(opts) {
6791        if ($.isArray(opts) && opts.length == this._ticks.length) {
6792            var t;
6793            for (var i=0; i<opts.length; i++) {
6794                t = this._ticks[i];
6795                t.value = opts[i];
6796                t.label = t.formatter(t.formatString, opts[i]);
6797                t.label = t.prefix + t.label;
6798                t._elem.html(t.label);
6799            }
6800            t = null;
6801            this.min = $.jqplot.arrayMin(opts);
6802            this.max = $.jqplot.arrayMax(opts);
6803            this.pack();
6804        }
6805        // Not implemented yet.
6806        // else if ($.isPlainObject(opts)) {
6807        // 
6808        // }
6809    };
6810    
6811    // called with scope of axis
6812    $.jqplot.LinearAxisRenderer.prototype.pack = function(pos, offsets) {
6813        // Add defaults for repacking from resetTickValues function.
6814        pos = pos || {};
6815        offsets = offsets || this._offsets;
6816        
6817        var ticks = this._ticks;
6818        var max = this.max;
6819        var min = this.min;
6820        var offmax = offsets.max;
6821        var offmin = offsets.min;
6822        var lshow = (this._label == null) ? false : this._label.show;
6823        
6824        for (var p in pos) {
6825            this._elem.css(p, pos[p]);
6826        }
6827        
6828        this._offsets = offsets;
6829        // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left.
6830        var pixellength = offmax - offmin;
6831        var unitlength = max - min;
6832        
6833        // point to unit and unit to point conversions references to Plot DOM element top left corner.
6834        if (this.breakPoints) {
6835            unitlength = unitlength - this.breakPoints[1] + this.breakPoints[0];
6836            
6837            this.p2u = function(p){
6838                return (p - offmin) * unitlength / pixellength + min;
6839            };
6840        
6841            this.u2p = function(u){
6842                if (u > this.breakPoints[0] && u < this.breakPoints[1]){
6843                    u = this.breakPoints[0];
6844                }
6845                if (u <= this.breakPoints[0]) {
6846                    return (u - min) * pixellength / unitlength + offmin;
6847                }
6848                else {
6849                    return (u - this.breakPoints[1] + this.breakPoints[0] - min) * pixellength / unitlength + offmin;
6850                }
6851            };
6852                
6853            if (this.name.charAt(0) == 'x'){
6854                this.series_u2p = function(u){
6855                    if (u > this.breakPoints[0] && u < this.breakPoints[1]){
6856                        u = this.breakPoints[0];
6857                    }
6858                    if (u <= this.breakPoints[0]) {
6859                        return (u - min) * pixellength / unitlength;
6860                    }
6861                    else {
6862                        return (u - this.breakPoints[1] + this.breakPoints[0] - min) * pixellength / unitlength;
6863                    }
6864                };
6865                this.series_p2u = function(p){
6866                    return p * unitlength / pixellength + min;
6867                };
6868            }
6869        
6870            else {
6871                this.series_u2p = function(u){
6872                    if (u > this.breakPoints[0] && u < this.breakPoints[1]){
6873                        u = this.breakPoints[0];
6874                    }
6875                    if (u >= this.breakPoints[1]) {
6876                        return (u - max) * pixellength / unitlength;
6877                    }
6878                    else {
6879                        return (u + this.breakPoints[1] - this.breakPoints[0] - max) * pixellength / unitlength;
6880                    }
6881                };
6882                this.series_p2u = function(p){
6883                    return p * unitlength / pixellength + max;
6884                };
6885            }
6886        }
6887        else {
6888            this.p2u = function(p){
6889                return (p - offmin) * unitlength / pixellength + min;
6890            };
6891        
6892            this.u2p = function(u){
6893                return (u - min) * pixellength / unitlength + offmin;
6894            };
6895                
6896            if (this.name == 'xaxis' || this.name == 'x2axis'){
6897                this.series_u2p = function(u){
6898                    return (u - min) * pixellength / unitlength;
6899                };
6900                this.series_p2u = function(p){
6901                    return p * unitlength / pixellength + min;
6902                };
6903            }
6904        
6905            else {
6906                this.series_u2p = function(u){
6907                    return (u - max) * pixellength / unitlength;
6908                };
6909                this.series_p2u = function(p){
6910                    return p * unitlength / pixellength + max;
6911                };
6912            }
6913        }
6914        
6915        if (this.show) {
6916            if (this.name == 'xaxis' || this.name == 'x2axis') {
6917                for (var i=0; i<ticks.length; i++) {
6918                    var t = ticks[i];
6919                    if (t.show && t.showLabel) {
6920                        var shim;
6921                        
6922                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
6923                            // will need to adjust auto positioning based on which axis this is.
6924                            var temp = (this.name == 'xaxis') ? 1 : -1;
6925                            switch (t.labelPosition) {
6926                                case 'auto':
6927                                    // position at end
6928                                    if (temp * t.angle < 0) {
6929                                        shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
6930                                    }
6931                                    // position at start
6932                                    else {
6933                                        shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
6934                                    }
6935                                    break;
6936                                case 'end':
6937                                    shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
6938                                    break;
6939                                case 'start':
6940                                    shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
6941                                    break;
6942                                case 'middle':
6943                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
6944                                    break;
6945                                default:
6946                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
6947                                    break;
6948                            }
6949                        }
6950                        else {
6951                            shim = -t.getWidth()/2;
6952                        }
6953                        var val = this.u2p(t.value) + shim + 'px';
6954                        t._elem.css('left', val);
6955                        t.pack();
6956                    }
6957                }
6958                if (lshow) {
6959                    var w = this._label._elem.outerWidth(true);
6960                    this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px');
6961                    if (this.name == 'xaxis') {
6962                        this._label._elem.css('bottom', '0px');
6963                    }
6964                    else {
6965                        this._label._elem.css('top', '0px');
6966                    }
6967                    this._label.pack();
6968                }
6969            }
6970            else {
6971                for (var i=0; i<ticks.length; i++) {
6972                    var t = ticks[i];
6973                    if (t.show && t.showLabel) {                        
6974                        var shim;
6975                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
6976                            // will need to adjust auto positioning based on which axis this is.
6977                            var temp = (this.name == 'yaxis') ? 1 : -1;
6978                            switch (t.labelPosition) {
6979                                case 'auto':
6980                                    // position at end
6981                                case 'end':
6982                                    if (temp * t.angle < 0) {
6983                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
6984                                    }
6985                                    else {
6986                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
6987                                    }
6988                                    break;
6989                                case 'start':
6990                                    if (t.angle > 0) {
6991                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
6992                                    }
6993                                    else {
6994                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
6995                                    }
6996                                    break;
6997                                case 'middle':
6998                                    // if (t.angle > 0) {
6999                                    //     shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
7000                                    // }
7001                                    // else {
7002                                    //     shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
7003                                    // }
7004                                    shim = -t.getHeight()/2;
7005                                    break;
7006                                default:
7007                                    shim = -t.getHeight()/2;
7008                                    break;
7009                            }
7010                        }
7011                        else {
7012                            shim = -t.getHeight()/2;
7013                        }
7014                        
7015                        var val = this.u2p(t.value) + shim + 'px';
7016                        t._elem.css('top', val);
7017                        t.pack();
7018                    }
7019                }
7020                if (lshow) {
7021                    var h = this._label._elem.outerHeight(true);
7022                    this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px');
7023                    if (this.name == 'yaxis') {
7024                        this._label._elem.css('left', '0px');
7025                    }
7026                    else {
7027                        this._label._elem.css('right', '0px');
7028                    }   
7029                    this._label.pack();
7030                }
7031            }
7032        }
7033
7034        ticks = null;
7035    };
7036
7037
7038    /**
7039    * The following code was generaously given to me a while back by Scott Prahl.
7040    * He did a good job at computing axes min, max and number of ticks for the 
7041    * case where the user has not set any scale related parameters (tickInterval,
7042    * numberTicks, min or max).  I had ignored this use case for a long time,
7043    * focusing on the more difficult case where user has set some option controlling
7044    * tick generation.  Anyway, about time I got this into jqPlot.
7045    * Thanks Scott!!
7046    */
7047    
7048    /**
7049    * Copyright (c) 2010 Scott Prahl
7050    * The next three routines are currently available for use in all personal 
7051    * or commercial projects under both the MIT and GPL version 2.0 licenses. 
7052    * This means that you can choose the license that best suits your project 
7053    * and use it accordingly. 
7054    */
7055
7056    // A good format string depends on the interval. If the interval is greater 
7057    // than 1 then there is no need to show any decimal digits. If it is < 1.0, then
7058    // use the magnitude of the interval to determine the number of digits to show.
7059    function bestFormatString (interval)
7060    {
7061        var fstr;
7062        interval = Math.abs(interval);
7063        if (interval >= 10) {
7064            fstr = '%d';
7065        }
7066
7067        else if (interval > 1) {
7068            if (interval === parseInt(interval, 10)) {
7069                fstr = '%d';
7070            }
7071            else {
7072                fstr = '%.1f';
7073            }
7074        }
7075
7076        else {
7077            var expv = -Math.floor(Math.log(interval)/Math.LN10);
7078            fstr = '%.' + expv + 'f';
7079        }
7080        
7081        return fstr; 
7082    }
7083
7084    var _factors = [0.1, 0.2, 0.3, 0.4, 0.5, 0.8, 1, 2, 3, 4, 5];
7085
7086    var _getLowerFactor = function(f) {
7087        var i = _factors.indexOf(f);
7088        if (i > 0) {
7089            return _factors[i-1];
7090        }
7091        else {
7092            return _factors[_factors.length - 1] / 100;
7093        }
7094    };
7095
7096    var _getHigherFactor = function(f) {
7097        var i = _factors.indexOf(f);
7098        if (i < _factors.length-1) {
7099            return _factors[i+1];
7100        }
7101        else {
7102            return _factors[0] * 100;
7103        }
7104    };
7105
7106    // Given a fixed minimum and maximum and a target number ot ticks
7107    // figure out the best interval and 
7108    // return min, max, number ticks, format string and tick interval
7109    function bestConstrainedInterval(min, max, nttarget) {
7110        // run through possible number to ticks and see which interval is best
7111        var low = Math.floor(nttarget/2);
7112        var hi = Math.ceil(nttarget*1.5);
7113        var badness = Number.MAX_VALUE;
7114        var r = (max - min);
7115        var temp;
7116        var sd;
7117        var bestNT;
7118        var gsf = $.jqplot.getSignificantFigures;
7119        var fsd;
7120        var fs;
7121        var currentNT;
7122        var bestPrec;
7123
7124        for (var i=0, l=hi-low+1; i<l; i++) {
7125            currentNT = low + i;
7126            temp = r/(currentNT-1);
7127            sd = gsf(temp);
7128
7129            temp = Math.abs(nttarget - currentNT) + sd.digitsRight;
7130            if (temp < badness) {
7131                badness = temp;
7132                bestNT = currentNT;
7133                bestPrec = sd.digitsRight;
7134            }
7135            else if (temp === badness) {
7136                // let nicer ticks trump number ot ticks
7137                if (sd.digitsRight < bestPrec) {
7138                    bestNT = currentNT;
7139                    bestPrec = sd.digitsRight;
7140                }
7141            }
7142
7143        }
7144
7145        fsd = Math.max(bestPrec, Math.max(gsf(min).digitsRight, gsf(max).digitsRight));
7146        if (fsd === 0) {
7147            fs = '%d';
7148        }
7149        else {
7150            fs = '%.' + fsd + 'f';
7151        }
7152        temp = r / (bestNT - 1);
7153        // min, max, number ticks, format string, tick interval
7154        return [min, max, bestNT, fs, temp];
7155    }
7156
7157    // This will return an interval of form 2 * 10^n, 5 * 10^n or 10 * 10^n
7158    // it is based soley on the range and number of ticks.  So if user specifies
7159    // number of ticks, use this.
7160    function bestInterval(range, numberTicks) {
7161        numberTicks = numberTicks || 7;
7162        var minimum = range / (numberTicks - 1);
7163        var magnitude = Math.pow(10, Math.floor(Math.log(minimum) / Math.LN10));
7164        var residual = minimum / magnitude;
7165        var interval;
7166        // "nicest" ranges are 1, 2, 5 or powers of these.
7167        // for magnitudes below 1, only allow these. 
7168        if (magnitude < 1) {
7169            if (residual > 5) {
7170                interval = 10 * magnitude;
7171            }
7172            else if (residual > 2) {
7173                interval = 5 * magnitude;
7174            }
7175            else if (residual > 1) {
7176                interval = 2 * magnitude;
7177            }
7178            else {
7179                interval = magnitude;
7180            }
7181        }
7182        // for large ranges (whole integers), allow intervals like 3, 4 or powers of these.
7183        // this helps a lot with poor choices for number of ticks. 
7184        else {
7185            if (residual > 5) {
7186                interval = 10 * magnitude;
7187            }
7188            else if (residual > 4) {
7189                interval = 5 * magnitude;
7190            }
7191            else if (residual > 3) {
7192                interval = 4 * magnitude;
7193            }
7194            else if (residual > 2) {
7195                interval = 3 * magnitude;
7196            }
7197            else if (residual > 1) {
7198                interval = 2 * magnitude;
7199            }
7200            else {
7201                interval = magnitude;
7202            }
7203        }
7204
7205        return interval;
7206    }
7207
7208    // This will return an interval of form 2 * 10^n, 5 * 10^n or 10 * 10^n
7209    // it is based soley on the range of data, number of ticks must be computed later.
7210    function bestLinearInterval(range, scalefact) {
7211        scalefact = scalefact || 1;
7212        var expv = Math.floor(Math.log(range)/Math.LN10);
7213        var magnitude = Math.pow(10, expv);
7214        // 0 < f < 10
7215        var f = range / magnitude;
7216        var fact;
7217        // for large plots, scalefact will decrease f and increase number of ticks.
7218        // for small plots, scalefact will increase f and decrease number of ticks.
7219        f = f/scalefact;
7220
7221        // for large plots, smaller interval, more ticks.
7222        if (f<=0.38) {
7223            fact = 0.1;
7224        }
7225        else if (f<=1.6) {
7226            fact = 0.2;
7227        }
7228        else if (f<=4.0) {
7229            fact = 0.5;
7230        }
7231        else if (f<=8.0) {
7232            fact = 1.0;
7233        }
7234        // for very small plots, larger interval, less ticks in number ticks
7235        else if (f<=16.0) {
7236            fact = 2;
7237        }
7238        else {
7239            fact = 5;
7240        } 
7241
7242        return fact*magnitude; 
7243    }
7244
7245    function bestLinearComponents(range, scalefact) {
7246        var expv = Math.floor(Math.log(range)/Math.LN10);
7247        var magnitude = Math.pow(10, expv);
7248        // 0 < f < 10
7249        var f = range / magnitude;
7250        var interval;
7251        var fact;
7252        // for large plots, scalefact will decrease f and increase number of ticks.
7253        // for small plots, scalefact will increase f and decrease number of ticks.
7254        f = f/scalefact;
7255
7256        // for large plots, smaller interval, more ticks.
7257        if (f<=0.38) {
7258            fact = 0.1;
7259        }
7260        else if (f<=1.6) {
7261            fact = 0.2;
7262        }
7263        else if (f<=4.0) {
7264            fact = 0.5;
7265        }
7266        else if (f<=8.0) {
7267            fact = 1.0;
7268        }
7269        // for very small plots, larger interval, less ticks in number ticks
7270        else if (f<=16.0) {
7271            fact = 2;
7272        }
7273        // else if (f<=20.0) {
7274        //     fact = 3;
7275        // }
7276        // else if (f<=24.0) {
7277        //     fact = 4;
7278        // }
7279        else {
7280            fact = 5;
7281        } 
7282
7283        interval = fact * magnitude;
7284
7285        return [interval, fact, magnitude];
7286    }
7287
7288    // Given the min and max for a dataset, return suitable endpoints
7289    // for the graphing, a good number for the number of ticks, and a
7290    // format string so that extraneous digits are not displayed.
7291    // returned is an array containing [min, max, nTicks, format]
7292    $.jqplot.LinearTickGenerator = function(axis_min, axis_max, scalefact, numberTicks, keepMin, keepMax) {
7293        // Set to preserve EITHER min OR max.
7294        // If min is preserved, max must be free.
7295        keepMin = (keepMin === null) ? false : keepMin;
7296        keepMax = (keepMax === null || keepMin) ? false : keepMax;
7297        // if endpoints are equal try to include zero otherwise include one
7298        if (axis_min === axis_max) {
7299            axis_max = (axis_max) ? 0 : 1;
7300        }
7301
7302        scalefact = scalefact || 1.0;
7303
7304        // make sure range is positive
7305        if (axis_max < axis_min) {
7306            var a = axis_max;
7307            axis_max = axis_min;
7308            axis_min = a;
7309        }
7310
7311        var r = [];
7312        var ss = bestLinearInterval(axis_max - axis_min, scalefact);
7313
7314        var gsf = $.jqplot.getSignificantFigures;
7315        
7316        if (numberTicks == null) {
7317
7318            // Figure out the axis min, max and number of ticks
7319            // the min and max will be some multiple of the tick interval,
7320            // 1*10^n, 2*10^n or 5*10^n.  This gaurantees that, if the
7321            // axis min is negative, 0 will be a tick.
7322            if (!keepMin && !keepMax) {
7323                r[0] = Math.floor(axis_min / ss) * ss;  // min
7324                r[1] = Math.ceil(axis_max / ss) * ss;   // max
7325                r[2] = Math.round((r[1]-r[0])/ss+1.0);  // number of ticks
7326                r[3] = bestFormatString(ss);            // format string
7327                r[4] = ss;                              // tick Interval
7328            }
7329
7330            else if (keepMin) {
7331                r[0] = axis_min;                                        // min
7332                r[2] = Math.ceil((axis_max - axis_min) / ss + 1.0);     // number of ticks
7333                r[1] = axis_min + (r[2] - 1) * ss;                      // max
7334                var digitsMin = gsf(axis_min).digitsRight;
7335                var digitsSS = gsf(ss).digitsRight;
7336                if (digitsMin < digitsSS) {
7337                    r[3] = bestFormatString(ss);
7337                        // format string
7338                }
7339                else {
7340                    r[3] = '%.' + digitsMin + 'f';
7341                }
7342                r[4] = ss;                                              // tick Interval
7343            }
7344
7345            else if (keepMax) {
7346                r[1] = axis_max;                                        // max
7347                r[2] = Math.ceil((axis_max - axis_min) / ss + 1.0);     // number of ticks
7348                r[0] = axis_max - (r[2] - 1) * ss;                      // min
7349                var digitsMax = gsf(axis_max).digitsRight;
7350                var digitsSS = gsf(ss).digitsRight;
7351                if (digitsMax < digitsSS) {
7352                    r[3] = bestFormatString(ss);                        // format string
7353                }
7354                else {
7355                    r[3] = '%.' + digitsMax + 'f';
7356                }
7357                r[4] = ss;                                              // tick Interval
7358            }
7359        }
7360
7361        else {
7362            var tempr = [];
7363
7364            // Figure out the axis min, max and number of ticks
7365            // the min and max will be some multiple of the tick interval,
7366            // 1*10^n, 2*10^n or 5*10^n.  This gaurantees that, if the
7367            // axis min is negative, 0 will be a tick.
7368            tempr[0] = Math.floor(axis_min / ss) * ss;  // min
7369            tempr[1] = Math.ceil(axis_max / ss) * ss;   // max
7370            tempr[2] = Math.round((tempr[1]-tempr[0])/ss+1.0);    // number of ticks
7371            tempr[3] = bestFormatString(ss);            // format string
7372            tempr[4] = ss;                              // tick Interval
7373
7374            // first, see if we happen to get the right number of ticks
7375            if (tempr[2] === numberTicks) {
7376                r = tempr;
7377            }
7378
7379            else {
7380
7381                var newti = bestInterval(tempr[1] - tempr[0], numberTicks);
7382
7383                r[0] = tempr[0];                        // min
7384                r[2] = numberTicks;                     // number of ticks
7385                r[4] = newti;                           // tick interval
7386                r[3] = bestFormatString(newti);         // format string
7387                r[1] = r[0] + (r[2] - 1) * r[4];        // max
7388            }
7389        }
7390
7391        return r;
7392    };
7393
7394    $.jqplot.LinearTickGenerator.bestLinearInterval = bestLinearInterval;
7395    $.jqplot.LinearTickGenerator.bestInterval = bestInterval;
7396    $.jqplot.LinearTickGenerator.bestLinearComponents = bestLinearComponents;
7397    $.jqplot.LinearTickGenerator.bestConstrainedInterval = bestConstrainedInterval;
7398
7399
7400    // class: $.jqplot.MarkerRenderer
7401    // The default jqPlot marker renderer, rendering the points on the line.
7402    $.jqplot.MarkerRenderer = function(options){
7403        // Group: Properties
7404        
7405        // prop: show
7406        // whether or not to show the marker.
7407        this.show = true;
7408        // prop: style
7409        // One of diamond, circle, square, x, plus, dash, filledDiamond, filledCircle, filledSquare
7410        this.style = 'filledCircle';
7411        // prop: lineWidth
7412        // size of the line for non-filled markers.
7413        this.lineWidth = 2;
7414        // prop: size
7415        // Size of the marker (diameter or circle, length of edge of square, etc.)
7416        this.size = 9.0;
7417        // prop: color
7418        // color of marker.  Will be set to color of series by default on init.
7419        this.color = '#666666';
7420        // prop: shadow
7421        // whether or not to draw a shadow on the line
7422        this.shadow = true;
7423        // prop: shadowAngle
7424        // Shadow angle in degrees
7425        this.shadowAngle = 45;
7426        // prop: shadowOffset
7427        // Shadow offset from line in pixels
7428        this.shadowOffset = 1;
7429        // prop: shadowDepth
7430        // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
7431        this.shadowDepth = 3;
7432        // prop: shadowAlpha
7433        // Alpha channel transparency of shadow.  0 = transparent.
7434        this.shadowAlpha = '0.07';
7435        // prop: shadowRenderer
7436        // Renderer that will draws the shadows on the marker.
7437        this.shadowRenderer = new $.jqplot.ShadowRenderer();
7438        // prop: shapeRenderer
7439        // Renderer that will draw the marker.
7440        this.shapeRenderer = new $.jqplot.ShapeRenderer();
7441        
7442        $.extend(true, this, options);
7443    };
7444    
7445    $.jqplot.MarkerRenderer.prototype.init = function(options) {
7446        $.extend(true, this, options);
7447        var sdopt = {angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, lineWidth:this.lineWidth, depth:this.shadowDepth, closePath:true};
7448        if (this.style.indexOf('filled') != -1) {
7449            sdopt.fill = true;
7450        }
7451        if (this.style.indexOf('ircle') != -1) {
7452            sdopt.isarc = true;
7453            sdopt.closePath = false;
7454        }
7455        this.shadowRenderer.init(sdopt);
7456        
7457        var shopt = {fill:false, isarc:false, strokeStyle:this.color, fillStyle:this.color, lineWidth:this.lineWidth, closePath:true};
7458        if (this.style.indexOf('filled') != -1) {
7459            shopt.fill = true;
7460        }
7461        if (this.style.indexOf('ircle') != -1) {
7462            shopt.isarc = true;
7463            shopt.closePath = false;
7464        }
7465        this.shapeRenderer.init(shopt);
7466    };
7467    
7468    $.jqplot.MarkerRenderer.prototype.drawDiamond = function(x, y, ctx, fill, options) {
7469        var stretch = 1.2;
7470        var dx = this.size/2/stretch;
7471        var dy = this.size/2*stretch;
7472        var points = [[x-dx, y], [x, y+dy], [x+dx, y], [x, y-dy]];
7473        if (this.shadow) {
7474            this.shadowRenderer.draw(ctx, points);
7475        }
7476        this.shapeRenderer.draw(ctx, points, options);
7477    };
7478    
7479    $.jqplot.MarkerRenderer.prototype.drawPlus = function(x, y, ctx, fill, options) {
7480        var stretch = 1.0;
7481        var dx = this.size/2*stretch;
7482        var dy = this.size/2*stretch;
7483        var points1 = [[x, y-dy], [x, y+dy]];
7484        var points2 = [[x+dx, y], [x-dx, y]];
7485        var opts = $.extend(true, {}, this.options, {closePath:false});
7486        if (this.shadow) {
7487            this.shadowRenderer.draw(ctx, points1, {closePath:false});
7488            this.shadowRenderer.draw(ctx, points2, {closePath:false});
7489        }
7490        this.shapeRenderer.draw(ctx, points1, opts);
7491        this.shapeRenderer.draw(ctx, points2, opts);
7492    };
7493    
7494    $.jqplot.MarkerRenderer.prototype.drawX = function(x, y, ctx, fill, options) {
7495        var stretch = 1.0;
7496        var dx = this.size/2*stretch;
7497        var dy = this.size/2*stretch;
7498        var opts = $.extend(true, {}, this.options, {closePath:false});
7499        var points1 = [[x-dx, y-dy], [x+dx, y+dy]];
7500        var points2 = [[x-dx, y+dy], [x+dx, y-dy]];
7501        if (this.shadow) {
7502            this.shadowRenderer.draw(ctx, points1, {closePath:false});
7503            this.shadowRenderer.draw(ctx, points2, {closePath:false});
7504        }
7505        this.shapeRenderer.draw(ctx, points1, opts);
7506        this.shapeRenderer.draw(ctx, points2, opts);
7507    };
7508    
7509    $.jqplot.MarkerRenderer.prototype.drawDash = function(x, y, ctx, fill, options) {
7510        var stretch = 1.0;
7511        var dx = this.size/2*stretch;
7512        var dy = this.size/2*stretch;
7513        var points = [[x-dx, y], [x+dx, y]];
7514        if (this.shadow) {
7515            this.shadowRenderer.draw(ctx, points);
7516        }
7517        this.shapeRenderer.draw(ctx, points, options);
7518    };
7519    
7520    $.jqplot.MarkerRenderer.prototype.drawLine = function(p1, p2, ctx, fill, options) {
7521        var points = [p1, p2];
7522        if (this.shadow) {
7523            this.shadowRenderer.draw(ctx, points);
7524        }
7525        this.shapeRenderer.draw(ctx, points, options);
7526    };
7527    
7528    $.jqplot.MarkerRenderer.prototype.drawSquare = function(x, y, ctx, fill, options) {
7529        var stretch = 1.0;
7530        var dx = this.size/2/stretch;
7531        var dy = this.size/2*stretch;
7532        var points = [[x-dx, y-dy], [x-dx, y+dy], [x+dx, y+dy], [x+dx, y-dy]];
7533        if (this.shadow) {
7534            this.shadowRenderer.draw(ctx, points);
7535        }
7536        this.shapeRenderer.draw(ctx, points, options);
7537    };
7538    
7539    $.jqplot.MarkerRenderer.prototype.drawCircle = function(x, y, ctx, fill, options) {
7540        var radius = this.size/2;
7541        var end = 2*Math.PI;
7542        var points = [x, y, radius, 0, end, true];
7543        if (this.shadow) {
7544            this.shadowRenderer.draw(ctx, points);
7545        }
7546        this.shapeRenderer.draw(ctx, points, options);
7547    };
7548    
7549    $.jqplot.MarkerRenderer.prototype.draw = function(x, y, ctx, options) {
7550        options = options || {};
7551        // hack here b/c shape renderer uses canvas based color style options
7552        // and marker uses css style names.
7553        if (options.show == null || options.show != false) {
7554            if (options.color && !options.fillStyle) {
7555                options.fillStyle = options.color;
7556            }
7557            if (options.color && !options.strokeStyle) {
7558                options.strokeStyle = options.color;
7559            }
7560            switch (this.style) {
7561                case 'diamond':
7562                    this.drawDiamond(x,y,ctx, false, options);
7563                    break;
7564                case 'filledDiamond':
7565                    this.drawDiamond(x,y,ctx, true, options);
7566                    break;
7567                case 'circle':
7568                    this.drawCircle(x,y,ctx, false, options);
7569                    break;
7570                case 'filledCircle':
7571                    this.drawCircle(x,y,ctx, true, options);
7572                    break;
7573                case 'square':
7574                    this.drawSquare(x,y,ctx, false, options);
7575                    break;
7576                case 'filledSquare':
7577                    this.drawSquare(x,y,ctx, true, options);
7578                    break;
7579                case 'x':
7580                    this.drawX(x,y,ctx, true, options);
7581                    break;
7582                case 'plus':
7583                    this.drawPlus(x,y,ctx, true, options);
7584                    break;
7585                case 'dash':
7586                    this.drawDash(x,y,ctx, true, options);
7587                    break;
7588                case 'line':
7589                    this.drawLine(x, y, ctx, false, options);
7590                    break;
7591                default:
7592                    this.drawDiamond(x,y,ctx, false, options);
7593                    break;
7594            }
7595        }
7596    };
7597    
7598    // class: $.jqplot.shadowRenderer
7599    // The default jqPlot shadow renderer, rendering shadows behind shapes.
7600    $.jqplot.ShadowRenderer = function(options){ 
7601        // Group: Properties
7602        
7603        // prop: angle
7604        // Angle of the shadow in degrees.  Measured counter-clockwise from the x axis.
7605        this.angle = 45;
7606        // prop: offset
7607        // Pixel offset at the given shadow angle of each shadow stroke from the last stroke.
7608        this.offset = 1;
7609        // prop: alpha
7610        // alpha transparency of shadow stroke.
7611        this.alpha = 0.07;
7612        // prop: lineWidth
7613        // width of the shadow line stroke.
7614        this.lineWidth = 1.5;
7615        // prop: lineJoin
7616        // How line segments of the shadow are joined.
7617        this.lineJoin = 'miter';
7618        // prop: lineCap
7619        // how ends of the shadow line are rendered.
7620        this.lineCap = 'round';
7621        // prop; closePath
7622        // whether line path segment is closed upon itself.
7623        this.closePath = false;
7624        // prop: fill
7625        // whether to fill the shape.
7626        this.fill = false;
7627        // prop: depth
7628        // how many times the shadow is stroked.  Each stroke will be offset by offset at angle degrees.
7629        this.depth = 3;
7630        this.strokeStyle = 'rgba(0,0,0,0.1)';
7631        // prop: isarc
7632        // whether the shadow is an arc or not.
7633        this.isarc = false;
7634        
7635        $.extend(true, this, options);
7636    };
7637    
7638    $.jqplot.ShadowRenderer.prototype.init = function(options) {
7639        $.extend(true, this, options);
7640    };
7641    
7642    // function: draw
7643    // draws an transparent black (i.e. gray) shadow.
7644    //
7645    // ctx - canvas drawing context
7646    // points - array of points or [x, y, radius, start angle (rad), end angle (rad)]
7647    $.jqplot.ShadowRenderer.prototype.draw = function(ctx, points, options) {
7648        ctx.save();
7649        var opts = (options != null) ? options : {};
7650        var fill = (opts.fill != null) ? opts.fill : this.fill;
7651        var fillRect = (opts.fillRect != null) ? opts.fillRect : this.fillRect;
7652        var closePath = (opts.closePath != null) ? opts.closePath : this.closePath;
7653        var offset = (opts.offset != null) ? opts.offset : this.offset;
7654        var alpha = (opts.alpha != null) ? opts.alpha : this.alpha;
7655        var depth = (opts.depth != null) ? opts.depth : this.depth;
7656        var isarc = (opts.isarc != null) ? opts.isarc : this.isarc;
7657        var linePattern = (opts.linePattern != null) ? opts.linePattern : this.linePattern;
7658        ctx.lineWidth = (opts.lineWidth != null) ? opts.lineWidth : this.lineWidth;
7659        ctx.lineJoin = (opts.lineJoin != null) ? opts.lineJoin : this.lineJoin;
7660        ctx.lineCap = (opts.lineCap != null) ? opts.lineCap : this.lineCap;
7661        ctx.strokeStyle = opts.strokeStyle || this.strokeStyle || 'rgba(0,0,0,'+alpha+')';
7662        ctx.fillStyle = opts.fillStyle || this.fillStyle || 'rgba(0,0,0,'+alpha+')';
7663        for (var j=0; j<depth; j++) {
7664            var ctxPattern = $.jqplot.LinePattern(ctx, linePattern);
7665            ctx.translate(Math.cos(this.angle*Math.PI/180)*offset, Math.sin(this.angle*Math.PI/180)*offset);
7666            ctxPattern.beginPath();
7667            if (isarc) {
7668                ctx.arc(points[0], points[1], points[2], points[3], points[4], true);                
7669            }
7670            else if (fillRect) {
7671                if (fillRect) {
7672                    ctx.fillRect(points[0], points[1], points[2], points[3]);
7673                }
7674            }
7675            else if (points && points.length){
7676                var move = true;
7677                for (var i=0; i<points.length; i++) {
7678                    // skip to the first non-null point and move to it.
7679                    if (points[i][0] != null && points[i][1] != null) {
7680                        if (move) {
7681                            ctxPattern.moveTo(points[i][0], points[i][1]);
7682                            move = false;
7683                        }
7684                        else {
7685                            ctxPattern.lineTo(points[i][0], points[i][1]);
7686                        }
7687                    }
7688                    else {
7689                        move = true;
7690                    }
7691                }
7692                
7693            }
7694            if (closePath) {
7695                ctxPattern.closePath();
7696            }
7697            if (fill) {
7698                ctx.fill();
7699            }
7700            else {
7701                ctx.stroke();
7702            }
7703        }
7704        ctx.restore();
7705    };
7706    
7707    // class: $.jqplot.shapeRenderer
7708    // The default jqPlot shape renderer.  Given a set of points will
7709    // plot them and either stroke a line (fill = false) or fill them (fill = true).
7710    // If a filled shape is desired, closePath = true must also be set to close
7711    // the shape.
7712    $.jqplot.ShapeRenderer = function(options){
7713        
7714        this.lineWidth = 1.5;
7715        // prop: linePattern
7716        // line pattern 'dashed', 'dotted', 'solid', some combination
7717        // of '-' and '.' characters such as '.-.' or a numerical array like 
7718        // [draw, skip, draw, skip, ...] such as [1, 10] to draw a dotted line, 
7719        // [1, 10, 20, 10] to draw a dot-dash line, and so on.
7720        this.linePattern = 'solid';
7721        // prop: lineJoin
7722        // How line segments of the shadow are joined.
7723        this.lineJoin = 'miter';
7724        // prop: lineCap
7725        // how ends of the shadow line are rendered.
7726        this.lineCap = 'round';
7727        // prop; closePath
7728        // whether line path segment is closed upon itself.
7729        this.closePath = false;
7730        // prop: fill
7731        // whether to fill the shape.
7732        this.fill = false;
7733        // prop: isarc
7734        // whether the shadow is an arc or not.
7735        this.isarc = false;
7736        // prop: fillRect
7737        // true to draw shape as a filled rectangle.
7738        this.fillRect = false;
7739        // prop: strokeRect
7740        // true to draw shape as a stroked rectangle.
7741        this.strokeRect = false;
7742        // prop: clearRect
7743        // true to cear a rectangle.
7744        this.clearRect = false;
7745        // prop: strokeStyle
7746        // css color spec for the stoke style
7747        this.strokeStyle = '#999999';
7748        // prop: fillStyle
7749        // css color spec for the fill style.
7750        this.fillStyle = '#999999'; 
7751        
7752        $.extend(true, this, options);
7753    };
7754    
7755    $.jqplot.ShapeRenderer.prototype.init = function(options) {
7756        $.extend(true, this, options);
7757    };
7758    
7759    // function: draw
7760    // draws the shape.
7761    //
7762    // ctx - canvas drawing context
7763    // points - array of points for shapes or 
7764    // [x, y, width, height] for rectangles or
7765    // [x, y, radius, start angle (rad), end angle (rad)] for circles and arcs.
7766    $.jqplot.ShapeRenderer.prototype.draw = function(ctx, points, options) {
7767        ctx.save();
7768        var opts = (options != null) ? options : {};
7769        var fill = (opts.fill != null) ? opts.fill : this.fill;
7770        var closePath = (opts.closePath != null) ? opts.closePath : this.closePath;
7771        var fillRect = (opts.fillRect != null) ? opts.fillRect : this.fillRect;
7772        var strokeRect = (opts.strokeRect != null) ? opts.strokeRect : this.strokeRect;
7773        var clearRect = (opts.clearRect != null) ? opts.clearRect : this.clearRect;
7774        var isarc = (opts.isarc != null) ? opts.isarc : this.isarc;
7775        var linePattern = (opts.linePattern != null) ? opts.linePattern : this.linePattern;
7776        var ctxPattern = $.jqplot.LinePattern(ctx, linePattern);
7777        ctx.lineWidth = opts.lineWidth || this.lineWidth;
7778        ctx.lineJoin = opts.lineJoin || this.lineJoin;
7779        ctx.lineCap = opts.lineCap || this.lineCap;
7780        ctx.strokeStyle = (opts.strokeStyle || opts.color) || this.strokeStyle;
7781        ctx.fillStyle = opts.fillStyle || this.fillStyle;
7782        ctx.beginPath();
7783        if (isarc) {
7784            ctx.arc(points[0], points[1], points[2], points[3], points[4], true);   
7785            if (closePath) {
7786                ctx.closePath();
7787            }
7788            if (fill) {
7789                ctx.fill();
7790            }
7791            else {
7792                ctx.stroke();
7793            }
7794            ctx.restore();
7795            return;
7796        }
7797        else if (clearRect) {
7798            ctx.clearRect(points[0], points[1], points[2], points[3]);
7799            ctx.restore();
7800            return;
7801        }
7802        else if (fillRect || strokeRect) {
7803            if (fillRect) {
7804                ctx.fillRect(points[0], points[1], points[2], points[3]);
7805            }
7806            if (strokeRect) {
7807                ctx.strokeRect(points[0], points[1], points[2], points[3]);
7808                ctx.restore();
7809                return;
7810            }
7811        }
7812        else if (points && points.length){
7813            var move = true;
7814            for (var i=0; i<points.length; i++) {
7815                // skip to the first non-null point and move to it.
7816                if (points[i][0] != null && points[i][1] != null) {
7817                    if (move) {
7818                        ctxPattern.moveTo(points[i][0], points[i][1]);
7819                        move = false;
7820                    }
7821                    else {
7822                        ctxPattern.lineTo(points[i][0], points[i][1]);
7823                    }
7824                }
7825                else {
7826                    move = true;
7827                }
7828            }
7829            if (closePath) {
7830                ctxPattern.closePath();
7831            }
7832            if (fill) {
7833                ctx.fill();
7834            }
7835            else {
7836                ctx.stroke();
7837            }
7838        }
7839        ctx.restore();
7840    };
7841    
7842    // class $.jqplot.TableLegendRenderer
7843    // The default legend renderer for jqPlot.
7844    $.jqplot.TableLegendRenderer = function(){
7845        //
7846    };
7847    
7848    $.jqplot.TableLegendRenderer.prototype.init = function(options) {
7849        $.extend(true, this, options);
7850    };
7851        
7852    $.jqplot.TableLegendRenderer.prototype.addrow = function (label, color, pad, reverse) {
7853        var rs = (pad) ? this.rowSpacing+'px' : '0px';
7854        var tr;
7855        var td;
7856        var elem;
7857        var div0;
7858        var div1;
7859        elem = document.createElement('tr');
7860        tr = $(elem);
7861        tr.addClass('jqplot-table-legend');
7862        elem = null;
7863
7864        if (reverse){
7865            tr.prependTo(this._elem);
7866        }
7867
7868        else{
7869            tr.appendTo(this._elem);
7870        }
7871
7872        if (this.showSwatches) {
7873            td = $(document.createElement('td'));
7874            td.addClass('jqplot-table-legend jqplot-table-legend-swatch');
7875            td.css({textAlign: 'center', paddingTop: rs});
7876
7877            div0 = $(document.createElement('div'));
7878            div0.addClass('jqplot-table-legend-swatch-outline');
7879            div1 = $(document.createElement('div'));
7880            div1.addClass('jqplot-table-legend-swatch');
7881            div1.css({backgroundColor: color, borderColor: color});
7882
7883            tr.append(td.append(div0.append(div1)));
7884
7885            // $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+
7886            // '<div><div class="jqplot-table-legend-swatch" style="background-color:'+color+';border-color:'+color+';"></div>'+
7887            // '</div></td>').appendTo(tr);
7888        }
7889        if (this.showLabels) {
7890            td = $(document.createElement('td'));
7891            td.addClass('jqplot-table-legend jqplot-table-legend-label');
7892            td.css('paddingTop', rs);
7893            tr.append(td);
7894
7895            // elem = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
7896            // elem.appendTo(tr);
7897            if (this.escapeHtml) {
7898                td.text(label);
7899            }
7900            else {
7901                td.html(label);
7902            }
7903        }
7904        td = null;
7905        div0 = null;
7906        div1 = null;
7907        tr = null;
7908        elem = null;
7909    };
7910    
7911    // called with scope of legend
7912    $.jqplot.TableLegendRenderer.prototype.draw = function() {
7913        if (this._elem) {
7914            this._elem.emptyForce();
7915            this._elem = null;
7916        }
7917
7918        if (this.show) {
7919            var series = this._series;
7920            // make a table.  one line label per row.
7921            var elem = document.createElement('table');
7922            this._elem = $(elem);
7923            this._elem.addClass('jqplot-table-legend');
7924
7925            var ss = {position:'absolute'};
7926            if (this.background) {
7927                ss['background'] = this.background;
7928            }
7929            if (this.border) {
7930                ss['border'] = this.border;
7931            }
7932            if (this.fontSize) {
7933                ss['fontSize'] = this.fontSize;
7934            }
7935            if (this.fontFamily) {
7936                ss['fontFamily'] = this.fontFamily;
7937            }
7938            if (this.textColor) {
7939                ss['textColor'] = this.textColor;
7940            }
7941            if (this.marginTop != null) {
7942                ss['marginTop'] = this.marginTop;
7943            }
7944            if (this.marginBottom != null) {
7945                ss['marginBottom'] = this.marginBottom;
7946            }
7947            if (this.marginLeft != null) {
7948                ss['marginLeft'] = this.marginLeft;
7949            }
7950            if (this.marginRight != null) {
7951                ss['marginRight'] = this.marginRight;
7952            }
7953            
7954        
7955            var pad = false, 
7956                reverse = false,
7957                s;
7958            for (var i = 0; i< series.length; i++) {
7959                s = series[i];
7960                if (s._stack || s.renderer.constructor == $.jqplot.BezierCurveRenderer){
7961                    reverse = true;
7962                }
7963                if (s.show && s.showLabel) {
7964                    var lt = this.labels[i] || s.label.toString();
7965                    if (lt) {
7966                        var color = s.color;
7967                        if (reverse && i < series.length - 1){
7968                            pad = true;
7969                        }
7970                        else if (reverse && i == series.length - 1){
7971                            pad = false;
7972                        }
7973                        this.renderer.addrow.call(this, lt, color, pad, reverse);
7974                        pad = true;
7975                    }
7976                    // let plugins add more rows to legend.  Used by trend line plugin.
7977                    for (var j=0; j<$.jqplot.addLegendRowHooks.length; j++) {
7978                        var item = $.jqplot.addLegendRowHooks[j].call(this, s);
7979                        if (item) {
7980                            this.renderer.addrow.call(this, item.label, item.color, pad);
7981                            pad = true;
7982                        } 
7983                    }
7984                    lt = null;
7985                }
7986            }
7987        }
7988        return this._elem;
7989    };
7990    
7991    $.jqplot.TableLegendRenderer.prototype.pack = function(offsets) {
7992        if (this.show) {       
7993            if (this.placement == 'insideGrid') {
7994                switch (this.location) {
7995                    case 'nw':
7996                        var a = offsets.left;
7997                        var b = offsets.top;
7998                        this._elem.css('left', a);
7999                        this._elem.css('top', b);
8000                        break;
8001                    case 'n':
8002                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
8003                        var b = offsets.top;
8004                        this._elem.css('left', a);
8005                        this._elem.css('top', b);
8006                        break;
8007                    case 'ne':
8008                        var a = offsets.right;
8009                        var b = offsets.top;
8010                        this._elem.css({right:a, top:b});
8011                        break;
8012                    case 'e':
8013                        var a = offsets.right;
8014                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
8015                        this._elem.css({right:a, top:b});
8016                        break;
8017                    case 'se':
8018                        var a = offsets.right;
8019                        var b = offsets.bottom;
8020                        this._elem.css({right:a, bottom:b});
8021                        break;
8022                    case 's':
8023                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
8024                        var b = offsets.bottom;
8025                        this._elem.css({left:a, bottom:b});
8026                        break;
8027                    case 'sw':
8028                        var a = offsets.left;
8029                        var b = offsets.bottom;
8030                        this._elem.css({left:a, bottom:b});
8031                        break;
8032                    case 'w':
8033                        var a = offsets.left;
8034                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
8035                        this._elem.css({left:a, top:b});
8036                        break;
8037                    default:  // same as 'se'
8038                        var a = offsets.right;
8039                        var b = offsets.bottom;
8040                        this._elem.css({right:a, bottom:b});
8041                        break;
8042                }
8043                
8044            }
8045            else if (this.placement == 'outside'){
8046                switch (this.location) {
8047                    case 'nw':
8048                        var a = this._plotDimensions.width - offsets.left;
8049                        var b = offsets.top;
8050                        this._elem.css('right', a);
8051                        this._elem.css('top', b);
8052                        break;
8053                    case 'n':
8054                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
8055                        var b = this._plotDimensions.height - offsets.top;
8056                        this._elem.css('left', a);
8057                        this._elem.css('bottom', b);
8058                        break;
8059                    case 'ne':
8060                        var a = this._plotDimensions.width - offsets.right;
8061                        var b = offsets.top;
8062                        this._elem.css({left:a, top:b});
8063                        break;
8064                    case 'e':
8065                        var a = this._plotDimensions.width - offsets.right;
8066                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
8067                        this._elem.css({left:a, top:b});
8068                        break;
8069                    case 'se':
8070                        var a = this._plotDimensions.width - offsets.right;
8071                        var b = offsets.bottom;
8072                        this._elem.css({left:a, bottom:b});
8073                        break;
8074                    case 's':
8075                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
8076                        var b = this._plotDimensions.height - offsets.bottom;
8077                        this._elem.css({left:a, top:b});
8078                        break;
8079                    case 'sw':
8080                        var a = this._plotDimensions.width - offsets.left;
8081                        var b = offsets.bottom;
8082                        this._elem.css({right:a, bottom:b});
8083                        break;
8084                    case 'w':
8085                        var a = this._plotDimensions.width - offsets.left;
8086                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
8087                        this._elem.css({right:a, top:b});
8088                        break;
8089                    default:  // same as 'se'
8090                        var a = offsets.right;
8091                        var b = offsets.bottom;
8092                        this._elem.css({right:a, bottom:b});
8093                        break;
8094                }
8095            }
8096            else {
8097                switch (this.location) {
8098                    case 'nw':
8099                        this._elem.css({left:0, top:offsets.top});
8100                        break;
8101                    case 'n':
8102                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
8103                        this._elem.css({left: a, top:offsets.top});
8104                        break;
8105                    case 'ne':
8106                        this._elem.css({right:0, top:offsets.top});
8107                        break;
8108                    case 'e':
8109                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
8110                        this._elem.css({right:offsets.right, top:b});
8111                        break;
8112                    case 'se':
8113                        this._elem.css({right:offsets.right, bottom:offsets.bottom});
8114                        break;
8115                    case 's':
8116                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
8117                        this._elem.css({left: a, bottom:offsets.bottom});
8118                        break;
8119                    case 'sw':
8120                        this._elem.css({left:offsets.left, bottom:offsets.bottom});
8121                        break;
8122                    case 'w':
8123                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
8124                        this._elem.css({left:offsets.left, top:b});
8125                        break;
8126                    default:  // same as 'se'
8127                        this._elem.css({right:offsets.right, bottom:offsets.bottom});
8128                        break;
8129                }
8130            }
8131        } 
8132    };
8133
8134    /**
8135     * Class: $.jqplot.ThemeEngine
8136     * Theme Engine provides a programatic way to change some of the  more
8137     * common jqplot styling options such as fonts, colors and grid options.
8138     * A theme engine instance is created with each plot.  The theme engine
8139     * manages a collection of themes which can be modified, added to, or 
8140     * applied to the plot.
8141     * 
8142     * The themeEngine class is not instantiated directly.
8143     * When a plot is initialized, the current plot options are scanned
8144     * an a default theme named "Default" is created.  This theme is
8145     * used as the basis for other themes added to the theme engine and
8146     * is always available.
8147     * 
8148     * A theme is a simple javascript object with styling parameters for
8149     * various entities of the plot.  A theme has the form:
8150     * 
8151     * 
8152     * > {
8153     * >     _name:f "Default",
8154     * >     target: {
8155     * >         backgroundColor: "transparent"
8156     * >     },
8157     * >     legend: {
8158     * >         textColor: null,
8159     * >         fontFamily: null,
8160     * >         fontSize: null,
8161     * >         border: null,
8162     * >         background: null
8163     * >     },
8164     * >     title: {
8165     * >         textColor: "rgb(102, 102, 102)",
8166     * >         fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif",
8167     * >         fontSize: "19.2px",
8168     * >         textAlign: "center"
8169     * >     },
8170     * >     seriesStyles: {},
8171     * >     series: [{
8172     * >         color: "#4bb2c5",
8173     * >         lineWidth: 2.5,
8174     * >         linePattern: "solid",
8175     * >         shadow: true,
8176     * >         fillColor: "#4bb2c5",
8177     * >         showMarker: true,
8178     * >         markerOptions: {
8179     * >             color: "#4bb2c5",
8180     * >             show: true,
8181     * >             style: 'filledCircle',
8182     * >             lineWidth: 1.5,
8183     * >             size: 4,
8184     * >             shadow: true
8185     * >         }
8186     * >     }],
8187     * >     grid: {
8188     * >
8188         drawGridlines: true,
8189     * >         gridLineColor: "#cccccc",
8190     * >         gridLineWidth: 1,
8191     * >         backgroundColor: "#fffdf6",
8192     * >         borderColor: "#999999",
8193     * >         borderWidth: 2,
8194     * >         shadow: true
8195     * >     },
8196     * >     axesStyles: {
8197     * >         label: {},
8198     * >         ticks: {}
8199     * >     },
8200     * >     axes: {
8201     * >         xaxis: {
8202     * >             borderColor: "#999999",
8203     * >             borderWidth: 2,
8204     * >             ticks: {
8205     * >                 show: true,
8206     * >                 showGridline: true,
8207     * >                 showLabel: true,
8208     * >                 showMark: true,
8209     * >                 size: 4,
8210     * >                 textColor: "",
8211     * >                 whiteSpace: "nowrap",
8212     * >                 fontSize: "12px",
8213     * >                 fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif"
8214     * >             },
8215     * >             label: {
8216     * >                 textColor: "rgb(102, 102, 102)",
8217     * >                 whiteSpace: "normal",
8218     * >                 fontSize: "14.6667px",
8219     * >                 fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif",
8220     * >                 fontWeight: "400"
8221     * >             }
8222     * >         },
8223     * >         yaxis: {
8224     * >             borderColor: "#999999",
8225     * >             borderWidth: 2,
8226     * >             ticks: {
8227     * >                 show: true,
8228     * >                 showGridline: true,
8229     * >                 showLabel: true,
8230     * >                 showMark: true,
8231     * >                 size: 4,
8232     * >                 textColor: "",
8233     * >                 whiteSpace: "nowrap",
8234     * >                 fontSize: "12px",
8235     * >                 fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif"
8236     * >             },
8237     * >             label: {
8238     * >                 textColor: null,
8239     * >                 whiteSpace: null,
8240     * >                 fontSize: null,
8241     * >                 fontFamily: null,
8242     * >                 fontWeight: null
8243     * >             }
8244     * >         },
8245     * >         x2axis: {...
8246     * >         },
8247     * >         ...
8248     * >         y9axis: {...
8249     * >         }
8250     * >     }
8251     * > }
8252     * 
8253     * "seriesStyles" is a style object that will be applied to all series in the plot.
8254     * It will forcibly override any styles applied on the individual series.  "axesStyles" is
8255     * a style object that will be applied to all axes in the plot.  It will also forcibly
8256     * override any styles on the individual axes.
8257     * 
8258     * The example shown above has series options for a line series.  Options for other
8259     * series types are shown below:
8260     * 
8261     * Bar Series:
8262     * 
8263     * > {
8264     * >     color: "#4bb2c5",
8265     * >     seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
8266     * >     lineWidth: 2.5,
8267     * >     shadow: true,
8268     * >     barPadding: 2,
8269     * >     barMargin: 10,
8270     * >     barWidth: 15.09375,
8271     * >     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)"]
8272     * > }
8273     * 
8274     * Pie Series:
8275     * 
8276     * > {
8277     * >     seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
8278     * >     padding: 20,
8279     * >     sliceMargin: 0,
8280     * >     fill: true,
8281     * >     shadow: true,
8282     * >     startAngle: 0,
8283     * >     lineWidth: 2.5,
8284     * >     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)"]
8285     * > }
8286     * 
8287     * Funnel Series:
8288     * 
8289     * > {
8290     * >     color: "#4bb2c5",
8291     * >     lineWidth: 2,
8292     * >     shadow: true,
8293     * >     padding: {
8294     * >         top: 20,
8295     * >         right: 20,
8296     * >         bottom: 20,
8297     * >         left: 20
8298     * >     },
8299     * >     sectionMargin: 6,
8300     * >     seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
8301     * >     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)"]
8302     * > }
8303     * 
8304     */
8305    $.jqplot.ThemeEngine = function(){
8306        // Group: Properties
8307        //
8308        // prop: themes
8309        // hash of themes managed by the theme engine.  
8310        // Indexed by theme name.
8311        this.themes = {};
8312        // prop: activeTheme
8313        // Pointer to currently active theme
8314        this.activeTheme=null;
8315        
8316    };
8317    
8318    // called with scope of plot
8319    $.jqplot.ThemeEngine.prototype.init = function() {
8320        // get the Default theme from the current plot settings.
8321        var th = new $.jqplot.Theme({_name:'Default'});
8322        var n, i, nn;
8323        
8324        for (n in th.target) {
8325            if (n == "textColor") {
8326                th.target[n] = this.target.css('color');
8327            }
8328            else {
8329                th.target[n] = this.target.css(n);
8330            }
8331        }
8332        
8333        if (this.title.show && this.title._elem) {
8334            for (n in th.title) {
8335                if (n == "textColor") {
8336                    th.title[n] = this.title._elem.css('color');
8337                }
8338                else {
8339                    th.title[n] = this.title._elem.css(n);
8340                }
8341            }
8342        }
8343        
8344        for (n in th.grid) {
8345            th.grid[n] = this.grid[n];
8346        }
8347        if (th.grid.backgroundColor == null && this.grid.background != null) {
8348            th.grid.backgroundColor = this.grid.background;
8349        }
8350        if (this.legend.show && this.legend._elem) {
8351            for (n in th.legend) {
8352                if (n == 'textColor') {
8353                    th.legend[n] = this.legend._elem.css('color');
8354                }
8355                else {
8356                    th.legend[n] = this.legend._elem.css(n);
8357                }
8358            }
8359        }
8360        var s;
8361        
8362        for (i=0; i<this.series.length; i++) {
8363            s = this.series[i];
8364            if (s.renderer.constructor == $.jqplot.LineRenderer) {
8365                th.series.push(new LineSeriesProperties());
8366            }
8367            else if (s.renderer.constructor == $.jqplot.BarRenderer) {
8368                th.series.push(new BarSeriesProperties());
8369            }
8370            else if (s.renderer.constructor == $.jqplot.PieRenderer) {
8371                th.series.push(new PieSeriesProperties());
8372            }
8373            else if (s.renderer.constructor == $.jqplot.DonutRenderer) {
8374                th.series.push(new DonutSeriesProperties());
8375            }
8376            else if (s.renderer.constructor == $.jqplot.FunnelRenderer) {
8377                th.series.push(new FunnelSeriesProperties());
8378            }
8379            else if (s.renderer.constructor == $.jqplot.MeterGaugeRenderer) {
8380                th.series.push(new MeterSeriesProperties());
8381            }
8382            else {
8383                th.series.push({});
8384            }
8385            for (n in th.series[i]) {
8386                th.series[i][n] = s[n];
8387            }
8388        }
8389        var a, ax;
8390        for (n in this.axes) {
8391            ax = this.axes[n];
8392            a = th.axes[n] = new AxisProperties();
8393            a.borderColor = ax.borderColor;
8394            a.borderWidth = ax.borderWidth;
8395            if (ax._ticks && ax._ticks[0]) {
8396                for (nn in a.ticks) {
8397                    if (ax._ticks[0].hasOwnProperty(nn)) {
8398                        a.ticks[nn] = ax._ticks[0][nn];
8399                    }
8400                    else if (ax._ticks[0]._elem){
8401                        a.ticks[nn] = ax._ticks[0]._elem.css(nn);
8402                    }
8403                }
8404            }
8405            if (ax._label && ax._label.show) {
8406                for (nn in a.label) {
8407                    // a.label[nn] = ax._label._elem.css(nn);
8408                    if (ax._label[nn]) {
8409                        a.label[nn] = ax._label[nn];
8410                    }
8411                    else if (ax._label._elem){
8412                        if (nn == 'textColor') {
8413                            a.label[nn] = ax._label._elem.css('color');
8414                        }
8415                        else {
8416                            a.label[nn] = ax._label._elem.css(nn);
8417                        }
8418                    }
8419                }
8420            }
8421        }
8422        this.themeEngine._add(th);
8423        this.themeEngine.activeTheme  = this.themeEngine.themes[th._name];
8424    };
8425    /**
8426     * Group: methods
8427     * 
8428     * method: get
8429     * 
8430     * Get and return the named theme or the active theme if no name given.
8431     * 
8432     * parameter:
8433     * 
8434     * name - name of theme to get.
8435     * 
8436     * returns:
8437     * 
8438     * Theme instance of given name.
8439     */   
8440    $.jqplot.ThemeEngine.prototype.get = function(name) {
8441        if (!name) {
8442            // return the active theme
8443            return this.activeTheme;
8444        }
8445        else {
8446            return this.themes[name];
8447        }
8448    };
8449    
8450    function numericalOrder(a,b) { return a-b; }
8451    
8452    /**
8453     * method: getThemeNames
8454     * 
8455     * Return the list of theme names in this manager in alpha-numerical order.
8456     * 
8457     * parameter:
8458     * 
8459     * None
8460     * 
8461     * returns:
8462     * 
8463     * A the list of theme names in this manager in alpha-numerical order.
8464     */       
8465    $.jqplot.ThemeEngine.prototype.getThemeNames = function() {
8466        var tn = [];
8467        for (var n in this.themes) {
8468            tn.push(n);
8469        }
8470        return tn.sort(numericalOrder);
8471    };
8472
8473    /**
8474     * method: getThemes
8475     * 
8476     * Return a list of themes in alpha-numerical order by name.
8477     * 
8478     * parameter:
8479     * 
8480     * None
8481     * 
8482     * returns:
8483     * 
8484     * A list of themes in alpha-numerical order by name.
8485     */ 
8486    $.jqplot.ThemeEngine.prototype.getThemes = function() {
8487        var tn = [];
8488        var themes = [];
8489        for (var n in this.themes) {
8490            tn.push(n);
8491        }
8492        tn.sort(numericalOrder);
8493        for (var i=0; i<tn.length; i++) {
8494            themes.push(this.themes[tn[i]]);
8495        }
8496        return themes;
8497    };
8498    
8499    $.jqplot.ThemeEngine.prototype.activate = function(plot, name) {
8500        // sometimes need to redraw whole plot.
8501        var redrawPlot = false;
8502        if (!name && this.activeTheme && this.activeTheme._name) {
8503            name = this.activeTheme._name;
8504        }
8505        if (!this.themes.hasOwnProperty(name)) {
8506            throw new Error("No theme of that name");
8507        }
8508        else {
8509            var th = this.themes[name];
8510            this.activeTheme = th;
8511            var val, checkBorderColor = false, checkBorderWidth = false;
8512            var arr = ['xaxis', 'x2axis', 'yaxis', 'y2axis'];
8513            
8514            for (i=0; i<arr.length; i++) {
8515                var ax = arr[i];
8516                if (th.axesStyles.borderColor != null) {
8517                    plot.axes[ax].borderColor = th.axesStyles.borderColor;
8518                }
8519                if (th.axesStyles.borderWidth != null) {
8520                    plot.axes[ax].borderWidth = th.axesStyles.borderWidth;
8521                }
8522            }
8523            
8524            for (var axname in plot.axes) {
8525                var axis = plot.axes[axname];
8526                if (axis.show) {
8527                    var thaxis = th.axes[axname] || {};
8528                    var thaxstyle = th.axesStyles;
8529                    var thax = $.jqplot.extend(true, {}, thaxis, thaxstyle);
8530                    val = (th.axesStyles.borderColor != null) ? th.axesStyles.borderColor : thax.borderColor;
8531                    if (thax.borderColor != null) {
8532                        axis.borderColor = thax.borderColor;
8533                        redrawPlot = true;
8534                    }
8535                    val = (th.axesStyles.borderWidth != null) ? th.axesStyles.borderWidth : thax.borderWidth;
8536                    if (thax.borderWidth != null) {
8537                        axis.borderWidth = thax.borderWidth;
8538                        redrawPlot = true;
8539                    }
8540                    if (axis._ticks && axis._ticks[0]) {
8541                        for (var nn in thax.ticks) {
8542                            // val = null;
8543                            // if (th.axesStyles.ticks && th.axesStyles.ticks[nn] != null) {
8544                            //     val = th.axesStyles.ticks[nn];
8545                            // }
8546                            // else if (thax.ticks[nn] != null){
8547                            //     val = thax.ticks[nn]
8548                            // }
8549                            val = thax.ticks[nn];
8550                            if (val != null) {
8551                                axis.tickOptions[nn] = val;
8552                                axis._ticks = [];
8553                                redrawPlot = true;
8554                            }
8555                        }
8556                    }
8557                    if (axis._label && axis._label.show) {
8558                        for (var nn in thax.label) {
8559                            // val = null;
8560                            // if (th.axesStyles.label && th.axesStyles.label[nn] != null) {
8561                            //     val = th.axesStyles.label[nn];
8562                            // }
8563                            // else if (thax.label && thax.label[nn] != null){
8564                            //     val = thax.label[nn]
8565                            // }
8566                            val = thax.label[nn];
8567                            if (val != null) {
8568                                axis.labelOptions[nn] = val;
8569                                redrawPlot = true;
8570                            }
8571                        }
8572                    }
8573                    
8574                }
8575            }            
8576            
8577            for (var n in th.grid) {
8578                if (th.grid[n] != null) {
8579                    plot.grid[n] = th.grid[n];
8580                }
8581            }
8582            if (!redrawPlot) {
8583                plot.grid.draw();
8584            }
8585            
8586            if (plot.legend.show) { 
8587                for (n in th.legend) {
8588                    if (th.legend[n] != null) {
8589                        plot.legend[n] = th.legend[n];
8590                    }
8591                }
8592            }
8593            if (plot.title.show) {
8594                for (n in th.title) {
8595                    if (th.title[n] != null) {
8596                        plot.title[n] = th.title[n];
8597                    }
8598                }
8599            }
8600            
8601            var i;
8602            for (i=0; i<th.series.length; i++) {
8603                var opts = {};
8604                var redrawSeries = false;
8605                for (n in th.series[i]) {
8606                    val = (th.seriesStyles[n] != null) ? th.seriesStyles[n] : th.series[i][n];
8607                    if (val != null) {
8608                        opts[n] = val;
8609                        if (n == 'color') {
8610                            plot.series[i].renderer.shapeRenderer.f
8610illStyle = val;
8611                            plot.series[i].renderer.shapeRenderer.strokeStyle = val;
8612                            plot.series[i][n] = val;
8613                        }
8614                        else if ((n == 'lineWidth') || (n == 'linePattern')) {
8615                            plot.series[i].renderer.shapeRenderer[n] = val;
8616                            plot.series[i][n] = val;
8617                        }
8618                        else if (n == 'markerOptions') {
8619                            merge (plot.series[i].markerOptions, val);
8620                            merge (plot.series[i].markerRenderer, val);
8621                        }
8622                        else {
8623                            plot.series[i][n] = val;
8624                        }
8625                        redrawPlot = true;
8626                    }
8627                }
8628            }
8629            
8630            if (redrawPlot) {
8631                plot.target.empty();
8632                plot.draw();
8633            }
8634            
8635            for (n in th.target) {
8636                if (th.target[n] != null) {
8637                    plot.target.css(n, th.target[n]);
8638                }
8639            }
8640        }
8641        
8642    };
8643    
8644    $.jqplot.ThemeEngine.prototype._add = function(theme, name) {
8645        if (name) {
8646            theme._name = name;
8647        }
8648        if (!theme._name) {
8649            theme._name = Date.parse(new Date());
8650        }
8651        if (!this.themes.hasOwnProperty(theme._name)) {
8652            this.themes[theme._name] = theme;
8653        }
8654        else {
8655            throw new Error("jqplot.ThemeEngine Error: Theme already in use");
8656        }
8657    };
8658    
8659    // method remove
8660    // Delete the named theme, return true on success, false on failure.
8661    
8662
8663    /**
8664     * method: remove
8665     * 
8666     * Remove the given theme from the themeEngine.
8667     * 
8668     * parameters:
8669     * 
8670     * name - name of the theme to remove.
8671     * 
8672     * returns:
8673     * 
8674     * true on success, false on failure.
8675     */
8676    $.jqplot.ThemeEngine.prototype.remove = function(name) {
8677        if (name == 'Default') {
8678            return false;
8679        }
8680        return delete this.themes[name];
8681    };
8682
8683    /**
8684     * method: newTheme
8685     * 
8686     * Create a new theme based on the default theme, adding it the themeEngine.
8687     * 
8688     * parameters:
8689     * 
8690     * name - name of the new theme.
8691     * obj - optional object of styles to be applied to this new theme.
8692     * 
8693     * returns:
8694     * 
8695     * new Theme object.
8696     */
8697    $.jqplot.ThemeEngine.prototype.newTheme = function(name, obj) {
8698        if (typeof(name) == 'object') {
8699            obj = obj || name;
8700            name = null;
8701        }
8702        if (obj && obj._name) {
8703            name = obj._name;
8704        }
8705        else {
8706            name = name || Date.parse(new Date());
8707        }
8708        // var th = new $.jqplot.Theme(name);
8709        var th = this.copy(this.themes['Default']._name, name);
8710        $.jqplot.extend(th, obj);
8711        return th;
8712    };
8713    
8714    // function clone(obj) {
8715    //     return eval(obj.toSource());
8716    // }
8717    
8718    function clone(obj){
8719        if(obj == null || typeof(obj) != 'object'){
8720            return obj;
8721        }
8722    
8723        var temp = new obj.constructor();
8724        for(var key in obj){
8725            temp[key] = clone(obj[key]);
8726        }   
8727        return temp;
8728    }
8729    
8730    $.jqplot.clone = clone;
8731    
8732    function merge(obj1, obj2) {
8733        if (obj2 ==  null || typeof(obj2) != 'object') {
8734            return;
8735        }
8736        for (var key in obj2) {
8737            if (key == 'highlightColors') {
8738                obj1[key] = clone(obj2[key]);
8739            }
8740            if (obj2[key] != null && typeof(obj2[key]) == 'object') {
8741                if (!obj1.hasOwnProperty(key)) {
8742                    obj1[key] = {};
8743                }
8744                merge(obj1[key], obj2[key]);
8745            }
8746            else {
8747                obj1[key] = obj2[key];
8748            }
8749        }
8750    }
8751    
8752    $.jqplot.merge = merge;
8753    
8754        // Use the jQuery 1.3.2 extend function since behaviour in jQuery 1.4 seems problematic
8755    $.jqplot.extend = function() {
8756        // copy reference to target object
8757        var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
8758
8759        // Handle a deep copy situation
8760        if ( typeof target === "boolean" ) {
8761            deep = target;
8762            target = arguments[1] || {};
8763            // skip the boolean and the target
8764            i = 2;
8765        }
8766
8767        // Handle case when target is a string or something (possible in deep copy)
8768        if ( typeof target !== "object" && !toString.call(target) === "[object Function]" ) {
8769            target = {};
8770        }
8771
8772        for ( ; i < length; i++ ){
8773            // Only deal with non-null/undefined values
8774            if ( (options = arguments[ i ]) != null ) {
8775                // Extend the base object
8776                for ( var name in options ) {
8777                    var src = target[ name ], copy = options[ name ];
8778
8779                    // Prevent never-ending loop
8780                    if ( target === copy ) {
8781                        continue;
8782                    }
8783
8784                    // Recurse if we're merging object values
8785                    if ( deep && copy && typeof copy === "object" && !copy.nodeType ) {
8786                        target[ name ] = $.jqplot.extend( deep, 
8787                            // Never move original objects, clone them
8788                            src || ( copy.length != null ? [ ] : { } )
8789                        , copy );
8790                    }
8791                    // Don't bring in undefined values
8792                    else if ( copy !== undefined ) {
8793                        target[ name ] = copy;
8794                    }
8795                }
8796            }
8797        }
8798        // Return the modified object
8799        return target;
8800    };
8801
8802    /**
8803     * method: rename
8804     * 
8805     * Rename a theme.
8806     * 
8807     * parameters:
8808     * 
8809     * oldName - current name of the theme.
8810     * newName - desired name of the theme.
8811     * 
8812     * returns:
8813     * 
8814     * new Theme object.
8815     */
8816    $.jqplot.ThemeEngine.prototype.rename = function (oldName, newName) {
8817        if (oldName == 'Default' || newName == 'Default') {
8818            throw new Error ("jqplot.ThemeEngine Error: Cannot rename from/to Default");
8819        }
8820        if (this.themes.hasOwnProperty(newName)) {
8821            throw new Error ("jqplot.ThemeEngine Error: New name already in use.");
8822        }
8823        else if (this.themes.hasOwnProperty(oldName)) {
8824            var th = this.copy (oldName, newName);
8825            this.remove(oldName);
8826            return th;
8827        }
8828        throw new Error("jqplot.ThemeEngine Error: Old name or new name invalid");
8829    };
8830
8831    /**
8832     * method: copy
8833     * 
8834     * Create a copy of an existing theme in the themeEngine, adding it the themeEngine.
8835     * 
8836     * parameters:
8837     * 
8838     * sourceName - name of the existing theme.
8839     * targetName - name of the copy.
8840     * obj - optional object of style parameter to apply to the new theme.
8841     * 
8842     * returns:
8843     * 
8844     * new Theme object.
8845     */
8846    $.jqplot.ThemeEngine.prototype.copy = function (sourceName, targetName, obj) {
8847        if (targetName == 'Default') {
8848            throw new Error ("jqplot.ThemeEngine Error: Cannot copy over Default theme");
8849        }
8850        if (!this.themes.hasOwnProperty(sourceName)) {
8851            var s = "jqplot.ThemeEngine Error: Source name invalid";
8852            throw new Error(s);
8853        }
8854        if (this.themes.hasOwnProperty(targetName)) {
8855            var s = "jqplot.ThemeEngine Error: Target name invalid";
8856            throw new Error(s);
8857        }
8858        else {
8859            var th = clone(this.themes[sourceName]);
8860            th._name = targetName;
8861            $.jqplot.extend(true, th, obj);
8862            this._add(th);
8863            return th;
8864        }
8865    };
8866    
8867    
8868    $.jqplot.Theme = function(name, obj) {
8869        if (typeof(name) == 'object') {
8870            obj = obj || name;
8871            name = null;
8872        }
8873        name = name || Date.parse(new Date());
8874        this._name = name;
8875        this.target = {
8876            backgroundColor: null
8877        };
8878        this.legend = {
8879            textColor: null,
8880            fontFamily: null,
8881            fontSize: null,
8882            border: null,
8883            background: null
8884        };
8885        this.title = {
8886            textColor: null,
8887            fontFamily: null,
8888            fontSize: null,
8889            textAlign: null
8890        };
8891        this.seriesStyles = {};
8892        this.series = [];
8893        this.grid = {
8894            drawGridlines: null,
8895            gridLineColor: null,
8896            gridLineWidth: null,
8897            backgroundColor: null,
8898            borderColor: null,
8899            borderWidth: null,
8900            shadow: null
8901        };
8902        this.axesStyles = {label:{}, ticks:{}};
8903        this.axes = {};
8904        if (typeof(obj) == 'string') {
8905            this._name = obj;
8906        }
8907        else if(typeof(obj) == 'object') {
8908            $.jqplot.extend(true, this, obj);
8909        }
8910    };
8911    
8912    var AxisProperties = function() {
8913        this.borderColor = null;
8914        this.borderWidth = null;
8915        this.ticks = new AxisTicks();
8916        this.label = new AxisLabel();
8917    };
8918    
8919    var AxisTicks = function() {
8920        this.show = null;
8921        this.showGridline = null;
8922        this.showLabel = null;
8923        this.showMark = null;
8924        this.size = null;
8925        this.textColor = null;
8926        this.whiteSpace = null;
8927        this.fontSize = null;
8928        this.fontFamily = null;
8929    };
8930    
8931    var AxisLabel = function() {
8932        this.textColor = null;
8933        this.whiteSpace = null;
8934        this.fontSize = null;
8935        this.fontFamily = null;
8936        this.fontWeight = null;
8937    };
8938    
8939    var LineSeriesProperties = function() {
8940        this.color=null;
8941        this.lineWidth=null;
8942        this.linePattern=null;
8943        this.shadow=null;
8944        this.fillColor=null;
8945        this.showMarker=null;
8946        this.markerOptions = new MarkerOptions();
8947    };
8948    
8949    var MarkerOptions = function() {
8950        this.show = null;
8951        this.style = null;
8952        this.lineWidth = null;
8953        this.size = null;
8954        this.color = null;
8955        this.shadow = null;
8956    };
8957    
8958    var BarSeriesProperties = function() {
8959        this.color=null;
8960        this.seriesColors=null;
8961        this.lineWidth=null;
8962        this.shadow=null;
8963        this.barPadding=null;
8964        this.barMargin=null;
8965        this.barWidth=null;
8966        this.highlightColors=null;
8967    };
8968    
8969    var PieSeriesProperties = function() {
8970        this.seriesColors=null;
8971        this.padding=null;
8972        this.sliceMargin=null;
8973        this.fill=null;
8974        this.shadow=null;
8975        this.startAngle=null;
8976        this.lineWidth=null;
8977        this.highlightColors=null;
8978    };
8979    
8980    var DonutSeriesProperties = function() {
8981        this.seriesColors=null;
8982        this.padding=null;
8983        this.sliceMargin=null;
8984        this.fill=null;
8985        this.shadow=null;
8986        this.startAngle=null;
8987        this.lineWidth=null;
8988        this.innerDiameter=null;
8989        this.thickness=null;
8990        this.ringMargin=null;
8991        this.highlightColors=null;
8992    };
8993    
8994    var FunnelSeriesProperties = function() {
8995        this.color=null;
8996        this.lineWidth=null;
8997        this.shadow=null;
8998        this.padding=null;
8999        this.sectionMargin=null;
9000        this.seriesColors=null;
9001        this.highlightColors=null;
9002    };
9003    
9004    var MeterSeriesProperties = function() {
9005        this.padding=null;
9006        this.backgroundColor=null;
9007        this.ringColor=null;
9008        this.tickColor=null;
9009        this.ringWidth=null;
9010        this.intervalColors=null;
9011        this.intervalInnerRadius=null;
9012        this.intervalOuterRadius=null;
9013        this.hubRadius=null;
9014        this.needleThickness=null;
9015        this.needlePad=null;
9016    };
9017        
9018
9019
9020
9021    $.fn.jqplotChildText = function() {
9022        return $(this).contents().filter(function() {
9023            return this.nodeType == 3;  // Node.TEXT_NODE not defined in I7
9024        }).text();
9025    };
9026
9027    // Returns font style as abbreviation for "font" property.
9028    $.fn.jqplotGetComputedFontStyle = function() {
9029        var css = window.getComputedStyle ?  window.getComputedStyle(this[0], "") : this[0].currentStyle;
9030        var attrs = css['font-style'] ? ['font-style', 'font-weight', 'font-size', 'font-family'] : ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily'];
9031        var style = [];
9032
9033        for (var i=0 ; i < attrs.length; ++i) {
9034            var attr = String(css[attrs[i]]);
9035
9036            if (attr && attr != 'normal') {
9037                style.push(attr);
9038            }
9039        }
9040        return style.join(' ');
9041    };
9042
9043    /**
9044     * Namespace: $.fn
9045     * jQuery namespace to attach functions to jQuery elements.
9046     *  
9047     */
9048
9049    $.fn.jqplotToImageCanvas = function(options) {
9050
9051        options = options || {};
9052        var x_offset = (options.x_offset == null) ? 0 : options.x_offset;
9053        var y_offset = (options.y_offset == null) ? 0 : options.y_offset;
9054        var backgroundColor = (options.backgroundColor == null) ? 'rgb(255,255,255)' : options.backgroundColor;
9055
9056        if ($(this).width() == 0 || $(this).height() == 0) {
9057            return null;
9058        }
9059
9060        // excanvas and hence IE < 9 do not support toDataURL and cannot export images.
9061        if ($.jqplot.use_excanvas) {
9062            return null;
9063        }
9064        
9065        var newCanvas = document.createElement("canvas");
9066        var h = $(this).outerHeight(true);
9067        var w = $(this).outerWidth(true);
9068        var offs = $(this).offset();
9069        var plotleft = offs.left;
9070        var plottop = offs.top;
9071        var transx = 0, transy = 0;
9072
9073        // have to check if any elements are hanging outside of plot area before rendering,
9074        // since changing width of canvas will erase canvas.
9075
9076        var clses = ['jqplot-table-legend', 'jqplot-xaxis-tick', 'jqplot-x2axis-tick', 'jqplot-yaxis-tick', 'jqplot-y2axis-tick', 'jqplot-y3axis-tick', 
9077        'jqplot-y4axis-tick', 'jqplot-y5axis-tick', 'jqplot-y6axis-tick', 'jqplot-y7axis-tick', 'jqplot-y8axis-tick', 'jqplot-y9axis-tick',
9078        'jqplot-xaxis-label', 'jqplot-x2axis-label', 'jqplot-yaxis-label', 'jqplot-y2axis-label', 'jqplot-y3axis-label', 'jqplot-y4axis-label', 
9079        'jqplot-y5axis-label', 'jqplot-y6axis-label', 'jqplot-y7axis-label', 'jqplot-y8axis-label', 'jqplot-y9axis-label' ];
9080
9081        var temptop, templeft, tempbottom, tempright;
9082
9083        for (var i = 0; i < clses.length; i++) {
9084            $(this).find('.'+clses[i]).each(function() {
9085                temptop = $(this).offset().top - plottop;
9086                templeft = $(this).offset().left - plotleft;
9087                tempright = templeft + $(this).outerWidth(true) + transx;
9088                tempbottom = temptop + $(this).outerHeight(true) + transy;
9089                if (templeft < -transx) {
9090                    w = w - transx - templeft;
9091                    transx = -templeft;
9092                }
9093                if (temptop < -transy) {
9094                    h = h - transy - temptop;
9095                    transy = - temptop;
9096                }
9097                if (tempright > w) {
9098                    w = tempright;
9099                }
9100                if (tempbottom > h) {
9101                    h =  tempbottom;
9102                }
9103            });
9104        }
9105
9106        newCanvas.width = w + Number(x_offset);
9107        newCanvas.height = h + Number(y_offset);
9108
9109        var newContext = newCanvas.getContext("2d"); 
9110
9111        newContext.save();
9112        newContext.fillStyle = backgroundColor;
9113        newContext.fillRect(0,0, newCanvas.width, newCanvas.height);
9114        newContext.restore();
9115
9116        newContext.translate(transx, transy);
9117        newContext.textAlign = 'left';
9118        newContext.textBaseline = 'top';
9119
9120        function getLineheight(el) {
9121            var lineheight = parseInt($(el).css('line-height'), 10);
9122
9123            if (isNaN(lineheight)) {
9124                lineheight = parseInt($(el).css('font-size'), 10) * 1.2;
9125            }
9126            return lineheight;
9127        }
9128
9129        function writeWrappedText (el, context, text, left, top, canvasWidth) {
9130            var lineheight = getLineheight(el);
9131            var tagwidth = $(el).innerWidth();
9132            var tagheight = $(el).innerHeight();
9133            var words = text.split(/\s+/);
9134            var wl = words.length;
9135            var w = '';
9136            var breaks = [];
9137            var temptop = top;
9138            var templeft = left;
9139
9140            for (var i=0; i<wl; i++) {
9141                w += words[i];
9142                if (context.measureText(w).width > tagwidth) {
9143                    breaks.push(i);
9144                    w = '';
9145                    i--;
9146                }   
9147            }
9148            if (breaks.length === 0) {
9149                // center text if necessary
9150                if ($(el).css('textAlign') === 'center') {
9151                    templeft = left + (canvasWidth - context.measureText(w).width)/2  - transx;
9152                }
9153                context.fillText(text, templeft, top);
9154            }
9155            else {
9156                w = words.slice(0, breaks[0]).join(' ');
9157                // center text if necessary
9158                if ($(el).css('textAlign') === 'center') {
9159                    templeft = left + (canvasWidth - context.measureText(w).width)/2  - transx;
9160                }
9161                context.fillText(w, templeft, temptop);
9162                temptop += lineheight;
9163                for (var i=1, l=breaks.length; i<l; i++) {
9164                    w = words.slice(breaks[i-1], breaks[i]).join(' ');
9165                    // center text if necessary
9166                    if ($(el).css('textAlign') === 'center') {
9167                        templeft = left + (canvasWidth - context.measureText(w).width)/2  - transx;
9168                    }
9169                    context.fillText(w, templeft, temptop);
9170                    temptop += lineheight;
9171                }
9172                w = words.slice(breaks[i-1], words.length).join(' ');
9173                // center text if necessary
9174                if ($(el).css('textAlign') === 'center') {
9175                    templeft = left + (canvasWidth - context.measureText(w).width)/2  - transx;
9176                }
9177                context.fillText(w, templeft, temptop);
9178            }
9179
9180        }
9181
9182        function _jqpToImage(el, x_offset, y_offset) {
9183            var tagname = el.tagName.toLowerCase();
9184            var p = $(el).position();
9185            var css = window.getComputedStyle ?  window.getComputedStyle(el, "") : el.currentStyle; // for IE < 9
9186            var left = x_offset + p.left + parseInt(css.marginLeft, 10) + parseInt(css.borderLeftWidth, 10) + parseInt(css.paddingLeft, 10);
9187            var top = y_offset + p.top + parseInt(css.marginTop, 10) + parseInt(css.borderTopWidth, 10)+ parseInt(css.paddingTop, 10);
9188            var w = newCanvas.width;
9189            // var left = x_offset + p.left + $(el).css('marginLeft') + $(el).css('borderLeftWidth') 
9190
9191            // somehow in here, for divs within divs, the width of the inner div should be used instead of the canvas.
9192
9193            if ((tagname == 'div' || tagname == 'span') && !$(el).hasClass('jqplot-highlighter-tooltip')) {
9194                $(el).children().each(function() {
9195                    _jqpToImage(this, left, top);
9196                });
9197                var text = $(el).jqplotChildText();
9198
9199                if (text) {
9200                    newContext.font = $(el).jqplotGetComputedFontStyle();
9201                    newContext.fillStyle = $(el).css('color');
9202
9203                    writeWrappedText(el, newContext, text, left, top, w);
9204                }
9205            }
9206
9207            // handle the standard table legend
9208
9209            else if (tagname === 'table' && $(el).hasClass('jqplot-table-legend')) {
9210                newContext.strokeStyle = $(el).css('border-top-color');
9211                newContext.fillStyle = $(el).css('background-color');
9212                newContext.fillRect(left, top, $(el).innerWidth(), $(el).innerHeight());
9213                if (parseInt($(el).css('border-top-width'), 10) > 0) {
9214                    newContext.strokeRect(left, top, $(el).innerWidth(), $(el).innerHeight());
9215                }
9216
9217                // find all the swatches
9218                $(el).find('div.jqplot-table-legend-swatch-outline').each(function() {
9219                    // get the first div and stroke it
9220                    var elem = $(this);
9221                    newContext.strokeStyle = elem.css('border-top-color');
9222                    var l = left + elem.position().left;
9223                    var t = top + elem.position().top;
9224                    newContext.strokeRect(l, t, elem.innerWidth(), elem.innerHe
9224ight());
9225
9226                    // now fill the swatch
9227                    
9228                    l += parseInt(elem.css('padding-left'), 10);
9229                    t += parseInt(elem.css('padding-top'), 10);
9230                    var h = elem.innerHeight() - 2 * parseInt(elem.css('padding-top'), 10);
9231                    var w = elem.innerWidth() - 2 * parseInt(elem.css('padding-left'), 10);
9232
9233                    var swatch = elem.children('div.jqplot-table-legend-swatch');
9234                    newContext.fillStyle = swatch.css('background-color');
9235                    newContext.fillRect(l, t, w, h);
9236                });
9237
9238                // now add text
9239
9240                $(el).find('td.jqplot-table-legend-label').each(function(){
9241                    var elem = $(this);
9242                    var l = left + elem.position().left;
9243                    var t = top + elem.position().top + parseInt(elem.css('padding-top'), 10);
9244                    newContext.font = elem.jqplotGetComputedFontStyle();
9245                    newContext.fillStyle = elem.css('color');
9246                    writeWrappedText(elem, newContext, elem.text(), l, t, w);
9247                });
9248
9249                var elem = null;
9250            }
9251
9252            else if (tagname == 'canvas') {
9253                newContext.drawImage(el, left, top);
9254            }
9255        }
9256        $(this).children().each(function() {
9257            _jqpToImage(this, x_offset, y_offset);
9258        });
9259        return newCanvas;
9260    };
9261
9262    // return the raw image data string.
9263    // Should work on canvas supporting browsers.
9264    $.fn.jqplotToImageStr = function(options) {
9265        var imgCanvas = $(this).jqplotToImageCanvas(options);
9266        if (imgCanvas) {
9267            return imgCanvas.toDataURL("image/png");
9268        }
9269        else {
9270            return null;
9271        }
9272    };
9273
9274    // return a DOM <img> element and return it.
9275    // Should work on canvas supporting browsers.
9276    $.fn.jqplotToImageElem = function(options) {
9277        var elem = document.createElement("img");
9278        var str = $(this).jqplotToImageStr(options);
9279        elem.src = str;
9280        return elem;
9281    };
9282
9283    // return a string for an <img> element and return it.
9284    // Should work on canvas supporting browsers.
9285    $.fn.jqplotToImageElemStr = function(options) {
9286        var str = '<img src='+$(this).jqplotToImageStr(options)+' />';
9287        return str;
9288    };
9289
9290    // Not guaranteed to work, even on canvas supporting browsers due to 
9291    // limitations with location.href and browser support.
9292    $.fn.jqplotSaveImage = function() {
9293        var imgData = $(this).jqplotToImageStr({});
9294        if (imgData) {
9295            window.location.href = imgData.replace("image/png", "image/octet-stream");
9296        }
9297
9298    };
9299
9300    // Not guaranteed to work, even on canvas supporting browsers due to
9301    // limitations with window.open and arbitrary data.
9302    $.fn.jqplotViewImage = function() {
9303        var imgStr = $(this).jqplotToImageElemStr({});
9304        var imgData = $(this).jqplotToImageStr({});
9305        if (imgStr) {
9306            var w = window.open('');
9307            w.document.open("image/png");
9308            w.document.write(imgStr);
9309            w.document.close();
9310            w = null;
9311        }
9312    };
9313    
9314
9315
9316
9317    /** 
9318     * @description
9319     * <p>Object with extended date parsing and formatting capabilities.
9320     * This library borrows many concepts and ideas from the Date Instance 
9321     * Methods by Ken Snyder along with some parts of Ken's actual code.</p>
9322     *
9323     * <p>jsDate takes a different approach by not extending the built-in 
9324     * Date Object, improving date parsing, allowing for multiple formatting 
9325     * syntaxes and multiple and more easily expandable localization.</p>
9326     * 
9327     * @author Chris Leonello
9328     * @date #date#
9329     * @version #VERSION#
9330     * @copyright (c) 2010-2013 Chris Leonello
9331     * jsDate is currently available for use in all personal or commercial projects 
9332     * under both the MIT and GPL version 2.0 licenses. This means that you can 
9333     * choose the license that best suits your project and use it accordingly.
9334     * 
9335     * <p>Ken's original Date Instance Methods and copyright notice:</p>
9336     * <pre>
9337     * Ken Snyder (ken d snyder at gmail dot com)
9338     * 2008-09-10
9339     * version 2.0.2 (http://kendsnyder.com/sandbox/date/)     
9340     * Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/)
9341     * </pre>
9342     * 
9343     * @class
9344     * @name jsDate
9345     * @param  {String | Number | Array | Date&nbsp;Object | Options&nbsp;Object} arguments Optional arguments, either a parsable date/time string,
9346     * a JavaScript timestamp, an array of numbers of form [year, month, day, hours, minutes, seconds, milliseconds],
9347     * a Date object, or an options object of form {syntax: "perl", date:some Date} where all options are optional.
9348     */
9349     
9350    var jsDate = function () {
9351    
9352        this.syntax = jsDate.config.syntax;
9353        this._type = "jsDate";
9354        this.proxy = new Date();
9355        this.options = {};
9356        this.locale = jsDate.regional.getLocale();
9357        this.formatString = '';
9358        this.defaultCentury = jsDate.config.defaultCentury;
9359
9360        switch ( arguments.length ) {
9361            case 0:
9362                break;
9363            case 1:
9364                // other objects either won't have a _type property or,
9365                // if they do, it shouldn't be set to "jsDate", so
9366                // assume it is an options argument.
9367                if (get_type(arguments[0]) == "[object Object]" && arguments[0]._type != "jsDate") {
9368                    var opts = this.options = arguments[0];
9369                    this.syntax = opts.syntax || this.syntax;
9370                    this.defaultCentury = opts.defaultCentury || this.defaultCentury;
9371                    this.proxy = jsDate.createDate(opts.date);
9372                }
9373                else {
9374                    this.proxy = jsDate.createDate(arguments[0]);
9375                }
9376                break;
9377            default:
9378                var a = [];
9379                for ( var i=0; i<arguments.length; i++ ) {
9380                    a.push(arguments[i]);
9381                }
9382                // this should be the current date/time?
9383                this.proxy = new Date();
9384                this.proxy.setFullYear.apply( this.proxy, a.slice(0,3) );
9385                if ( a.slice(3).length ) {
9386                    this.proxy.setHours.apply( this.proxy, a.slice(3) );
9387                }
9388                break;
9389        }
9390    };
9391    
9392    /**
9393     * @namespace Configuration options that will be used as defaults for all instances on the page.
9394     * @property {String} defaultLocale The default locale to use [en].
9395     * @property {String} syntax The default syntax to use [perl].
9396     * @property {Number} defaultCentury The default centry for 2 digit dates.
9397     */
9398    jsDate.config = {
9399        defaultLocale: 'en',
9400        syntax: 'perl',
9401        defaultCentury: 1900
9402    };
9403        
9404    /**
9405     * Add an arbitrary amount to the currently stored date
9406     * 
9407     * @param {Number} number      
9408     * @param {String} unit
9409     * @returns {jsDate}       
9410     */
9411     
9412    jsDate.prototype.add = function(number, unit) {
9413        var factor = multipliers[unit] || multipliers.day;
9414        if (typeof factor == 'number') {
9415            this.proxy.setTime(this.proxy.getTime() + (factor * number));
9416        } else {
9417            factor.add(this, number);
9418        }
9419        return this;
9420    };
9421        
9422    /**
9423     * Create a new jqplot.date object with the same date
9424     * 
9425     * @returns {jsDate}
9426     */  
9427     
9428    jsDate.prototype.clone = function() {
9429            return new jsDate(this.proxy.getTime());
9430    };
9431
9432    /**
9433     * Get the UTC TimeZone Offset of this date in milliseconds.
9434     *
9435     * @returns {Number}
9436     */
9437
9438    jsDate.prototype.getUtcOffset = function() {
9439        return this.proxy.getTimezoneOffset() * 60000;
9440    };
9441
9442    /**
9443     * Find the difference between this jsDate and another date.
9444     * 
9445     * @param {String| Number| Array| jsDate&nbsp;Object| Date&nbsp;Object} dateObj
9446     * @param {String} unit
9447     * @param {Boolean} allowDecimal
9448     * @returns {Number} Number of units difference between dates.
9449     */
9450     
9451    jsDate.prototype.diff = function(dateObj, unit, allowDecimal) {
9452        // ensure we have a Date object
9453        dateObj = new jsDate(dateObj);
9454        if (dateObj === null) {
9455            return null;
9456        }
9457        // get the multiplying factor integer or factor function
9458        var factor = multipliers[unit] || multipliers.day;
9459        if (typeof factor == 'number') {
9460            // multiply
9461            var unitDiff = (this.proxy.getTime() - dateObj.proxy.getTime()) / factor;
9462        } else {
9463            // run function
9464            var unitDiff = factor.diff(this.proxy, dateObj.proxy);
9465        }
9466        // if decimals are not allowed, round toward zero
9467        return (allowDecimal ? unitDiff : Math[unitDiff > 0 ? 'floor' : 'ceil'](unitDiff));          
9468    };
9469    
9470    /**
9471     * Get the abbreviated name of the current week day
9472     * 
9473     * @returns {String}
9474     */   
9475     
9476    jsDate.prototype.getAbbrDayName = function() {
9477        return jsDate.regional[this.locale]["dayNamesShort"][this.proxy.getDay()];
9478    };
9479    
9480    /**
9481     * Get the abbreviated name of the current month
9482     * 
9483     * @returns {String}
9484     */
9485     
9486    jsDate.prototype.getAbbrMonthName = function() {
9487        return jsDate.regional[this.locale]["monthNamesShort"][this.proxy.getMonth()];
9488    };
9489    
9490    /**
9491     * Get UPPER CASE AM or PM for the current time
9492     * 
9493     * @returns {String}
9494     */
9495     
9496    jsDate.prototype.getAMPM = function() {
9497        return this.proxy.getHours() >= 12 ? 'PM' : 'AM';
9498    };
9499    
9500    /**
9501     * Get lower case am or pm for the current time
9502     * 
9503     * @returns {String}
9504     */
9505     
9506    jsDate.prototype.getAmPm = function() {
9507        return this.proxy.getHours() >= 12 ? 'pm' : 'am';
9508    };
9509    
9510    /**
9511     * Get the century (19 for 20th Century)
9512     *
9513     * @returns {Integer} Century (19 for 20th century).
9514     */
9515    jsDate.prototype.getCentury = function() { 
9516        return parseInt(this.proxy.getFullYear()/100, 10);
9517    };
9518    
9519    /**
9520     * Implements Date functionality
9521     */
9522    jsDate.prototype.getDate = function() {
9523        return this.proxy.getDate();
9524    };
9525    
9526    /**
9527     * Implements Date functionality
9528     */
9529    jsDate.prototype.getDay = function() {
9530        return this.proxy.getDay();
9531    };
9532    
9533    /**
9534     * Get the Day of week 1 (Monday) thru 7 (Sunday)
9535     * 
9536     * @returns {Integer} Day of week 1 (Monday) thru 7 (Sunday)
9537     */
9538    jsDate.prototype.getDayOfWeek = function() { 
9539        var dow = this.proxy.getDay(); 
9540        return dow===0?7:dow; 
9541    };
9542    
9543    /**
9544     * Get the day of the year
9545     * 
9546     * @returns {Integer} 1 - 366, day of the year
9547     */
9548    jsDate.prototype.getDayOfYear = function() {
9549        var d = this.proxy;
9550        var ms = d - new Date('' + d.getFullYear() + '/1/1 GMT');
9551        ms += d.getTimezoneOffset()*60000;
9552        d = null;
9553        return parseInt(ms/60000/60/24, 10)+1;
9554    };
9555    
9556    /**
9557     * Get the name of the current week day
9558     * 
9559     * @returns {String}
9560     */  
9561     
9562    jsDate.prototype.getDayName = function() {
9563        return jsDate.regional[this.locale]["dayNames"][this.proxy.getDay()];
9564    };
9565    
9566    /**
9567     * Get the week number of the given year, starting with the first Sunday as the first week
9568     * @returns {Integer} Week number (13 for the 13th full week of the year).
9569     */
9570    jsDate.prototype.getFullWeekOfYear = function() {
9571        var d = this.proxy;
9572        var doy = this.getDayOfYear();
9573        var rdow = 6-d.getDay();
9574        var woy = parseInt((doy+rdow)/7, 10);
9575        return woy;
9576    };
9577    
9578    /**
9579     * Implements Date functionality
9580     */
9581    jsDate.prototype.getFullYear = function() {
9582        return this.proxy.getFullYear();
9583    };
9584    
9585    /**
9586     * Get the GMT offset in hours and minutes (e.g. +06:30)
9587     * 
9588     * @returns {String}
9589     */
9590     
9591    jsDate.prototype.getGmtOffset = function() {
9592        // divide the minutes offset by 60
9593        var hours = this.proxy.getTimezoneOffset() / 60;
9594        // decide if we are ahead of or behind GMT
9595        var prefix = hours < 0 ? '+' : '-';
9596        // remove the negative sign if any
9597        hours = Math.abs(hours);
9598        // add the +/- to the padded number of hours to : to the padded minutes
9599        return prefix + addZeros(Math.floor(hours), 2) + ':' + addZeros((hours % 1) * 60, 2);
9600    };
9601    
9602    /**
9603     * Implements Date functionality
9604     */
9605    jsDate.prototype.getHours = function() {
9606        return this.proxy.getHours();
9607    };
9608    
9609    /**
9610     * Get the current hour on a 12-hour scheme
9611     * 
9612     * @returns {Integer}
9613     */
9614     
9615    jsDate.prototype.getHours12  = function() {
9616        var hours = this.proxy.getHours();
9617        return hours > 12 ? hours - 12 : (hours == 0 ? 12 : hours);
9618    };
9619    
9620    
9621    jsDate.prototype.getIsoWeek = function() {
9622        var d = this.proxy;
9623        var woy = this.getWeekOfYear();
9624        var dow1_1 = (new Date('' + d.getFullYear() + '/1/1')).getDay();
9625        // First week is 01 and not 00 as in the case of %U and %W,
9626        // so we add 1 to the final result except if day 1 of the year
9627        // is a Monday (then %W returns 01).
9628        // We also need to subtract 1 if the day 1 of the year is 
9629        // Friday-Sunday, so the resulting equation becomes:
9630        var idow = woy + (dow1_1 > 4 || dow1_1 <= 1 ? 0 : 1);
9631        if(idow == 53 && (new Date('' + d.getFullYear() + '/12/31')).getDay() < 4)
9632        {
9633            idow = 1;
9634        }
9635        else if(idow === 0)
9636        {
9637            d = new jsDate(new Date('' + (d.getFullYear()-1) + '/12/31'));
9638            idow = d.getIsoWeek();
9639        }
9640        d = null;
9641        return idow;
9642    };
9643    
9644    /**
9645     * Implements Date functionality
9646     */
9647    jsDate.prototype.getMilliseconds = function() {
9648        return this.proxy.getMilliseconds();
9649    };
9650    
9651    /**
9652     * Implements Date functionality
9653     */
9654    jsDate.prototype.getMinutes = function() {
9655        return this.proxy.getMinutes();
9656    };
9657    
9658    /**
9659     * Implements Date functionality
9660     */
9661    jsDate.prototype.getMonth = function() {
9662        return this.proxy.getMonth();
9663    };
9664    
9665    /**
9666     * Get the name of the current month
9667     * 
9668     * @returns {String}
9669     */
9670     
9671    jsDate.prototype.getMonthName = function() {
9672        return jsDate.regional[this.locale]["monthNames"][this.proxy.getMonth()];
9673    };
9674    
9675    /**
9676     * Get the number of the current month, 1-12
9677     * 
9678     * @returns {Integer}
9679     */
9680     
9681    jsDate.prototype.getMonthNumber = function() {
9682        return this.proxy.getMonth() + 1;
9683    };
9684    
9685    /**
9686     * Implements Date functionality
9687     */
9688    jsDate.prototype.getSeconds = function() {
9689        return this.proxy.getSeconds();
9690    };
9691    
9692    /**
9693     * Return a proper two-digit year integer
9694     * 
9695     * @returns {Integer}
9696     */
9697     
9698    jsDate.prototype.getShortYear = function() {
9699        return this.proxy.getYear() % 100;
9700    };
9701    
9702    /**
9703     * Implements Date functionality
9704     */
9705    jsDate.prototype.getTime = function() {
9706        return this.proxy.getTime();
9707    };
9708    
9709    /**
9710     * Get the timezone abbreviation
9711     *
9712     * @returns {String} Abbreviation for the timezone
9713     */
9714    jsDate.prototype.getTimezoneAbbr = function() {
9715        return this.proxy.toString().replace(/^.*\(([^)]+)\)$/, '$1'); 
9716    };
9717    
9718    /**
9719     * Get the browser-reported name for the current timezone (e.g. MDT, Mountain Daylight Time)
9720     * 
9721     * @returns {String}
9722     */
9723    jsDate.prototype.getTimezoneName = function() {
9724        var match = /(?:\((.+)\)$| ([A-Z]{3}) )/.exec(this.toString());
9725        return match[1] || match[2] || 'GMT' + this.getGmtOffset();
9726    }; 
9727    
9728    /**
9729     * Implements Date functionality
9730     */
9731    jsDate.prototype.getTimezoneOffset = function() {
9732        return this.proxy.getTimezoneOffset();
9733    };
9734    
9735    
9736    /**
9737     * Get the week number of the given year, starting with the first Monday as the first week
9738     * @returns {Integer} Week number (13 for the 13th week of the year).
9739     */
9740    jsDate.prototype.getWeekOfYear = function() {
9741        var doy = this.getDayOfYear();
9742        var rdow = 7 - this.getDayOfWeek();
9743        var woy = parseInt((doy+rdow)/7, 10);
9744        return woy;
9745    };
9746    
9747    /**
9748     * Get the current date as a Unix timestamp
9749     * 
9750     * @returns {Integer}
9751     */
9752     
9753    jsDate.prototype.getUnix = function() {
9754        return Math.round(this.proxy.getTime() / 1000, 0);
9755    }; 
9756    
9757    /**
9758     * Implements Date functionality
9759     */
9760    jsDate.prototype.getYear = function() {
9761        return this.proxy.getYear();
9762    };
9763    
9764    /**
9765     * Return a date one day ahead (or any other unit)
9766     * 
9767     * @param {String} unit Optional, year | month | day | week | hour | minute | second | millisecond
9768     * @returns {jsDate}
9769     */
9770     
9771    jsDate.prototype.next = function(unit) {
9772        unit = unit || 'day';
9773        return this.clone().add(1, unit);
9774    };
9775    
9776    /**
9777     * Set the jsDate instance to a new date.
9778     *
9779     * @param  {String | Number | Array | Date Object | jsDate Object | Options Object} arguments Optional arguments, 
9780     * either a parsable date/time string,
9781     * a JavaScript timestamp, an array of numbers of form [year, month, day, hours, minutes, seconds, milliseconds],
9782     * a Date object, jsDate Object or an options object of form {syntax: "perl", date:some Date} where all options are optional.
9783     */
9784    jsDate.prototype.set = function() {
9785        switch ( arguments.length ) {
9786            case 0:
9787                this.proxy = new Date();
9788                break;
9789            case 1:
9790                // other objects either won't have a _type property or,
9791                // if they do, it shouldn't be set to "jsDate", so
9792                // assume it is an options argument.
9793                if (get_type(arguments[0]) == "[object Object]" && arguments[0]._type != "jsDate") {
9794                    var opts = this.options = arguments[0];
9795                    this.syntax = opts.syntax || this.syntax;
9796                    this.defaultCentury = opts.defaultCentury || this.defaultCentury;
9797                    this.proxy = jsDate.createDate(opts.date);
9798                }
9799                else {
9800                    this.proxy = jsDate.createDate(arguments[0]);
9801                }
9802                break;
9803            default:
9804                var a = [];
9805                for ( var i=0; i<arguments.length; i++ ) {
9806                    a.push(arguments[i]);
9807                }
9808                // this should be the current date/time
9809                this.proxy = new Date();
9810                this.proxy.setFullYear.apply( this.proxy, a.slice(0,3) );
9811                if ( a.slice(3).length ) {
9812                    this.proxy.setHours.apply( this.proxy, a.slice(3) );
9813                }
9814                break;
9815        }
9816        return this;
9817    };
9818    
9819    /**
9820     * Sets the day of the month for a specified date according to local time.
9821     * @param {Integer} dayValue An integer from 1 to 31, representing the day of the month. 
9822     */
9823    jsDate.prototype.setDate = function(n) {
9824        this.proxy.setDate(n);
9825        return this;
9826    };
9827    
9828    /**
9829     * Sets the full year for a specified date according to local time.
9830     * @param {Integer} yearValue The numeric value of the year, for example, 1995.  
9831     * @param {Integer} monthValue Optional, between 0 and 11 representing the months January through December.  
9832     * @param {Integer} dayValue Optional, between 1 and 31 representing the day of the month. If you specify the dayValue parameter, you must also specify the monthValue. 
9833     */
9834    jsDate.prototype.setFullYear = function() {
9835        this.proxy.setFullYear.apply(this.proxy, arguments);
9836        return this;
9837    };
9838    
9839    /**
9840     * Sets the hours for a specified date according to local time.
9841     * 
9842     * @param {Integer} hoursValue An integer between 0 and 23, representing the hour.  
9843     * @param {Integer} minutesValue Optional, An integer between 0 and 59, representing the minutes.  
9844     * @param {Integer} secondsValue Optional, An integer between 0 and 59, representing the seconds. 
9845     * If you specify the secondsValue parameter, you must also specify the minutesValue.  
9846     * @param {Integer} msValue Optional, A number between 0 and 999, representing the milliseconds. 
9847     * If you specify the msValue parameter, you must also specify the minutesValue and secondsValue. 
9848     */
9849    jsDate.prototype.setHours = function() {
9850        this.proxy.setHours.apply(this.proxy, arguments);
9851        return this;
9852    };
9853    
9854    /**
9855     * Implements Date functionality
9856     */ 
9857    jsDate.prototype.setMilliseconds = function(n) {
9858        this.proxy.setMilliseconds(n);
9859        return this;
9860    };
9861    
9862    /**
9863     * Implements Date functionality
9864     */ 
9865    jsDate.prototype.setMinutes = function() {
9866        this.proxy.setMinutes.apply(this.proxy, arguments);
9867        return this;
9868    };
9869    
9870    /**
9871     * Implements Date functionality
9872     */ 
9873    jsDate.prototype.setMonth = function() {
9874        this.proxy.setMonth.apply(this.proxy, arguments);
9875        return this;
9876    };
9877    
9878    /**
9879     * Implements Date functionality
9880     */ 
9881    jsDate.prototype.setSeconds = function() {
9882        this.proxy.setSeconds.apply(this.proxy, arguments);
9883        return this;
9884    };
9885    
9886    /**
9887     * Implements Date functionality
9888     */ 
9889    jsDate.prototype.setTime = function(n) {
9890        this.proxy.setTime(n);
9891        return this;
9892    };
9893    
9894    /**
9895     * Implements Date functionality
9896     */ 
9897    jsDate.prototype.setYear = function() {
9898        this.proxy.setYear.apply(this.proxy, arguments);
9899        return this;
9900    };
9901    
9902    /**
9903     * Provide a formatted string representation of this date.
9904     * 
9905     * @param {String}
9905 formatString A format string.  
9906     * See: {@link jsDate.formats}.
9907     * @returns {String} Date String.
9908     */
9909            
9910    jsDate.prototype.strftime = function(formatString) {
9911        formatString = formatString || this.formatString || jsDate.regional[this.locale]['formatString'];
9912        return jsDate.strftime(this, formatString, this.syntax);
9913    };
9914        
9915    /**
9916     * Return a String representation of this jsDate object.
9917     * @returns {String} Date string.
9918     */
9919    
9920    jsDate.prototype.toString = function() {
9921        return this.proxy.toString();
9922    };
9923        
9924    /**
9925     * Convert the current date to an 8-digit integer (%Y%m%d)
9926     * 
9927     * @returns {Integer}
9928     */
9929     
9930    jsDate.prototype.toYmdInt = function() {
9931        return (this.proxy.getFullYear() * 10000) + (this.getMonthNumber() * 100) + this.proxy.getDate();
9932    };
9933    
9934    /**
9935     * @namespace Holds localizations for month/day names.
9936     * <p>jsDate attempts to detect locale when loaded and defaults to 'en'.
9937     * If a localization is detected which is not available, jsDate defaults to 'en'.
9938     * Additional localizations can be added after jsDate loads.  After adding a localization,
9939     * call the jsDate.regional.getLocale() method.  Currently, en, fr and de are defined.</p>
9940     * 
9941     * <p>Localizations must be an object and have the following properties defined:  monthNames, monthNamesShort, dayNames, dayNamesShort and Localizations are added like:</p>
9942     * <pre class="code">
9943     * jsDate.regional['en'] = {
9944     * monthNames      : 'January February March April May June July August September October November December'.split(' '),
9945     * monthNamesShort : 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' '),
9946     * dayNames        : 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' '),
9947     * dayNamesShort   : 'Sun Mon Tue Wed Thu Fri Sat'.split(' ')
9948     * };
9949     * </pre>
9950     * <p>After adding localizations, call <code>jsDate.regional.getLocale();</code> to update the locale setting with the
9951     * new localizations.</p>
9952     */
9953     
9954    jsDate.regional = {
9955        'en': {
9956            monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'],
9957            monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun','Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
9958            dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
9959            dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
9960            formatString: '%Y-%m-%d %H:%M:%S'
9961        },
9962        
9963        'fr': {
9964            monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre'],
9965            monthNamesShort: ['Jan','Fév','Mar','Avr','Mai','Jun','Jul','Aoû','Sep','Oct','Nov','Déc'],
9966            dayNames: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'],
9967            dayNamesShort: ['Dim','Lun','Mar','Mer','Jeu','Ven','Sam'],
9968            formatString: '%Y-%m-%d %H:%M:%S'
9969        },
9970        
9971        'de': {
9972            monthNames: ['Januar','Februar','März','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember'],
9973            monthNamesShort: ['Jan','Feb','Mär','Apr','Mai','Jun','Jul','Aug','Sep','Okt','Nov','Dez'],
9974            dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'],
9975            dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'],
9976            formatString: '%Y-%m-%d %H:%M:%S'
9977        },
9978        
9979        'es': {
9980            monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio', 'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'],
9981            monthNamesShort: ['Ene','Feb','Mar','Abr','May','Jun', 'Jul','Ago','Sep','Oct','Nov','Dic'],
9982            dayNames: ['Domingo','Lunes','Martes','Mi&eacute;rcoles','Jueves','Viernes','S&aacute;bado'],
9983            dayNamesShort: ['Dom','Lun','Mar','Mi&eacute;','Juv','Vie','S&aacute;b'],
9984            formatString: '%Y-%m-%d %H:%M:%S'
9985        },
9986        
9987        'ru': {
9988            monthNames: ['Январь','Февраль','Март','Апрель','Май','Июнь','Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'],
9989            monthNamesShort: ['Янв','Фев','Мар','Апр','Май','Июн','Июл','Авг','Сен','Окт','Ноя','Дек'],
9990            dayNames: ['воскресенье','понедельник','вторник','среда','четверг','пятница','суббота'],
9991            dayNamesShort: ['вск','пнд','втр','срд','чтв','птн','сбт'],
9992            formatString: '%Y-%m-%d %H:%M:%S'
9993        },
9994        
9995        'ar': {
9996            monthNames: ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'آذار', 'حزيران','تموز', 'آب', 'أيلول',   'تشرين الأول', 'تشرين الثاني', 'كانون الأول'],
9997            monthNamesShort: ['1','2','3','4','5','6','7','8','9','10','11','12'],
9998            dayNames: ['السبت', 'الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة'],
9999            dayNamesShort: ['سبت', 'أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة'],
10000            formatString: '%Y-%m-%d %H:%M:%S'
10001        },
10002        
10003        'pt': {
10004            monthNames: ['Janeiro','Fevereiro','Mar&ccedil;o','Abril','Maio','Junho','Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'],
10005            monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'],
10006            dayNames: ['Domingo','Segunda-feira','Ter&ccedil;a-feira','Quarta-feira','Quinta-feira','Sexta-feira','S&aacute;bado'],
10007            dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','S&aacute;b'],
10008            formatString: '%Y-%m-%d %H:%M:%S'   
10009        },
10010        
10011        'pt-BR': {
10012            monthNames: ['Janeiro','Fevereiro','Mar&ccedil;o','Abril','Maio','Junho', 'Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'],
10013            monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'],
10014            dayNames: ['Domingo','Segunda-feira','Ter&ccedil;a-feira','Quarta-feira','Quinta-feira','Sexta-feira','S&aacute;bado'],
10015            dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','S&aacute;b'],
10016            formatString: '%Y-%m-%d %H:%M:%S'
10017        },
10018        
10019        'pl': {
10020            monthNames: ['Styczeń','Luty','Marzec','Kwiecień','Maj','Czerwiec','Lipiec','Sierpień','Wrzesień','Październik','Listopad','Grudzień'],
10021            monthNamesShort: ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze','Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru'],
10022            dayNames: ['Niedziela', 'Poniedziałek', 'Wtorek', 'Środa', 'Czwartek', 'Piątek', 'Sobota'],
10023            dayNamesShort: ['Ni', 'Pn', 'Wt', 'Śr', 'Cz', 'Pt', 'Sb'],
10024            formatString: '%Y-%m-%d %H:%M:%S'
10025        },
10026
10027        'nl': {
10028            monthNames: ['Januari','Februari','Maart','April','Mei','Juni','July','Augustus','September','Oktober','November','December'],
10029            monthNamesShort: ['Jan','Feb','Mar','Apr','Mei','Jun','Jul','Aug','Sep','Okt','Nov','Dec'],
10030            dayNames:','['Zondag','Maandag','Dinsdag','Woensdag','Donderdag','Vrijdag','Zaterdag'],
10031            dayNamesShort: ['Zo','Ma','Di','Wo','Do','Vr','Za'],
10032            formatString: '%Y-%m-%d %H:%M:%S'
10033        },
10034
10035        'sv': {
10036            monthNames: ['januari','februari','mars','april','maj','juni','juli','augusti','september','oktober','november','december'],
10037          monthNamesShort: ['jan','feb','mar','apr','maj','jun','jul','aug','sep','okt','nov','dec'],
10038            dayNames: ['söndag','måndag','tisdag','onsdag','torsdag','fredag','lördag'],
10039            dayNamesShort: ['sön','mån','tis','ons','tor','fre','lör'],
10040            formatString: '%Y-%m-%d %H:%M:%S'
10041        }
10042    
10043    };
10044    
10045    // Set english variants to 'en'
10046    jsDate.regional['en-US'] = jsDate.regional['en-GB'] = jsDate.regional['en'];
10047    
10048    /**
10049     * Try to determine the users locale based on the lang attribute of the html page.  Defaults to 'en'
10050     * if it cannot figure out a locale of if the locale does not have a localization defined.
10051     * @returns {String} locale
10052     */
10053     
10054    jsDate.regional.getLocale = function () {
10055        var l = jsDate.config.defaultLocale;
10056        
10057        if ( document && document.getElementsByTagName('html') && document.getElementsByTagName('html')[0].lang ) {
10058            l = document.getElementsByTagName('html')[0].lang;
10059            if (!jsDate.regional.hasOwnProperty(l)) {
10060                l = jsDate.config.defaultLocale;
10061            }
10062        }
10063        
10064        return l;
10065    };
10066    
10067    // ms in day
10068    var day = 24 * 60 * 60 * 1000;
10069    
10070    // padd a number with zeros
10071    var addZeros = function(num, digits) {
10072        num = String(num);
10073        var i = digits - num.length;
10074        var s = String(Math.pow(10, i)).slice(1);
10075        return s.concat(num);
10076    };
10077
10078    // representations used for calculating differences between dates.
10079    // This borrows heavily from Ken Snyder's work.
10080    var multipliers = {
10081        millisecond: 1,
10082        second: 1000,
10083        minute: 60 * 1000,
10084        hour: 60 * 60 * 1000,
10085        day: day,
10086        week: 7 * day,
10087        month: {
10088            // add a number of months
10089            add: function(d, number) {
10090                // add any years needed (increments of 12)
10091                multipliers.year.add(d, Math[number > 0 ? 'floor' : 'ceil'](number / 12));
10092                // ensure that we properly wrap betwen December and January
10093                // 11 % 12 = 11
10094                // 12 % 12 = 0
10095                var prevMonth = d.getMonth() + (number % 12);
10096                if (prevMonth == 12) {
10097                    prevMonth = 0;
10098                    d.setYear(d.getFullYear() + 1);
10099                } else if (prevMonth == -1) {
10100                    prevMonth = 11;
10101                    d.setYear(d.getFullYear() - 1);
10102                }
10103                d.setMonth(prevMonth);
10104            },
10105            // get the number of months between two Date objects (decimal to the nearest day)
10106            diff: function(d1, d2) {
10107                // get the number of years
10108                var diffYears = d1.getFullYear() - d2.getFullYear();
10109                // get the number of remaining months
10110                var diffMonths = d1.getMonth() - d2.getMonth() + (diffYears * 12);
10111                // get the number of remaining days
10112                var diffDays = d1.getDate() - d2.getDate();
10113                // return the month difference with the days difference as a decimal
10114                return diffMonths + (diffDays / 30);
10115            }
10116        },
10117        year: {
10118            // add a number of years
10119            add: function(d, number) {
10120                d.setYear(d.getFullYear() + Math[number > 0 ? 'floor' : 'ceil'](number));
10121            },
10122            // get the number of years between two Date objects (decimal to the nearest day)
10123            diff: function(d1, d2) {
10124                return multipliers.month.diff(d1, d2) / 12;
10125            }
10126        }        
10127    };
10128    //
10129    // Alias each multiplier with an 's' to allow 'year' and 'years' for example.
10130    // This comes from Ken Snyders work.
10131    //
10132    for (var unit in multipliers) {
10133        if (unit.substring(unit.length - 1) != 's') { // IE will iterate newly added properties :|
10134            multipliers[unit + 's'] = multipliers[unit];
10135        }
10136    }
10137    
10138    //
10139    // take a jsDate instance and a format code and return the formatted value.
10140    // This is a somewhat modified version of Ken Snyder's method.
10141    //
10142    var format = function(d, code, syntax) {
10143        // if shorcut codes are used, recursively expand those.
10144        if (jsDate.formats[syntax]["shortcuts"][code]) {
10145            return jsDate.strftime(d, jsDate.formats[syntax]["shortcuts"][code], syntax);
10146        } else {
10147            // get the format code function and addZeros() argument
10148            var getter = (jsDate.formats[syntax]["codes"][code] || '').split('.');
10149            var nbr = d['get' + getter[0]] ? d['get' + getter[0]]() : '';
10150            if (getter[1]) {
10151                nbr = addZeros(nbr, getter[1]);
10152            }
10153            return nbr;
10154        }       
10155    };
10156    
10157    /**
10158     * @static
10159     * Static function for convert a date to a string according to a given format.  Also acts as namespace for strftime format codes.
10160     * <p>strftime formatting can be accomplished without creating a jsDate object by calling jsDate.strftime():</p>
10161     * <pre class="code">
10162     * var formattedDate = jsDate.strftime('Feb 8, 2006 8:48:32', '%Y-%m-%d %H:%M:%S');
10163     * </pre>
10164     * @param {String | Number | Array | jsDate&nbsp;Object | Date&nbsp;Object} date A parsable date string, JavaScript time stamp, Array of form [year, month, day, hours, minutes, seconds, milliseconds], jsDate Object or Date object.
10165     * @param {String} formatString String with embedded date formatting codes.  
10166     * See: {@link jsDate.formats}. 
10167     * @param {String} syntax Optional syntax to use [default perl].
10168     * @param {String} locale Optional locale to use.
10169     * @returns {String} Formatted representation of the date.
10170    */
10171    //
10172    // Logic as implemented here is very similar to Ken Snyder's Date Instance Methods.
10173    //
10174    jsDate.strftime = function(d, formatString, syntax, locale) {
10175        var syn = 'perl';
10176        var loc = jsDate.regional.getLocale();
10177        
10178        // check if syntax and locale are available or reversed
10179        if (syntax && jsDate.formats.hasOwnProperty(syntax)) {
10180            syn = syntax;
10181        }
10182        else if (syntax && jsDate.regional.hasOwnProperty(syntax)) {
10183            loc = syntax;
10184        }
10185        
10186        if (locale && jsDate.formats.hasOwnProperty(locale)) {
10187            syn = locale;
10188        }
10189        else if (locale && jsDate.regional.hasOwnProperty(locale)) {
10190            loc = locale;
10191        }
10192        
10193        if (get_type(d) != "[object Object]" || d._type != "jsDate") {
10194            d = new jsDate(d);
10195            d.locale = loc;
10196        }
10197        if (!formatString) {
10198            formatString = d.formatString || jsDate.regional[loc]['formatString'];
10199        }
10200        // default the format string to year-month-day
10201        var source = formatString || '%Y-%m-%d', 
10202            result = '', 
10203            match;
10204        // replace each format code
10205        while (source.length > 0) {
10206            if (match = source.match(jsDate.formats[syn].codes.matcher)) {
10207                result += source.slice(0, match.index);
10208                result += (match[1] || '') + format(d, match[2], syn);
10209                source = source.slice(match.index + match[0].length);
10210            } else {
10211                result += source;
10212                source = '';
10213            }
10214        }
10215        return result;
10216    };
10217    
10218    /**
10219     * @namespace
10220     * Namespace to hold format codes and format shortcuts.  "perl" and "php" format codes 
10221     * and shortcuts are defined by default.  Additional codes and shortcuts can be
10222     * added like:
10223     * 
10224     * <pre class="code">
10225     * jsDate.formats["perl"] = {
10226     *     "codes": {
10227     *         matcher: /someregex/,
10228     *         Y: "fullYear",  // name of "get" method without the "get",
10229     *         ...,            // more codes
10230     *     },
10231     *     "shortcuts": {
10232     *         F: '%Y-%m-%d',
10233     *         ...,            // more shortcuts
10234     *     }
10235     * };
10236     * </pre>
10237     * 
10238     * <p>Additionally, ISO and SQL shortcuts are defined and can be accesses via:
10239     * <code>jsDate.formats.ISO</code> and <code>jsDate.formats.SQL</code>
10240     */
10241    
10242    jsDate.formats = {
10243        ISO:'%Y-%m-%dT%H:%M:%S.%N%G',
10244        SQL:'%Y-%m-%d %H:%M:%S'
10245    };
10246    
10247    /**
10248     * Perl format codes and shortcuts for strftime.
10249     * 
10250     * A hash (object) of codes where each code must be an array where the first member is 
10251     * the name of a Date.prototype or jsDate.prototype function to call
10252     * and optionally a second member indicating the number to pass to addZeros()
10253     * 
10254     * <p>The following format codes are defined:</p>
10255     * 
10256     * <pre class="code">
10257     * Code    Result                    Description
10258     * == Years ==           
10259     * %Y      2008                      Four-digit year
10260     * %y      08                        Two-digit year
10261     * 
10262     * == Months ==          
10263     * %m      09                        Two-digit month
10264     * %#m     9                         One or two-digit month
10265     * %B      September                 Full month name
10266     * %b      Sep                       Abbreviated month name
10267     * 
10268     * == Days ==            
10269     * %d      05                        Two-digit day of month
10270     * %#d     5                         One or two-digit day of month
10271     * %e      5                         One or two-digit day of month
10272     * %A      Sunday                    Full name of the day of the week
10273     * %a      Sun                       Abbreviated name of the day of the week
10274     * %w      0                         Number of the day of the week (0 = Sunday, 6 = Saturday)
10275     * 
10276     * == Hours ==           
10277     * %H      23                        Hours in 24-hour format (two digits)
10278     * %#H     3                         Hours in 24-hour integer format (one or two digits)
10279     * %I      11                        Hours in 12-hour format (two digits)
10280     * %#I     3                         Hours in 12-hour integer format (one or two digits)
10281     * %p      PM                        AM or PM
10282     * 
10283     * == Minutes ==         
10284     * %M      09                        Minutes (two digits)
10285     * %#M     9                         Minutes (one or two digits)
10286     * 
10287     * == Seconds ==         
10288     * %S      02                        Seconds (two digits)
10289     * %#S     2                         Seconds (one or two digits)
10290     * %s      1206567625723             Unix timestamp (Seconds past 1970-01-01 00:00:00)
10291     * 
10292     * == Milliseconds ==    
10293     * %N      008                       Milliseconds (three digits)
10294     * %#N     8                         Milliseconds (one to three digits)
10295     * 
10296     * == Timezone ==        
10297     * %O      360                       difference in minutes between local time and GMT
10298     * %Z      Mountain Standard Time    Name of timezone as reported by browser
10299     * %G      06:00                     Hours and minutes between GMT
10300     * 
10301     * == Shortcuts ==       
10302     * %F      2008-03-26                %Y-%m-%d
10303     * %T      05:06:30                  %H:%M:%S
10304     * %X      05:06:30                  %H:%M:%S
10305     * %x      03/26/08                  %m/%d/%y
10306     * %D      03/26/08                  %m/%d/%y
10307     * %#c     Wed Mar 26 15:31:00 2008  %a %b %e %H:%M:%S %Y
10308     * %v      3-Sep-2008                %e-%b-%Y
10309     * %R      15:31                     %H:%M
10310     * %r      03:31:00 PM               %I:%M:%S %p
10311     * 
10312     * == Characters ==      
10313     * %n      \n                        Newline
10314     * %t      \t                        Tab
10315     * %%      %                         Percent Symbol
10316     * </pre>
10317     * 
10318     * <p>Formatting shortcuts that will be translated into their longer version.
10319     * Be sure that format shortcuts do not refer to themselves: this will cause an infinite loop.</p>
10320     * 
10321     * <p>Format codes and format shortcuts can be redefined after the jsDate
10322     * module is imported.</p>
10323     * 
10324     * <p>Note that if you redefine the whole hash (object), you must supply a "matcher"
10325     * regex for the parser.  The default matcher is:</p>
10326     * 
10327     * <code>/()%(#?(%|[a-z]))/i</code>
10328     * 
10329     * <p>which corresponds to the Perl syntax used by default.</p>
10330     * 
10331     * <p>By customizing the matcher and format codes, nearly any strftime functionality is possible.</p>
10332     */
10333     
10334    jsDate.formats.perl = {
10335        codes: {
10336            //
10337            // 2-part regex matcher for format codes
10338            //
10339            // first match must be the character before the code (to account for escaping)
10340            // second match must be the format code character(s)
10341            //
10342            matcher: /()%(#?(%|[a-z]))/i,
10343            // year
10344            Y: 'FullYear',
10345            y: 'ShortYear.2',
10346            // month
10347            m: 'MonthNumber.2',
10348            '#m': 'MonthNumber',
10349            B: 'MonthName',
10350            b: 'AbbrMonthName',
10351            // day
10352            d: 'Date.2',
10353            '#d': 'Date',
10354            e: 'Date',
10355            A: 'DayName',
10356            a: 'AbbrDayName',
10357            w: 'Day',
10358            // hours
10359            H: 'Hours.2',
10360            '#H': 'Hours',
10361            I: 'Hours12.2',
10362            '#I': 'Hours12',
10363            p: 'AMPM',
10364            // minutes
10365            M: 'Minutes.2',
10366            '#M': 'Minutes',
10367            // seconds
10368            S: 'Seconds.2',
10369            '#S': 'Seconds',
10370            s: 'Unix',
10371            // milliseconds
10372            N: 'Milliseconds.3',
10373            '#N': 'Milliseconds',
10374            // timezone
10375            O: 'TimezoneOffset',
10376            Z: 'TimezoneName',
10377            G: 'GmtOffset'  
10378        },
10379        
10380        shortcuts: {
10381            // date
10382            F: '%Y-%m-%d',
10383            // time
10384            T: '%H:%M:%S',
10385            X: '%H:%M:%S',
10386            // local format date
10387            x: '%m/%d/%y',
10388            D: '%m/%d/%y',
10389            // local format extended
10390            '#c': '%a %b %e %H:%M:%S %Y',
10391            // local format short
10392            v: '%e-%b-%Y',
10393            R: '%H:%M',
10394            r: '%I:%M:%S %p',
10395            // tab and newline
10396            t: '\t',
10397            n: '\n',
10398            '%': '%'
10399        }
10400    };
10401    
10402    /**
10403     * PHP format codes and shortcuts for strftime.
10404     * 
10405     * A hash (object) of codes where each code must be an array where the first member is 
10406     * the name of a Date.prototype or jsDate.prototype function to call
10407     * and optionally a second member indicating the number to pass to addZeros()
10408     * 
10409     * <p>The following format codes are defined:</p>
10410     * 
10411     * <pre class="code">
10412     * Code    Result                    Description
10413     * === Days ===        
10414     * %a      Sun through Sat           An abbreviated textual representation of the day
10415     * %A      Sunday - Saturday         A full textual representation of the day
10416     * %d      01 to 31                  Two-digit day of the month (with leading zeros)
10417     * %e      1 to 31                   Day of the month, with a space preceding single digits.
10418     * %j      001 to 366                Day of the year, 3 digits with leading zeros
10419     * %u      1 - 7 (Mon - Sun)         ISO-8601 numeric representation of the day of the week
10420     * %w      0 - 6 (Sun - Sat)         Numeric representation of the day of the week
10421     *                                  
10422     * === Week ===                     
10423     * %U      13                        Full Week number, starting with the first Sunday as the first week
10424     * %V      01 through 53             ISO-8601:1988 week number, starting with the first week of the year 
10425     *                                   with at least 4 weekdays, with Monday being the start of the week
10426     * %W      46                        A numeric representation of the week of the year, 
10427     *                                   starting with the first Monday as the first week
10428     * === Month ===                    
10429     * %b      Jan through Dec           Abbreviated month name, based on the locale
10430     * %B      January - December        Full month name, based on the locale
10431     * %h      Jan through Dec           Abbreviated month name, based on the locale (an alias of %b)
10432     * %m      01 - 12 (Jan - Dec)       Two digit representation of the month
10433     * 
10434     * === Year ===                     
10435     * %C      19                        Two digit century (year/100, truncated to an integer)
10436     * %y      09 for 2009               Two digit year
10437     * %Y      2038                      Four digit year
10438     * 
10439     * === Time ===                     
10440     * %H      00 through 23             Two digit representation of the hour in 24-hour format
10441     * %I      01 through 12             Two digit representation of the hour in 12-hour format
10442     * %l      1 through 12              Hour in 12-hour format, with a space preceeding single digits
10443     * %M      00 through 59             Two digit representation of the minute
10444     * %p      AM/PM                     UPPER-CASE 'AM' or 'PM' based on the given time
10445     * %P      am/pm                     lower-case 'am' or 'pm' based on the given time
10446     * %r      09:34:17 PM               Same as %I:%M:%S %p
10447     * %R      00:35                     Same as %H:%M
10448     * %S      00 through 59             Two digit representation of the second
10449     * %T      21:34:17                  Same as %H:%M:%S
10450     * %X      03:59:16                  Preferred time representation based on locale, without the date
10451     * %z      -0500 or EST              Either the time zone offset from UTC or the abbreviation
10452     * %Z      -0500 or EST              The time zone offset/abbreviation option NOT given by %z
10453     * 
10454     * === Time and Date ===            
10455     * %D      02/05/09                  Same as %m/%d/%y
10456     * %F      2009-02-05                Same as %Y-%m-%d (commonly used in database datestamps)
10457     * %s      305815200                 Unix Epoch Time timestamp (same as the time() function)
10458     * %x      02/05/09                  Preferred date representation, without the time
10459     * 
10460     * === Miscellaneous ===            
10461     * %n        ---                     A newline character (\n)
10462     * %t        ---                     A Tab character (\t)
10463     * %%        ---                     A literal percentage character (%)
10464     * </pre>
10465     */
10466 
10467    jsDate.formats.php = {
10468        codes: {
10469            //
10470            // 2-part regex matcher for format codes
10471            //
10472            // first match must be the character before the code (to account for escaping)
10473            // second match must be the format code character(s)
10474            //
10475            matcher: /()%((%|[a-z]))/i,
10476            // day
10477            a: 'AbbrDayName',
10478            A: 'DayName',
10479            d: 'Date.2',
10480            e: 'Date',
10481            j: 'DayOfYear.3',
10482            u: 'DayOfWeek',
10483            w: 'Day',
10484            // week
10485            U: 'FullWeekOfYear.2',
10486            V: 'IsoWeek.2',
10487            W: 'WeekOfYear.2',
10488            // month
10489            b: 'AbbrMonthName',
10490            B: 'MonthName',
10491            m: 'MonthNumber.2',
10492            h: 'AbbrMonthName',
10493            // year
10494            C: 'Century.2',
10495            y: 'ShortYear.2',
10496            Y: 'FullYear',
10497            // time
10498            H: 'Hours.2',
10499            I: 'Hours12.2',
10500            l: 'Hours12',
10501            p: 'AMPM',
10502            P: 'AmPm',
10503            M: 'Minutes.2',
10504            S: 'Seconds.2',
10505            s: 'Unix',
10506            O: 'TimezoneOffset',
10507            z: 'GmtOffset',
10508            Z: 'TimezoneAbbr'
10509        },
10510        
10511        shortcuts: {
10512            D: '%m/%d/%y',
10513            F: '%Y-%m-%d',
10514            T: '%H:%M:%S',
10515            X: '%H:%M:%S',
10516            x: '%m/%d/%y',
10517            R: '%H:%M',
10518            r: '%I:%M:%S %p',
10519            t: '\t',
10520            n: '\n',
10521            '%': '%'
10522        }
10523    };   
10524    //
10525    // Conceptually, the logic implemented here is similar to Ken Snyder's Date Instance Methods.
10526    // I use his idea of a set of parsers which can be regular expressions or functions,
10527    // iterating through those, and then seeing if Date.parse() will create a date.
10528    // The parser expressions and functions are a little different and some bugs have been
10529    // worked out.  Also, a lot of "pre-parsing" is done to fix implementation
10530    // variations of Date.parse() between browsers.
10531    //
10532    jsDate.createDate = function(date) {
10533        // if passing in multiple arguments, try Date constructor
10534        if (date == null) {
10535            return new Date();
10536        }
10537        // If the passed value is already a date object, return it
10538        if (date instanceof Date) {
10539            return date;
10540        }
10541        // if (typeof date == 'number') return new Date(date * 1000);
10542        // If the passed value is an integer, interpret it as a javascript timestamp
10543        if (typeof date == 'number') {
10544            return new Date(date);
10545        }
10546        
10547        // Before passing strings into Date.parse(), have to normalize them for certain conditions.
10548        // If strings are not formatted staccording to the EcmaScript spec, results from Date parse will be implementation dependent.  
10549        // 
10550        // For example: 
10551        //  * FF and Opera assume 2 digit dates are pre y2k, Chome assumes <50 is pre y2k, 50+ is 21st century.  
10552        //  * Chrome will correctly parse '1984-1-25' into localtime, FF and Opera will not parse.
10553        //  * Both FF, Chrome and Opera will parse '1984/1/25' into localtime.
10554        
10555        // remove leading and trailing spaces
10556        var parsable = String(date).replace(/^\s*(.+)\s*$/g, '$1');
10557        
10558        // replace dahses (-) with slashes (/) in dates like n[nnn]/n[n]/n[nnn]
10559        parsable = parsable.replace(/^([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,4})/, "$1/$2/$3");
10560        
10561        /////////
10562        // Need to check for '15-Dec-09' also.
10563        // FF will not parse, but Chrome will.
10564        // Chrome will set date to 2009 as well.
10565        /////////
10566        
10567        // first check for 'dd-mmm-yyyy' or 'dd/mmm/yyyy' like '15-Dec-2010'
10568        parsable = parsable.replace(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{4})/i, "$1 $2 $3");
10569        
10570        // Now check for 'dd-mmm-yy' or 'dd/mmm/yy' and normalize years to default century.
10571        var match = parsable.match(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{2})\D*/i);
10572        if (match && match.length > 3) {
10573            var m3 = parseFloat(match[3]);
10574            var ny = jsDate.config.defaultCentury + m3;
10575            ny = String(ny);
10576            
10577            // now replace 2 digit year with 4 digit year
10578            parsable = parsable.replace(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{2})\D*/i, match[1] +' '+ match[2] +' '+ ny);
10579            
10580        }
10581        
10582        // Check for '1/19/70 8:14PM'
10583        // where starts with mm/dd/yy or yy/mm/dd and have something after
10584        // Check if 1st postiion is greater than 31, assume it is year.
10585        // Assme all 2 digit years are 1900's.
10586        // Finally, change them into US style mm/dd/yyyy representations.
10587        match = parsable.match(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})[^0-9]/);
10588        
10589        function h1(parsable, match) {
10590            var m1 = parseFloat(match[1]);
10591            var m2 = parseFloat(match[2]);
10592            var m3 = parseFloat(match[3]);
10593            var cent = jsDate.config.defaultCentury;
10594            var ny, nd, nm, str;
10595            
10596            if (m1 > 31) { // first number is a year
10597                nd = m3;
10598                nm = m2;
10599                ny = cent + m1;
10600            }
10601            
10602            else { // last number is the year
10603                nd = m2;
10604                nm = m1;
10605                ny = cent + m3;
10606            }
10607            
10608            str = nm+'/'+nd+'/'+ny;
10609            
10610            // now replace 2 digit year with 4 digit year
10611            return  parsable.replace(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})/, str);
10612        
10613        }
10614        
10615        if (match && match.length > 3) {
10616            parsable = h1(parsable, match);
10617        }
10618        
10619        // Now check for '1/19/70' with nothing after and do as above
10620        var match = parsable.match(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})$/);
10621        
10622        if (match && match.length > 3) {
10623            parsable = h1(parsable, match);
10624        }
10625                
10626        
10627        var i = 0;
10628        var length = jsDate.matchers.length;
10629        var pattern,
10630            ms,
10631            current = parsable,
10632            obj;
10633        while (i < length) {
10634            ms = Date.parse(current);
10635            if (!isNaN(ms)) {
10636                return new Date(ms);
10637            }
10638            pattern = jsDate.matchers[i];
10639            if (typeof pattern == 'function') {
10640                obj = pattern.call(jsDate, current);
10641                if (obj instanceof Date) {
10642                    return obj;
10643                }
10644            } else {
10645                current = parsable.replace(pattern[0], pattern[1]);
10646            }
10647            i++;
10648        }
10649        return NaN;
10650    };
10651    
10652
10653    /**
10654     * @static
10655     * Handy static utility function to return the number of days in a given month.
10656     * @param {Integer} year Year
10657     * @param {Integer} month Month (1-12)
10658     * @returns {Integer} Number of days in the month.
10659    */
10660    //
10661    // handy utility method Borrowed right from Ken Snyder's Date Instance Mehtods.
10662    // 
10663    jsDate.daysInMonth = function(year, month) {
10664        if (month == 2) {
10665            return new Date(year, 1, 29).getDate() == 29 ? 29 : 28;
10666        }
10667        return [undefined,31,undefined,31,30,31,30,31,31,30,31,30,31][month];
10668    };
10669
10670
10671    //
10672    // An Array of regular expressions or functions that will attempt to match the date string.
10673    // Functions are called with scope of a jsDate instance.
10674    //
10675    jsDate.matchers = [
10676        // convert dd.mmm.yyyy to mm/dd/yyyy (world date to US date).
10677        [/(3[01]|[0-2]\d)\s*\.\s*(1[0-2]|0\d)\s*\.\s*([1-9]\d{3})/, '$2/$1/$3'],
10678        // convert yyyy-mm-dd to mm/dd/yyyy (ISO date to US date).
10679        [/([1-9]\d{3})\s*-\s*(1[0-2]|0\d)\s*-\s*(3[01]|[0-2]\d)/, '$2/$3/$1'],
10680        // Handle 12 hour or 24 hour time with milliseconds am/pm and optional date part.
10681        function(str) { 
10682            var match = str.match(/^(?:(.+)\s+)?([012]?\d)(?:\s*\:\s*(\d\d))?(?:\s*\:\s*(\d\d(\.\d*)?))?\s*(am|pm)?\s*$/i);
10683            //                   opt. date      hour       opt. minute     opt. second       opt. msec   opt. am or pm
10684            if (match) {
10685                if (match[1]) {
10686                    var d = this.createDate(match[1]);
10687                    if (isNaN(d)) {
10688                        return;
10689                    }
10690                } else {
10691                    var d = new Date();
10692                    d.setMilliseconds(0);
10693                }
10694                var hour = parseFloat(match[2]);
10695                if (match[6]) {
10696                    hour = match[6].toLowerCase() == 'am' ? (hour == 12 ? 0 : hour) : (hour == 12 ? 12 : hour + 12);
10697                }
10698                d.setHours(hour, parseInt(match[3] || 0, 10), parseInt(match[4] || 0, 10), ((parseFloat(match[5] || 0)) || 0)*1000);
10699                return d;
10700            }
10701            else {
10702                return str;
10703            }
10704        },
10705        // Handle ISO timestamp with time zone.
10706        function(str) {
10707            var match = str.match(/^(?:(.+))[T|\s+]([012]\d)(?:\:(\d\d))(?:\:(\d\d))(?:\.\d+)([\+\-]\d\d\:\d\d)$/i);
10708            if (match) {
10709                if (match[1]) {
10710                    var d = this.createDate(match[1]);
10711                    if (isNaN(d)) {
10712                        return;
10713                    }
10714                } else {
10715                    var d = new Date();
10716                    d.setMilliseconds(0);
10717                }
10718                var hour = parseFloat(match[2]);
10719                d.setHours(hour, parseInt(match[3], 10), parseInt(match[4], 10), parseFloat(match[5])*1000);
10720                return d;
10721            }
10722            else {
10723                    return str;
10724            }
10725        },
10726        // Try to match ambiguous strings like 12/8/22.
10727        // Use FF date assumption that 2 digit years are 20th century (i.e. 1900's).
10728        // This may be redundant with pre processing of date already performed.
10729        function(str) {
10730            var match = str.match(/^([0-3]?\d)\s*[-\/.\s]{1}\s*([a-zA-Z]{3,9})\s*[-\/.\s]{1}\s*([0-3]?\d)$/);
10731            if (match) {
10732                var d = new Date();
10733                var cent = jsDate.config.defaultCentury;
10734                var m1 = parseFloat(match[1]);
10735                var m3 = parseFloat(match[3]);
10736                var ny, nd, nm;
10737                if (m1 > 31) { // first number is a year
10738                    nd = m3;
10739                    ny = cent + m1;
10740                }
10741                
10742                else { // last number is the year
10743                    nd = m1;
10744                    ny = cent + m3;
10745                }
10746                
10747                var nm = inArray(match[2], jsDate.regional[jsDate.regional.getLocale()]["monthNamesShort"]);
10748                
10749                if (nm == -1) {
10750                    nm = inArray(match[2], jsDate.regional[jsDate.regional.getLocale()]["monthNames"]);
10751                }
10752            
10753                d.setFullYear(ny, nm, nd);
10754                d.setHours(0,0,0,0);
10755                return d;
10756            }
10757            
10758            else {
10759                return str;
10760            }
10761        }      
10762    ];
10763
10764    //
10765    // I think John Reisig published this method on his blog, ejohn.
10766    //
10767    function inArray( elem, array ) {
10768        if ( array.indexOf ) {
10769            return array.indexOf( elem );
10770        }
10771
10772        for ( var i = 0, length = array.length; i < length; i++ ) {
10773            if ( array[ i ] === elem ) {
10774                return i;
10775            }
10776        }
10777
10778        return -1;
10779    }
10780    
10781    //
10782    // Thanks to Kangax, Christian Sciberras and Stack Overflow for this method.
10783    //
10784    function get_type(thing){
10785        if(thing===null) return "[object Null]"; // special case
10786        return Object.prototype.toString.call(thing);
10787    }
10788    
10789    $.jsDate = jsDate;
10790
10791      
10792    /**
10793     * JavaScript printf/sprintf functions.
10794     * 
10795     * This code has been adapted from the publicly available sprintf methods
10796     * by Ash Searle. His original header follows:
10797     *
10798     *     This code is unrestricted: you are free to use it however you like.
10799     *     
10800     *     The functions should work as expected, performing left or right alignment,
10801     *     truncating strings, outputting numbers with a required precision etc.
10802     *
10803     *     For complex cases, these functions follow the Perl implementations of
10804     *     (s)printf, allowing arguments to be passed out-of-order, and to set the
10805     *     precision or length of the output based on arguments instead of fixed
10806     *     numbers.
10807     *
10808     *     See http://perldoc.perl.org/functions/sprintf.html for more information.
10809     *
10810     *     Implemented:
10811     *     - zero and space-padding
10812     *     - right and left-alignment,
10813     *     - base X prefix (binary, octal and hex)
10814     *     - positive number prefix
10815     *     - (minimum) width
10816     *     - precision / truncation / maximum width
10817     *     - out of order arguments
10818     *
10819     *     Not implemented (yet):
10820     *     - vector flag
10821     *     - size (bytes, words, long-words etc.)
10822     *     
10823     *     Will not implement:
10824     *     - %n or %p (no pass-by-reference in JavaScript)
10825     *
10826     *     @version 2007.04.27
10827     *     @author Ash Searle 
10828     * 
10829     * You can see the original work and comments on his blog:
10830     * http://hexmen.com/blog/2007/03/printf-sprintf/
10831     * http://hexmen.com/js/sprintf.js
10832     */
10833     
10834     /**
10835      * @Modifications 2009.05.26
10836      * @author Chris Leonello
10837      * 
10838      * Added %p %P specifier
10839      * Acts like %g or %G but will not add more significant digits to the output than present in the input.
10840      * Example:
10841      * Format: '%.3p', Input: 0.012, Output: 0.012
10842      * Format: '%.3g', Input: 0.012, Output: 0.0120
10843      * Format: '%.4p', Input: 12.0, Output: 12.0
10844      * Format: '%.4g', Input: 12.0, Output: 12.00
10845      * Format: '%.4p', Input: 4.321e-5, Output: 4.321e-5
10846      * Format: '%.4g', Input: 4.321e-5, Output: 4.3210e-5
10847      * 
10848      * Example:
10849      * >>> $.jqplot.sprintf('%.2f, %d', 23.3452, 43.23)
10850      * "23.35, 43"
10851      * >>> $.jqplot.sprintf("no value: %n, decimal with thousands separator: %'d", 23.3452, 433524)
10852      * "no value: , decimal with thousands separator: 433,524"
10853      */
10854    $.jqplot.sprintf = function() {
10855        function pad(str, len, chr, leftJustify) {
10856            var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr);
10857            return leftJustify ? str + padding : padding + str;
10858
10859        }
10860
10861        function thousand_separate(value) {
10862            var value_str = new String(value);
10863            for (var i=10; i>0; i--) {
10864                if (value_str == (value_str = value_str.replace(/^(\d+)(\d{3})/, "$1"+$.jqplot.sprintf.thousandsSeparator+"$2"))) break;
10865            }
10866            return value_str; 
10867        }
10868
10869        function justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace) {
10870            var diff = minWidth - value.length;
10871            if (diff > 0) {
10872                var spchar = ' ';
10873                if (htmlSpace) { spchar = '&nbsp;'; }
10874                if (leftJustify || !zeroPad) {
10875                    value = pad(value, minWidth, spchar, leftJustify);
10876                } else {
10877                    value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length);
10878                }
10879            }
10880            return value;
10881        }
10882
10883        function formatBaseX(value, base, prefix, leftJustify, minWidth, precision, zeroPad, htmlSpace) {
10884            // Note: casts negative numbers to positive ones
10885            var number = value >>> 0;
10886            prefix = prefix && number && {'2': '0b', '8': '0', '16': '0x'}[base] || '';
10887            value = prefix + pad(number.toString(base), precision || 0, '0', false);
10888            return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace);
10889        }
10890
10891        function formatString(value, leftJustify, minWidth, precision, zeroPad, htmlSpace) {
10892            if (precision != null) {
10893                value = value.slice(0, precision);
10894            }
10895            return justify(value, '', leftJustify, minWidth, zeroPad, htmlSpace);
10896        }
10897
10898        var a = arguments, i = 0, format = a[i++];
10899
10900        return format.replace($.jqplot.sprintf.regex, function(substring, valueIndex, flags, minWidth, _, precision, type) {
10901            if (substring == '%%') { return '%'; }
10902
10903            // parse flags
10904            var leftJustify = false, positivePrefix = '', zeroPad = false, prefixBaseX = false, htmlSpace = false, thousandSeparation = false;
10905            for (var j = 0; flags && j < flags.length; j++) switch (flags.charAt(j)) {
10906                case ' ': positivePrefix = ' '; break;
10907                case '+': positivePrefix = '+'; break;
10908                case '-': leftJustify = true; break;
10909                case '0': zeroPad = true; break;
10910                case '#': prefixBaseX = true; break;
10911                case '&': htmlSpace = true; break;
10912                case '\'': thousandSeparation = true; break;
10913            }
10914
10915            // parameters may be null, undefined, empty-string or real valued
10916            // we want to ignore null, undefined and empty-string values
10917
10918            if (!minWidth) {
10919                minWidth = 0;
10920            } 
10921            else if (minWidth == '*') {
10922                minWidth = +a[i++];
10923            } 
10924            else if (minWidth.charAt(0) == '*') {
10925                minWidth = +a[minWidth.slice(1, -1)];
10926            } 
10927            else {
10928                minWidth = +minWidth;
10929            }
10930
10931            // Note: undocumented perl feature:
10932            if (minWidth < 0) {
10933                minWidth = -minWidth;
10934                leftJustify = true;
10935            }
10936
10937            if (!isFinite(minWidth)) {
10938                throw new Error('$.jqplot.sprintf: (minimum-)width must be finite');
10939            }
10940
10941            if (!precision) {
10942                precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : void(0);
10943            } 
10944            else if (precision == '*') {
10945                precision = +a[i++];
10946            } 
10947            else if (precision.charAt(0) == '*') {
10948                precision = +a[precision.slice(1, -1)];
10949            } 
10950            else {
10951                precision = +precision;
10952            }
10953
10954            // grab value using valueIndex if required?
10955            var value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++];
10956
10957            switch (type) {
10958            case 's': {
10959                if (value == null) {
10960                    return '';
10961                }
10962                return formatString(String(value), leftJustify, minWidth, precision, zeroPad, htmlSpace);
10963            }
10964            case 'c': return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad, htmlSpace);
10965            case 'b': return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad,htmlSpace);
10966            case 'o': return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace);
10967            case 'x': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace);
10968            case 'X': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace).toUpperCase();
10969            case 'u': return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace);
10970            case 'i': {
10971              var number = parseInt(+value, 10);
10972              if (isNaN(number)) {
10973                return '';
10974              }
10975              var prefix = number < 0 ? '-' : positivePrefix;
10976              var number_str = thousandSeparation ? thousand_separate(String(Math.abs(number))): String(Math.abs(number));
10977              value = prefix + pad(number_str, precision, '0', false);
10978              //value = prefix + pad(String(Math.abs(number)), precision, '0', false);
10979              return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace);
10980                  }
10981            case 'd': {
10982              var number = Math.round(+value);
10983              if (isNaN(number)) {
10984                return '';
10985              }
10986              var prefix = number < 0 ? '-' : positivePrefix;
10987              var number_str = thousandSeparation ? thousand_separate(String(Math.abs(number))): String(Math.abs(number));
10988              value = prefix + pad(number_str, precision, '0', false);
10989              return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace);
10990                  }
10991            case 'e':
10992            case 'E':
10993            case 'f':
10994            case 'F':
10995            case 'g':
10996            case 'G':
10997                      {
10998                      var number = +value;
10999                      if (isNaN(number)) {
11000                          return '';
11001                      }
11002                      var prefix = number < 0 ? '-' : positivePrefix;
11003                      var method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())];
11004                      var textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2];
11005                      var number_str = Math.abs(number)[method](precision);
11006                      
11007                      // Apply the decimal mark properly by splitting the number by the
11008                      //   decimalMark, applying thousands separator, and then placing it
11009                      //   back in.
11010                      var parts = number_str.toString().split('.');
11011                      parts[0] = thousandSeparation ? thousand_separate(parts[0]) : parts[0];
11012                      number_str = parts.join($.jqplot.sprintf.decimalMark);
11013                      
11014                      value = prefix + number_str;
11015                      var justified = justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace)[textTransform]();
11016                      
11017                      return justified;
11018                  }
11019            case 'p':
11020            case 'P':
11021            {
11022                // make sure number is a number
11023                var number = +value;
11024                if (isNaN(number)) {
11025                    return '';
11026                }
11027                var prefix = number < 0 ? '-' : positivePrefix;
11028
11029                var parts = String(Number(Math.abs(number)).toExponential()).split(/e|E/);
11030                var sd = (parts[0].indexOf('.') != -1) ? parts[0].length - 1 : String(number).length;
11031                var zeros = (parts[1] < 0) ? -parts[1] - 1 : 0;
11032                
11033                if (Math.abs(number) < 1) {
11034                    if (sd + zeros  <= precision) {
11035                        value = prefix + Math.abs(number).toPrecision(sd);
11036                    }
11037                    else {
11038                        if (sd  <= precision - 1) {
11039                            value = prefix + Math.abs(number).toExponential(sd-1);
11040                        }
11041                        else {
11042                            value = prefix + Math.abs(number).toExponential(precision-1);
11043                        }
11044                    }
11045                }
11046                else {
11047                    var prec = (sd <= precision) ? sd : precision;
11048                    value = prefix + Math.abs(number).toPrecision(prec);
11049                }
11050                var textTransform = ['toString', 'toUpperCase']['pP'.indexOf(type) % 2];
11051                return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace)[textTransform]();
11052            }
11053            case 'n': return '';
11054            default: return substring;
11055            }
11056        });
11057    };
11058
11059    $.jqplot.sprintf.thousandsSeparator = ',';
11060    // Specifies the decimal mark for floating point values. By default a period '.'
11061    // is used. If you change this value to for example a comma be sure to also
11062    // change the thousands separator or else this won't work since a simple String
11063    // replace is used (replacing all periods with the mark specified here).
11064    $.jqplot.sprintf.decimalMark = '.';
11065    
11066    $.jqplot.sprintf.regex = /%%|%(\d+\$)?([-+#0&\' ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([nAscboxXuidfegpEGP])/g;
11067
11068    $.jqplot.getSignificantFigures = function(number) {
11069        var parts = String(Number(Math.abs(number)).toExponential()).split(/e|E/);
11070        // total significant digits
11071        var sd = (parts[0].indexOf('.') != -1) ? parts[0].length - 1 : parts[0].length;
11072        var zeros = (parts[1] < 0) ? -parts[1] - 1 : 0;
11073        // exponent
11074        var expn = parseInt(parts[1], 10);
11075        // digits to the left of the decimal place
11076        var dleft = (expn + 1 > 0) ? expn + 1 : 0;
11077        // digits to the right of the decimal place
11078        var dright = (sd <= dleft) ? 0 : sd - expn - 1;
11079        return {significantDigits: sd, digitsLeft: dleft, digitsRight: dright, zeros: zeros, exponent: expn} ;
11080    };
11081
11082    $.jqplot.getPrecision = function(number) {
11083        return $.jqplot.getSignificantFigures(number).digitsRight;
11084    };
11085
11086  
11087
11088
11089    var backCompat = $.uiBackCompat !== false;
11090
11091    $.jqplot.effects = {
11092        effect: {}
11093    };
11094
11095    // prefix used for storing data on .data()
11096    var dataSpace = "jqplot.storage.";
11097
11098    /******************************************************************************/
11099    /*********************************** EFFECTS **********************************/
11100    /******************************************************************************/
11101
11102    $.extend( $.jqplot.effects, {
11103        version: "1.9pre",
11104
11105        // Saves a set of properties in a data storage
11106        save: function( element, set ) {
11107            for( var i=0; i < set.length; i++ ) {
11108                if ( set[ i ] !== null ) {
11109                    element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
11110                }
11111            }
11112        },
11113
11114        // Restores a set of previously saved properties from a data storage
11115        restore: function( element, set ) {
11116            for( var i=0; i < set.length; i++ ) {
11117                if ( set[ i ] !== null ) {
11118                    element.css( set[ i ], element.data( dataSpace + set[ i ] ) );
11119                }
11120            }
11121        },
11122
11123        setMode: function( el, mode ) {
11124            if (mode === "toggle") {
11125                mode = el.is( ":hidden" ) ? "show" : "hide";
11126            }
11127            return mode;
11128        },
11129
vendor: 4,910 bytes, lines 11130-11274
11130        // Wraps the element around a wrapper that copies position properties
11131        createWrapper: function( element ) {
11132
11133            // if the element is already wrapped, return it
11134            if ( element.parent().is( ".ui-effects-wrapper" )) {
11135                return element.parent();
11136            }
11137
11138            // wrap the element
11139            var props = {
11140                    width: element.outerWidth(true),
11141                    height: element.outerHeight(true),
11142                    "float": element.css( "float" )
11143                },
11144                wrapper = $( "<div></div>" )
11145                    .addClass( "ui-effects-wrapper" )
11146                    .css({
11147                        fontSize: "100%",
11148                        background: "transparent",
11149                        border: "none",
11150                        margin: 0,
11151                        padding: 0
11152                    }),
11153                // Store the size in case width/height are defined in % - Fixes #5245
11154                size = {
11155                    width: element.width(),
11156                    height: element.height()
11157                },
11158                active = document.activeElement;
11159
11160            element.wrap( wrapper );
11161
11162            // Fixes #7595 - Elements lose focus when wrapped.
11163            if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
11164                $( active ).focus();
11165            }
11166
11167            wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually loose the reference to the wrapped element
11168
11169            // transfer positioning properties to the wrapper
11170            if ( element.css( "position" ) === "static" ) {
11171                wrapper.css({ position: "relative" });
11172                element.css({ position: "relative" });
11173            } else {
11174                $.extend( props, {
11175                    position: element.css( "position" ),
11176                    zIndex: element.css( "z-index" )
11177                });
11178                $.each([ "top", "left", "bottom", "right" ], function(i, pos) {
11179                    props[ pos ] = element.css( pos );
11180                    if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
11181                        props[ pos ] = "auto";
11182                    }
11183                });
11184                element.css({
11185                    position: "relative",
11186                    top: 0,
11187                    left: 0,
11188                    right: "auto",
11189                    bottom: "auto"
11190                });
11191            }
11192            element.css(size);
11193
11194            return wrapper.css( props ).show();
11195        },
11196
11197        removeWrapper: function( element ) {
11198            var active = document.activeElement;
11199
11200            if ( element.parent().is( ".ui-effects-wrapper" ) ) {
11201                element.parent().replaceWith( element );
11202
11203                // Fixes #7595 - Elements lose focus when wrapped.
11204                if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
11205                    $( active ).focus();
11206                }
11207            }
11208
11209
11210            return element;
11211        }
11212    });
11213
11214    // return an effect options object for the given parameters:
11215    function _normalizeArguments( effect, options, speed, callback ) {
11216
11217        // short path for passing an effect options object:
11218        if ( $.isPlainObject( effect ) ) {
11219            return effect;
11220        }
11221
11222        // convert to an object
11223        effect = { effect: effect };
11224
11225        // catch (effect)
11226        if ( options === undefined ) {
11227            options = {};
11228        }
11229
11230        // catch (effect, callback)
11231        if ( $.isFunction( options ) ) {
11232            callback = options;
11233            speed = null;
11234            options = {};
11235        }
11236
11237        // catch (effect, speed, ?)
11238        if ( $.type( options ) === "number" || $.fx.speeds[ options ]) {
11239            callback = speed;
11240            speed = options;
11241            options = {};
11242        }
11243
11244        // catch (effect, options, callback)
11245        if ( $.isFunction( speed ) ) {
11246            callback = speed;
11247            speed = null;
11248        }
11249
11250        // add options to effect
11251        if ( options ) {
11252            $.extend( effect, options );
11253        }
11254
11255        speed = speed || options.duration;
11256        effect.duration = $.fx.off ? 0 : typeof speed === "number"
11257            ? speed : speed in $.fx.speeds ? $.fx.speeds[ speed ] : $.fx.speeds._default;
11258
11259        effect.complete = callback || options.complete;
11260
11261        return effect;
11262    }
11263
11264    function standardSpeed( speed ) {
11265        // valid standard speeds
11266        if ( !speed || typeof speed === "number" || $.fx.speeds[ speed ] ) {
11267            return true;
11268        }
11269
11270        // invalid strings - treat as "normal" speed
11271        if ( typeof speed === "string" && !$.jqplot.effects.effect[ speed ] ) {
11272            // TODO: remove in 2.0 (#7115)
11273            if ( backCompat && $.jqplot.effects[ speed ] ) {
11274                return false;
11275            }
11276            return true;
11277        }
11278
11279        return false;
11280    }
11281
11282    $.fn.extend({
11283        jqplotEffect: function( effect, options, speed, callback ) {
11284            var args = _normalizeArguments.apply( this, arguments ),
11285                mode = args.mode,
11286                queue = args.queue,
11287                effectMethod = $.jqplot.effects.effect[ args.effect ],
11288
11289                // DEPRECATED: remove in 2.0 (#7115)
11290                oldEffectMethod = !effectMethod && backCompat && $.jqplot.effects[ args.effect ];
11291
11292            if ( $.fx.off || !( effectMethod || oldEffectMethod ) ) {
11293                // delegate to the original method (e.g., .show()) if possible
11294                if ( mode ) {
11295                    return this[ mode ]( args.duration, args.complete );
11296                } else {
11297                    return this.each( function() {
11298                        if ( args.complete ) {
11299                            args.complete.call( this );
11300                        }
11301                    });
11302                }
11303            }
11304
11305            function run( next ) {
11306                var elem = $( this ),
11307                    complete = args.complete,
11308                    mode = args.mode;
11309
11310                function done() {
11311                    if ( $.isFunction( complete ) ) {
11312                        complete.call( elem[0] );
11313                    }
11314                    if ( $.isFunction( next ) ) {
11315                        next();
11316                    }
11317                }
11318
11319                // if the element is hiddden and mode is hide,
11320                // or element is visible and mode is show
11321                if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
11322                    done();
11323                } else {
11324                    effectMethod.call( elem[0], args, done );
11325                }
11326            }
11327
11328            // TODO: remove this check in 2.0, effectMethod will always be true
11329            if ( effectMethod ) {
11330                return queue === false ? this.each( run ) : this.queue( queue || "fx", run );
11331            } else {
11332                // DEPRECATED: remove in 2.0 (#7115)
11333                return oldEffectMethod.call(this, {
11334                    options: args,
11335                    duration: args.duration,
11336                    callback: args.complete,
11337                    mode: args.mode
11338                });
11339            }
11340        }
11341    });
11342
11343
11344
11345
11346    var rvertical = /up|down|vertical/,
11347        rpositivemotion = /up|left|vertical|horizontal/;
11348
11349    $.jqplot.effects.effect.blind = function( o, done ) {
11350        // Create element
11351        var el = $( this ),
11352            props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
11353            mode = $.jqplot.effects.setMode( el, o.mode || "hide" ),
11354            direction = o.direction || "up",
11355            vertical = rvertical.test( direction ),
11356            ref = vertical ? "height" : "width",
11357            ref2 = vertical ? "top" : "left",
11358            motion = rpositivemotion.test( direction ),
11359            animation = {},
11360            show = mode === "show",
11361            wrapper, distance, top;
11362
11363        // // if already wrapped, the wrapper's properties are my property. #6245
11364        if ( el.parent().is( ".ui-effects-wrapper" ) ) {
11365            $.jqplot.effects.save( el.parent(), props );
11366        } else {
11367            $.jqplot.effects.save( el, props );
11368        }
11369        el.show();
11370        top = parseInt(el.css('top'), 10);
11371        wrapper = $.jqplot.effects.createWrapper( el ).css({
11372            overflow: "hidden"
11373        });
11374
11375        distance = vertical ? wrapper[ ref ]() + top : wrapper[ ref ]();
11376
11377        animation[ ref ] = show ? String(distance) : '0';
11378        if ( !motion ) {
11379            el
11380                .css( vertical ? "bottom" : "right", 0 )
11381                .css( vertical ? "top" : "left", "" )
11382                .css({ position: "absolute" });
11383            animation[ ref2 ] = show ? '0' : String(distance);
11384        }
11385
11386        // // start at 0 if we are showing
11387        if ( show ) {
11388            wrapper.css( ref, 0 );
11389            if ( ! motion ) {
11390                wrapper.css( ref2, distance );
11391            }
11392        }
11393
11394        // // Animate
11395        wrapper.animate( animation, {
11396            duration: o.duration,
11397            easing: o.easing,
11398            queue: false,
11399            complete: function() {
11400                if ( mode === "hide" ) {
11401                    el.hide();
11402                }
11403                $.jqplot.effects.restore( el, props );
11404                $.jqplot.effects.removeWrapper( el );
11405                done();
11406            }
11407        });
11408
11409    };
11410
11411})(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.