[ Index ]

PHP Cross Reference of vtigercrm-6.1.0

title

Body

[close]

/libraries/jquery/jqplot/ -> jquery.jqplot.js (source)

   1  /**
   2   * Title: jqPlot Charts
   3   * 
   4   * Pure JavaScript plotting plugin for jQuery.
   5   * 
   6   * About: Version
   7   * 
   8   * version: 1.0.2 
   9   * revision: 1108
  10   * 
  11   * About: Copyright & License
  12   * 
  13   * Copyright (c) 2009-2011 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.errorBackground;
 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.2";
 248      $.jqplot.revision = "1108";
 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 = ($.browser.msie && !$.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 oject.  Axis properties can be set or overriden 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 oject.  Legend properties can be set or overriden 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 oject.  Title properties can be set or overriden 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          // wether 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 oject.  Series properties can be set or overriden by the 
1144       * options passed in from the user.
1145       */
1146      function Series() {
1147          $.jqplot.ElemContainer.call(this);
1148          // Group: Properties
1149          // Properties will be assigned from a series array at the top level of the
1150          // options.  If you had two series and wanted to change the color and line
1151          // width of the first and set the second to use the secondary y axis with
1152          // no shadow and supply custom labels for each:
1153          // > {
1154          // >    series:[
1155          // >        {color: '#ff4466', lineWidth: 5, label:'good line'},
1156          // >        {yaxis: 'y2axis', shadow: false, label:'bad line'}
1157          // >    ]
1158          // > }
1159  
1160          // prop: show
1161          // wether or not to draw the series.
1162          this.show = true;
1163          // prop: xaxis
1164          // which x axis to use with this series, either 'xaxis' or 'x2axis'.
1165          this.xaxis = 'xaxis';
1166          this._xaxis;
1167          // prop: yaxis
1168          // which y axis to use with this series, either 'yaxis' or 'y2axis'.
1169          this.yaxis = 'yaxis';
1170          this._yaxis;
1171          this.gridBorderWidth = 2.0;
1172          // prop: renderer
1173          // A class of a renderer which will draw the series, 
1174          // see <$.jqplot.LineRenderer>.
1175          this.renderer = $.jqplot.LineRenderer;
1176          // prop: rendererOptions
1177          // Options to pass on to the renderer.
1178          this.rendererOptions = {};
1179          this.data = [];
1180          this.gridData = [];
1181          // prop: label
1182          // Line label to use in the legend.
1183          this.label = '';
1184          // prop: showLabel
1185          // true to show label for this series in the legend.
1186          this.showLabel = true;
1187          // prop: color
1188          // css color spec for the series
1189          this.color;
1190          // prop: negativeColor
1191          // css color spec used for filled (area) plots that are filled to zero and
1192          // the "useNegativeColors" option is true.
1193          this.negativeColor;
1194          // prop: lineWidth
1195          // width of the line in pixels.  May have different meanings depending on renderer.
1196          this.lineWidth = 2.5;
1197          // prop: lineJoin
1198          // Canvas lineJoin style between segments of series.
1199          this.lineJoin = 'round';
1200          // prop: lineCap
1201          // Canvas lineCap style at ends of line.
1202          this.lineCap = 'round';
1203          // prop: linePattern
1204          // line pattern 'dashed', 'dotted', 'solid', some combination
1205          // of '-' and '.' characters such as '.-.' or a numerical array like 
1206          // [draw, skip, draw, skip, ...] such as [1, 10] to draw a dotted line, 
1207          // [1, 10, 20, 10] to draw a dot-dash line, and so on.
1208          this.linePattern = 'solid';
1209          this.shadow = true;
1210          // prop: shadowAngle
1211          // Shadow angle in degrees
1212          this.shadowAngle = 45;
1213          // prop: shadowOffset
1214          // Shadow offset from line in pixels
1215          this.shadowOffset = 1.25;
1216          // prop: shadowDepth
1217          // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
1218          this.shadowDepth = 3;
1219          // prop: shadowAlpha
1220          // Alpha channel transparency of shadow.  0 = transparent.
1221          this.shadowAlpha = '0.1';
1222          // prop: breakOnNull
1223          // Wether line segments should be be broken at null value.
1224          // False will join point on either side of line.
1225          this.breakOnNull = false;
1226          // prop: markerRenderer
1227          // A class of a renderer which will draw marker (e.g. circle, square, ...) at the data points,
1228          // see <$.jqplot.MarkerRenderer>.
1229          this.markerRenderer = $.jqplot.MarkerRenderer;
1230          // prop: markerOptions
1231          // renderer specific options to pass to the markerRenderer,
1232          // see <$.jqplot.MarkerRenderer>.
1233          this.markerOptions = {};
1234          // prop: showLine
1235          // wether to actually draw the line or not.  Series will still be renderered, even if no line is drawn.
1236          this.showLine = true;
1237          // prop: showMarker
1238          // wether or not to show the markers at the data points.
1239          this.showMarker = true;
1240          // prop: index
1241          // 0 based index of this series in the plot series array.
1242          this.index;
1243          // prop: fill
1244          // true or false, wether to fill under lines or in bars.
1245          // May not be implemented in all renderers.
1246          this.fill = false;
1247          // prop: fillColor
1248          // CSS color spec to use for fill under line.  Defaults to line color.
1249          this.fillColor;
1250          // prop: fillAlpha
1251          // Alpha transparency to apply to the fill under the line.
1252          // Use this to adjust alpha separate from fill color.
1253          this.fillAlpha;
1254          // prop: fillAndStroke
1255          // If true will stroke the line (with color this.color) as well as fill under it.
1256          // Applies only when fill is true.
1257          this.fillAndStroke = false;
1258          // prop: disableStack
1259          // true to not stack this series with other series in the plot.
1260          // To render properly, non-stacked series must come after any stacked series
1261          // in the plot's data series array.  So, the plot's data series array would look like:
1262          // > [stackedSeries1, stackedSeries2, ..., nonStackedSeries1, nonStackedSeries2, ...]
1263          // disableStack will put a gap in the stacking order of series, and subsequent
1264          // stacked series will not fill down through the non-stacked series and will
1265          // most likely not stack properly on top of the non-stacked series.
1266          this.disableStack = false;
1267          // _stack is set by the Plot if the plot is a stacked chart.
1268          // will stack lines or bars on top of one another to build a "mountain" style chart.
1269          // May not be implemented in all renderers.
1270          this._stack = false;
1271          // prop: neighborThreshold
1272          // how close or far (in pixels) the cursor must be from a point marker to detect the point.
1273          this.neighborThreshold = 4;
1274          // prop: fillToZero
1275          // true will force bar and filled series to fill toward zero on the fill Axis.
1276          this.fillToZero = false;
1277          // prop: fillToValue
1278          // fill a filled series to this value on the fill axis.
1279          // Works in conjunction with fillToZero, so that must be true.
1280          this.fillToValue = 0;
1281          // prop: fillAxis
1282          // Either 'x' or 'y'.  Which axis to fill the line toward if fillToZero is true.
1283          // 'y' means fill up/down to 0 on the y axis for this series.
1284          this.fillAxis = 'y';
1285          // prop: useNegativeColors
1286          // true to color negative values differently in filled and bar charts.
1287          this.useNegativeColors = true;
1288          this._stackData = [];
1289          // _plotData accounts for stacking.  If plots not stacked, _plotData and data are same.  If
1290          // stacked, _plotData is accumulation of stacking data.
1291          this._plotData = [];
1292          // _plotValues hold the individual x and y values that will be plotted for this series.
1293          this._plotValues = {x:[], y:[]};
1294          // statistics about the intervals between data points.  Used for auto scaling.
1295          this._intervals = {x:{}, y:{}};
1296          // data from the previous series, for stacked charts.
1297          this._prevPlotData = [];
1298          this._prevGridData = [];
1299          this._stackAxis = 'y';
1300          this._primaryAxis = '_xaxis';
1301          // give each series a canvas to draw on.  This should allow for redrawing speedups.
1302          this.canvas = new $.jqplot.GenericCanvas();
1303          this.shadowCanvas = new $.jqplot.GenericCanvas();
1304          this.plugins = {};
1305          // sum of y values in this series.
1306          this._sumy = 0;
1307          this._sumx = 0;
1308          this._type = '';
1309      }
1310      
1311      Series.prototype = new $.jqplot.ElemContainer();
1312      Series.prototype.constructor = Series;
1313      
1314      Series.prototype.init = function(index, gridbw, plot) {
1315          // weed out any null values in the data.
1316          this.index = index;
1317          this.gridBorderWidth = gridbw;
1318          var d = this.data;
1319          var temp = [], i;
1320          for (i=0; i<d.length; i++) {
1321              if (! this.breakOnNull) {
1322                  if (d[i] == null || d[i][0] == null || d[i][1] == null) {
1323                      continue;
1324                  }
1325                  else {
1326                      temp.push(d[i]);
1327                  }
1328              }
1329              else {
1330                  // TODO: figure out what to do with null values
1331                  // probably involve keeping nulls in data array
1332                  // and then updating renderers to break line
1333                  // when it hits null value.
1334                  // For now, just keep value.
1335                  temp.push(d[i]);
1336              }
1337          }
1338          this.data = temp;
1339  
1340          // parse the renderer options and apply default colors if not provided
1341          // Set color even if not shown, so series don't change colors when other
1342          // series on plot shown/hidden.
1343          if (!this.color) {
1344              this.color = plot.colorGenerator.get(this.index);
1345          }
1346          if (!this.negativeColor) {
1347              this.negativeColor = plot.negativeColorGenerator.get(this.index);
1348          }
1349  
1350  
1351          if (!this.fillColor) {
1352              this.fillColor = this.color;
1353          }
1354          if (this.fillAlpha) {
1355              var comp = $.jqplot.normalize2rgb(this.fillColor);
1356              var comp = $.jqplot.getColorComponents(comp);
1357              this.fillColor = 'rgba('+comp[0]+','+comp[1]+','+comp[2]+','+this.fillAlpha+')';
1358          }
1359          if ($.isFunction(this.renderer)) {
1360              this.renderer = new this.renderer();  
1361          }
1362          this.renderer.init.call(this, this.rendererOptions, plot);
1363          this.markerRenderer = new this.markerRenderer();
1364          if (!this.markerOptions.color) {
1365              this.markerOptions.color = this.color;
1366          }
1367          if (this.markerOptions.show == null) {
1368              this.markerOptions.show = this.showMarker;
1369          }
1370          this.showMarker = this.markerOptions.show;
1371          // the markerRenderer is called within it's own scaope, don't want to overwrite series options!!
1372          this.markerRenderer.init(this.markerOptions);
1373      };
1374      
1375      // data - optional data point array to draw using this series renderer
1376      // gridData - optional grid data point array to draw using this series renderer
1377      // stackData - array of cumulative data for stacked plots.
1378      Series.prototype.draw = function(sctx, opts, plot) {
1379          var options = (opts == undefined) ? {} : opts;
1380          sctx = (sctx == undefined) ? this.canvas._ctx : sctx;
1381          
1382          var j, data, gridData;
1383          
1384          // hooks get called even if series not shown
1385          // we don't clear canvas here, it would wipe out all other series as well.
1386          for (j=0; j<$.jqplot.preDrawSeriesHooks.length; j++) {
1387              $.jqplot.preDrawSeriesHooks[j].call(this, sctx, options);
1388          }
1389          if (this.show) {
1390              this.renderer.setGridData.call(this, plot);
1391              if (!options.preventJqPlotSeriesDrawTrigger) {
1392                  $(sctx.canvas).trigger('jqplotSeriesDraw', [this.data, this.gridData]);
1393              }
1394              data = [];
1395              if (options.data) {
1396                  data = options.data;
1397              }
1398              else if (!this._stack) {
1399                  data = this.data;
1400              }
1401              else {
1402                  data = this._plotData;
1403              }
1404              gridData = options.gridData || this.renderer.makeGridData.call(this, data, plot);
1405  
1406              if (this._type === 'line' && this.renderer.smooth && this.renderer._smoothedData.length) {
1407                  gridData = this.renderer._smoothedData;
1408              }
1409  
1410              this.renderer.draw.call(this, sctx, gridData, options, plot);
1411          }
1412          
1413          for (j=0; j<$.jqplot.postDrawSeriesHooks.length; j++) {
1414              $.jqplot.postDrawSeriesHooks[j].call(this, sctx, options, plot);
1415          }
1416          
1417          sctx = opts = plot = j = data = gridData = null;
1418      };
1419      
1420      Series.prototype.drawShadow = function(sctx, opts, plot) {
1421          var options = (opts == undefined) ? {} : opts;
1422          sctx = (sctx == undefined) ? this.shadowCanvas._ctx : sctx;
1423          
1424          var j, data, gridData;
1425          
1426          // hooks get called even if series not shown
1427          // we don't clear canvas here, it would wipe out all other series as well.
1428          for (j=0; j<$.jqplot.preDrawSeriesShadowHooks.length; j++) {
1429              $.jqplot.preDrawSeriesShadowHooks[j].call(this, sctx, options);
1430          }
1431          if (this.shadow) {
1432              this.renderer.setGridData.call(this, plot);
1433  
1434              data = [];
1435              if (options.data) {
1436                  data = options.data;
1437              }
1438              else if (!this._stack) {
1439                  data = this.data;
1440              }
1441              else {
1442                  data = this._plotData;
1443              }
1444              gridData = options.gridData || this.renderer.makeGridData.call(this, data, plot);
1445          
1446              this.renderer.drawShadow.call(this, sctx, gridData, options, plot);
1447          }
1448          
1449          for (j=0; j<$.jqplot.postDrawSeriesShadowHooks.length; j++) {
1450              $.jqplot.postDrawSeriesShadowHooks[j].call(this, sctx, options);
1451          }
1452          
1453          sctx = opts = plot = j = data = gridData = null;
1454          
1455      };
1456      
1457      // toggles series display on plot, e.g. show/hide series
1458      Series.prototype.toggleDisplay = function(ev, callback) {
1459          var s, speed;
1460          if (ev.data.series) {
1461              s = ev.data.series;
1462          }
1463          else {
1464              s = this;
1465          }
1466  
1467          if (ev.data.speed) {
1468              speed = ev.data.speed;
1469          }
1470          if (speed) {
1471              // this can be tricky because series may not have a canvas element if replotting.
1472              if (s.canvas._elem.is(':hidden') || !s.show) {
1473                  s.show = true;
1474  
1475                  s.canvas._elem.removeClass('jqplot-series-hidden');
1476                  if (s.shadowCanvas._elem) {
1477                      s.shadowCanvas._elem.fadeIn(speed);
1478                  }
1479                  s.canvas._elem.fadeIn(speed, callback);
1480                  s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).fadeIn(speed);
1481              }
1482              else {
1483                  s.show = false;
1484  
1485                  s.canvas._elem.addClass('jqplot-series-hidden');
1486                  if (s.shadowCanvas._elem) {
1487                      s.shadowCanvas._elem.fadeOut(speed);
1488                  }
1489                  s.canvas._elem.fadeOut(speed, callback);
1490                  s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).fadeOut(speed);
1491              }
1492          }
1493          else {
1494              // this can be tricky because series may not have a canvas element if replotting.
1495              if (s.canvas._elem.is(':hidden') || !s.show) {
1496                  s.show = true;
1497  
1498                  s.canvas._elem.removeClass('jqplot-series-hidden');
1499                  if (s.shadowCanvas._elem) {
1500                      s.shadowCanvas._elem.show();
1501                  }
1502                  s.canvas._elem.show(0, callback);
1503                  s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).show();
1504              }
1505              else {
1506                  s.show = false;
1507  
1508                  s.canvas._elem.addClass('jqplot-series-hidden');
1509                  if (s.shadowCanvas._elem) {
1510                      s.shadowCanvas._elem.hide();
1511                  }
1512                  s.canvas._elem.hide(0, callback);
1513                  s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).hide();
1514              }
1515          }
1516      };
1517      
1518  
1519  
1520      /**
1521       * Class: Grid
1522       * 
1523       * Object representing the grid on which the plot is drawn.  The grid in this
1524       * context is the area bounded by the axes, the area which will contain the series.
1525       * Note, the series are drawn on their own canvas.
1526       * The Grid object cannot be instantiated directly, but is created by the Plot oject.  
1527       * Grid properties can be set or overriden by the options passed in from the user.
1528       */
1529      function Grid() {
1530          $.jqplot.ElemContainer.call(this);
1531          // Group: Properties
1532          
1533          // prop: drawGridlines
1534          // wether to draw the gridlines on the plot.
1535          this.drawGridlines = true;
1536          // prop: gridLineColor
1537          // color of the grid lines.
1538          this.gridLineColor = '#cccccc';
1539          // prop: gridLineWidth
1540          // width of the grid lines.
1541          this.gridLineWidth = 1.0;
1542          // prop: background
1543          // css spec for the background color.
1544          this.background = '#fffdf6';
1545          // prop: borderColor
1546          // css spec for the color of the grid border.
1547          this.borderColor = '#999999';
1548          // prop: borderWidth
1549          // width of the border in pixels.
1550          this.borderWidth = 2.0;
1551          // prop: drawBorder
1552          // True to draw border around grid.
1553          this.drawBorder = true;
1554          // prop: shadow
1555          // wether to show a shadow behind the grid.
1556          this.shadow = true;
1557          // prop: shadowAngle
1558          // shadow angle in degrees
1559          this.shadowAngle = 45;
1560          // prop: shadowOffset
1561          // Offset of each shadow stroke from the border in pixels
1562          this.shadowOffset = 1.5;
1563          // prop: shadowWidth
1564          // width of the stoke for the shadow
1565          this.shadowWidth = 3;
1566          // prop: shadowDepth
1567          // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
1568          this.shadowDepth = 3;
1569          // prop: shadowColor
1570          // an optional css color spec for the shadow in 'rgba(n, n, n, n)' form
1571          this.shadowColor = null;
1572          // prop: shadowAlpha
1573          // Alpha channel transparency of shadow.  0 = transparent.
1574          this.shadowAlpha = '0.07';
1575          this._left;
1576          this._top;
1577          this._right;
1578          this._bottom;
1579          this._width;
1580          this._height;
1581          this._axes = [];
1582          // prop: renderer
1583          // Instance of a renderer which will actually render the grid,
1584          // see <$.jqplot.CanvasGridRenderer>.
1585          this.renderer = $.jqplot.CanvasGridRenderer;
1586          // prop: rendererOptions
1587          // Options to pass on to the renderer,
1588          // see <$.jqplot.CanvasGridRenderer>.
1589          this.rendererOptions = {};
1590          this._offsets = {top:null, bottom:null, left:null, right:null};
1591      }
1592      
1593      Grid.prototype = new $.jqplot.ElemContainer();
1594      Grid.prototype.constructor = Grid;
1595      
1596      Grid.prototype.init = function() {
1597          if ($.isFunction(this.renderer)) {
1598              this.renderer = new this.renderer();  
1599          }
1600          this.renderer.init.call(this, this.rendererOptions);
1601      };
1602      
1603      Grid.prototype.createElement = function(offsets,plot) {
1604          this._offsets = offsets;
1605          return this.renderer.createElement.call(this, plot);
1606      };
1607      
1608      Grid.prototype.draw = function() {
1609          this.renderer.draw.call(this);
1610      };
1611      
1612      $.jqplot.GenericCanvas = function() {
1613          $.jqplot.ElemContainer.call(this);
1614          this._ctx;  
1615      };
1616      
1617      $.jqplot.GenericCanvas.prototype = new $.jqplot.ElemContainer();
1618      $.jqplot.GenericCanvas.prototype.constructor = $.jqplot.GenericCanvas;
1619      
1620      $.jqplot.GenericCanvas.prototype.createElement = function(offsets, clss, plotDimensions, plot) {
1621          this._offsets = offsets;
1622          var klass = 'jqplot';
1623          if (clss != undefined) {
1624              klass = clss;
1625          }
1626          var elem;
1627  
1628          elem = plot.canvasManager.getCanvas();
1629          
1630          // if new plotDimensions supplied, use them.
1631          if (plotDimensions != null) {
1632              this._plotDimensions = plotDimensions;
1633          }
1634          
1635          elem.width = this._plotDimensions.width - this._offsets.left - this._offsets.right;
1636          elem.height = this._plotDimensions.height - this._offsets.top - this._offsets.bottom;
1637          this._elem = $(elem);
1638          this._elem.css({ position: 'absolute', left: this._offsets.left, top: this._offsets.top });
1639          
1640          this._elem.addClass(klass);
1641          
1642          elem = plot.canvasManager.initCanvas(elem);
1643          
1644          elem = null;
1645          return this._elem;
1646      };
1647      
1648      $.jqplot.GenericCanvas.prototype.setContext = function() {
1649          this._ctx = this._elem.get(0).getContext("2d");
1650          return this._ctx;
1651      };
1652      
1653      // Memory Leaks patch
1654      $.jqplot.GenericCanvas.prototype.resetCanvas = function() {
1655        if (this._elem) {
1656          if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) {
1657             window.G_vmlCanvasManager.uninitElement(this._elem.get(0));
1658          }
1659          
1660          //this._elem.remove();
1661          this._elem.emptyForce();
1662        }
1663        
1664        this._ctx = null;
1665      };
1666      
1667      $.jqplot.HooksManager = function () {
1668          this.hooks =[];
1669          this.args = [];
1670      };
1671      
1672      $.jqplot.HooksManager.prototype.addOnce = function(fn, args) {
1673          args = args || [];
1674          var havehook = false;
1675          for (var i=0, l=this.hooks.length; i<l; i++) {
1676              if (this.hooks[i] == fn) {
1677                  havehook = true;
1678              }
1679          }
1680          if (!havehook) {
1681              this.hooks.push(fn);
1682              this.args.push(args);
1683          }
1684      };
1685      
1686      $.jqplot.HooksManager.prototype.add = function(fn, args) {
1687          args = args || [];
1688          this.hooks.push(fn);
1689          this.args.push(args);
1690      };
1691      
1692      $.jqplot.EventListenerManager = function () {
1693          this.hooks =[];
1694      };
1695      
1696      $.jqplot.EventListenerManager.prototype.addOnce = function(ev, fn) {
1697          var havehook = false, h, i;
1698          for (var i=0, l=this.hooks.length; i<l; i++) {
1699              h = this.hooks[i];
1700              if (h[0] == ev && h[1] == fn) {
1701                  havehook = true;
1702              }
1703          }
1704          if (!havehook) {
1705              this.hooks.push([ev, fn]);
1706          }
1707      };
1708      
1709      $.jqplot.EventListenerManager.prototype.add = function(ev, fn) {
1710          this.hooks.push([ev, fn]);
1711      };
1712  
1713  
1714      var _axisNames = ['yMidAxis', 'xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis'];
1715  
1716      /**
1717       * Class: jqPlot
1718       * Plot object returned by call to $.jqplot.  Handles parsing user options,
1719       * creating sub objects (Axes, legend, title, series) and rendering the plot.
1720       */
1721      function jqPlot() {
1722          // Group: Properties
1723          // These properties are specified at the top of the options object
1724          // like so:
1725          // > {
1726          // >     axesDefaults:{min:0},
1727          // >     series:[{color:'#6633dd'}],
1728          // >     title: 'A Plot'
1729          // > }
1730          //
1731  
1732          // prop: animate
1733          // True to animate the series on initial plot draw (renderer dependent).
1734          // Actual animation functionality must be supported in the renderer.
1735          this.animate = false;
1736          // prop: animateReplot
1737          // True to animate series after a call to the replot() method.
1738          // Use with caution!  Replots can happen very frequently under
1739          // certain circumstances (e.g. resizing, dragging points) and
1740          // animation in these situations can cause problems.
1741          this.animateReplot = false;
1742          // prop: axes
1743          // up to 4 axes are supported, each with it's own options, 
1744          // See <Axis> for axis specific options.
1745          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')};
1746          this.baseCanvas = new $.jqplot.GenericCanvas();
1747          // true to intercept right click events and fire a 'jqplotRightClick' event.
1748          // this will also block the context menu.
1749          this.captureRightClick = false;
1750          // prop: data
1751          // user's data.  Data should *NOT* be specified in the options object,
1752          // but be passed in as the second argument to the $.jqplot() function.
1753          // The data property is described here soley for reference. 
1754          // The data should be in the form of an array of 2D or 1D arrays like
1755          // > [ [[x1, y1], [x2, y2],...], [y1, y2, ...] ].
1756          this.data = [];
1757          // prop: dataRenderer
1758          // A callable which can be used to preprocess data passed into the plot.
1759          // Will be called with 2 arguments, the plot data and a reference to the plot.
1760          this.dataRenderer;
1761          // prop: dataRendererOptions
1762          // Options that will be passed to the dataRenderer.
1763          // Can be of any type.
1764          this.dataRendererOptions;
1765          this.defaults = {
1766              // prop: axesDefaults
1767              // default options that will be applied to all axes.
1768              // see <Axis> for axes options.
1769              axesDefaults: {},
1770              axes: {xaxis:{}, yaxis:{}, x2axis:{}, y2axis:{}, y3axis:{}, y4axis:{}, y5axis:{}, y6axis:{}, y7axis:{}, y8axis:{}, y9axis:{}, yMidAxis:{}},
1771              // prop: seriesDefaults
1772              // default options that will be applied to all series.
1773              // see <Series> for series options.
1774              seriesDefaults: {},
1775              series:[]
1776          };
1777          // prop: defaultAxisStart
1778          // 1-D data series are internally converted into 2-D [x,y] data point arrays
1779          // by jqPlot.  This is the default starting value for the missing x or y value.
1780          // The added data will be a monotonically increasing series (e.g. [1, 2, 3, ...])
1781          // starting at this value.
1782          this.defaultAxisStart = 1;
1783          // this.doCustomEventBinding = true;
1784          // prop: drawIfHidden
1785          // True to execute the draw method even if the plot target is hidden.
1786          // Generally, this should be false.  Most plot elements will not be sized/
1787          // positioned correclty if renderered into a hidden container.  To render into
1788          // a hidden container, call the replot method when the container is shown.
1789          this.drawIfHidden = false;
1790          this.eventCanvas = new $.jqplot.GenericCanvas();
1791          // prop: fillBetween
1792          // Fill between 2 line series in a plot.
1793          // Options object:
1794          // {
1795          //    series1: first index (0 based) of series in fill
1796          //    series2: second index (0 based) of series in fill
1797          //    color: color of fill [default fillColor of series1]
1798          //    baseSeries:  fill will be drawn below this series (0 based index)
1799          //    fill: false to turn off fill [default true].
1800          //  }
1801          this.fillBetween = {
1802              series1: null,
1803              series2: null,
1804              color: null,
1805              baseSeries: 0,
1806              fill: true
1807          };
1808          // prop; fontFamily
1809          // css spec for the font-family attribute.  Default for the entire plot.
1810          this.fontFamily;
1811          // prop: fontSize
1812          // css spec for the font-size attribute.  Default for the entire plot.
1813          this.fontSize;
1814          // prop: grid
1815          // See <Grid> for grid specific options.
1816          this.grid = new Grid();
1817          // prop: legend
1818          // see <$.jqplot.TableLegendRenderer>
1819          this.legend = new Legend();
1820          // prop: noDataIndicator
1821          // Options to set up a mock plot with a data loading indicator if no data is specified.
1822          this.negativeSeriesColors = $.jqplot.config.defaultNegativeColors;
1823          this.noDataIndicator = {    
1824              show: false,
1825              indicator: 'Loading Data...',
1826              axes: {
1827                  xaxis: {
1828                      min: 0,
1829                      max: 10,
1830                      tickInterval: 2,
1831                      show: true
1832                  },
1833                  yaxis: {
1834                      min: 0,
1835                      max: 12,
1836                      tickInterval: 3,
1837                      show: true
1838                  }
1839              }
1840          };
1841          // container to hold all of the merged options.  Convienence for plugins.
1842          this.options = {};
1843          this.previousSeriesStack = [];
1844          // Namespece to hold plugins.  Generally non-renderer plugins add themselves to here.
1845          this.plugins = {};
1846          // prop: series
1847          // Array of series object options.
1848          // see <Series> for series specific options.
1849          this.series = [];
1850          // array of series indicies. Keep track of order
1851          // which series canvases are displayed, lowest
1852          // to highest, back to front.
1853          this.seriesStack = [];
1854          // prop: seriesColors
1855          // Ann array of CSS color specifications that will be applied, in order,
1856          // to the series in the plot.  Colors will wrap around so, if their
1857          // are more series than colors, colors will be reused starting at the
1858          // beginning.  For pie charts, this specifies the colors of the slices.
1859          this.seriesColors = $.jqplot.config.defaultColors;
1860          // prop: sortData
1861          // false to not sort the data passed in by the user.
1862          // Many bar, stakced and other graphs as well as many plugins depend on
1863          // having sorted data.
1864          this.sortData = true;
1865          // prop: stackSeries
1866          // true or false, creates a stack or "mountain" plot.
1867          // Not all series renderers may implement this option.
1868          this.stackSeries = false;
1869          // a shortcut for axis syncTicks options.  Not implemented yet.
1870          this.syncXTicks = true;
1871          // a shortcut for axis syncTicks options.  Not implemented yet.
1872          this.syncYTicks = true;
1873          // the jquery object for the dom target.
1874          this.target = null; 
1875          // The id of the dom element to render the plot into
1876          this.targetId = null;
1877          // prop textColor
1878          // css spec for the css color attribute.  Default for the entire plot.
1879          this.textColor;
1880          // prop: title
1881          // Title object.  See <Title> for specific options.  As a shortcut, you
1882          // can specify the title option as just a string like: title: 'My Plot'
1883          // and this will create a new title object with the specified text.
1884          this.title = new Title();
1885          // Count how many times the draw method has been called while the plot is visible.
1886          // Mostly used to test if plot has never been dran (=0), has been successfully drawn
1887          // into a visible container once (=1) or draw more than once into a visible container.
1888          // Can use this in tests to see if plot has been visibly drawn at least one time.
1889          // After plot has been visibly drawn once, it generally doesn't need redrawn if its
1890          // container is hidden and shown.
1891          this._drawCount = 0;
1892          // sum of y values for all series in plot.
1893          // used in mekko chart.
1894          this._sumy = 0;
1895          this._sumx = 0;
1896          // array to hold the cumulative stacked series data.
1897          // used to ajust the individual series data, which won't have access to other
1898          // series data.
1899          this._stackData = [];
1900          // array that holds the data to be plotted. This will be the series data
1901          // merged with the the appropriate data from _stackData according to the stackAxis.
1902          this._plotData = [];
1903          this._width = null;
1904          this._height = null; 
1905          this._plotDimensions = {height:null, width:null};
1906          this._gridPadding = {top:null, right:null, bottom:null, left:null};
1907          this._defaultGridPadding = {top:10, right:10, bottom:23, left:10};
1908  
1909          this._addDomReference = $.jqplot.config.addDomReference;
1910  
1911          this.preInitHooks = new $.jqplot.HooksManager();
1912          this.postInitHooks = new $.jqplot.HooksManager();
1913          this.preParseOptionsHooks = new $.jqplot.HooksManager();
1914          this.postParseOptionsHooks = new $.jqplot.HooksManager();
1915          this.preDrawHooks = new $.jqplot.HooksManager();
1916          this.postDrawHooks = new $.jqplot.HooksManager();
1917          this.preDrawSeriesHooks = new $.jqplot.HooksManager();
1918          this.postDrawSeriesHooks = new $.jqplot.HooksManager();
1919          this.preDrawLegendHooks = new $.jqplot.HooksManager();
1920          this.addLegendRowHooks = new $.jqplot.HooksManager();
1921          this.preSeriesInitHooks = new $.jqplot.HooksManager();
1922          this.postSeriesInitHooks = new $.jqplot.HooksManager();
1923          this.preParseSeriesOptionsHooks = new $.jqplot.HooksManager();
1924          this.postParseSeriesOptionsHooks = new $.jqplot.HooksManager();
1925          this.eventListenerHooks = new $.jqplot.EventListenerManager();
1926          this.preDrawSeriesShadowHooks = new $.jqplot.HooksManager();
1927          this.postDrawSeriesShadowHooks = new $.jqplot.HooksManager();
1928          
1929          this.colorGenerator = new $.jqplot.ColorGenerator();
1930          this.negativeColorGenerator = new $.jqplot.ColorGenerator();
1931  
1932          this.canvasManager = new $.jqplot.CanvasManager();
1933  
1934          this.themeEngine = new $.jqplot.ThemeEngine();
1935          
1936          var seriesColorsIndex = 0;
1937  
1938          // Group: methods
1939          //
1940          // method: init
1941          // sets the plot target, checks data and applies user
1942          // options to plot.
1943          this.init = function(target, data, options) {
1944              options = options || {};
1945              for (var i=0; i<$.jqplot.preInitHooks.length; i++) {
1946                  $.jqplot.preInitHooks[i].call(this, target, data, options);
1947              }
1948  
1949              for (var i=0; i<this.preInitHooks.hooks.length; i++) {
1950                  this.preInitHooks.hooks[i].call(this, target, data, options);
1951              }
1952              
1953              this.targetId = '#'+target;
1954              this.target = $('#'+target);
1955  
1956              //////
1957              // Add a reference to plot
1958              //////
1959              if (this._addDomReference) {
1960                  this.target.data('jqplot', this);
1961              }
1962              // remove any error class that may be stuck on target.
1963              this.target.removeClass('jqplot-error');
1964              if (!this.target.get(0)) {
1965                  throw "No plot target specified";
1966              }
1967              
1968              // make sure the target is positioned by some means and set css
1969              if (this.target.css('position') == 'static') {
1970                  this.target.css('position', 'relative');
1971              }
1972              if (!this.target.hasClass('jqplot-target')) {
1973                  this.target.addClass('jqplot-target');
1974              }
1975              
1976              // if no height or width specified, use a default.
1977              if (!this.target.height()) {
1978                  var h;
1979                  if (options && options.height) {
1980                      h = parseInt(options.height, 10);
1981                  }
1982                  else if (this.target.attr('data-height')) {
1983                      h = parseInt(this.target.attr('data-height'), 10);
1984                  }
1985                  else {
1986                      h = parseInt($.jqplot.config.defaultHeight, 10);
1987                  }
1988                  this._height = h;
1989                  this.target.css('height', h+'px');
1990              }
1991              else {
1992                  this._height = h = this.target.height();
1993              }
1994              if (!this.target.width()) {
1995                  var w;
1996                  if (options && options.width) {
1997                      w = parseInt(options.width, 10);
1998                  }
1999                  else if (this.target.attr('data-width')) {
2000                      w = parseInt(this.target.attr('data-width'), 10);
2001                  }
2002                  else {
2003                      w = parseInt($.jqplot.config.defaultWidth, 10);
2004                  }
2005                  this._width = w;
2006                  this.target.css('width', w+'px');
2007              }
2008              else {
2009                  this._width = w = this.target.width();
2010              }
2011  
2012              for (var i=0, l=_axisNames.length; i<l; i++) {
2013                  this.axes[_axisNames[i]] = new Axis(_axisNames[i]);
2014              }
2015              
2016              this._plotDimensions.height = this._height;
2017              this._plotDimensions.width = this._width;
2018              this.grid._plotDimensions = this._plotDimensions;
2019              this.title._plotDimensions = this._plotDimensions;
2020              this.baseCanvas._plotDimensions = this._plotDimensions;
2021              this.eventCanvas._plotDimensions = this._plotDimensions;
2022              this.legend._plotDimensions = this._plotDimensions;
2023              if (this._height <=0 || this._width <=0 || !this._height || !this._width) {
2024                  throw "Canvas dimension not set";
2025              }
2026              
2027              if (options.dataRenderer && $.isFunction(options.dataRenderer)) {
2028                  if (options.dataRendererOptions) {
2029                      this.dataRendererOptions = options.dataRendererOptions;
2030                  }
2031                  this.dataRenderer = options.dataRenderer;
2032                  data = this.dataRenderer(data, this, this.dataRendererOptions);
2033              }
2034              
2035              if (options.noDataIndicator && $.isPlainObject(options.noDataIndicator)) {
2036                  $.extend(true, this.noDataIndicator, options.noDataIndicator);
2037              }
2038              
2039              if (data == null || $.isArray(data) == false || data.length == 0 || $.isArray(data[0]) == false || data[0].length == 0) {
2040                  
2041                  if (this.noDataIndicator.show == false) {
2042                      throw "No Data";
2043                  }
2044                  
2045                  else {
2046                      // have to be descructive here in order for plot to not try and render series.
2047                      // This means that $.jqplot() will have to be called again when there is data.
2048                      //delete options.series;
2049                      
2050                      for (var ax in this.noDataIndicator.axes) {
2051                          for (var prop in this.noDataIndicator.axes[ax]) {
2052                              this.axes[ax][prop] = this.noDataIndicator.axes[ax][prop];
2053                          }
2054                      }
2055                      
2056                      this.postDrawHooks.add(function() {
2057                          var eh = this.eventCanvas.getHeight();
2058                          var ew = this.eventCanvas.getWidth();
2059                          var temp = $('<div class="jqplot-noData-container" style="position:absolute;"></div>');
2060                          this.target.append(temp);
2061                          temp.height(eh);
2062                          temp.width(ew);
2063                          temp.css('top', this.eventCanvas._offsets.top);
2064                          temp.css('left', this.eventCanvas._offsets.left);
2065                          
2066                          var temp2 = $('<div class="jqplot-noData-contents" style="text-align:center; position:relative; margin-left:auto; margin-right:auto;"></div>');
2067                          temp.append(temp2);
2068                          temp2.html(this.noDataIndicator.indicator);
2069                          var th = temp2.height();
2070                          var tw = temp2.width();
2071                          temp2.height(th);
2072                          temp2.width(tw);
2073                          temp2.css('top', (eh - th)/2 + 'px');
2074                      });
2075  
2076                  }
2077              }
2078              
2079              // make a copy of the data
2080              this.data = $.extend(true, [], data);
2081              
2082              this.parseOptions(options);
2083              
2084              if (this.textColor) {
2085                  this.target.css('color', this.textColor);
2086              }
2087              if (this.fontFamily) {
2088                  this.target.css('font-family', this.fontFamily);
2089              }
2090              if (this.fontSize) {
2091                  this.target.css('font-size', this.fontSize);
2092              }
2093              
2094              this.title.init();
2095              this.legend.init();
2096              this._sumy = 0;
2097              this._sumx = 0;
2098              for (var i=0; i<this.series.length; i++) {
2099                  // set default stacking order for series canvases
2100                  this.seriesStack.push(i);
2101                  this.previousSeriesStack.push(i);
2102                  this.series[i].shadowCanvas._plotDimensions = this._plotDimensions;
2103                  this.series[i].canvas._plotDimensions = this._plotDimensions;
2104                  for (var j=0; j<$.jqplot.preSeriesInitHooks.length; j++) {
2105                      $.jqplot.preSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
2106                  }
2107                  for (var j=0; j<this.preSeriesInitHooks.hooks.length; j++) {
2108                      this.preSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
2109                  }
2110                  this.populatePlotData(this.series[i], i);
2111                  this.series[i]._plotDimensions = this._plotDimensions;
2112                  this.series[i].init(i, this.grid.borderWidth, this);
2113                  for (var j=0; j<$.jqplot.postSeriesInitHooks.length; j++) {
2114                      $.jqplot.postSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
2115                  }
2116                  for (var j=0; j<this.postSeriesInitHooks.hooks.length; j++) {
2117                      this.postSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
2118                  }
2119                  this._sumy += this.series[i]._sumy;
2120                  this._sumx += this.series[i]._sumx;
2121              }
2122  
2123              var name,
2124                  axis;
2125              for (var i=0, l=_axisNames.length; i<l; i++) {
2126                  name = _axisNames[i];
2127                  axis = this.axes[name];
2128                  axis._plotDimensions = this._plotDimensions;
2129                  axis.init();
2130                  if (this.axes[name].borderColor == null) {
2131                      if (name.charAt(0) !== 'x' && axis.useSeriesColor === true && axis.show) {
2132                          axis.borderColor = axis._series[0].color;
2133                      }
2134                      else {
2135                          axis.borderColor = this.grid.borderColor;
2136                      }
2137                  }
2138              }
2139              
2140              if (this.sortData) {
2141                  sortData(this.series);
2142              }
2143              this.grid.init();
2144              this.grid._axes = this.axes;
2145              
2146              this.legend._series = this.series;
2147  
2148              for (var i=0; i<$.jqplot.postInitHooks.length; i++) {
2149                  $.jqplot.postInitHooks[i].call(this, target, this.data, options);
2150              }
2151  
2152              for (var i=0; i<this.postInitHooks.hooks.length; i++) {
2153                  this.postInitHooks.hooks[i].call(this, target, this.data, options);
2154              }
2155          };  
2156          
2157          // method: resetAxesScale
2158          // Reset the specified axes min, max, numberTicks and tickInterval properties to null
2159          // or reset these properties on all axes if no list of axes is provided.
2160          //
2161          // Parameters:
2162          // axes - Boolean to reset or not reset all axes or an array or object of axis names to reset.
2163          this.resetAxesScale = function(axes, options) {
2164              var opts = options || {};
2165              var ax = axes || this.axes;
2166              if (ax === true) {
2167                  ax = this.axes;
2168              }
2169              if ($.isArray(ax)) {
2170                  for (var i = 0; i < ax.length; i++) {
2171                      this.axes[ax[i]].resetScale(opts[ax[i]]);
2172                  }
2173              }
2174              else if (typeof(ax) === 'object') {
2175                  for (var name in ax) {
2176                      this.axes[name].resetScale(opts[name]);
2177                  }
2178              }
2179          };
2180          // method: reInitialize
2181          // reinitialize plot for replotting.
2182          // not called directly.
2183          this.reInitialize = function (data, opts) {
2184              // Plot should be visible and have a height and width.
2185              // If plot doesn't have height and width for some
2186              // reason, set it by other means.  Plot must not have
2187              // a display:none attribute, however.
2188  
2189              var options = $.extend(true, {}, this.options, opts);
2190  
2191              var target = this.targetId.substr(1);
2192              var tdata = (data == null) ? this.data : data;
2193  
2194              for (var i=0; i<$.jqplot.preInitHooks.length; i++) {
2195                  $.jqplot.preInitHooks[i].call(this, target, tdata, options);
2196              }
2197  
2198              for (var i=0; i<this.preInitHooks.hooks.length; i++) {
2199                  this.preInitHooks.hooks[i].call(this, target, tdata, options);
2200              }
2201              
2202              this._height = this.target.height();
2203              this._width = this.target.width();
2204              
2205              if (this._height <=0 || this._width <=0 || !this._height || !this._width) {
2206                  throw "Target dimension not set";
2207              }
2208              
2209              this._plotDimensions.height = this._height;
2210              this._plotDimensions.width = this._width;
2211              this.grid._plotDimensions = this._plotDimensions;
2212              this.title._plotDimensions = this._plotDimensions;
2213              this.baseCanvas._plotDimensions = this._plotDimensions;
2214              this.eventCanvas._plotDimensions = this._plotDimensions;
2215              this.legend._plotDimensions = this._plotDimensions;
2216  
2217              var name,
2218                  t, 
2219                  j, 
2220                  axis;
2221  
2222              for (var i=0, l=_axisNames.length; i<l; i++) {
2223                  name = _axisNames[i];
2224                  axis = this.axes[name];
2225  
2226                  // Memory Leaks patch : clear ticks elements
2227                  t = axis._ticks;
2228                  for (var j = 0, tlen = t.length; j < tlen; j++) {
2229                    var el = t[j]._elem;
2230                    if (el) {
2231                      // if canvas renderer
2232                      if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) {
2233                        window.G_vmlCanvasManager.uninitElement(el.get(0));
2234                      }
2235                      el.emptyForce();
2236                      el = null;
2237                      t._elem = null;
2238                    }
2239                  }
2240                  t = null;
2241  
2242                  delete axis.ticks;
2243                  delete axis._ticks;
2244                  this.axes[name] = new Axis(name);
2245                  this.axes[name]._plotWidth = this._width;
2246                  this.axes[name]._plotHeight = this._height;
2247              }
2248              
2249              if (data) {
2250                  if (options.dataRenderer && $.isFunction(options.dataRenderer)) {
2251                      if (options.dataRendererOptions) {
2252                          this.dataRendererOptions = options.dataRendererOptions;
2253                      }
2254                      this.dataRenderer = options.dataRenderer;
2255                      data = this.dataRenderer(data, this, this.dataRendererOptions);
2256                  }
2257                  
2258                  // make a copy of the data
2259                  this.data = $.extend(true, [], data);
2260              }
2261  
2262              if (opts) {
2263                  this.parseOptions(options);
2264              }
2265              
2266              this.title._plotWidth = this._width;
2267              
2268              if (this.textColor) {
2269                  this.target.css('color', this.textColor);
2270              }
2271              if (this.fontFamily) {
2272                  this.target.css('font-family', this.fontFamily);
2273              }
2274              if (this.fontSize) {
2275                  this.target.css('font-size', this.fontSize);
2276              }
2277  
2278              this.title.init();
2279              this.legend.init();
2280              this._sumy = 0;
2281              this._sumx = 0;
2282  
2283              this.seriesStack = [];
2284              this.previousSeriesStack = [];
2285  
2286              for (var i=0, l=this.series.length; i<l; i++) {
2287                  // set default stacking order for series canvases
2288                  this.seriesStack.push(i);
2289                  this.previousSeriesStack.push(i);
2290                  this.series[i].shadowCanvas._plotDimensions = this._plotDimensions;
2291                  this.series[i].canvas._plotDimensions = this._plotDimensions;
2292                  for (var j=0; j<$.jqplot.preSeriesInitHooks.length; j++) {
2293                      $.jqplot.preSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
2294                  }
2295                  for (var j=0; j<this.preSeriesInitHooks.hooks.length; j++) {
2296                      this.preSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
2297                  }
2298                  this.populatePlotData(this.series[i], i);
2299                  this.series[i]._plotDimensions = this._plotDimensions;
2300                  this.series[i].init(i, this.grid.borderWidth, this);
2301                  for (var j=0; j<$.jqplot.postSeriesInitHooks.length; j++) {
2302                      $.jqplot.postSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
2303                  }
2304                  for (var j=0; j<this.postSeriesInitHooks.hooks.length; j++) {
2305                      this.postSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
2306                  }
2307                  this._sumy += this.series[i]._sumy;
2308                  this._sumx += this.series[i]._sumx;
2309              }
2310  
2311              for (var i=0, l=_axisNames.length; i<l; i++) {
2312                  name = _axisNames[i];
2313                  axis = this.axes[name];
2314  
2315                  axis._plotDimensions = this._plotDimensions;
2316                  axis.init();
2317                  if (axis.borderColor == null) {
2318                      if (name.charAt(0) !== 'x' && axis.useSeriesColor === true && axis.show) {
2319                          axis.borderColor = axis._series[0].color;
2320                      }
2321                      else {
2322                          axis.borderColor = this.grid.borderColor;
2323                      }
2324                  }
2325              }
2326              
2327              if (this.sortData) {
2328                  sortData(this.series);
2329              }
2330              this.grid.init();
2331              this.grid._axes = this.axes;
2332              
2333              this.legend._series = this.series;
2334  
2335              for (var i=0, l=$.jqplot.postInitHooks.length; i<l; i++) {
2336                  $.jqplot.postInitHooks[i].call(this, target, this.data, options);
2337              }
2338  
2339              for (var i=0, l=this.postInitHooks.hooks.length; i<l; i++) {
2340                  this.postInitHooks.hooks[i].call(this, target, this.data, options);
2341              }
2342          };
2343  
2344  
2345  
2346          // method: quickInit
2347          // 
2348          // Quick reinitialization plot for replotting.
2349          // Does not parse options ore recreate axes and series.
2350          // not called directly.
2351          this.quickInit = function () {
2352              // Plot should be visible and have a height and width.
2353              // If plot doesn't have height and width for some
2354              // reason, set it by other means.  Plot must not have
2355              // a display:none attribute, however.
2356              
2357              this._height = this.target.height();
2358              this._width = this.target.width();
2359              
2360              if (this._height <=0 || this._width <=0 || !this._height || !this._width) {
2361                  throw "Target dimension not set";
2362              }
2363              
2364              this._plotDimensions.height = this._height;
2365              this._plotDimensions.width = this._width;
2366              this.grid._plotDimensions = this._plotDimensions;
2367              this.title._plotDimensions = this._plotDimensions;
2368              this.baseCanvas._plotDimensions = this._plotDimensions;
2369              this.eventCanvas._plotDimensions = this._plotDimensions;
2370              this.legend._plotDimensions = this._plotDimensions;
2371              
2372              for (var n in this.axes) {
2373                  this.axes[n]._plotWidth = this._width;
2374                  this.axes[n]._plotHeight = this._height;
2375              }
2376              
2377              this.title._plotWidth = this._width;
2378              
2379              if (this.textColor) {
2380                  this.target.css('color', this.textColor);
2381              }
2382              if (this.fontFamily) {
2383                  this.target.css('font-family', this.fontFamily);
2384              }
2385              if (this.fontSize) {
2386                  this.target.css('font-size', this.fontSize);
2387              }
2388              
2389              this._sumy = 0;
2390              this._sumx = 0;
2391              for (var i=0; i<this.series.length; i++) {
2392                  this.populatePlotData(this.series[i], i);
2393                  if (this.series[i]._type === 'line' && this.series[i].renderer.bands.show) {
2394                      this.series[i].renderer.initBands.call(this.series[i], this.series[i].renderer.options, this);
2395                  }
2396                  this.series[i]._plotDimensions = this._plotDimensions;
2397                  this.series[i].canvas._plotDimensions = this._plotDimensions;
2398                  //this.series[i].init(i, this.grid.borderWidth);
2399                  this._sumy += this.series[i]._sumy;
2400                  this._sumx += this.series[i]._sumx;
2401              }
2402  
2403              var name;
2404              
2405              for (var j=0; j<12; j++) {
2406                  name = _axisNames[j];
2407                  // Memory Leaks patch : clear ticks elements
2408                  var t = this.axes[name]._ticks;
2409                  for (var i = 0; i < t.length; i++) {
2410                    var el = t[i]._elem;
2411                    if (el) {
2412                      // if canvas renderer
2413                      if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) {
2414                        window.G_vmlCanvasManager.uninitElement(el.get(0));
2415                      }
2416                      el.emptyForce();
2417                      el = null;
2418                      t._elem = null;
2419                    }
2420                  }
2421                  t = null;
2422                  
2423                  this.axes[name]._plotDimensions = this._plotDimensions;
2424                  this.axes[name]._ticks = [];
2425                  // this.axes[name].renderer.init.call(this.axes[name], {});
2426              }
2427              
2428              if (this.sortData) {
2429                  sortData(this.series);
2430              }
2431              
2432              this.grid._axes = this.axes;
2433              
2434              this.legend._series = this.series;
2435          };
2436          
2437          // sort the series data in increasing order.
2438          function sortData(series) {
2439              var d, sd, pd, ppd, ret;
2440              for (var i=0; i<series.length; i++) {
2441                  var check;
2442                  var bat = [series[i].data, series[i]._stackData, series[i]._plotData, series[i]._prevPlotData];
2443                  for (var n=0; n<4; n++) {
2444                      check = true;
2445                      d = bat[n];
2446                      if (series[i]._stackAxis == 'x') {
2447                          for (var j = 0; j < d.length; j++) {
2448                              if (typeof(d[j][1]) != "number") {
2449                                  check = false;
2450                                  break;
2451                              }
2452                          }
2453                          if (check) {
2454                              d.sort(function(a,b) { return a[1] - b[1]; });
2455                          }
2456                      }
2457                      else {
2458                          for (var j = 0; j < d.length; j++) {
2459                              if (typeof(d[j][0]) != "number") {
2460                                  check = false;
2461                                  break;
2462                              }
2463                          }
2464                          if (check) {
2465                              d.sort(function(a,b) { return a[0] - b[0]; });
2466                          }
2467                      }
2468                  }
2469                 
2470              }
2471          }
2472          
2473          // populate the _stackData and _plotData arrays for the plot and the series.
2474          this.populatePlotData = function(series, index) {
2475              // if a stacked chart, compute the stacked data
2476              this._plotData = [];
2477              this._stackData = [];
2478              series._stackData = [];
2479              series._plotData = [];
2480              var plotValues = {x:[], y:[]};
2481              if (this.stackSeries && !series.disableStack) {
2482                  series._stack = true;
2483                  var sidx = series._stackAxis == 'x' ? 0 : 1;
2484                  var idx = sidx ? 0 : 1;
2485                  // push the current data into stackData
2486                  //this._stackData.push(this.series[i].data);
2487                  var temp = $.extend(true, [], series.data);
2488                  // create the data that will be plotted for this series
2489                  var plotdata = $.extend(true, [], series.data);
2490                  // for first series, nothing to add to stackData.
2491                  for (var j=0; j<index; j++) {
2492                      var cd = this.series[j].data;
2493                      for (var k=0; k<cd.length; k++) {
2494                          temp[k][0] += cd[k][0];
2495                          temp[k][1] += cd[k][1];
2496                          // only need to sum up the stack axis column of data
2497                          // and only sum if it is of same sign.
2498                          if (series.data[k][sidx] * cd[k][sidx] >= 0) {
2499                              plotdata[k][sidx] += cd[k][sidx];
2500                          }
2501                      }
2502                  }
2503                  for (var i=0; i<plotdata.length; i++) {
2504                      plotValues.x.push(plotdata[i][0]);
2505                      plotValues.y.push(plotdata[i][1]);
2506                  }
2507                  this._plotData.push(plotdata);
2508                  this._stackData.push(temp);
2509                  series._stackData = temp;
2510                  series._plotData = plotdata;
2511                  series._plotValues = plotValues;
2512              }
2513              else {
2514                  for (var i=0; i<series.data.length; i++) {
2515                      plotValues.x.push(series.data[i][0]);
2516                      plotValues.y.push(series.data[i][1]);
2517                  }
2518                  this._stackData.push(series.data);
2519                  this.series[index]._stackData = series.data;
2520                  this._plotData.push(series.data);
2521                  series._plotData = series.data;
2522                  series._plotValues = plotValues;
2523              }
2524              if (index>0) {
2525                  series._prevPlotData = this.series[index-1]._plotData;
2526              }
2527              series._sumy = 0;
2528              series._sumx = 0;
2529              for (i=series.data.length-1; i>-1; i--) {
2530                  series._sumy += series.data[i][1];
2531                  series._sumx += series.data[i][0];
2532              }
2533          };
2534          
2535          // function to safely return colors from the color array and wrap around at the end.
2536          this.getNextSeriesColor = (function(t) {
2537              var idx = 0;
2538              var sc = t.seriesColors;
2539              
2540              return function () { 
2541                  if (idx < sc.length) {
2542                      return sc[idx++];
2543                  }
2544                  else {
2545                      idx = 0;
2546                      return sc[idx++];
2547                  }
2548              };
2549          })(this);
2550      
2551          this.parseOptions = function(options){
2552              for (var i=0; i<this.preParseOptionsHooks.hooks.length; i++) {
2553                  this.preParseOptionsHooks.hooks[i].call(this, options);
2554              }
2555              for (var i=0; i<$.jqplot.preParseOptionsHooks.length; i++) {
2556                  $.jqplot.preParseOptionsHooks[i].call(this, options);
2557              }
2558              this.options = $.extend(true, {}, this.defaults, options);
2559              var opts = this.options;
2560              this.animate = opts.animate;
2561              this.animateReplot = opts.animateReplot;
2562              this.stackSeries = opts.stackSeries;
2563              if ($.isPlainObject(opts.fillBetween)) {
2564  
2565                  var temp = ['series1', 'series2', 'color', 'baseSeries', 'fill'], 
2566                      tempi;
2567  
2568                  for (var i=0, l=temp.length; i<l; i++) {
2569                      tempi = temp[i];
2570                      if (opts.fillBetween[tempi] != null) {
2571                          this.fillBetween[tempi] = opts.fillBetween[tempi];
2572                      }
2573                  }
2574              }
2575  
2576              if (opts.seriesColors) {
2577                  this.seriesColors = opts.seriesColors;
2578              }
2579              if (opts.negativeSeriesColors) {
2580                  this.negativeSeriesColors = opts.negativeSeriesColors;
2581              }
2582              if (opts.captureRightClick) {
2583                  this.captureRightClick = opts.captureRightClick;
2584              }
2585              this.defaultAxisStart = (options && options.defaultAxisStart != null) ? options.defaultAxisStart : this.defaultAxisStart;
2586              this.colorGenerator.setColors(this.seriesColors);
2587              this.negativeColorGenerator.setColors(this.negativeSeriesColors);
2588              // var cg = new this.colorGenerator(this.seriesColors);
2589              // var ncg = new this.colorGenerator(this.negativeSeriesColors);
2590              // this._gridPadding = this.options.gridPadding;
2591              $.extend(true, this._gridPadding, opts.gridPadding);
2592              this.sortData = (opts.sortData != null) ? opts.sortData : this.sortData;
2593              for (var i=0; i<12; i++) {
2594                  var n = _axisNames[i];
2595                  var axis = this.axes[n];
2596                  axis._options = $.extend(true, {}, opts.axesDefaults, opts.axes[n]);
2597                  $.extend(true, axis, opts.axesDefaults, opts.axes[n]);
2598                  axis._plotWidth = this._width;
2599                  axis._plotHeight = this._height;
2600              }
2601              // if (this.data.length == 0) {
2602              //     this.data = [];
2603              //     for (var i=0; i<this.options.series.length; i++) {
2604              //         this.data.push(this.options.series.data);
2605              //     }    
2606              // }
2607                  
2608              var normalizeData = function(data, dir, start) {
2609                  // return data as an array of point arrays,
2610                  // in form [[x1,y1...], [x2,y2...], ...]
2611                  var temp = [];
2612                  var i;
2613                  dir = dir || 'vertical';
2614                  if (!$.isArray(data[0])) {
2615                      // we have a series of scalars.  One line with just y values.
2616                      // turn the scalar list of data into a data array of form:
2617                      // [[1, data[0]], [2, data[1]], ...]
2618                      for (i=0; i<data.length; i++) {
2619                          if (dir == 'vertical') {
2620                              temp.push([start + i, data[i]]);   
2621                          }
2622                          else {
2623                              temp.push([data[i], start+i]);
2624                          }
2625                      }
2626                  }            
2627                  else {
2628                      // we have a properly formatted data series, copy it.
2629                      $.extend(true, temp, data);
2630                  }
2631                  return temp;
2632              };
2633  
2634              var colorIndex = 0;
2635              this.series = [];
2636              for (var i=0; i<this.data.length; i++) {
2637                  var temp = new Series();
2638                  for (var j=0; j<$.jqplot.preParseSeriesOptionsHooks.length; j++) {
2639                      $.jqplot.preParseSeriesOptionsHooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]);
2640                  }
2641                  for (var j=0; j<this.preParseSeriesOptionsHooks.hooks.length; j++) {
2642                      this.preParseSeriesOptionsHooks.hooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]);
2643                  }
2644                  $.extend(true, temp, {seriesColors:this.seriesColors, negativeSeriesColors:this.negativeSeriesColors}, this.options.seriesDefaults, this.options.series[i], {rendererOptions:{animation:{show: this.animate}}});
2645                  var dir = 'vertical';
2646                  if (temp.renderer === $.jqplot.BarRenderer && temp.rendererOptions && temp.rendererOptions.barDirection == 'horizontal' && temp.transposeData === true) {
2647                      dir = 'horizontal';
2648                  }
2649                  temp.data = normalizeData(this.data[i], dir, this.defaultAxisStart);
2650                  switch (temp.xaxis) {
2651                      case 'xaxis':
2652                          temp._xaxis = this.axes.xaxis;
2653                          break;
2654                      case 'x2axis':
2655                          temp._xaxis = this.axes.x2axis;
2656                          break;
2657                      default:
2658                          break;
2659                  }
2660                  temp._yaxis = this.axes[temp.yaxis];
2661                  temp._xaxis._series.push(temp);
2662                  temp._yaxis._series.push(temp);
2663                  if (temp.show) {
2664                      temp._xaxis.show = true;
2665                      temp._yaxis.show = true;
2666                  }
2667                  else {
2668                      if (temp._xaxis.scaleToHiddenSeries) {
2669                          temp._xaxis.show = true;
2670                      }
2671                      if (temp._yaxis.scaleToHiddenSeries) {
2672                          temp._yaxis.show = true;
2673                      }
2674                  }
2675  
2676                  // // parse the renderer options and apply default colors if not provided
2677                  // if (!temp.color && temp.show != false) {
2678                  //     temp.color = cg.next();
2679                  //     colorIndex = cg.getIndex() - 1;;
2680                  // }
2681                  // if (!temp.negativeColor && temp.show != false) {
2682                  //     temp.negativeColor = ncg.get(colorIndex);
2683                  //     ncg.setIndex(colorIndex);
2684                  // }
2685                  if (!temp.label) {
2686                      temp.label = 'Series '+ (i+1).toString();
2687                  }
2688                  // temp.rendererOptions.show = temp.show;
2689                  // $.extend(true, temp.renderer, {color:this.seriesColors[i]}, this.rendererOptions);
2690                  this.series.push(temp);  
2691                  for (var j=0; j<$.jqplot.postParseSeriesOptionsHooks.length; j++) {
2692                      $.jqplot.postParseSeriesOptionsHooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]);
2693                  }
2694                  for (var j=0; j<this.postParseSeriesOptionsHooks.hooks.length; j++) {
2695                      this.postParseSeriesOptionsHooks.hooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]);
2696                  }
2697              }
2698              
2699              // copy the grid and title options into this object.
2700              $.extend(true, this.grid, this.options.grid);
2701              // if axis border properties aren't set, set default.
2702              for (var i=0, l=_axisNames.length; i<l; i++) {
2703                  var n = _axisNames[i];
2704                  var axis = this.axes[n];
2705                  if (axis.borderWidth == null) {
2706                      axis.borderWidth =this.grid.borderWidth;
2707                  }
2708              }
2709              
2710              if (typeof this.options.title == 'string') {
2711                  this.title.text = this.options.title;
2712              }
2713              else if (typeof this.options.title == 'object') {
2714                  $.extend(true, this.title, this.options.title);
2715              }
2716              this.title._plotWidth = this._width;
2717              this.legend.setOptions(this.options.legend);
2718              
2719              for (var i=0; i<$.jqplot.postParseOptionsHooks.length; i++) {
2720                  $.jqplot.postParseOptionsHooks[i].call(this, options);
2721              }
2722              for (var i=0; i<this.postParseOptionsHooks.hooks.length; i++) {
2723                  this.postParseOptionsHooks.hooks[i].call(this, options);
2724              }
2725          };
2726          
2727          // method: destroy
2728          // Releases all resources occupied by the plot
2729          this.destroy = function() {
2730              this.canvasManager.freeAllCanvases();
2731              if (this.eventCanvas && this.eventCanvas._elem) {
2732                  this.eventCanvas._elem.unbind();
2733              }
2734              // Couple of posts on Stack Overflow indicate that empty() doesn't
2735              // always cear up the dom and release memory.  Sometimes setting
2736              // innerHTML property to null is needed.  Particularly on IE, may 
2737              // have to directly set it to null, bypassing $.
2738              this.target.empty();
2739  
2740              this.target[0].innerHTML = '';
2741          };
2742          
2743          // method: replot
2744          // Does a reinitialization of the plot followed by
2745          // a redraw.  Method could be used to interactively
2746          // change plot characteristics and then replot.
2747          //
2748          // Parameters:
2749          // options - Options used for replotting.
2750          //
2751          // Properties:
2752          // clear - false to not clear (empty) the plot container before replotting (default: true).
2753          // resetAxes - true to reset all axes min, max, numberTicks and tickInterval setting so axes will rescale themselves.
2754          //             optionally pass in list of axes to reset (e.g. ['xaxis', 'y2axis']) (default: false).
2755          this.replot = function(options) {
2756              var opts =  options || {};
2757              var data = opts.data || null;
2758              var clear = (opts.clear === false) ? false : true;
2759              var resetAxes = opts.resetAxes || false;
2760              delete opts.data;
2761              delete opts.clear;
2762              delete opts.resetAxes;
2763  
2764              this.target.trigger('jqplotPreReplot');
2765              
2766              if (clear) {
2767                  this.destroy();
2768              }
2769              // if have data or other options, full reinit.
2770              // otherwise, quickinit.
2771              if (data || !$.isEmptyObject(opts)) {
2772                  this.reInitialize(data, opts);
2773              }
2774              else {
2775                  this.quickInit();
2776              }
2777  
2778              if (resetAxes) {
2779                  this.resetAxesScale(resetAxes, opts.axes);
2780              }
2781              this.draw();
2782              this.target.trigger('jqplotPostReplot');
2783          };
2784          
2785          // method: redraw
2786          // Empties the plot target div and redraws the plot.
2787          // This enables plot data and properties to be changed
2788          // and then to comletely clear the plot and redraw.
2789          // redraw *will not* reinitialize any plot elements.
2790          // That is, axes will not be autoscaled and defaults
2791          // will not be reapplied to any plot elements.  redraw
2792          // is used primarily with zooming. 
2793          //
2794          // Parameters:
2795          // clear - false to not clear (empty) the plot container before redrawing (default: true).
2796          this.redraw = function(clear) {
2797              clear = (clear != null) ? clear : true;
2798              this.target.trigger('jqplotPreRedraw');
2799              if (clear) {
2800                  this.canvasManager.freeAllCanvases();
2801                  this.eventCanvas._elem.unbind();
2802                  // Dont think I bind any events to the target, this shouldn't be necessary.
2803                  // It will remove user's events.
2804                  // this.target.unbind();
2805                  this.target.empty();
2806              }
2807               for (var ax in this.axes) {
2808                  this.axes[ax]._ticks = [];
2809              }
2810              for (var i=0; i<this.series.length; i++) {
2811                  this.populatePlotData(this.series[i], i);
2812              }
2813              this._sumy = 0;
2814              this._sumx = 0;
2815              for (i=0; i<this.series.length; i++) {
2816                  this._sumy += this.series[i]._sumy;
2817                  this._sumx += this.series[i]._sumx;
2818              }
2819              this.draw();
2820              this.target.trigger('jqplotPostRedraw');
2821          };
2822          
2823          // method: draw
2824          // Draws all elements of the plot into the container.
2825          // Does not clear the container before drawing.
2826          this.draw = function(){
2827              if (this.drawIfHidden || this.target.is(':visible')) {
2828                  this.target.trigger('jqplotPreDraw');
2829                  var i,
2830                      j,
2831                      l,
2832                      tempseries;
2833                  for (i=0, l=$.jqplot.preDrawHooks.length; i<l; i++) {
2834                      $.jqplot.preDrawHooks[i].call(this);
2835                  }
2836                  for (i=0, l=this.preDrawHooks.length; i<l; i++) {
2837                      this.preDrawHooks.hooks[i].apply(this, this.preDrawSeriesHooks.args[i]);
2838                  }
2839                  // create an underlying canvas to be used for special features.
2840                  this.target.append(this.baseCanvas.createElement({left:0, right:0, top:0, bottom:0}, 'jqplot-base-canvas', null, this));
2841                  this.baseCanvas.setContext();
2842                  this.target.append(this.title.draw());
2843                  this.title.pack({top:0, left:0});
2844                  
2845                  // make room  for the legend between the grid and the edge.
2846                  // pass a dummy offsets object and a reference to the plot.
2847                  var legendElem = this.legend.draw({}, this);
2848                  
2849                  var gridPadding = {top:0, left:0, bottom:0, right:0};
2850                  
2851                  if (this.legend.placement == "outsideGrid") {
2852                      // temporarily append the legend to get dimensions
2853                      this.target.append(legendElem);
2854                      switch (this.legend.location) {
2855                          case 'n':
2856                              gridPadding.top += this.legend.getHeight();
2857                              break;
2858                          case 's':
2859                              gridPadding.bottom += this.legend.getHeight();
2860                              break;
2861                          case 'ne':
2862                          case 'e':
2863                          case 'se':
2864                              gridPadding.right += this.legend.getWidth();
2865                              break;
2866                          case 'nw':
2867                          case 'w':
2868                          case 'sw':
2869                              gridPadding.left += this.legend.getWidth();
2870                              break;
2871                          default:  // same as 'ne'
2872                              gridPadding.right += this.legend.getWidth();
2873                              break;
2874                      }
2875                      legendElem = legendElem.detach();
2876                  }
2877                  
2878                  var ax = this.axes;
2879                  var name;
2880                  // draw the yMidAxis first, so xaxis of pyramid chart can adjust itself if needed.
2881                  for (i=0; i<12; i++) {
2882                      name = _axisNames[i];
2883                      this.target.append(ax[name].draw(this.baseCanvas._ctx, this));
2884                      ax[name].set();
2885                  }
2886                  if (ax.yaxis.show) {
2887                      gridPadding.left += ax.yaxis.getWidth();
2888                  }
2889                  var ra = ['y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis'];
2890                  var rapad = [0, 0, 0, 0, 0, 0, 0, 0];
2891                  var gpr = 0;
2892                  var n;
2893                  for (n=0; n<8; n++) {
2894                      if (ax[ra[n]].show) {
2895                          gpr += ax[ra[n]].getWidth();
2896                          rapad[n] = gpr;
2897                      }
2898                  }
2899                  gridPadding.right += gpr;
2900                  if (ax.x2axis.show) {
2901                      gridPadding.top += ax.x2axis.getHeight();
2902                  }
2903                  if (this.title.show) {
2904                      gridPadding.top += this.title.getHeight();
2905                  }
2906                  if (ax.xaxis.show) {
2907                      gridPadding.bottom += ax.xaxis.getHeight();
2908                  }
2909                  
2910                  // end of gridPadding adjustments.
2911  
2912                  // if user passed in gridDimensions option, check against calculated gridPadding
2913                  if (this.options.gridDimensions && $.isPlainObject(this.options.gridDimensions)) {
2914                      var gdw = parseInt(this.options.gridDimensions.width, 10) || 0;
2915                      var gdh = parseInt(this.options.gridDimensions.height, 10) || 0;
2916                      var widthAdj = (this._width - gridPadding.left - gridPadding.right - gdw)/2;
2917                      var heightAdj = (this._height - gridPadding.top - gridPadding.bottom - gdh)/2;
2918  
2919                      if (heightAdj >= 0 && widthAdj >= 0) {
2920                          gridPadding.top += heightAdj;
2921                          gridPadding.bottom += heightAdj;
2922                          gridPadding.left += widthAdj;
2923                          gridPadding.right += widthAdj;
2924                      }
2925                  }
2926                  var arr = ['top', 'bottom', 'left', 'right'];
2927                  for (var n in arr) {
2928                      if (this._gridPadding[arr[n]] == null && gridPadding[arr[n]] > 0) {
2929                          this._gridPadding[arr[n]] = gridPadding[arr[n]];
2930                      }
2931                      else if (this._gridPadding[arr[n]] == null) {
2932                          this._gridPadding[arr[n]] = this._defaultGridPadding[arr[n]];
2933                      }
2934                  }
2935                  
2936                  var legendPadding = this._gridPadding;
2937                  
2938                  if (this.legend.placement === 'outsideGrid') {
2939                      legendPadding = {top:this.title.getHeight(), left: 0, right: 0, bottom: 0};
2940                      if (this.legend.location === 's') {
2941                          legendPadding.left = this._gridPadding.left;
2942                          legendPadding.right = this._gridPadding.right;
2943                      }
2944                  }
2945                  
2946                  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});
2947                  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});
2948                  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});
2949                  for (i=8; i>0; i--) {
2950                      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});
2951                  }
2952                  var ltemp = (this._width - this._gridPadding.left - this._gridPadding.right)/2.0 + this._gridPadding.left - ax.yMidAxis.getWidth()/2.0;
2953                  ax.yMidAxis.pack({position:'absolute', top:0, left:ltemp, zIndex:9, textAlign: 'center'}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top});
2954              
2955                  this.target.append(this.grid.createElement(this._gridPadding, this));
2956                  this.grid.draw();
2957                  
2958                  var series = this.series;
2959                  var seriesLength = series.length;
2960                  // put the shadow canvases behind the series canvases so shadows don't overlap on stacked bars.
2961                  for (i=0, l=seriesLength; i<l; i++) {
2962                      // draw series in order of stacking.  This affects only
2963                      // order in which canvases are added to dom.
2964                      j = this.seriesStack[i];
2965                      this.target.append(series[j].shadowCanvas.createElement(this._gridPadding, 'jqplot-series-shadowCanvas', null, this));
2966                      series[j].shadowCanvas.setContext();
2967                      series[j].shadowCanvas._elem.data('seriesIndex', j);
2968                  }
2969                  
2970                  for (i=0, l=seriesLength; i<l; i++) {
2971                      // draw series in order of stacking.  This affects only
2972                      // order in which canvases are added to dom.
2973                      j = this.seriesStack[i];
2974                      this.target.append(series[j].canvas.createElement(this._gridPadding, 'jqplot-series-canvas', null, this));
2975                      series[j].canvas.setContext();
2976                      series[j].canvas._elem.data('seriesIndex', j);
2977                  }
2978                  // Need to use filled canvas to capture events in IE.
2979                  // Also, canvas seems to block selection of other elements in document on FF.
2980                  this.target.append(this.eventCanvas.createElement(this._gridPadding, 'jqplot-event-canvas', null, this));
2981                  this.eventCanvas.setContext();
2982                  this.eventCanvas._ctx.fillStyle = 'rgba(0,0,0,0)';
2983                  this.eventCanvas._ctx.fillRect(0,0,this.eventCanvas._ctx.canvas.width, this.eventCanvas._ctx.canvas.height);
2984              
2985                  // bind custom event handlers to regular events.
2986                  this.bindCustomEvents();
2987              
2988                  // draw legend before series if the series needs to know the legend dimensions.
2989                  if (this.legend.preDraw) {  
2990                      this.eventCanvas._elem.before(legendElem);
2991                      this.legend.pack(legendPadding);
2992                      if (this.legend._elem) {
2993                          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}});
2994                      }
2995                      else {
2996                          this.drawSeries();
2997                      }
2998                  }
2999                  else {  // draw series before legend
3000                      this.drawSeries();
3001                      if (seriesLength) {
3002                          $(series[seriesLength-1].canvas._elem).after(legendElem);
3003                      }
3004                      this.legend.pack(legendPadding);                
3005                  }
3006              
3007                  // register event listeners on the overlay canvas
3008                  for (var i=0, l=$.jqplot.eventListenerHooks.length; i<l; i++) {
3009                      // in the handler, this will refer to the eventCanvas dom element.
3010                      // make sure there are references back into plot objects.
3011                      this.eventCanvas._elem.bind($.jqplot.eventListenerHooks[i][0], {plot:this}, $.jqplot.eventListenerHooks[i][1]);
3012                  }
3013              
3014                  // register event listeners on the overlay canvas
3015                  for (var i=0, l=this.eventListenerHooks.hooks.length; i<l; i++) {
3016                      // in the handler, this will refer to the eventCanvas dom element.
3017                      // make sure there are references back into plot objects.
3018                      this.eventCanvas._elem.bind(this.eventListenerHooks.hooks[i][0], {plot:this}, this.eventListenerHooks.hooks[i][1]);
3019                  }
3020  
3021                  var fb = this.fillBetween;
3022                  if (fb.fill && fb.series1 !== fb.series2 && fb.series1 < seriesLength && fb.series2 < seriesLength && series[fb.series1]._type === 'line' && series[fb.series2]._type === 'line') {
3023                      this.doFillBetweenLines();
3024                  }
3025  
3026                  for (var i=0, l=$.jqplot.postDrawHooks.length; i<l; i++) {
3027                      $.jqplot.postDrawHooks[i].call(this);
3028                  }
3029  
3030                  for (var i=0, l=this.postDrawHooks.hooks.length; i<l; i++) {
3031                      this.postDrawHooks.hooks[i].apply(this, this.postDrawHooks.args[i]);
3032                  }
3033              
3034                  if (this.target.is(':visible')) {
3035                      this._drawCount += 1;
3036                  }
3037  
3038                  var temps, 
3039                      tempr,
3040                      sel,
3041                      _els;
3042                  // ughh.  ideally would hide all series then show them.
3043                  for (i=0, l=seriesLength; i<l; i++) {
3044                      temps = series[i];
3045                      tempr = temps.renderer;
3046                      sel = '.jqplot-point-label.jqplot-series-'+i;
3047                      if (tempr.animation && tempr.animation._supported && tempr.animation.show && (this._drawCount < 2 || this.animateReplot)) {
3048                          _els = this.target.find(sel);
3049                          _els.stop(true, true).hide();
3050                          temps.canvas._elem.stop(true, true).hide();
3051                          temps.shadowCanvas._elem.stop(true, true).hide();
3052                          temps.canvas._elem.jqplotEffect('blind', {mode: 'show', direction: tempr.animation.direction}, tempr.animation.speed);
3053                          temps.shadowCanvas._elem.jqplotEffect('blind', {mode: 'show', direction: tempr.animation.direction}, tempr.animation.speed);
3054                          _els.fadeIn(tempr.animation.speed*0.8);
3055                      }
3056                  }
3057                  _els = null;
3058              
3059                  this.target.trigger('jqplotPostDraw', [this]);
3060              }
3061          };
3062  
3063          jqPlot.prototype.doFillBetweenLines = function () {
3064              var fb = this.fillBetween;
3065              var sid1 = fb.series1;
3066              var sid2 = fb.series2;
3067              // first series should always be lowest index
3068              var id1 = (sid1 < sid2) ? sid1 : sid2;
3069              var id2 = (sid2 >  sid1) ? sid2 : sid1;
3070  
3071              var series1 = this.series[id1];
3072              var series2 = this.series[id2];
3073  
3074              if (series2.renderer.smooth) {
3075                  var tempgd = series2.renderer._smoothedData.slice(0).reverse();
3076              }
3077              else {
3078                  var tempgd = series2.gridData.slice(0).reverse();
3079              }
3080  
3081              if (series1.renderer.smooth) {
3082                  var gd = series1.renderer._smoothedData.concat(tempgd);
3083              }
3084              else {
3085                  var gd = series1.gridData.concat(tempgd);
3086              }
3087  
3088              var color = (fb.color !== null) ? fb.color : this.series[sid1].fillColor;
3089              var baseSeries = (fb.baseSeries !== null) ? fb.baseSeries : id1;
3090  
3091              // now apply a fill to the shape on the lower series shadow canvas,
3092              // so it is behind both series.
3093              var sr = this.series[baseSeries].renderer.shapeRenderer;
3094              var opts = {fillStyle: color, fill: true, closePath: true};
3095              sr.draw(series1.shadowCanvas._ctx, gd, opts);
3096          };
3097          
3098          this.bindCustomEvents = function() {
3099              this.eventCanvas._elem.bind('click', {plot:this}, this.onClick);
3100              this.eventCanvas._elem.bind('dblclick', {plot:this}, this.onDblClick);
3101              this.eventCanvas._elem.bind('mousedown', {plot:this}, this.onMouseDown);
3102              this.eventCanvas._elem.bind('mousemove', {plot:this}, this.onMouseMove);
3103              this.eventCanvas._elem.bind('mouseenter', {plot:this}, this.onMouseEnter);
3104              this.eventCanvas._elem.bind('mouseleave', {plot:this}, this.onMouseLeave);
3105              if (this.captureRightClick) {
3106                  this.eventCanvas._elem.bind('mouseup', {plot:this}, this.onRightClick);
3107                  this.eventCanvas._elem.get(0).oncontextmenu = function() {
3108                      return false;
3109                  };
3110              }
3111              else {
3112                  this.eventCanvas._elem.bind('mouseup', {plot:this}, this.onMouseUp);
3113              }
3114          };
3115          
3116          function getEventPosition(ev) {
3117              var plot = ev.data.plot;
3118              var go = plot.eventCanvas._elem.offset();
3119              var gridPos = {x:ev.pageX - go.left, y:ev.pageY - go.top};
3120              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};
3121              var an = ['xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis'];
3122              var ax = plot.axes;
3123              var n, axis;
3124              for (n=11; n>0; n--) {
3125                  axis = an[n-1];
3126                  if (ax[axis].show) {
3127                      dataPos[axis] = ax[axis].series_p2u(gridPos[axis.charAt(0)]);
3128                  }
3129              }
3130  
3131              return {offsets:go, gridPos:gridPos, dataPos:dataPos};
3132          }
3133          
3134          
3135          // function to check if event location is over a area area
3136          function checkIntersection(gridpos, plot) {
3137              var series = plot.series;
3138              var i, j, k, s, r, x, y, theta, sm, sa, minang, maxang;
3139              var d0, d, p, pp, points, bw, hp;
3140              var threshold, t;
3141              for (k=plot.seriesStack.length-1; k>=0; k--) {
3142                  i = plot.seriesStack[k];
3143                  s = series[i];
3144                  hp = s._highlightThreshold;
3145                  switch (s.renderer.constructor) {
3146                      case $.jqplot.BarRenderer:
3147                          x = gridpos.x;
3148                          y = gridpos.y;
3149                          for (j=0; j<s._barPoints.length; j++) {
3150                              points = s._barPoints[j];
3151                              p = s.gridData[j];
3152                              if (x>points[0][0] && x<points[2][0] && y>points[2][1] && y<points[0][1]) {
3153                                  return {seriesIndex:s.index, pointIndex:j, gridData:p, data:s.data[j], points:s._barPoints[j]};
3154                              }
3155                          }
3156                          break;
3157                      case $.jqplot.PyramidRenderer:
3158                          x = gridpos.x;
3159                          y = gridpos.y;
3160                          for (j=0; j<s._barPoints.length; j++) {
3161                              points = s._barPoints[j];
3162                              p = s.gridData[j];
3163                              if (x > points[0][0] + hp[0][0] && x < points[2][0] + hp[2][0] && y > points[2][1] && y < points[0][1]) {
3164                                  return {seriesIndex:s.index, pointIndex:j, gridData:p, data:s.data[j], points:s._barPoints[j]};
3165                              }
3166                          }
3167                          break;
3168                      
3169                      case $.jqplot.DonutRenderer:
3170                          sa = s.startAngle/180*Math.PI;
3171                          x = gridpos.x - s._center[0];
3172                          y = gridpos.y - s._center[1];
3173                          r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
3174                          if (x > 0 && -y >= 0) {
3175                              theta = 2*Math.PI - Math.atan(-y/x);
3176                          }
3177                          else if (x > 0 && -y < 0) {
3178                              theta = -Math.atan(-y/x);
3179                          }
3180                          else if (x < 0) {
3181                              theta = Math.PI - Math.atan(-y/x);
3182                          }
3183                          else if (x == 0 && -y > 0) {
3184                              theta = 3*Math.PI/2;
3185                          }
3186                          else if (x == 0 && -y < 0) {
3187                              theta = Math.PI/2;
3188                          }
3189                          else if (x == 0 && y == 0) {
3190                              theta = 0;
3191                          }
3192                          if (sa) {
3193                              theta -= sa;
3194                              if (theta < 0) {
3195                                  theta += 2*Math.PI;
3196                              }
3197                              else if (theta > 2*Math.PI) {
3198                                  theta -= 2*Math.PI;
3199                              }
3200                          }
3201              
3202                          sm = s.sliceMargin/180*Math.PI;
3203                          if (r < s._radius && r > s._innerRadius) {
3204                              for (j=0; j<s.gridData.length; j++) {
3205                                  minang = (j>0) ? s.gridData[j-1][1]+sm : sm;
3206                                  maxang = s.gridData[j][1];
3207                                  if (theta > minang && theta < maxang) {
3208                                      return {seriesIndex:s.index, pointIndex:j, gridData:s.gridData[j], data:s.data[j]};
3209                                  }
3210                              }
3211                          }
3212                          break;
3213                          
3214                      case $.jqplot.PieRenderer:
3215                          sa = s.startAngle/180*Math.PI;
3216                          x = gridpos.x - s._center[0];
3217                          y = gridpos.y - s._center[1];
3218                          r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
3219                          if (x > 0 && -y >= 0) {
3220                              theta = 2*Math.PI - Math.atan(-y/x);
3221                          }
3222                          else if (x > 0 && -y < 0) {
3223                              theta = -Math.atan(-y/x);
3224                          }
3225                          else if (x < 0) {
3226                              theta = Math.PI - Math.atan(-y/x);
3227                          }
3228                          else if (x == 0 && -y > 0) {
3229                              theta = 3*Math.PI/2;
3230                          }
3231                          else if (x == 0 && -y < 0) {
3232                              theta = Math.PI/2;
3233                          }
3234                          else if (x == 0 && y == 0) {
3235                              theta = 0;
3236                          }
3237                          if (sa) {
3238                              theta -= sa;
3239                              if (theta < 0) {
3240                                  theta += 2*Math.PI;
3241                              }
3242                              else if (theta > 2*Math.PI) {
3243                                  theta -= 2*Math.PI;
3244                              }
3245                          }
3246              
3247                          sm = s.sliceMargin/180*Math.PI;
3248                          if (r < s._radius) {
3249                              for (j=0; j<s.gridData.length; j++) {
3250                                  minang = (j>0) ? s.gridData[j-1][1]+sm : sm;
3251                                  maxang = s.gridData[j][1];
3252                                  if (theta > minang && theta < maxang) {
3253                                      return {seriesIndex:s.index, pointIndex:j, gridData:s.gridData[j], data:s.data[j]};
3254                                  }
3255                              }
3256                          }
3257                          break;
3258                          
3259                      case $.jqplot.BubbleRenderer:
3260                          x = gridpos.x;
3261                          y = gridpos.y;
3262                          var ret = null;
3263                          
3264                          if (s.show) {
3265                              for (var j=0; j<s.gridData.length; j++) {
3266                                  p = s.gridData[j];
3267                                  d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) );
3268                                  if (d <= p[2] && (d <= d0 || d0 == null)) {
3269                                     d0 = d;
3270                                     ret = {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
3271                                  }
3272                              }
3273                              if (ret != null) {
3274                                  return ret;
3275                              }
3276                          }
3277                          break;
3278                          
3279                      case $.jqplot.FunnelRenderer:
3280                          x = gridpos.x;
3281                          y = gridpos.y;
3282                          var v = s._vertices,
3283                              vfirst = v[0],
3284                              vlast = v[v.length-1],
3285                              lex,
3286                              rex,
3287                              cv;
3288      
3289                          // equations of right and left sides, returns x, y values given height of section (y value and 2 points)
3290      
3291                          function findedge (l, p1 , p2) {
3292                              var m = (p1[1] - p2[1])/(p1[0] - p2[0]);
3293                              var b = p1[1] - m*p1[0];
3294                              var y = l + p1[1];
3295          
3296                              return [(y - b)/m, y];
3297                          }
3298      
3299                          // check each section
3300                          lex = findedge(y, vfirst[0], vlast[3]);
3301                          rex = findedge(y, vfirst[1], vlast[2]);
3302                          for (j=0; j<v.length; j++) {
3303                              cv = v[j];
3304                              if (y >= cv[0][1] && y <= cv[3][1] && x >= lex[0] && x <= rex[0]) {
3305                                  return {seriesIndex:s.index, pointIndex:j, gridData:null, data:s.data[j]};
3306                              }
3307                          }         
3308                          break;           
3309                      
3310                      case $.jqplot.LineRenderer:
3311                          x = gridpos.x;
3312                          y = gridpos.y;
3313                          r = s.renderer;
3314                          if (s.show) {
3315                              if ((s.fill || (s.renderer.bands.show && s.renderer.bands.fill)) && (!plot.plugins.highlighter || !plot.plugins.highlighter.show)) {
3316                                  // first check if it is in bounding box
3317                                  var inside = false;
3318                                  if (x>s._boundingBox[0][0] && x<s._boundingBox[1][0] && y>s._boundingBox[1][1] && y<s._boundingBox[0][1]) { 
3319                                      // now check the crossing number   
3320                                      
3321                                      var numPoints = s._areaPoints.length;
3322                                      var ii;
3323                                      var j = numPoints-1;
3324  
3325                                      for(var ii=0; ii < numPoints; ii++) { 
3326                                          var vertex1 = [s._areaPoints[ii][0], s._areaPoints[ii][1]];
3327                                          var vertex2 = [s._areaPoints[j][0], s._areaPoints[j][1]];
3328  
3329                                          if (vertex1[1] < y && vertex2[1] >= y || vertex2[1] < y && vertex1[1] >= y)     {
3330                                              if (vertex1[0] + (y - vertex1[1]) / (vertex2[1] - vertex1[1]) * (vertex2[0] - vertex1[0]) < x) {
3331                                                  inside = !inside;
3332                                              }
3333                                          }
3334  
3335                                          j = ii;
3336                                      }        
3337                                  }
3338                                  if (inside) {
3339                                      return {seriesIndex:i, pointIndex:null, gridData:s.gridData, data:s.data, points:s._areaPoints};
3340                                  }
3341                                  break;
3342                                  
3343                              }
3344  
3345                              else {
3346                                  t = s.markerRenderer.size/2+s.neighborThreshold;
3347                                  threshold = (t > 0) ? t : 0;
3348                                  for (var j=0; j<s.gridData.length; j++) {
3349                                      p = s.gridData[j];
3350                                      // neighbor looks different to OHLC chart.
3351                                      if (r.constructor == $.jqplot.OHLCRenderer) {
3352                                          if (r.candleStick) {
3353                                              var yp = s._yaxis.series_u2p;
3354                                              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])) {
3355                                                  return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
3356                                              }
3357                                          }
3358                                          // if an open hi low close chart
3359                                          else if (!r.hlc){
3360                                              var yp = s._yaxis.series_u2p;
3361                                              if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) {
3362                                                  return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
3363                                              }
3364                                          }
3365                                          // a hi low close chart
3366                                          else {
3367                                              var yp = s._yaxis.series_u2p;
3368                                              if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) {
3369                                                  return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
3370                                              }
3371                                          }
3372                              
3373                                      }
3374                                      else if (p[0] != null && p[1] != null){
3375                                          d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) );
3376                                          if (d <= threshold && (d <= d0 || d0 == null)) {
3377                                             d0 = d;
3378                                             return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
3379                                          }
3380                                      }
3381                                  } 
3382                              }
3383                          }
3384                          break;
3385                          
3386                      default:
3387                          x = gridpos.x;
3388                          y = gridpos.y;
3389                          r = s.renderer;
3390                          if (s.show) {
3391                              t = s.markerRenderer.size/2+s.neighborThreshold;
3392                              threshold = (t > 0) ? t : 0;
3393                              for (var j=0; j<s.gridData.length; j++) {
3394                                  p = s.gridData[j];
3395                                  // neighbor looks different to OHLC chart.
3396                                  if (r.constructor == $.jqplot.OHLCRenderer) {
3397                                      if (r.candleStick) {
3398                                          var yp = s._yaxis.series_u2p;
3399                                          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])) {
3400                                              return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
3401                                          }
3402                                      }
3403                                      // if an open hi low close chart
3404                                      else if (!r.hlc){
3405                                          var yp = s._yaxis.series_u2p;
3406                                          if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) {
3407                                              return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
3408                                          }
3409                                      }
3410                                      // a hi low close chart
3411                                      else {
3412                                          var yp = s._yaxis.series_u2p;
3413                                          if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) {
3414                                              return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
3415                                          }
3416                                      }
3417                              
3418                                  }
3419                                  else {
3420                                      d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) );
3421                                      if (d <= threshold && (d <= d0 || d0 == null)) {
3422                                         d0 = d;
3423                                         return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
3424                                      }
3425                                  }
3426                              } 
3427                          }
3428                          break;
3429                  }
3430              }
3431              
3432              return null;
3433          }
3434          
3435          
3436          
3437          this.onClick = function(ev) {
3438              // Event passed in is normalized and will have data attribute.
3439              // Event passed out is unnormalized.
3440              var positions = getEventPosition(ev);
3441              var p = ev.data.plot;
3442              var neighbor = checkIntersection(positions.gridPos, p);
3443              var evt = $.Event('jqplotClick');
3444              evt.pageX = ev.pageX;
3445              evt.pageY = ev.pageY;
3446              $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
3447          };
3448          
3449          this.onDblClick = function(ev) {
3450              // Event passed in is normalized and will have data attribute.
3451              // Event passed out is unnormalized.
3452              var positions = getEventPosition(ev);
3453              var p = ev.data.plot;
3454              var neighbor = checkIntersection(positions.gridPos, p);
3455              var evt = $.Event('jqplotDblClick');
3456              evt.pageX = ev.pageX;
3457              evt.pageY = ev.pageY;
3458              $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
3459          };
3460          
3461          this.onMouseDown = function(ev) {
3462              var positions = getEventPosition(ev);
3463              var p = ev.data.plot;
3464              var neighbor = checkIntersection(positions.gridPos, p);
3465              var evt = $.Event('jqplotMouseDown');
3466              evt.pageX = ev.pageX;
3467              evt.pageY = ev.pageY;
3468              $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
3469          };
3470          
3471          this.onMouseUp = function(ev) {
3472              var positions = getEventPosition(ev);
3473              var evt = $.Event('jqplotMouseUp');
3474              evt.pageX = ev.pageX;
3475              evt.pageY = ev.pageY;
3476              $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, ev.data.plot]);
3477          };
3478          
3479          this.onRightClick = function(ev) {
3480              var positions = getEventPosition(ev);
3481              var p = ev.data.plot;
3482              var neighbor = checkIntersection(positions.gridPos, p);
3483              if (p.captureRightClick) {
3484                  if (ev.which == 3) {
3485                  var evt = $.Event('jqplotRightClick');
3486                  evt.pageX = ev.pageX;
3487                  evt.pageY = ev.pageY;
3488                      $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
3489                  }
3490                  else {
3491                  var evt = $.Event('jqplotMouseUp');
3492                  evt.pageX = ev.pageX;
3493                  evt.pageY = ev.pageY;
3494                      $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
3495                  }
3496              }
3497          };
3498          
3499          this.onMouseMove = function(ev) {
3500              var positions = getEventPosition(ev);
3501              var p = ev.data.plot;
3502              var neighbor = checkIntersection(positions.gridPos, p);
3503              var evt = $.Event('jqplotMouseMove');
3504              evt.pageX = ev.pageX;
3505              evt.pageY = ev.pageY;
3506              $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
3507          };
3508          
3509          this.onMouseEnter = function(ev) {
3510              var positions = getEventPosition(ev);
3511              var p = ev.data.plot;
3512              var evt = $.Event('jqplotMouseEnter');
3513              evt.pageX = ev.pageX;
3514              evt.pageY = ev.pageY;
3515              evt.relatedTarget = ev.relatedTarget;
3516              $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, p]);
3517          };
3518          
3519          this.onMouseLeave = function(ev) {
3520              var positions = getEventPosition(ev);
3521              var p = ev.data.plot;
3522              var evt = $.Event('jqplotMouseLeave');
3523              evt.pageX = ev.pageX;
3524              evt.pageY = ev.pageY;
3525              evt.relatedTarget = ev.relatedTarget;
3526              $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, p]);
3527          };
3528          
3529          // method: drawSeries
3530          // Redraws all or just one series on the plot.  No axis scaling
3531          // is performed and no other elements on the plot are redrawn.
3532          // options is an options object to pass on to the series renderers.
3533          // It can be an empty object {}.  idx is the series index
3534          // to redraw if only one series is to be redrawn.
3535          this.drawSeries = function(options, idx){
3536              var i, series, ctx;
3537              // if only one argument passed in and it is a number, use it ad idx.
3538              idx = (typeof(options) === "number" && idx == null) ? options : idx;
3539              options = (typeof(options) === "object") ? options : {};
3540              // draw specified series
3541              if (idx != undefined) {
3542                  series = this.series[idx];
3543                  ctx = series.shadowCanvas._ctx;
3544                  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
3545                  series.drawShadow(ctx, options, this);
3546                  ctx = series.canvas._ctx;
3547                  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
3548                  series.draw(ctx, options, this);
3549                  if (series.renderer.constructor == $.jqplot.BezierCurveRenderer) {
3550                      if (idx < this.series.length - 1) {
3551                          this.drawSeries(idx+1); 
3552                      }
3553                  }
3554              }
3555              
3556              else {
3557                  // if call series drawShadow method first, in case all series shadows
3558                  // should be drawn before any series.  This will ensure, like for 
3559                  // stacked bar plots, that shadows don't overlap series.
3560                  for (i=0; i<this.series.length; i++) {
3561                      // first clear the canvas
3562                      series = this.series[i];
3563                      ctx = series.shadowCanvas._ctx;
3564                      ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
3565                      series.drawShadow(ctx, options, this);
3566                      ctx = series.canvas._ctx;
3567                      ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
3568                      series.draw(ctx, options, this);
3569                  }
3570              }
3571              options = idx = i = series = ctx = null;
3572          };
3573          
3574          // method: moveSeriesToFront
3575          // This method requires jQuery 1.4+
3576          // Moves the specified series canvas in front of all other series canvases.
3577          // This effectively "draws" the specified series on top of all other series,
3578          // although it is performed through DOM manipulation, no redrawing is performed.
3579          //
3580          // Parameters:
3581          // idx - 0 based index of the series to move.  This will be the index of the series
3582          // as it was first passed into the jqplot function.
3583          this.moveSeriesToFront = function (idx) { 
3584              idx = parseInt(idx, 10);
3585              var stackIndex = $.inArray(idx, this.seriesStack);
3586              // if already in front, return
3587              if (stackIndex == -1) {
3588                  return;
3589              }
3590              if (stackIndex == this.seriesStack.length -1) {
3591                  this.previousSeriesStack = this.seriesStack.slice(0);
3592                  return;
3593              }
3594              var opidx = this.seriesStack[this.seriesStack.length -1];
3595              var serelem = this.series[idx].canvas._elem.detach();
3596              var shadelem = this.series[idx].shadowCanvas._elem.detach();
3597              this.series[opidx].shadowCanvas._elem.after(shadelem);
3598              this.series[opidx].canvas._elem.after(serelem);
3599              this.previousSeriesStack = this.seriesStack.slice(0);
3600              this.seriesStack.splice(stackIndex, 1);
3601              this.seriesStack.push(idx);
3602          };
3603          
3604          // method: moveSeriesToBack
3605          // This method requires jQuery 1.4+
3606          // Moves the specified series canvas behind all other series canvases.
3607          //
3608          // Parameters:
3609          // idx - 0 based index of the series to move.  This will be the index of the series
3610          // as it was first passed into the jqplot function.
3611          this.moveSeriesToBack = function (idx) {
3612              idx = parseInt(idx, 10);
3613              var stackIndex = $.inArray(idx, this.seriesStack);
3614              // if already in back, return
3615              if (stackIndex == 0 || stackIndex == -1) {
3616                  return;
3617              }
3618              var opidx = this.seriesStack[0];
3619              var serelem = this.series[idx].canvas._elem.detach();
3620              var shadelem = this.series[idx].shadowCanvas._elem.detach();
3621              this.series[opidx].shadowCanvas._elem.before(shadelem);
3622              this.series[opidx].canvas._elem.before(serelem);
3623              this.previousSeriesStack = this.seriesStack.slice(0);
3624              this.seriesStack.splice(stackIndex, 1);
3625              this.seriesStack.unshift(idx);
3626          };
3627          
3628          // method: restorePreviousSeriesOrder
3629          // This method requires jQuery 1.4+
3630          // Restore the series canvas order to its previous state.
3631          // Useful to put a series back where it belongs after moving
3632          // it to the front.
3633          this.restorePreviousSeriesOrder = function () {
3634              var i, j, serelem, shadelem, temp, move, keep;
3635              // if no change, return.
3636              if (this.seriesStack == this.previousSeriesStack) {
3637                  return;
3638              }
3639              for (i=1; i<this.previousSeriesStack.length; i++) {
3640                  move = this.previousSeriesStack[i];
3641                  keep = this.previousSeriesStack[i-1];
3642                  serelem = this.series[move].canvas._elem.detach();
3643                  shadelem = this.series[move].shadowCanvas._elem.detach();
3644                  this.series[keep].shadowCanvas._elem.after(shadelem);
3645                  this.series[keep].canvas._elem.after(serelem);
3646              }
3647              temp = this.seriesStack.slice(0);
3648              this.seriesStack = this.previousSeriesStack.slice(0);
3649              this.previousSeriesStack = temp;
3650          };
3651          
3652          // method: restoreOriginalSeriesOrder
3653          // This method requires jQuery 1.4+
3654          // Restore the series canvas order to its original order
3655          // when the plot was created.
3656          this.restoreOriginalSeriesOrder = function () {
3657              var i, j, arr=[], serelem, shadelem;
3658              for (i=0; i<this.series.length; i++) {
3659                  arr.push(i);
3660              }
3661              if (this.seriesStack == arr) {
3662                  return;
3663              }
3664              this.previousSeriesStack = this.seriesStack.slice(0);
3665              this.seriesStack = arr;
3666              for (i=1; i<this.seriesStack.length; i++) {
3667                  serelem = this.series[i].canvas._elem.detach();
3668                  shadelem = this.series[i].shadowCanvas._elem.detach();
3669                  this.series[i-1].shadowCanvas._elem.after(shadelem);
3670                  this.series[i-1].canvas._elem.after(serelem);
3671              }
3672          };
3673          
3674          this.activateTheme = function (name) {
3675              this.themeEngine.activate(this, name);
3676          };
3677      }
3678      
3679      
3680      // conpute a highlight color or array of highlight colors from given colors.
3681      $.jqplot.computeHighlightColors  = function(colors) {
3682          var ret;
3683          if ($.isArray(colors)) {
3684              ret = [];
3685              for (var i=0; i<colors.length; i++){
3686                  var rgba = $.jqplot.getColorComponents(colors[i]);
3687                  var newrgb = [rgba[0], rgba[1], rgba[2]];
3688                  var sum = newrgb[0] + newrgb[1] + newrgb[2];
3689                  for (var j=0; j<3; j++) {
3690                      // when darkening, lowest color component can be is 60.
3691                      newrgb[j] = (sum > 660) ?  newrgb[j] * 0.85 : 0.73 * newrgb[j] + 90;
3692                      newrgb[j] = parseInt(newrgb[j], 10);
3693                      (newrgb[j] > 255) ? 255 : newrgb[j];
3694                  }
3695                  // newrgb[3] = (rgba[3] > 0.4) ? rgba[3] * 0.4 : rgba[3] * 1.5;
3696                  // newrgb[3] = (rgba[3] > 0.5) ? 0.8 * rgba[3] - .1 : rgba[3] + 0.2;
3697                  newrgb[3] = 0.3 + 0.35 * rgba[3];
3698                  ret.push('rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+newrgb[3]+')');
3699              }
3700          }
3701          else {
3702              var rgba = $.jqplot.getColorComponents(colors);
3703              var newrgb = [rgba[0], rgba[1], rgba[2]];
3704              var sum = newrgb[0] + newrgb[1] + newrgb[2];
3705              for (var j=0; j<3; j++) {
3706                  // when darkening, lowest color component can be is 60.
3707                  // newrgb[j] = (sum > 570) ?  newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]);
3708                  // newrgb[j] = parseInt(newrgb[j], 10);
3709                  newrgb[j] = (sum > 660) ?  newrgb[j] * 0.85 : 0.73 * newrgb[j] + 90;
3710                  newrgb[j] = parseInt(newrgb[j], 10);
3711                  (newrgb[j] > 255) ? 255 : newrgb[j];
3712              }
3713              // newrgb[3] = (rgba[3] > 0.4) ? rgba[3] * 0.4 : rgba[3] * 1.5;
3714              // newrgb[3] = (rgba[3] > 0.5) ? 0.8 * rgba[3] - .1 : rgba[3] + 0.2;
3715              newrgb[3] = 0.3 + 0.35 * rgba[3];
3716              ret = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+newrgb[3]+')';
3717          }
3718          return ret;
3719      };
3720          
3721     $.jqplot.ColorGenerator = function(colors) {
3722          colors = colors || $.jqplot.config.defaultColors;
3723          var idx = 0;
3724          
3725          this.next = function () { 
3726              if (idx < colors.length) {
3727                  return colors[idx++];
3728              }
3729              else {
3730                  idx = 0;
3731                  return colors[idx++];
3732              }
3733          };
3734          
3735          this.previous = function () { 
3736              if (idx > 0) {
3737                  return colors[idx--];
3738              }
3739              else {
3740                  idx = colors.length-1;
3741                  return colors[idx];
3742              }
3743          };
3744          
3745          // get a color by index without advancing pointer.
3746          this.get = function(i) {
3747              var idx = i - colors.length * Math.floor(i/colors.length);
3748              return colors[idx];
3749          };
3750          
3751          this.setColors = function(c) {
3752              colors = c;
3753          };
3754          
3755          this.reset = function() {
3756              idx = 0;
3757          };
3758  
3759          this.getIndex = function() {
3760              return idx;
3761          };
3762  
3763          this.setIndex = function(index) {
3764              idx = index;
3765          };
3766      };
3767  
3768      // convert a hex color string to rgb string.
3769      // h - 3 or 6 character hex string, with or without leading #
3770      // a - optional alpha
3771      $.jqplot.hex2rgb = function(h, a) {
3772          h = h.replace('#', '');
3773          if (h.length == 3) {
3774              h = h.charAt(0)+h.charAt(0)+h.charAt(1)+h.charAt(1)+h.charAt(2)+h.charAt(2);
3775          }
3776          var rgb;
3777          rgb = 'rgba('+parseInt(h.slice(0,2), 16)+', '+parseInt(h.slice(2,4), 16)+', '+parseInt(h.slice(4,6), 16);
3778          if (a) {
3779              rgb += ', '+a;
3780          }
3781          rgb += ')';
3782          return rgb;
3783      };
3784      
3785      // convert an rgb color spec to a hex spec.  ignore any alpha specification.
3786      $.jqplot.rgb2hex = function(s) {
3787          var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *(?:, *[0-9.]*)?\)/;
3788          var m = s.match(pat);
3789          var h = '#';
3790          for (var i=1; i<4; i++) {
3791              var temp;
3792              if (m[i].search(/%/) != -1) {
3793                  temp = parseInt(255*m[i]/100, 10).toString(16);
3794                  if (temp.length == 1) {
3795                      temp = '0'+temp;
3796                  }
3797              }
3798              else {
3799                  temp = parseInt(m[i], 10).toString(16);
3800                  if (temp.length == 1) {
3801                      temp = '0'+temp;
3802                  }
3803              }
3804              h += temp;
3805          }
3806          return h;
3807      };
3808      
3809      // given a css color spec, return an rgb css color spec
3810      $.jqplot.normalize2rgb = function(s, a) {
3811          if (s.search(/^ *rgba?\(/) != -1) {
3812              return s; 
3813          }
3814          else if (s.search(/^ *#?[0-9a-fA-F]?[0-9a-fA-F]/) != -1) {
3815              return $.jqplot.hex2rgb(s, a);
3816          }
3817          else {
3818              throw 'invalid color spec';
3819          }
3820      };
3821      
3822      // extract the r, g, b, a color components out of a css color spec.
3823      $.jqplot.getColorComponents = function(s) {
3824          // check to see if a color keyword.
3825          s = $.jqplot.colorKeywordMap[s] || s;
3826          var rgb = $.jqplot.normalize2rgb(s);
3827          var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *,? *([0-9.]* *)?\)/;
3828          var m = rgb.match(pat);
3829          var ret = [];
3830          for (var i=1; i<4; i++) {
3831              if (m[i].search(/%/) != -1) {
3832                  ret[i-1] = parseInt(255*m[i]/100, 10);
3833              }
3834              else {
3835                  ret[i-1] = parseInt(m[i], 10);
3836              }
3837          }
3838          ret[3] = parseFloat(m[4]) ? parseFloat(m[4]) : 1.0;
3839          return ret;
3840      };
3841      
3842      $.jqplot.colorKeywordMap = {
3843          aliceblue: 'rgb(240, 248, 255)',
3844          antiquewhite: 'rgb(250, 235, 215)',
3845          aqua: 'rgb( 0, 255, 255)',
3846          aquamarine: 'rgb(127, 255, 212)',
3847          azure: 'rgb(240, 255, 255)',
3848          beige: 'rgb(245, 245, 220)',
3849          bisque: 'rgb(255, 228, 196)',
3850          black: 'rgb( 0, 0, 0)',
3851          blanchedalmond: 'rgb(255, 235, 205)',
3852          blue: 'rgb( 0, 0, 255)',
3853          blueviolet: 'rgb(138, 43, 226)',
3854          brown: 'rgb(165, 42, 42)',
3855          burlywood: 'rgb(222, 184, 135)',
3856          cadetblue: 'rgb( 95, 158, 160)',
3857          chartreuse: 'rgb(127, 255, 0)',
3858          chocolate: 'rgb(210, 105, 30)',
3859          coral: 'rgb(255, 127, 80)',
3860          cornflowerblue: 'rgb(100, 149, 237)',
3861          cornsilk: 'rgb(255, 248, 220)',
3862          crimson: 'rgb(220, 20, 60)',
3863          cyan: 'rgb( 0, 255, 255)',
3864          darkblue: 'rgb( 0, 0, 139)',
3865          darkcyan: 'rgb( 0, 139, 139)',
3866          darkgoldenrod: 'rgb(184, 134, 11)',
3867          darkgray: 'rgb(169, 169, 169)',
3868          darkgreen: 'rgb( 0, 100, 0)',
3869          darkgrey: 'rgb(169, 169, 169)',
3870          darkkhaki: 'rgb(189, 183, 107)',
3871          darkmagenta: 'rgb(139, 0, 139)',
3872          darkolivegreen: 'rgb( 85, 107, 47)',
3873          darkorange: 'rgb(255, 140, 0)',
3874          darkorchid: 'rgb(153, 50, 204)',
3875          darkred: 'rgb(139, 0, 0)',
3876          darksalmon: 'rgb(233, 150, 122)',
3877          darkseagreen: 'rgb(143, 188, 143)',
3878          darkslateblue: 'rgb( 72, 61, 139)',
3879          darkslategray: 'rgb( 47, 79, 79)',
3880          darkslategrey: 'rgb( 47, 79, 79)',
3881          darkturquoise: 'rgb( 0, 206, 209)',
3882          darkviolet: 'rgb(148, 0, 211)',
3883          deeppink: 'rgb(255, 20, 147)',
3884          deepskyblue: 'rgb( 0, 191, 255)',
3885          dimgray: 'rgb(105, 105, 105)',
3886          dimgrey: 'rgb(105, 105, 105)',
3887          dodgerblue: 'rgb( 30, 144, 255)',
3888          firebrick: 'rgb(178, 34, 34)',
3889          floralwhite: 'rgb(255, 250, 240)',
3890          forestgreen: 'rgb( 34, 139, 34)',
3891          fuchsia: 'rgb(255, 0, 255)',
3892          gainsboro: 'rgb(220, 220, 220)',
3893          ghostwhite: 'rgb(248, 248, 255)',
3894          gold: 'rgb(255, 215, 0)',
3895          goldenrod: 'rgb(218, 165, 32)',
3896          gray: 'rgb(128, 128, 128)',
3897          grey: 'rgb(128, 128, 128)',
3898          green: 'rgb( 0, 128, 0)',
3899          greenyellow: 'rgb(173, 255, 47)',
3900          honeydew: 'rgb(240, 255, 240)',
3901          hotpink: 'rgb(255, 105, 180)',
3902          indianred: 'rgb(205, 92, 92)',
3903          indigo: 'rgb( 75, 0, 130)',
3904          ivory: 'rgb(255, 255, 240)',
3905          khaki: 'rgb(240, 230, 140)',
3906          lavender: 'rgb(230, 230, 250)',
3907          lavenderblush: 'rgb(255, 240, 245)',
3908          lawngreen: 'rgb(124, 252, 0)',
3909          lemonchiffon: 'rgb(255, 250, 205)',
3910          lightblue: 'rgb(173, 216, 230)',
3911          lightcoral: 'rgb(240, 128, 128)',
3912          lightcyan: 'rgb(224, 255, 255)',
3913          lightgoldenrodyellow: 'rgb(250, 250, 210)',
3914          lightgray: 'rgb(211, 211, 211)',
3915          lightgreen: 'rgb(144, 238, 144)',
3916          lightgrey: 'rgb(211, 211, 211)',
3917          lightpink: 'rgb(255, 182, 193)',
3918          lightsalmon: 'rgb(255, 160, 122)',
3919          lightseagreen: 'rgb( 32, 178, 170)',
3920          lightskyblue: 'rgb(135, 206, 250)',
3921          lightslategray: 'rgb(119, 136, 153)',
3922          lightslategrey: 'rgb(119, 136, 153)',
3923          lightsteelblue: 'rgb(176, 196, 222)',
3924          lightyellow: 'rgb(255, 255, 224)',
3925          lime: 'rgb( 0, 255, 0)',
3926          limegreen: 'rgb( 50, 205, 50)',
3927          linen: 'rgb(250, 240, 230)',
3928          magenta: 'rgb(255, 0, 255)',
3929          maroon: 'rgb(128, 0, 0)',
3930          mediumaquamarine: 'rgb(102, 205, 170)',
3931          mediumblue: 'rgb( 0, 0, 205)',
3932          mediumorchid: 'rgb(186, 85, 211)',
3933          mediumpurple: 'rgb(147, 112, 219)',
3934          mediumseagreen: 'rgb( 60, 179, 113)',
3935          mediumslateblue: 'rgb(123, 104, 238)',
3936          mediumspringgreen: 'rgb( 0, 250, 154)',
3937          mediumturquoise: 'rgb( 72, 209, 204)',
3938          mediumvioletred: 'rgb(199, 21, 133)',
3939          midnightblue: 'rgb( 25, 25, 112)',
3940          mintcream: 'rgb(245, 255, 250)',
3941          mistyrose: 'rgb(255, 228, 225)',
3942          moccasin: 'rgb(255, 228, 181)',
3943          navajowhite: 'rgb(255, 222, 173)',
3944          navy: 'rgb( 0, 0, 128)',
3945          oldlace: 'rgb(253, 245, 230)',
3946          olive: 'rgb(128, 128, 0)',
3947          olivedrab: 'rgb(107, 142, 35)',
3948          orange: 'rgb(255, 165, 0)',
3949          orangered: 'rgb(255, 69, 0)',
3950          orchid: 'rgb(218, 112, 214)',
3951          palegoldenrod: 'rgb(238, 232, 170)',
3952          palegreen: 'rgb(152, 251, 152)',
3953          paleturquoise: 'rgb(175, 238, 238)',
3954          palevioletred: 'rgb(219, 112, 147)',
3955          papayawhip: 'rgb(255, 239, 213)',
3956          peachpuff: 'rgb(255, 218, 185)',
3957          peru: 'rgb(205, 133, 63)',
3958          pink: 'rgb(255, 192, 203)',
3959          plum: 'rgb(221, 160, 221)',
3960          powderblue: 'rgb(176, 224, 230)',
3961          purple: 'rgb(128, 0, 128)',
3962          red: 'rgb(255, 0, 0)',
3963          rosybrown: 'rgb(188, 143, 143)',
3964          royalblue: 'rgb( 65, 105, 225)',
3965          saddlebrown: 'rgb(139, 69, 19)',
3966          salmon: 'rgb(250, 128, 114)',
3967          sandybrown: 'rgb(244, 164, 96)',
3968          seagreen: 'rgb( 46, 139, 87)',
3969          seashell: 'rgb(255, 245, 238)',
3970          sienna: 'rgb(160, 82, 45)',
3971          silver: 'rgb(192, 192, 192)',
3972          skyblue: 'rgb(135, 206, 235)',
3973          slateblue: 'rgb(106, 90, 205)',
3974          slategray: 'rgb(112, 128, 144)',
3975          slategrey: 'rgb(112, 128, 144)',
3976          snow: 'rgb(255, 250, 250)',
3977          springgreen: 'rgb( 0, 255, 127)',
3978          steelblue: 'rgb( 70, 130, 180)',
3979          tan: 'rgb(210, 180, 140)',
3980          teal: 'rgb( 0, 128, 128)',
3981          thistle: 'rgb(216, 191, 216)',
3982          tomato: 'rgb(255, 99, 71)',
3983          turquoise: 'rgb( 64, 224, 208)',
3984          violet: 'rgb(238, 130, 238)',
3985          wheat: 'rgb(245, 222, 179)',
3986          white: 'rgb(255, 255, 255)',
3987          whitesmoke: 'rgb(245, 245, 245)',
3988          yellow: 'rgb(255, 255, 0)',
3989          yellowgreen: 'rgb(154, 205, 50)'
3990      };
3991  
3992      
3993  
3994      // class: $.jqplot.AxisLabelRenderer
3995      // Renderer to place labels on the axes.
3996      $.jqplot.AxisLabelRenderer = function(options) {
3997          // Group: Properties
3998          $.jqplot.ElemContainer.call(this);
3999          // name of the axis associated with this tick
4000          this.axis;
4001          // prop: show
4002          // wether or not to show the tick (mark and label).
4003          this.show = true;
4004          // prop: label
4005          // The text or html for the label.
4006          this.label = '';
4007          this.fontFamily = null;
4008          this.fontSize = null;
4009          this.textColor = null;
4010          this._elem;
4011          // prop: escapeHTML
4012          // true to escape HTML entities in the label.
4013          this.escapeHTML = false;
4014          
4015          $.extend(true, this, options);
4016      };
4017      
4018      $.jqplot.AxisLabelRenderer.prototype = new $.jqplot.ElemContainer();
4019      $.jqplot.AxisLabelRenderer.prototype.constructor = $.jqplot.AxisLabelRenderer;
4020      
4021      $.jqplot.AxisLabelRenderer.prototype.init = function(options) {
4022          $.extend(true, this, options);
4023      };
4024      
4025      $.jqplot.AxisLabelRenderer.prototype.draw = function(ctx, plot) {
4026          // Memory Leaks patch
4027          if (this._elem) {
4028              this._elem.emptyForce();
4029              this._elem = null;
4030          }
4031  
4032          this._elem = $('<div style="position:absolute;" class="jqplot-'+this.axis+'-label"></div>');
4033          
4034          if (Number(this.label)) {
4035              this._elem.css('white-space', 'nowrap');
4036          }
4037          
4038          if (!this.escapeHTML) {
4039              this._elem.html(this.label);
4040          }
4041          else {
4042              this._elem.text(this.label);
4043          }
4044          if (this.fontFamily) {
4045              this._elem.css('font-family', this.fontFamily);
4046          }
4047          if (this.fontSize) {
4048              this._elem.css('font-size', this.fontSize);
4049          }
4050          if (this.textColor) {
4051              this._elem.css('color', this.textColor);
4052          }
4053          
4054          return this._elem;
4055      };
4056      
4057      $.jqplot.AxisLabelRenderer.prototype.pack = function() {
4058      };
4059  
4060      // class: $.jqplot.AxisTickRenderer
4061      // A "tick" object showing the value of a tick/gridline on the plot.
4062      $.jqplot.AxisTickRenderer = function(options) {
4063          // Group: Properties
4064          $.jqplot.ElemContainer.call(this);
4065          // prop: mark
4066          // tick mark on the axis.  One of 'inside', 'outside', 'cross', '' or null.
4067          this.mark = 'outside';
4068          // name of the axis associated with this tick
4069          this.axis;
4070          // prop: showMark
4071          // wether or not to show the mark on the axis.
4072          this.showMark = true;
4073          // prop: showGridline
4074          // wether or not to draw the gridline on the grid at this tick.
4075          this.showGridline = true;
4076          // prop: isMinorTick
4077          // if this is a minor tick.
4078          this.isMinorTick = false;
4079          // prop: size
4080          // Length of the tick beyond the grid in pixels.
4081          // DEPRECATED: This has been superceeded by markSize
4082          this.size = 4;
4083          // prop:  markSize
4084          // Length of the tick marks in pixels.  For 'cross' style, length
4085          // will be stoked above and below axis, so total length will be twice this.
4086          this.markSize = 6;
4087          // prop: show
4088          // wether or not to show the tick (mark and label).
4089          // Setting this to false requires more testing.  It is recommended
4090          // to set showLabel and showMark to false instead.
4091          this.show = true;
4092          // prop: showLabel
4093          // wether or not to show the label.
4094          this.showLabel = true;
4095          this.label = null;
4096          this.value = null;
4097          this._styles = {};
4098          // prop: formatter
4099          // A class of a formatter for the tick text.  sprintf by default.
4100          this.formatter = $.jqplot.DefaultTickFormatter;
4101          // prop: prefix
4102          // String to prepend to the tick label.
4103          // Prefix is prepended to the formatted tick label.
4104          this.prefix = '';
4105          // prop: suffix
4106          // String to append to the tick label.
4107          // Suffix is appended to the formatted tick label.
4108          this.suffix = '';
4109          // prop: formatString
4110          // string passed to the formatter.
4111          this.formatString = '';
4112          // prop: fontFamily
4113          // css spec for the font-family css attribute.
4114          this.fontFamily;
4115          // prop: fontSize
4116          // css spec for the font-size css attribute.
4117          this.fontSize;
4118          // prop: textColor
4119          // css spec for the color attribute.
4120          this.textColor;
4121          // prop: escapeHTML
4122          // true to escape HTML entities in the label.
4123          this.escapeHTML = false;
4124          this._elem;
4125          this._breakTick = false;
4126          
4127          $.extend(true, this, options);
4128      };
4129      
4130      $.jqplot.AxisTickRenderer.prototype.init = function(options) {
4131          $.extend(true, this, options);
4132      };
4133      
4134      $.jqplot.AxisTickRenderer.prototype = new $.jqplot.ElemContainer();
4135      $.jqplot.AxisTickRenderer.prototype.constructor = $.jqplot.AxisTickRenderer;
4136      
4137      $.jqplot.AxisTickRenderer.prototype.setTick = function(value, axisName, isMinor) {
4138          this.value = value;
4139          this.axis = axisName;
4140          if (isMinor) {
4141              this.isMinorTick = true;
4142          }
4143          return this;
4144      };
4145      
4146      $.jqplot.AxisTickRenderer.prototype.draw = function() {
4147          if (this.label === null) {
4148              this.label = this.prefix + this.formatter(this.formatString, this.value) + this.suffix;
4149          }
4150          var style = {position: 'absolute'};
4151          if (Number(this.label)) {
4152              style['whitSpace'] = 'nowrap';
4153          }
4154          
4155          // Memory Leaks patch
4156          if (this._elem) {
4157              this._elem.emptyForce();
4158              this._elem = null;
4159          }
4160  
4161          this._elem = $(document.createElement('div'));
4162          this._elem.addClass("jqplot-"+this.axis+"-tick");
4163          
4164          if (!this.escapeHTML) {
4165              this._elem.html(this.label);
4166          }
4167          else {
4168              this._elem.text(this.label);
4169          }
4170          
4171          this._elem.css(style);
4172  
4173          for (var s in this._styles) {
4174              this._elem.css(s, this._styles[s]);
4175          }
4176          if (this.fontFamily) {
4177              this._elem.css('font-family', this.fontFamily);
4178          }
4179          if (this.fontSize) {
4180              this._elem.css('font-size', this.fontSize);
4181          }
4182          if (this.textColor) {
4183              this._elem.css('color', this.textColor);
4184          }
4185          if (this._breakTick) {
4186              this._elem.addClass('jqplot-breakTick');
4187          }
4188          
4189          return this._elem;
4190      };
4191          
4192      $.jqplot.DefaultTickFormatter = function (format, val) {
4193          if (typeof val == 'number') {
4194              if (!format) {
4195                  format = $.jqplot.config.defaultTickFormatString;
4196              }
4197              return $.jqplot.sprintf(format, val);
4198          }
4199          else {
4200              return String(val);
4201          }
4202      };
4203          
4204      $.jqplot.PercentTickFormatter = function (format, val) {
4205          if (typeof val == 'number') {
4206              val = 100 * val;
4207              if (!format) {
4208                  format = $.jqplot.config.defaultTickFormatString;
4209              }
4210              return $.jqplot.sprintf(format, val);
4211          }
4212          else {
4213              return String(val);
4214          }
4215      };
4216      
4217      $.jqplot.AxisTickRenderer.prototype.pack = function() {
4218      };
4219       
4220      // Class: $.jqplot.CanvasGridRenderer
4221      // The default jqPlot grid renderer, creating a grid on a canvas element.
4222      // The renderer has no additional options beyond the <Grid> class.
4223      $.jqplot.CanvasGridRenderer = function(){
4224          this.shadowRenderer = new $.jqplot.ShadowRenderer();
4225      };
4226      
4227      // called with context of Grid object
4228      $.jqplot.CanvasGridRenderer.prototype.init = function(options) {
4229          this._ctx;
4230          $.extend(true, this, options);
4231          // set the shadow renderer options
4232          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};
4233          this.renderer.shadowRenderer.init(sopts);
4234      };
4235      
4236      // called with context of Grid.
4237      $.jqplot.CanvasGridRenderer.prototype.createElement = function(plot) {
4238          var elem;
4239          // Memory Leaks patch
4240          if (this._elem) {
4241            if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) {
4242              elem = this._elem.get(0);
4243              window.G_vmlCanvasManager.uninitElement(elem);
4244              elem = null;
4245            }
4246            
4247            this._elem.emptyForce();
4248            this._elem = null;
4249          }
4250        
4251          elem = plot.canvasManager.getCanvas();
4252  
4253          var w = this._plotDimensions.width;
4254          var h = this._plotDimensions.height;
4255          elem.width = w;
4256          elem.height = h;
4257          this._elem = $(elem);
4258          this._elem.addClass('jqplot-grid-canvas');
4259          this._elem.css({ position: 'absolute', left: 0, top: 0 });
4260          
4261          elem = plot.canvasManager.initCanvas(elem);
4262          
4263          this._top = this._offsets.top;
4264          this._bottom = h - this._offsets.bottom;
4265          this._left = this._offsets.left;
4266          this._right = w - this._offsets.right;
4267          this._width = this._right - this._left;
4268          this._height = this._bottom - this._top;
4269          // avoid memory leak
4270          elem = null;
4271          return this._elem;
4272      };
4273      
4274      $.jqplot.CanvasGridRenderer.prototype.draw = function() {
4275          this._ctx = this._elem.get(0).getContext("2d");
4276          var ctx = this._ctx;
4277          var axes = this._axes;
4278          // Add the grid onto the grid canvas.  This is the bottom most layer.
4279          ctx.save();
4280          ctx.clearRect(0, 0, this._plotDimensions.width, this._plotDimensions.height);
4281          ctx.fillStyle = this.backgroundColor || this.background;
4282          ctx.fillRect(this._left, this._top, this._width, this._height);
4283          
4284          ctx.save();
4285          ctx.lineJoin = 'miter';
4286          ctx.lineCap = 'butt';
4287          ctx.lineWidth = this.gridLineWidth;
4288          ctx.strokeStyle = this.gridLineColor;
4289          var b, e, s, m;
4290          var ax = ['xaxis', 'yaxis', 'x2axis', 'y2axis'];
4291          for (var i=4; i>0; i--) {
4292              var name = ax[i-1];
4293              var axis = axes[name];
4294              var ticks = axis._ticks;
4295              var numticks = ticks.length;
4296              if (axis.show) {
4297                  if (axis.drawBaseline) {
4298                      var bopts = {};
4299                      if (axis.baselineWidth !== null) {
4300                          bopts.lineWidth = axis.baselineWidth;
4301                      }
4302                      if (axis.baselineColor !== null) {
4303                          bopts.strokeStyle = axis.baselineColor;
4304                      }
4305                      switch (name) {
4306                          case 'xaxis':
4307                              drawLine (this._left, this._bottom, this._right, this._bottom, bopts);
4308                              break;
4309                          case 'yaxis':
4310                              drawLine (this._left, this._bottom, this._left, this._top, bopts);
4311                              break;
4312                          case 'x2axis':
4313                              drawLine (this._left, this._bottom, this._right, this._bottom, bopts);
4314                              break;
4315                          case 'y2axis':
4316                              drawLine (this._right, this._bottom, this._right, this._top, bopts);
4317                              break;
4318                      }
4319                  }
4320                  for (var j=numticks; j>0; j--) {
4321                      var t = ticks[j-1];
4322                      if (t.show) {
4323                          var pos = Math.round(axis.u2p(t.value)) + 0.5;
4324                          switch (name) {
4325                              case 'xaxis':
4326                                  // draw the grid line if we should
4327                                  if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) {
4328                                      drawLine(pos, this._top, pos, this._bottom);
4329                                  }
4330                                  // draw the mark
4331                                  if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) {
4332                                      s = t.markSize;
4333                                      m = t.mark;
4334                                      var pos = Math.round(axis.u2p(t.value)) + 0.5;
4335                                      switch (m) {
4336                                          case 'outside':
4337                                              b = this._bottom;
4338                                              e = this._bottom+s;
4339                                              break;
4340                                          case 'inside':
4341                                              b = this._bottom-s;
4342                                              e = this._bottom;
4343                                              break;
4344                                          case 'cross':
4345                                              b = this._bottom-s;
4346                                              e = this._bottom+s;
4347                                              break;
4348                                          default:
4349                                              b = this._bottom;
4350                                              e = this._bottom+s;
4351                                              break;
4352                                      }
4353                                      // draw the shadow
4354                                      if (this.shadow) {
4355                                          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});
4356                                      }
4357                                      // draw the line
4358                                      drawLine(pos, b, pos, e);
4359                                  }
4360                                  break;
4361                              case 'yaxis':
4362                                  // draw the grid line
4363                                  if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) {
4364                                      drawLine(this._right, pos, this._left, pos);
4365                                  }
4366                                  // draw the mark
4367                                  if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) {
4368                                      s = t.markSize;
4369                                      m = t.mark;
4370                                      var pos = Math.round(axis.u2p(t.value)) + 0.5;
4371                                      switch (m) {
4372                                          case 'outside':
4373                                              b = this._left-s;
4374                                              e = this._left;
4375                                              break;
4376                                          case 'inside':
4377                                              b = this._left;
4378                                              e = this._left+s;
4379                                              break;
4380                                          case 'cross':
4381                                              b = this._left-s;
4382                                              e = this._left+s;
4383                                              break;
4384                                          default:
4385                                              b = this._left-s;
4386                                              e = this._left;
4387                                              break;
4388                                              }
4389                                      // draw the shadow
4390                                      if (this.shadow) {
4391                                          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});
4392                                      }
4393                                      drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
4394                                  }
4395                                  break;
4396                              case 'x2axis':
4397                                  // draw the grid line
4398                                  if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) {
4399                                      drawLine(pos, this._bottom, pos, this._top);
4400                                  }
4401                                  // draw the mark
4402                                  if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) {
4403                                      s = t.markSize;
4404                                      m = t.mark;
4405                                      var pos = Math.round(axis.u2p(t.value)) + 0.5;
4406                                      switch (m) {
4407                                          case 'outside':
4408                                              b = this._top-s;
4409                                              e = this._top;
4410                                              break;
4411                                          case 'inside':
4412                                              b = this._top;
4413                                              e = this._top+s;
4414                                              break;
4415                                          case 'cross':
4416                                              b = this._top-s;
4417                                              e = this._top+s;
4418                                              break;
4419                                          default:
4420                                              b = this._top-s;
4421                                              e = this._top;
4422                                              break;
4423                                              }
4424                                      // draw the shadow
4425                                      if (this.shadow) {
4426                                          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});
4427                                      }
4428                                      drawLine(pos, b, pos, e);
4429                                  }
4430                                  break;
4431                              case 'y2axis':
4432                                  // draw the grid line
4433                                  if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) {
4434                                      drawLine(this._left, pos, this._right, pos);
4435                                  }
4436                                  // draw the mark
4437                                  if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) {
4438                                      s = t.markSize;
4439                                      m = t.mark;
4440                                      var pos = Math.round(axis.u2p(t.value)) + 0.5;
4441                                      switch (m) {
4442                                          case 'outside':
4443                                              b = this._right;
4444                                              e = this._right+s;
4445                                              break;
4446                                          case 'inside':
4447                                              b = this._right-s;
4448                                              e = this._right;
4449                                              break;
4450                                          case 'cross':
4451                                              b = this._right-s;
4452                                              e = this._right+s;
4453                                              break;
4454                                          default:
4455                                              b = this._right;
4456                                              e = this._right+s;
4457                                              break;
4458                                              }
4459                                      // draw the shadow
4460                                      if (this.shadow) {
4461                                          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});
4462                                      }
4463                                      drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
4464                                  }
4465                                  break;
4466                              default:
4467                                  break;
4468                          }
4469                      }
4470                  }
4471                  t = null;
4472              }
4473              axis = null;
4474              ticks = null;
4475          }
4476          // Now draw grid lines for additional y axes
4477          //////
4478          // TO DO: handle yMidAxis
4479          //////
4480          ax = ['y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis'];
4481          for (var i=7; i>0; i--) {
4482              var axis = axes[ax[i-1]];
4483              var ticks = axis._ticks;
4484              if (axis.show) {
4485                  var tn = ticks[axis.numberTicks-1];
4486                  var t0 = ticks[0];
4487                  var left = axis.getLeft();
4488                  var points = [[left, tn.getTop() + tn.getHeight()/2], [left, t0.getTop() + t0.getHeight()/2 + 1.0]];
4489                  // draw the shadow
4490                  if (this.shadow) {
4491                      this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', fill:false, closePath:false});
4492                  }
4493                  // draw the line
4494                  drawLine(points[0][0], points[0][1], points[1][0], points[1][1], {lineCap:'butt', strokeStyle:axis.borderColor, lineWidth:axis.borderWidth});
4495                  // draw the tick marks
4496                  for (var j=ticks.length; j>0; j--) {
4497                      var t = ticks[j-1];
4498                      s = t.markSize;
4499                      m = t.mark;
4500                      var pos = Math.round(axis.u2p(t.value)) + 0.5;
4501                      if (t.showMark && t.mark) {
4502                          switch (m) {
4503                              case 'outside':
4504                                  b = left;
4505                                  e = left+s;
4506                                  break;
4507                              case 'inside':
4508                                  b = left-s;
4509                                  e = left;
4510                                  break;
4511                              case 'cross':
4512                                  b = left-s;
4513                                  e = left+s;
4514                                  break;
4515                              default:
4516                                  b = left;
4517                                  e = left+s;
4518                                  break;
4519                          }
4520                          points = [[b,pos], [e,pos]];
4521                          // draw the shadow
4522                          if (this.shadow) {
4523                              this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false});
4524                          }
4525                          // draw the line
4526                          drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
4527                      }
4528                      t = null;
4529                  }
4530                  t0 = null;
4531              }
4532              axis = null;
4533              ticks =  null;
4534          }
4535          
4536          ctx.restore();
4537          
4538          function drawLine(bx, by, ex, ey, opts) {
4539              ctx.save();
4540              opts = opts || {};
4541              if (opts.lineWidth == null || opts.lineWidth != 0){
4542                  $.extend(true, ctx, opts);
4543                  ctx.beginPath();
4544                  ctx.moveTo(bx, by);
4545                  ctx.lineTo(ex, ey);
4546                  ctx.stroke();
4547                  ctx.restore();
4548              }
4549          }
4550          
4551          if (this.shadow) {
4552              var points = [[this._left, this._bottom], [this._right, this._bottom], [this._right, this._top]];
4553              this.renderer.shadowRenderer.draw(ctx, points);
4554          }
4555          // Now draw border around grid.  Use axis border definitions. start at
4556          // upper left and go clockwise.
4557          if (this.borderWidth != 0 && this.drawBorder) {
4558              drawLine (this._left, this._top, this._right, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth});
4559              drawLine (this._right, this._top, this._right, this._bottom, {lineCap:'round', strokeStyle:axes.y2axis.borderColor, lineWidth:axes.y2axis.borderWidth});
4560              drawLine (this._right, this._bottom, this._left, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth});
4561              drawLine (this._left, this._bottom, this._left, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth});
4562          }
4563          // ctx.lineWidth = this.borderWidth;
4564          // ctx.strokeStyle = this.borderColor;
4565          // ctx.strokeRect(this._left, this._top, this._width, this._height);
4566          
4567          ctx.restore();
4568          ctx =  null;
4569          axes = null;
4570      };
4571   
4572      // Class: $.jqplot.DivTitleRenderer
4573      // The default title renderer for jqPlot.  This class has no options beyond the <Title> class. 
4574      $.jqplot.DivTitleRenderer = function() {
4575      };
4576      
4577      $.jqplot.DivTitleRenderer.prototype.init = function(options) {
4578          $.extend(true, this, options);
4579      };
4580      
4581      $.jqplot.DivTitleRenderer.prototype.draw = function() {
4582          // Memory Leaks patch
4583          if (this._elem) {
4584              this._elem.emptyForce();
4585              this._elem = null;
4586          }
4587  
4588          var r = this.renderer;
4589          var elem = document.createElement('div');
4590          this._elem = $(elem);
4591          this._elem.addClass('jqplot-title');
4592  
4593          if (!this.text) {
4594              this.show = false;
4595              this._elem.height(0);
4596              this._elem.width(0);
4597          }
4598          else if (this.text) {
4599              var color;
4600              if (this.color) {
4601                  color = this.color;
4602              }
4603              else if (this.textColor) {
4604                  color = this.textColor;
4605              }
4606  
4607              // don't trust that a stylesheet is present, set the position.
4608              var styles = {position:'absolute', top:'0px', left:'0px'};
4609  
4610              if (this._plotWidth) {
4611                  styles['width'] = this._plotWidth+'px';
4612              }
4613              if (this.fontSize) {
4614                  styles['fontSize'] = this.fontSize;
4615              }
4616              if (typeof this.textAlign === 'string') {
4617                  styles['textAlign'] = this.textAlign;
4618              }
4619              else {
4620                  styles['textAlign'] = 'center';
4621              }
4622              if (color) {
4623                  styles['color'] = color;
4624              }
4625              if (this.paddingBottom) {
4626                  styles['paddingBottom'] = this.paddingBottom;
4627              }
4628              if (this.fontFamily) {
4629                  styles['fontFamily'] = this.fontFamily;
4630              }
4631  
4632              this._elem.css(styles);
4633              if (this.escapeHtml) {
4634                  this._elem.text(this.text);
4635              }
4636              else {
4637                  this._elem.html(this.text);
4638              }
4639  
4640  
4641              // styletext += (this._plotWidth) ? 'width:'+this._plotWidth+'px;' : '';
4642              // styletext += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
4643              // styletext += (this.textAlign) ? 'text-align:'+this.textAlign+';' : 'text-align:center;';
4644              // styletext += (color) ? 'color:'+color+';' : '';
4645              // styletext += (this.paddingBottom) ? 'padding-bottom:'+this.paddingBottom+';' : '';
4646              // this._elem = $('<div class="jqplot-title" style="'+styletext+'">'+this.text+'</div>');
4647              // if (this.fontFamily) {
4648              //     this._elem.css('font-family', this.fontFamily);
4649              // }
4650          }
4651  
4652          elem = null;
4653          
4654          return this._elem;
4655      };
4656      
4657      $.jqplot.DivTitleRenderer.prototype.pack = function() {
4658          // nothing to do here
4659      };
4660    
4661  
4662      var dotlen = 0.1;
4663  
4664      $.jqplot.LinePattern = function (ctx, pattern) {
4665  
4666          var defaultLinePatterns = {
4667              dotted: [ dotlen, $.jqplot.config.dotGapLength ],
4668              dashed: [ $.jqplot.config.dashLength, $.jqplot.config.gapLength ],
4669              solid: null
4670          };       
4671  
4672          if (typeof pattern === 'string') {
4673              if (pattern[0] === '.' || pattern[0] === '-') {
4674                  var s = pattern;
4675                  pattern = [];
4676                  for (var i=0, imax=s.length; i<imax; i++) {
4677                      if (s[i] === '.') {
4678                          pattern.push( dotlen );
4679                      }
4680                      else if (s[i] === '-') {
4681                          pattern.push( $.jqplot.config.dashLength );
4682                      }
4683                      else {
4684                          continue;
4685                      }
4686                      pattern.push( $.jqplot.config.gapLength );
4687                  }
4688              }
4689              else {
4690                  pattern = defaultLinePatterns[pattern];
4691              }
4692          }
4693  
4694          if (!(pattern && pattern.length)) {
4695              return ctx;
4696          }
4697  
4698          var patternIndex = 0;
4699          var patternDistance = pattern[0];
4700          var px = 0;
4701          var py = 0;
4702          var pathx0 = 0;
4703          var pathy0 = 0;
4704  
4705          var moveTo = function (x, y) {
4706              ctx.moveTo( x, y );
4707              px = x;
4708              py = y;
4709              pathx0 = x;
4710              pathy0 = y;
4711          };
4712  
4713          var lineTo = function (x, y) {
4714              var scale = ctx.lineWidth;
4715              var dx = x - px;
4716              var dy = y - py;
4717              var dist = Math.sqrt(dx*dx+dy*dy);
4718              if ((dist > 0) && (scale > 0)) {
4719                  dx /= dist;
4720                  dy /= dist;
4721                  while (true) {
4722                      var dp = scale * patternDistance;
4723                      if (dp < dist) {
4724                          px += dp * dx;
4725                          py += dp * dy;
4726                          if ((patternIndex & 1) == 0) {
4727                              ctx.lineTo( px, py );
4728                          }
4729                          else {
4730                              ctx.moveTo( px, py );
4731                          }
4732                          dist -= dp;
4733                          patternIndex++;
4734                          if (patternIndex >= pattern.length) {
4735                              patternIndex = 0;
4736                          }
4737                          patternDistance = pattern[patternIndex];
4738                      }
4739                      else {
4740                          px = x;
4741                          py = y;
4742                          if ((patternIndex & 1) == 0) {
4743                              ctx.lineTo( px, py );
4744                          }
4745                          else {
4746                              ctx.moveTo( px, py );
4747                          }
4748                          patternDistance -= dist / scale;
4749                          break;
4750                      }
4751                  }
4752              }
4753          };
4754  
4755          var beginPath = function () {
4756              ctx.beginPath();
4757          };
4758  
4759          var closePath = function () {
4760              lineTo( pathx0, pathy0 );
4761          };
4762  
4763          return {
4764              moveTo: moveTo,
4765              lineTo: lineTo,
4766              beginPath: beginPath,
4767              closePath: closePath
4768          };
4769      };
4770  
4771      // Class: $.jqplot.LineRenderer
4772      // The default line renderer for jqPlot, this class has no options beyond the <Series> class.
4773      // Draws series as a line.
4774      $.jqplot.LineRenderer = function(){
4775          this.shapeRenderer = new $.jqplot.ShapeRenderer();
4776          this.shadowRenderer = new $.jqplot.ShadowRenderer();
4777      };
4778      
4779      // called with scope of series.
4780      $.jqplot.LineRenderer.prototype.init = function(options, plot) {
4781          // Group: Properties
4782          //
4783          options = options || {};
4784          this._type='line';
4785          this.renderer.animation = {
4786              show: false,
4787              direction: 'left',
4788              speed: 2500,
4789              _supported: true
4790          };
4791          // prop: smooth
4792          // True to draw a smoothed (interpolated) line through the data points
4793          // with automatically computed number of smoothing points.
4794          // Set to an integer number > 2 to specify number of smoothing points
4795          // to use between each data point.
4796          this.renderer.smooth = false;  // true or a number > 2 for smoothing.
4797          this.renderer.tension = null; // null to auto compute or a number typically > 6.  Fewer points requires higher tension.
4798          // prop: constrainSmoothing
4799          // True to use a more accurate smoothing algorithm that will
4800          // not overshoot any data points.  False to allow overshoot but
4801          // produce a smoother looking line.
4802          this.renderer.constrainSmoothing = true;
4803          // this is smoothed data in grid coordinates, like gridData
4804          this.renderer._smoothedData = [];
4805          // this is smoothed data in plot units (plot coordinates), like plotData.
4806          this.renderer._smoothedPlotData = [];
4807          this.renderer._hiBandGridData = [];
4808          this.renderer._lowBandGridData = [];
4809          this.renderer._hiBandSmoothedData = [];
4810          this.renderer._lowBandSmoothedData = [];
4811  
4812          // prop: bandData
4813          // Data used to draw error bands or confidence intervals above/below a line.
4814          //
4815          // bandData can be input in 3 forms.  jqPlot will figure out which is the
4816          // low band line and which is the high band line for all forms:
4817          // 
4818          // A 2 dimensional array like [[yl1, yl2, ...], [yu1, yu2, ...]] where
4819          // [yl1, yl2, ...] are y values of the lower line and
4820          // [yu1, yu2, ...] are y values of the upper line.
4821          // In this case there must be the same number of y data points as data points
4822          // in the series and the bands will inherit the x values of the series.
4823          //
4824          // A 2 dimensional array like [[[xl1, yl1], [xl2, yl2], ...], [[xh1, yh1], [xh2, yh2], ...]]
4825          // where [xl1, yl1] are x,y data points for the lower line and
4826          // [xh1, yh1] are x,y data points for the high line.
4827          // x values do not have to correspond to the x values of the series and can
4828          // be of any arbitrary length.
4829          //
4830          // Can be of form [[yl1, yu1], [yl2, yu2], [yl3, yu3], ...] where
4831          // there must be 3 or more arrays and there must be the same number of arrays
4832          // as there are data points in the series.  In this case, 
4833          // [yl1, yu1] specifies the lower and upper y values for the 1st
4834          // data point and so on.  The bands will inherit the x
4835          // values from the series.
4836          this.renderer.bandData = [];
4837  
4838          // Group: bands
4839          // Banding around line, e.g error bands or confidence intervals.
4840          this.renderer.bands = {
4841              // prop: show
4842              // true to show the bands.  If bandData or interval is
4843              // supplied, show will be set to true by default.
4844              show: false,
4845              hiData: [],
4846              lowData: [],
4847              // prop: color
4848              // color of lines at top and bottom of bands [default: series color].
4849              color: this.color,
4850              // prop: showLines
4851              // True to show lines at top and bottom of bands [default: false].
4852              showLines: false,
4853              // prop: fill
4854              // True to fill area between bands [default: true].
4855              fill: true,
4856              // prop: fillColor
4857              // css color spec for filled area.  [default: series color].
4858              fillColor: null,
4859              _min: null,
4860              _max: null,
4861              // prop: interval
4862              // User specified interval above and below line for bands [default: '3%''].
4863              // Can be a value like 3 or a string like '3%' 
4864              // or an upper/lower array like [1, -2] or ['2%', '-1.5%']
4865              interval: '3%'
4866          };
4867  
4868  
4869          var lopts = {highlightMouseOver: options.highlightMouseOver, highlightMouseDown: options.highlightMouseDown, highlightColor: options.highlightColor};
4870          
4871          delete (options.highlightMouseOver);
4872          delete (options.highlightMouseDown);
4873          delete (options.highlightColor);
4874          
4875          $.extend(true, this.renderer, options);
4876  
4877          this.renderer.options = options;
4878  
4879          // if we are given some band data, and bands aren't explicity set to false in options, turn them on.
4880          if (this.renderer.bandData.length > 1 && (!options.bands || options.bands.show == null)) {
4881              this.renderer.bands.show = true;
4882          }
4883  
4884          // if we are given an interval, and bands aren't explicity set to false in options, turn them on.
4885          else if (options.bands && options.bands.show == null && options.bands.interval != null) {
4886              this.renderer.bands.show = true;
4887          }
4888  
4889          // if plot is filled, turn off bands.
4890          if (this.fill) {
4891              this.renderer.bands.show = false;
4892          }
4893  
4894          if (this.renderer.bands.show) {
4895              this.renderer.initBands.call(this, this.renderer.options, plot);
4896          }
4897  
4898  
4899          // smoothing is not compatible with stacked lines, disable
4900          if (this._stack) {
4901              this.renderer.smooth = false;
4902          }
4903  
4904          // set the shape renderer options
4905          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};
4906          this.renderer.shapeRenderer.init(opts);
4907  
4908          var shadow_offset = options.shadowOffset;
4909          // set the shadow renderer options
4910          if (shadow_offset == null) {
4911              // scale the shadowOffset to the width of the line.
4912              if (this.lineWidth > 2.5) {
4913                  shadow_offset = 1.25 * (1 + (Math.atan((this.lineWidth/2.5))/0.785398163 - 1)*0.6);
4914                  // var shadow_offset = this.shadowOffset;
4915              }
4916              // for skinny lines, don't make such a big shadow.
4917              else {
4918                  shadow_offset = 1.25 * Math.atan((this.lineWidth/2.5))/0.785398163;
4919              }
4920          }
4921          
4922          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};
4923          this.renderer.shadowRenderer.init(sopts);
4924          this._areaPoints = [];
4925          this._boundingBox = [[],[]];
4926          
4927          if (!this.isTrendline && this.fill || this.renderer.bands.show) {
4928              // Group: Properties
4929              //        
4930              // prop: highlightMouseOver
4931              // True to highlight area on a filled plot when moused over.
4932              // This must be false to enable highlightMouseDown to highlight when clicking on an area on a filled plot.
4933              this.highlightMouseOver = true;
4934              // prop: highlightMouseDown
4935              // True to highlight when a mouse button is pressed over an area on a filled plot.
4936              // This will be disabled if highlightMouseOver is true.
4937              this.highlightMouseDown = false;
4938              // prop: highlightColor
4939              // color to use when highlighting an area on a filled plot.
4940              this.highlightColor = null;
4941              // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
4942              if (lopts.highlightMouseDown && lopts.highlightMouseOver == null) {
4943                  lopts.highlightMouseOver = false;
4944              }
4945          
4946              $.extend(true, this, {highlightMouseOver: lopts.highlightMouseOver, highlightMouseDown: lopts.highlightMouseDown, highlightColor: lopts.highlightColor});
4947              
4948              if (!this.highlightColor) {
4949                  var fc = (this.renderer.bands.show) ? this.renderer.bands.fillColor : this.fillColor;
4950                  this.highlightColor = $.jqplot.computeHighlightColors(fc);
4951              }
4952              // turn off (disable) the highlighter plugin
4953              if (this.highlighter) {
4954                  this.highlighter.show = false;
4955              }
4956          }
4957          
4958          if (!this.isTrendline && plot) {
4959              plot.plugins.lineRenderer = {};
4960              plot.postInitHooks.addOnce(postInit);
4961              plot.postDrawHooks.addOnce(postPlotDraw);
4962              plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
4963              plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown);
4964              plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp);
4965              plot.eventListenerHooks.addOnce('jqplotClick', handleClick);
4966              plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick);
4967          }
4968  
4969      };
4970  
4971      $.jqplot.LineRenderer.prototype.initBands = function(options, plot) {
4972          // use bandData if no data specified in bands option
4973          //var bd = this.renderer.bandData;
4974          var bd = options.bandData || [];
4975          var bands = this.renderer.bands;
4976          bands.hiData = [];
4977          bands.lowData = [];
4978          var data = this.data;
4979          bands._max = null;
4980          bands._min = null;
4981          // If 2 arrays, and each array greater than 2 elements, assume it is hi and low data bands of y values.
4982          if (bd.length == 2) {
4983              // Do we have an array of x,y values?
4984              // like [[[1,1], [2,4], [3,3]], [[1,3], [2,6], [3,5]]]
4985              if ($.isArray(bd[0][0])) {
4986                  // since an arbitrary array of points, spin through all of them to determine max and min lines.
4987  
4988                  var p;
4989                  var bdminidx = 0, bdmaxidx = 0;
4990                  for (var i = 0, l = bd[0].length; i<l; i++) {
4991                      p = bd[0][i];
4992                      if ((p[1] != null && p[1] > bands._max) || bands._max == null) {
4993                          bands._max = p[1];
4994                      }
4995                      if ((p[1] != null && p[1] < bands._min) || bands._min == null) {
4996                          bands._min = p[1];
4997                      }
4998                  }
4999                  for (var i = 0, l = bd[1].length; i<l; i++) {
5000                      p = bd[1][i];
5001                      if ((p[1] != null && p[1] > bands._max) || bands._max == null) {
5002                          bands._max = p[1];
5003                          bdmaxidx = 1;
5004                      }
5005                      if ((p[1] != null && p[1] < bands._min) || bands._min == null) {
5006                          bands._min = p[1];
5007                          bdminidx = 1;
5008                      }
5009                  }
5010  
5011                  if (bdmaxidx === bdminidx) {
5012                      bands.show = false;
5013                  }
5014  
5015                  bands.hiData = bd[bdmaxidx];
5016                  bands.lowData = bd[bdminidx];
5017              }
5018              // else data is arrays of y values
5019              // like [[1,4,3], [3,6,5]]
5020              // must have same number of band data points as points in series
5021              else if (bd[0].length === data.length && bd[1].length === data.length) {
5022                  var hi = (bd[0][0] > bd[1][0]) ? 0 : 1;
5023                  var low = (hi) ? 0 : 1;
5024                  for (var i=0, l=data.length; i < l; i++) {
5025                      bands.hiData.push([data[i][0], bd[hi][i]]);
5026                      bands.lowData.push([data[i][0], bd[low][i]]);
5027                  }
5028              }
5029  
5030              // we don't have proper data array, don't show bands.
5031              else {
5032                  bands.show = false;
5033              }
5034          }
5035  
5036          // if more than 2 arrays, have arrays of [ylow, yhi] values.
5037          // note, can't distinguish case of [[ylow, yhi], [ylow, yhi]] from [[ylow, ylow], [yhi, yhi]]
5038          // this is assumed to be of the latter form.
5039          else if (bd.length > 2 && !$.isArray(bd[0][0])) {
5040              var hi = (bd[0][0] > bd[0][1]) ? 0 : 1;
5041              var low = (hi) ? 0 : 1;
5042              for (var i=0, l=bd.length; i<l; i++) {
5043                  bands.hiData.push([data[i][0], bd[i][hi]]);
5044                  bands.lowData.push([data[i][0], bd[i][low]]);
5045              }
5046          }
5047  
5048          // don't have proper data, auto calculate
5049          else {
5050              var intrv = bands.interval;
5051              var a = null;
5052              var b = null;
5053              var afunc = null;
5054              var bfunc = null;
5055  
5056              if ($.isArray(intrv)) {
5057                  a = intrv[0];
5058                  b = intrv[1];
5059              }
5060              else {
5061                  a = intrv;
5062              }
5063  
5064              if (isNaN(a)) {
5065                  // we have a string
5066                  if (a.charAt(a.length - 1) === '%') {
5067                      afunc = 'multiply';
5068                      a = parseFloat(a)/100 + 1;
5069                  }
5070              }
5071  
5072              else {
5073                  a = parseFloat(a);
5074                  afunc = 'add';
5075              }
5076  
5077              if (b !== null && isNaN(b)) {
5078                  // we have a string
5079                  if (b.charAt(b.length - 1) === '%') {
5080                      bfunc = 'multiply';
5081                      b = parseFloat(b)/100 + 1;
5082                  }
5083              }
5084  
5085              else if (b !== null) {
5086                  b = parseFloat(b);
5087                  bfunc = 'add';
5088              }
5089  
5090              if (a !== null) {
5091                  if (b === null) {
5092                      b = -a;
5093                      bfunc = afunc;
5094                      if (bfunc === 'multiply') {
5095                          b += 2;
5096                      }
5097                  }
5098  
5099                  // make sure a always applies to hi band.
5100                  if (a < b) {
5101                      var temp = a;
5102                      a = b;
5103                      b = temp;
5104                      temp = afunc;
5105                      afunc = bfunc;
5106                      bfunc = temp;
5107                  }
5108  
5109                  for (var i=0, l = data.length; i < l; i++) {
5110                      switch (afunc) {
5111                          case 'add':
5112                              bands.hiData.push([data[i][0], data[i][1] + a]);
5113                              break;
5114                          case 'multiply':
5115                              bands.hiData.push([data[i][0], data[i][1] * a]);
5116                              break;
5117                      }
5118                      switch (bfunc) {
5119                          case 'add':
5120                              bands.lowData.push([data[i][0], data[i][1] + b]);
5121                              break;
5122                          case 'multiply':
5123                              bands.lowData.push([data[i][0], data[i][1] * b]);
5124                              break;
5125                      }
5126                  }
5127              }
5128  
5129              else {
5130                  bands.show = false;
5131              }
5132          }
5133  
5134          var hd = bands.hiData;
5135          var ld = bands.lowData;
5136          for (var i = 0, l = hd.length; i<l; i++) {
5137              if ((hd[i][1] != null && hd[i][1] > bands._max) || bands._max == null) {
5138                  bands._max = hd[i][1];
5139              }
5140          }
5141          for (var i = 0, l = ld.length; i<l; i++) {
5142              if ((ld[i][1] != null && ld[i][1] < bands._min) || bands._min == null) {
5143                  bands._min = ld[i][1];
5144              }
5145          }
5146  
5147          // one last check for proper data
5148          // these don't apply any more since allowing arbitrary x,y values
5149          // if (bands.hiData.length != bands.lowData.length) {
5150          //     bands.show = false;
5151          // }
5152  
5153          // if (bands.hiData.length != this.data.length) {
5154          //     bands.show = false;
5155          // }
5156  
5157          if (bands.fillColor === null) {
5158              var c = $.jqplot.getColorComponents(bands.color);
5159              // now adjust alpha to differentiate fill
5160              c[3] = c[3] * 0.5;
5161              bands.fillColor = 'rgba(' + c[0] +', '+ c[1] +', '+ c[2] +', '+ c[3] + ')';
5162          }
5163      };
5164  
5165      function getSteps (d, f) {
5166          return (3.4182054+f) * Math.pow(d, -0.3534992);
5167      }
5168  
5169      function computeSteps (d1, d2) {
5170          var s = Math.sqrt(Math.pow((d2[0]- d1[0]), 2) + Math.pow ((d2[1] - d1[1]), 2));
5171          return 5.7648 * Math.log(s) + 7.4456;
5172      }
5173  
5174      function tanh (x) {
5175          var a = (Math.exp(2*x) - 1) / (Math.exp(2*x) + 1);
5176          return a;
5177      }
5178  
5179      //////////
5180      // computeConstrainedSmoothedData
5181      // An implementation of the constrained cubic spline interpolation
5182      // method as presented in:
5183      //
5184      // Kruger, CJC, Constrained Cubic Spine Interpolation for Chemical Engineering Applications
5185      // http://www.korf.co.uk/spline.pdf
5186      //
5187      // The implementation below borrows heavily from the sample Visual Basic
5188      // implementation by CJC Kruger found in http://www.korf.co.uk/spline.xls
5189      //
5190      /////////
5191  
5192      // called with scope of series
5193      function computeConstrainedSmoothedData (gd) {
5194          var smooth = this.renderer.smooth;
5195          var dim = this.canvas.getWidth();
5196          var xp = this._xaxis.series_p2u;
5197          var yp = this._yaxis.series_p2u; 
5198          var steps =null;
5199          var _steps = null;
5200          var dist = gd.length/dim;
5201          var _smoothedData = [];
5202          var _smoothedPlotData = [];
5203  
5204          if (!isNaN(parseFloat(smooth))) {
5205              steps = parseFloat(smooth);
5206          }
5207          else {
5208              steps = getSteps(dist, 0.5);
5209          }
5210  
5211          var yy = [];
5212          var xx = [];
5213  
5214          for (var i=0, l = gd.length; i<l; i++) {
5215              yy.push(gd[i][1]);
5216              xx.push(gd[i][0]);
5217          }
5218  
5219          function dxx(x1, x0) {
5220              if (x1 - x0 == 0) {
5221                  return Math.pow(10,10);
5222              }
5223              else {
5224                  return x1 - x0;
5225              }
5226          }
5227  
5228          var A, B, C, D;
5229          // loop through each line segment.  Have # points - 1 line segments.  Nmber segments starting at 1.
5230          var nmax = gd.length - 1;
5231          for (var num = 1, gdl = gd.length; num<gdl; num++) {
5232              var gxx = [];
5233              var ggxx = [];
5234              // point at each end of segment.
5235              for (var j = 0; j < 2; j++) {
5236                  var i = num - 1 + j; // point number, 0 to # points.
5237  
5238                  if (i == 0 || i == nmax) {
5239                      gxx[j] = Math.pow(10, 10);
5240                  }
5241                  else if (yy[i+1] - yy[i] == 0 || yy[i] - yy[i-1] == 0) {
5242                      gxx[j] = 0;
5243                  }
5244                  else if (((xx[i+1] - xx[i]) / (yy[i+1] - yy[i]) + (xx[i] - xx[i-1]) / (yy[i] - yy[i-1])) == 0 ) {
5245                      gxx[j] = 0;
5246                  }
5247                  else if ( (yy[i+1] - yy[i]) * (yy[i] - yy[i-1]) < 0 ) {
5248                      gxx[j] = 0;
5249                  }
5250  
5251                  else {
5252                      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]));
5253                  }
5254              }
5255  
5256              // Reset first derivative (slope) at first and last point
5257              if (num == 1) {
5258                  // First point has 0 2nd derivative
5259                  gxx[0] = 3 / 2 * (yy[1] - yy[0]) / dxx(xx[1], xx[0]) - gxx[1] / 2;
5260              }
5261              else if (num == nmax) {
5262                  // Last point has 0 2nd derivative
5263                  gxx[1] = 3 / 2 * (yy[nmax] - yy[nmax - 1]) / dxx(xx[nmax], xx[nmax - 1]) - gxx[0] / 2;
5264              }   
5265  
5266              // Calc second derivative at points
5267              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);
5268              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);
5269  
5270              // Calc constants for cubic interpolation
5271              D = 1 / 6 * (ggxx[1] - ggxx[0]) / dxx(xx[num], xx[num - 1]);
5272              C = 1 / 2 * (xx[num] * ggxx[0] - xx[num - 1] * ggxx[1]) / dxx(xx[num], xx[num - 1]);
5273              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]);
5274              A = yy[num - 1] - B * xx[num - 1] - C * Math.pow(xx[num - 1], 2) - D * Math.pow(xx[num - 1], 3);
5275  
5276              var increment = (xx[num] - xx[num - 1]) / steps;
5277              var temp, tempx;
5278  
5279              for (var j = 0, l = steps; j < l; j++) {
5280                  temp = [];
5281                  tempx = xx[num - 1] + j * increment;
5282                  temp.push(tempx);
5283                  temp.push(A + B * tempx + C * Math.pow(tempx, 2) + D * Math.pow(tempx, 3));
5284                  _smoothedData.push(temp);
5285                  _smoothedPlotData.push([xp(temp[0]), yp(temp[1])]);
5286              }
5287          }
5288  
5289          _smoothedData.push(gd[i]);
5290          _smoothedPlotData.push([xp(gd[i][0]), yp(gd[i][1])]);
5291  
5292          return [_smoothedData, _smoothedPlotData];
5293      }
5294  
5295      ///////
5296      // computeHermiteSmoothedData
5297      // A hermite spline smoothing of the plot data.
5298      // This implementation is derived from the one posted
5299      // by krypin on the jqplot-users mailing list:
5300      //
5301      // http://groups.google.com/group/jqplot-users/browse_thread/thread/748be6a445723cea?pli=1
5302      //
5303      // with a blog post:
5304      //
5305      // http://blog.statscollector.com/a-plugin-renderer-for-jqplot-to-draw-a-hermite-spline/
5306      //
5307      // and download of the original plugin:
5308      //
5309      // http://blog.statscollector.com/wp-content/uploads/2010/02/jqplot.hermiteSplineRenderer.js
5310      //////////
5311  
5312      // called with scope of series
5313      function computeHermiteSmoothedData (gd) {
5314          var smooth = this.renderer.smooth;
5315          var tension = this.renderer.tension;
5316          var dim = this.canvas.getWidth();
5317          var xp = this._xaxis.series_p2u;
5318          var yp = this._yaxis.series_p2u; 
5319          var steps =null;
5320          var _steps = null;
5321          var a = null;
5322          var a1 = null;
5323          var a2 = null;
5324          var slope = null;
5325          var slope2 = null;
5326          var temp = null;
5327          var t, s, h1, h2, h3, h4;
5328          var TiX, TiY, Ti1X, Ti1Y;
5329          var pX, pY, p;
5330          var sd = [];
5331          var spd = [];
5332          var dist = gd.length/dim;
5333          var min, max, stretch, scale, shift;
5334          var _smoothedData = [];
5335          var _smoothedPlotData = [];
5336          if (!isNaN(parseFloat(smooth))) {
5337              steps = parseFloat(smooth);
5338          }
5339          else {
5340              steps = getSteps(dist, 0.5);
5341          }
5342          if (!isNaN(parseFloat(tension))) {
5343              tension = parseFloat(tension);
5344          }
5345  
5346          for (var i=0, l = gd.length-1; i < l; i++) {
5347  
5348              if (tension === null) {
5349                  slope = Math.abs((gd[i+1][1] - gd[i][1]) / (gd[i+1][0] - gd[i][0]));
5350  
5351                  min = 0.3;
5352                  max = 0.6;
5353                  stretch = (max - min)/2.0;
5354                  scale = 2.5;
5355                  shift = -1.4;
5356  
5357                  temp = slope/scale + shift;
5358  
5359                  a1 = stretch * tanh(temp) - stretch * tanh(shift) + min;
5360  
5361                  // if have both left and right line segments, will use  minimum tension. 
5362                  if (i > 0) {
5363                      slope2 = Math.abs((gd[i][1] - gd[i-1][1]) / (gd[i][0] - gd[i-1][0]));
5364                  }
5365                  temp = slope2/scale + shift;
5366  
5367                  a2 = stretch * tanh(temp) - stretch * tanh(shift) + min;
5368  
5369                  a = (a1 + a2)/2.0;
5370  
5371              }
5372              else {
5373                  a = tension;
5374              }
5375              for (t=0; t < steps; t++) {
5376                  s = t / steps;
5377                  h1 = (1 + 2*s)*Math.pow((1-s),2);
5378                  h2 = s*Math.pow((1-s),2);
5379                  h3 = Math.pow(s,2)*(3-2*s);
5380                  h4 = Math.pow(s,2)*(s-1);     
5381                  
5382                  if (gd[i-1]) {  
5383                      TiX = a * (gd[i+1][0] - gd[i-1][0]); 
5384                      TiY = a * (gd[i+1][1] - gd[i-1][1]);
5385                  } else {
5386                      TiX = a * (gd[i+1][0] - gd[i][0]); 
5387                      TiY = a * (gd[i+1][1] - gd[i][1]);                                  
5388                  }
5389                  if (gd[i+2]) {  
5390                      Ti1X = a * (gd[i+2][0] - gd[i][0]); 
5391                      Ti1Y = a * (gd[i+2][1] - gd[i][1]);
5392                  } else {
5393                      Ti1X = a * (gd[i+1][0] - gd[i][0]); 
5394                      Ti1Y = a * (gd[i+1][1] - gd[i][1]);                                 
5395                  }
5396                  
5397                  pX = h1*gd[i][0] + h3*gd[i+1][0] + h2*TiX + h4*Ti1X;
5398                  pY = h1*gd[i][1] + h3*gd[i+1][1] + h2*TiY + h4*Ti1Y;
5399                  p = [pX, pY];
5400  
5401                  _smoothedData.push(p);
5402                  _smoothedPlotData.push([xp(pX), yp(pY)]);
5403              }
5404          }
5405          _smoothedData.push(gd[l]);
5406          _smoothedPlotData.push([xp(gd[l][0]), yp(gd[l][1])]);
5407  
5408          return [_smoothedData, _smoothedPlotData];
5409      }
5410      
5411      // setGridData
5412      // converts the user data values to grid coordinates and stores them
5413      // in the gridData array.
5414      // Called with scope of a series.
5415      $.jqplot.LineRenderer.prototype.setGridData = function(plot) {
5416          // recalculate the grid data
5417          var xp = this._xaxis.series_u2p;
5418          var yp = this._yaxis.series_u2p;
5419          var data = this._plotData;
5420          var pdata = this._prevPlotData;
5421          this.gridData = [];
5422          this._prevGridData = [];
5423          this.renderer._smoothedData = [];
5424          this.renderer._smoothedPlotData = [];
5425          this.renderer._hiBandGridData = [];
5426          this.renderer._lowBandGridData = [];
5427          this.renderer._hiBandSmoothedData = [];
5428          this.renderer._lowBandSmoothedData = [];
5429          var bands = this.renderer.bands;
5430          var hasNull = false;
5431          for (var i=0, l=data.length; i < l; i++) {
5432              // if not a line series or if no nulls in data, push the converted point onto the array.
5433              if (data[i][0] != null && data[i][1] != null) {
5434                  this.gridData.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]);
5435              }
5436              // else if there is a null, preserve it.
5437              else if (data[i][0] == null) {
5438                  hasNull = true;
5439                  this.gridData.push([null, yp.call(this._yaxis, data[i][1])]);
5440              }
5441              else if (data[i][1] == null) {
5442                  hasNull = true;
5443                  this.gridData.push([xp.call(this._xaxis, data[i][0]), null]);
5444              }
5445              // if not a line series or if no nulls in data, push the converted point onto the array.
5446              if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] != null) {
5447                  this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), yp.call(this._yaxis, pdata[i][1])]);
5448              }
5449              // else if there is a null, preserve it.
5450              else if (pdata[i] != null && pdata[i][0] == null) {
5451                  this._prevGridData.push([null, yp.call(this._yaxis, pdata[i][1])]);
5452              }  
5453              else if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] == null) {
5454                  this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), null]);
5455              }
5456          }
5457  
5458          // don't do smoothing or bands on broken lines.
5459          if (hasNull) {
5460              this.renderer.smooth = false;
5461              if (this._type === 'line') {
5462                  bands.show = false;
5463              }
5464          }
5465  
5466          if (this._type === 'line' && bands.show) {
5467              for (var i=0, l=bands.hiData.length; i<l; i++) {
5468                  this.renderer._hiBandGridData.push([xp.call(this._xaxis, bands.hiData[i][0]), yp.call(this._yaxis, bands.hiData[i][1])]);
5469              }
5470              for (var i=0, l=bands.lowData.length; i<l; i++) {
5471                  this.renderer._lowBandGridData.push([xp.call(this._xaxis, bands.lowData[i][0]), yp.call(this._yaxis, bands.lowData[i][1])]);
5472              }
5473          }
5474  
5475          // calculate smoothed data if enough points and no nulls
5476          if (this._type === 'line' && this.renderer.smooth && this.gridData.length > 2) {
5477              var ret;
5478              if (this.renderer.constrainSmoothing) {
5479                  ret = computeConstrainedSmoothedData.call(this, this.gridData);
5480                  this.renderer._smoothedData = ret[0];
5481                  this.renderer._smoothedPlotData = ret[1];
5482  
5483                  if (bands.show) {
5484                      ret = computeConstrainedSmoothedData.call(this, this.renderer._hiBandGridData);
5485                      this.renderer._hiBandSmoothedData = ret[0];
5486                      ret = computeConstrainedSmoothedData.call(this, this.renderer._lowBandGridData);
5487                      this.renderer._lowBandSmoothedData = ret[0];
5488                  }
5489  
5490                  ret = null;
5491              }
5492              else {
5493                  ret = computeHermiteSmoothedData.call(this, this.gridData);
5494                  this.renderer._smoothedData = ret[0];
5495                  this.renderer._smoothedPlotData = ret[1];
5496  
5497                  if (bands.show) {
5498                      ret = computeHermiteSmoothedData.call(this, this.renderer._hiBandGridData);
5499                      this.renderer._hiBandSmoothedData = ret[0];
5500                      ret = computeHermiteSmoothedData.call(this, this.renderer._lowBandGridData);
5501                      this.renderer._lowBandSmoothedData = ret[0];
5502                  }
5503  
5504                  ret = null;
5505              }
5506          }
5507      };
5508      
5509      // makeGridData
5510      // converts any arbitrary data values to grid coordinates and
5511      // returns them.  This method exists so that plugins can use a series'
5512      // linerenderer to generate grid data points without overwriting the
5513      // grid data associated with that series.
5514      // Called with scope of a series.
5515      $.jqplot.LineRenderer.prototype.makeGridData = function(data, plot) {
5516          // recalculate the grid data
5517          var xp = this._xaxis.series_u2p;
5518          var yp = this._yaxis.series_u2p;
5519          var gd = [];
5520          var pgd = [];
5521          this.renderer._smoothedData = [];
5522          this.renderer._smoothedPlotData = [];
5523          this.renderer._hiBandGridData = [];
5524          this.renderer._lowBandGridData = [];
5525          this.renderer._hiBandSmoothedData = [];
5526          this.renderer._lowBandSmoothedData = [];
5527          var bands = this.renderer.bands;
5528          var hasNull = false;
5529          for (var i=0; i<data.length; i++) {
5530              // if not a line series or if no nulls in data, push the converted point onto the array.
5531              if (data[i][0] != null && data[i][1] != null) {
5532                  gd.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]);
5533              }
5534              // else if there is a null, preserve it.
5535              else if (data[i][0] == null) {
5536                  hasNull = true;
5537                  gd.push([null, yp.call(this._yaxis, data[i][1])]);
5538              }
5539              else if (data[i][1] == null) {
5540                  hasNull = true;
5541                  gd.push([xp.call(this._xaxis, data[i][0]), null]);
5542              }
5543          }
5544  
5545          // don't do smoothing or bands on broken lines.
5546          if (hasNull) {
5547              this.renderer.smooth = false;
5548              if (this._type === 'line') {
5549                  bands.show = false;
5550              }
5551          }
5552  
5553          if (this._type === 'line' && bands.show) {
5554              for (var i=0, l=bands.hiData.length; i<l; i++) {
5555                  this.renderer._hiBandGridData.push([xp.call(this._xaxis, bands.hiData[i][0]), yp.call(this._yaxis, bands.hiData[i][1])]);
5556              }
5557              for (var i=0, l=bands.lowData.length; i<l; i++) {
5558                  this.renderer._lowBandGridData.push([xp.call(this._xaxis, bands.lowData[i][0]), yp.call(this._yaxis, bands.lowData[i][1])]);
5559              }
5560          }
5561  
5562          if (this._type === 'line' && this.renderer.smooth && gd.length > 2) {
5563              var ret;
5564              if (this.renderer.constrainSmoothing) {
5565                  ret = computeConstrainedSmoothedData.call(this, gd);
5566                  this.renderer._smoothedData = ret[0];
5567                  this.renderer._smoothedPlotData = ret[1];
5568  
5569                  if (bands.show) {
5570                      ret = computeConstrainedSmoothedData.call(this, this.renderer._hiBandGridData);
5571                      this.renderer._hiBandSmoothedData = ret[0];
5572                      ret = computeConstrainedSmoothedData.call(this, this.renderer._lowBandGridData);
5573                      this.renderer._lowBandSmoothedData = ret[0];
5574                  }
5575  
5576                  ret = null;
5577              }
5578              else {
5579                  ret = computeHermiteSmoothedData.call(this, gd);
5580                  this.renderer._smoothedData = ret[0];
5581                  this.renderer._smoothedPlotData = ret[1];
5582  
5583                  if (bands.show) {
5584                      ret = computeHermiteSmoothedData.call(this, this.renderer._hiBandGridData);
5585                      this.renderer._hiBandSmoothedData = ret[0];
5586                      ret = computeHermiteSmoothedData.call(this, this.renderer._lowBandGridData);
5587                      this.renderer._lowBandSmoothedData = ret[0];
5588                  }
5589  
5590                  ret = null;
5591              }
5592          }
5593          return gd;
5594      };
5595      
5596  
5597      // called within scope of series.
5598      $.jqplot.LineRenderer.prototype.draw = function(ctx, gd, options, plot) {
5599          var i;
5600          // get a copy of the options, so we don't modify the original object.
5601          var opts = $.extend(true, {}, options);
5602          var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
5603          var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
5604          var fill = (opts.fill != undefined) ? opts.fill : this.fill;
5605          var fillAndStroke = (opts.fillAndStroke != undefined) ? opts.fillAndStroke : this.fillAndStroke;
5606          var xmin, ymin, xmax, ymax;
5607          ctx.save();
5608          if (gd.length) {
5609              if (showLine) {
5610                  // if we fill, we'll have to add points to close the curve.
5611                  if (fill) {
5612                      if (this.fillToZero) { 
5613                          // have to break line up into shapes at axis crossings
5614                          var negativeColor = this.negativeColor;
5615                          if (! this.useNegativeColors) {
5616                              negativeColor = opts.fillStyle;
5617                          }
5618                          var isnegative = false;
5619                          var posfs = opts.fillStyle;
5620                      
5621                          // if stoking line as well as filling, get a copy of line data.
5622                          if (fillAndStroke) {
5623                              var fasgd = gd.slice(0);
5624                          }
5625                          // if not stacked, fill down to axis
5626                          if (this.index == 0 || !this._stack) {
5627                          
5628                              var tempgd = [];
5629                              var pd = (this.renderer.smooth) ? this.renderer._smoothedPlotData : this._plotData;
5630                              this._areaPoints = [];
5631                              var pyzero = this._yaxis.series_u2p(this.fillToValue);
5632                              var pxzero = this._xaxis.series_u2p(this.fillToValue);
5633  
5634                              opts.closePath = true;
5635                              
5636                              if (this.fillAxis == 'y') {
5637                                  tempgd.push([gd[0][0], pyzero]);
5638                                  this._areaPoints.push([gd[0][0], pyzero]);
5639                                  
5640                                  for (var i=0; i<gd.length-1; i++) {
5641                                      tempgd.push(gd[i]);
5642                                      this._areaPoints.push(gd[i]);
5643                                      // do we have an axis crossing?
5644                                      if (pd[i][1] * pd[i+1][1] < 0) {
5645                                          if (pd[i][1] < 0) {
5646                                              isnegative = true;
5647                                              opts.fillStyle = negativeColor;
5648                                          }
5649                                          else {
5650                                              isnegative = false;
5651                                              opts.fillStyle = posfs;
5652                                          }
5653                                          
5654                                          var xintercept = gd[i][0] + (gd[i+1][0] - gd[i][0]) * (pyzero-gd[i][1])/(gd[i+1][1] - gd[i][1]);
5655                                          tempgd.push([xintercept, pyzero]);
5656                                          this._areaPoints.push([xintercept, pyzero]);
5657                                          // now draw this shape and shadow.
5658                                          if (shadow) {
5659                                              this.renderer.shadowRenderer.draw(ctx, tempgd, opts);
5660                                          }
5661                                          this.renderer.shapeRenderer.draw(ctx, tempgd, opts);
5662                                          // now empty temp array and continue
5663                                          tempgd = [[xintercept, pyzero]];
5664                                          // this._areaPoints = [[xintercept, pyzero]];
5665                                      }   
5666                                  }
5667                                  if (pd[gd.length-1][1] < 0) {
5668                                      isnegative = true;
5669                                      opts.fillStyle = negativeColor;
5670                                  }
5671                                  else {
5672                                      isnegative = false;
5673                                      opts.fillStyle = posfs;
5674                                  }
5675                                  tempgd.push(gd[gd.length-1]);
5676                                  this._areaPoints.push(gd[gd.length-1]);
5677                                  tempgd.push([gd[gd.length-1][0], pyzero]); 
5678                                  this._areaPoints.push([gd[gd.length-1][0], pyzero]); 
5679                              }
5680                              // now draw the last area.
5681                              if (shadow) {
5682                                  this.renderer.shadowRenderer.draw(ctx, tempgd, opts);
5683                              }
5684                              this.renderer.shapeRenderer.draw(ctx, tempgd, opts);
5685                              
5686                              
5687                              // var gridymin = this._yaxis.series_u2p(0);
5688                              // // IE doesn't return new length on unshift
5689                              // gd.unshift([gd[0][0], gridymin]);
5690                              // len = gd.length;
5691                              // gd.push([gd[len - 1][0], gridymin]);                   
5692                          }
5693                          // if stacked, fill to line below 
5694                          else {
5695                              var prev = this._prevGridData;
5696                              for (var i=prev.length; i>0; i--) {
5697                                  gd.push(prev[i-1]);
5698                                  // this._areaPoints.push(prev[i-1]);
5699                              }
5700                              if (shadow) {
5701                                  this.renderer.shadowRenderer.draw(ctx, gd, opts);
5702                              }
5703                              this._areaPoints = gd;
5704                              this.renderer.shapeRenderer.draw(ctx, gd, opts);
5705                          }
5706                      }
5707                      /////////////////////////
5708                      // Not filled to zero
5709                      ////////////////////////
5710                      else {                    
5711                          // if stoking line as well as filling, get a copy of line data.
5712                          if (fillAndStroke) {
5713                              var fasgd = gd.slice(0);
5714                          }
5715                          // if not stacked, fill down to axis
5716                          if (this.index == 0 || !this._stack) {
5717                              // var gridymin = this._yaxis.series_u2p(this._yaxis.min) - this.gridBorderWidth / 2;
5718                              var gridymin = ctx.canvas.height;
5719                              // IE doesn't return new length on unshift
5720                              gd.unshift([gd[0][0], gridymin]);
5721                              var len = gd.length;
5722                              gd.push([gd[len - 1][0], gridymin]);                   
5723                          }
5724                          // if stacked, fill to line below 
5725                          else {
5726                              var prev = this._prevGridData;
5727                              for (var i=prev.length; i>0; i--) {
5728                                  gd.push(prev[i-1]);
5729                              }
5730                          }
5731                          this._areaPoints = gd;
5732                          
5733                          if (shadow) {
5734                              this.renderer.shadowRenderer.draw(ctx, gd, opts);
5735                          }
5736              
5737                          this.renderer.shapeRenderer.draw(ctx, gd, opts);                        
5738                      }
5739                      if (fillAndStroke) {
5740                          var fasopts = $.extend(true, {}, opts, {fill:false, closePath:false});
5741                          this.renderer.shapeRenderer.draw(ctx, fasgd, fasopts);
5742                          //////////
5743                          // TODO: figure out some way to do shadows nicely
5744                          // if (shadow) {
5745                          //     this.renderer.shadowRenderer.draw(ctx, fasgd, fasopts);
5746                          // }
5747                          // now draw the markers
5748                          if (this.markerRenderer.show) {
5749                              if (this.renderer.smooth) {
5750                                  fasgd = this.gridData;
5751                              }
5752                              for (i=0; i<fasgd.length; i++) {
5753                                  this.markerRenderer.draw(fasgd[i][0], fasgd[i][1], ctx, opts.markerOptions);
5754                              }
5755                          }
5756                      }
5757                  }
5758                  else {
5759  
5760                      if (this.renderer.bands.show) {
5761                          var bdat;
5762                          var bopts = $.extend(true, {}, opts);
5763                          if (this.renderer.bands.showLines) {
5764                              bdat = (this.renderer.smooth) ? this.renderer._hiBandSmoothedData : this.renderer._hiBandGridData;
5765                              this.renderer.shapeRenderer.draw(ctx, bdat, opts);
5766                              bdat = (this.renderer.smooth) ? this.renderer._lowBandSmoothedData : this.renderer._lowBandGridData;
5767                              this.renderer.shapeRenderer.draw(ctx, bdat, bopts);
5768                          }
5769  
5770                          if (this.renderer.bands.fill) {
5771                              if (this.renderer.smooth) {
5772                                  bdat = this.renderer._hiBandSmoothedData.concat(this.renderer._lowBandSmoothedData.reverse());
5773                              }
5774                              else {
5775                                  bdat = this.renderer._hiBandGridData.concat(this.renderer._lowBandGridData.reverse());
5776                              }
5777                              this._areaPoints = bdat;
5778                              bopts.closePath = true;
5779                              bopts.fill = true;
5780                              bopts.fillStyle = this.renderer.bands.fillColor;
5781                              this.renderer.shapeRenderer.draw(ctx, bdat, bopts);
5782                          }
5783                      }
5784  
5785                      if (shadow) {
5786                          this.renderer.shadowRenderer.draw(ctx, gd, opts);
5787                      }
5788      
5789                      this.renderer.shapeRenderer.draw(ctx, gd, opts);
5790                  }
5791              }
5792              // calculate the bounding box
5793              var xmin = xmax = ymin = ymax = null;
5794              for (i=0; i<this._areaPoints.length; i++) {
5795                  var p = this._areaPoints[i];
5796                  if (xmin > p[0] || xmin == null) {
5797                      xmin = p[0];
5798                  }
5799                  if (ymax < p[1] || ymax == null) {
5800                      ymax = p[1];
5801                  }
5802                  if (xmax < p[0] || xmax == null) {
5803                      xmax = p[0];
5804                  }
5805                  if (ymin > p[1] || ymin == null) {
5806                      ymin = p[1];
5807                  }
5808              }
5809  
5810              if (this.type === 'line' && this.renderer.bands.show) {
5811                  ymax = this._yaxis.series_u2p(this.renderer.bands._min);
5812                  ymin = this._yaxis.series_u2p(this.renderer.bands._max);
5813              }
5814  
5815              this._boundingBox = [[xmin, ymax], [xmax, ymin]];
5816          
5817              // now draw the markers
5818              if (this.markerRenderer.show && !fill) {
5819                  if (this.renderer.smooth) {
5820                      gd = this.gridData;
5821                  }
5822                  for (i=0; i<gd.length; i++) {
5823                      if (gd[i][0] != null && gd[i][1] != null) {
5824                          this.markerRenderer.draw(gd[i][0], gd[i][1], ctx, opts.markerOptions);
5825                      }
5826                  }
5827              }
5828          }
5829          
5830          ctx.restore();
5831      };  
5832      
5833      $.jqplot.LineRenderer.prototype.drawShadow = function(ctx, gd, options) {
5834          // This is a no-op, shadows drawn with lines.
5835      };
5836      
5837      // called with scope of plot.
5838      // make sure to not leave anything highlighted.
5839      function postInit(target, data, options) {
5840          for (var i=0; i<this.series.length; i++) {
5841              if (this.series[i].renderer.constructor == $.jqplot.LineRenderer) {
5842                  // don't allow mouseover and mousedown at same time.
5843                  if (this.series[i].highlightMouseOver) {
5844                      this.series[i].highlightMouseDown = false;
5845                  }
5846              }
5847          }
5848      }  
5849      
5850      // called within context of plot
5851      // create a canvas which we can draw on.
5852      // insert it before the eventCanvas, so eventCanvas will still capture events.
5853      function postPlotDraw() {
5854          // Memory Leaks patch    
5855          if (this.plugins.lineRenderer && this.plugins.lineRenderer.highlightCanvas) {
5856            this.plugins.lineRenderer.highlightCanvas.resetCanvas();
5857            this.plugins.lineRenderer.highlightCanvas = null;
5858          }
5859          
5860          this.plugins.lineRenderer.highlightedSeriesIndex = null;
5861          this.plugins.lineRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
5862          
5863          this.eventCanvas._elem.before(this.plugins.lineRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-lineRenderer-highlight-canvas', this._plotDimensions, this));
5864          this.plugins.lineRenderer.highlightCanvas.setContext();
5865          this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
5866      } 
5867      
5868      function highlight (plot, sidx, pidx, points) {
5869          var s = plot.series[sidx];
5870          var canvas = plot.plugins.lineRenderer.highlightCanvas;
5871          canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height);
5872          s._highlightedPoint = pidx;
5873          plot.plugins.lineRenderer.highlightedSeriesIndex = sidx;
5874          var opts = {fillStyle: s.highlightColor};
5875          if (s.type === 'line' && s.renderer.bands.show) {
5876              opts.fill = true;
5877              opts.closePath = true;
5878          }
5879          s.renderer.shapeRenderer.draw(canvas._ctx, points, opts);
5880          canvas = null;
5881      }
5882      
5883      function unhighlight (plot) {
5884          var canvas = plot.plugins.lineRenderer.highlightCanvas;
5885          canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
5886          for (var i=0; i<plot.series.length; i++) {
5887              plot.series[i]._highlightedPoint = null;
5888          }
5889          plot.plugins.lineRenderer.highlightedSeriesIndex = null;
5890          plot.target.trigger('jqplotDataUnhighlight');
5891          canvas = null;
5892      }
5893      
5894      
5895      function handleMove(ev, gridpos, datapos, neighbor, plot) {
5896          if (neighbor) {
5897              var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
5898              var evt1 = jQuery.Event('jqplotDataMouseOver');
5899              evt1.pageX = ev.pageX;
5900              evt1.pageY = ev.pageY;
5901              plot.target.trigger(evt1, ins);
5902              if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.lineRenderer.highlightedSeriesIndex)) {
5903                  var evt = jQuery.Event('jqplotDataHighlight');
5904          evt.which = ev.which;
5905                  evt.pageX = ev.pageX;
5906                  evt.pageY = ev.pageY;
5907                  plot.target.trigger(evt, ins);
5908                  highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points);
5909              }
5910          }
5911          else if (neighbor == null) {
5912              unhighlight (plot);
5913          }
5914      }
5915      
5916      function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
5917          if (neighbor) {
5918              var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
5919              if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.lineRenderer.highlightedSeriesIndex)) {
5920                  var evt = jQuery.Event('jqplotDataHighlight');
5921          evt.which = ev.which;
5922                  evt.pageX = ev.pageX;
5923                  evt.pageY = ev.pageY;
5924                  plot.target.trigger(evt, ins);
5925                  highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points);
5926              }
5927          }
5928          else if (neighbor == null) {
5929              unhighlight (plot);
5930          }
5931      }
5932      
5933      function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
5934          var idx = plot.plugins.lineRenderer.highlightedSeriesIndex;
5935          if (idx != null && plot.series[idx].highlightMouseDown) {
5936              unhighlight(plot);
5937          }
5938      }
5939      
5940      function handleClick(ev, gridpos, datapos, neighbor, plot) {
5941          if (neighbor) {
5942              var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
5943              var evt = jQuery.Event('jqplotDataClick');
5944          evt.which = ev.which;
5945              evt.pageX = ev.pageX;
5946              evt.pageY = ev.pageY;
5947              plot.target.trigger(evt, ins);
5948          }
5949      }
5950      
5951      function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
5952          if (neighbor) {
5953              var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
5954              var idx = plot.plugins.lineRenderer.highlightedSeriesIndex;
5955              if (idx != null && plot.series[idx].highlightMouseDown) {
5956                  unhighlight(plot);
5957              }
5958              var evt = jQuery.Event('jqplotDataRightClick');
5959          evt.which = ev.which;
5960              evt.pageX = ev.pageX;
5961              evt.pageY = ev.pageY;
5962              plot.target.trigger(evt, ins);
5963          }
5964      }
5965      
5966      
5967      // class: $.jqplot.LinearAxisRenderer
5968      // The default jqPlot axis renderer, creating a numeric axis.
5969      $.jqplot.LinearAxisRenderer = function() {
5970      };
5971      
5972      // called with scope of axis object.
5973      $.jqplot.LinearAxisRenderer.prototype.init = function(options){
5974          // prop: breakPoints
5975          // EXPERIMENTAL!! Use at your own risk!
5976          // Works only with linear axes and the default tick renderer.
5977          // Array of [start, stop] points to create a broken axis.
5978          // Broken axes have a "jump" in them, which is an immediate 
5979          // transition from a smaller value to a larger value.
5980          // Currently, axis ticks MUST be manually assigned if using breakPoints
5981          // by using the axis ticks array option.
5982          this.breakPoints = null;
5983          // prop: breakTickLabel
5984          // Label to use at the axis break if breakPoints are specified.
5985          this.breakTickLabel = "&asymp;";
5986          // prop: drawBaseline
5987          // True to draw the axis baseline.
5988          this.drawBaseline = true;
5989          // prop: baselineWidth
5990          // width of the baseline in pixels.
5991          this.baselineWidth = null;
5992          // prop: baselineColor
5993          // CSS color spec for the baseline.
5994          this.baselineColor = null;
5995          // prop: forceTickAt0
5996          // This will ensure that there is always a tick mark at 0.
5997          // If data range is strictly positive or negative,
5998          // this will force 0 to be inside the axis bounds unless
5999          // the appropriate axis pad (pad, padMin or padMax) is set
6000          // to 0, then this will force an axis min or max value at 0.
6001          // This has know effect when any of the following options
6002          // are set:  autoscale, min, max, numberTicks or tickInterval.
6003          this.forceTickAt0 = false;
6004          // prop: forceTickAt100
6005          // This will ensure that there is always a tick mark at 100.
6006          // If data range is strictly above or below 100,
6007          // this will force 100 to be inside the axis bounds unless
6008          // the appropriate axis pad (pad, padMin or padMax) is set
6009          // to 0, then this will force an axis min or max value at 100.
6010          // This has know effect when any of the following options
6011          // are set:  autoscale, min, max, numberTicks or tickInterval.
6012          this.forceTickAt100 = false;
6013          // prop: tickInset
6014          // Controls the amount to inset the first and last ticks from 
6015          // the edges of the grid, in multiples of the tick interval.
6016          // 0 is no inset, 0.5 is one half a tick interval, 1 is a full
6017          // tick interval, etc.
6018          this.tickInset = 0;
6019          // prop: minorTicks
6020          // Number of ticks to add between "major" ticks.
6021          // Major ticks are ticks supplied by user or auto computed.
6022          // Minor ticks cannot be created by user.
6023          this.minorTicks = 0;
6024          // prop: alignTicks
6025          // true to align tick marks across opposed axes
6026          // such as from the y2axis to yaxis.
6027          this.alignTicks = false;
6028          this._autoFormatString = '';
6029          this._overrideFormatString = false;
6030          this._scalefact = 1.0;
6031          $.extend(true, this, options);
6032          if (this.breakPoints) {
6033              if (!$.isArray(this.breakPoints)) {
6034                  this.breakPoints = null;
6035              }
6036              else if (this.breakPoints.length < 2 || this.breakPoints[1] <= this.breakPoints[0]) {
6037                  this.breakPoints = null;
6038              }
6039          }
6040          if (this.numberTicks != null && this.numberTicks < 2) {
6041              this.numberTicks = 2;
6042          }
6043          this.resetDataBounds();
6044      };
6045      
6046      // called with scope of axis
6047      $.jqplot.LinearAxisRenderer.prototype.draw = function(ctx, plot) {
6048          if (this.show) {
6049              // populate the axis label and value properties.
6050              // createTicks is a method on the renderer, but
6051              // call it within the scope of the axis.
6052              this.renderer.createTicks.call(this, plot);
6053              // fill a div with axes labels in the right direction.
6054              // Need to pregenerate each axis to get it's bounds and
6055              // position it and the labels correctly on the plot.
6056              var dim=0;
6057              var temp;
6058              // Added for theming.
6059              if (this._elem) {
6060                  // Memory Leaks patch
6061                  //this._elem.empty();
6062                  this._elem.emptyForce();
6063                  this._elem = null;
6064              }
6065              
6066              this._elem = $(document.createElement('div'));
6067              this._elem.addClass('jqplot-axis jqplot-'+this.name);
6068              this._elem.css('position', 'absolute');
6069  
6070              
6071              if (this.name == 'xaxis' || this.name == 'x2axis') {
6072                  this._elem.width(this._plotDimensions.width);
6073              }
6074              else {
6075                  this._elem.height(this._plotDimensions.height);
6076              }
6077              
6078              // create a _label object.
6079              this.labelOptions.axis = this.name;
6080              this._label = new this.labelRenderer(this.labelOptions);
6081              if (this._label.show) {
6082                  var elem = this._label.draw(ctx, plot);
6083                  elem.appendTo(this._elem);
6084                  elem = null;
6085              }
6086      
6087              var t = this._ticks;
6088              var tick;
6089              for (var i=0; i<t.length; i++) {
6090                  tick = t[i];
6091                  if (tick.show && tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
6092                      this._elem.append(tick.draw(ctx, plot));
6093                  }
6094              }
6095              tick = null;
6096              t = null;
6097          }
6098          return this._elem;
6099      };
6100      
6101      // called with scope of an axis
6102      $.jqplot.LinearAxisRenderer.prototype.reset = function() {
6103          this.min = this._options.min;
6104          this.max = this._options.max;
6105          this.tickInterval = this._options.tickInterval;
6106          this.numberTicks = this._options.numberTicks;
6107          this._autoFormatString = '';
6108          if (this._overrideFormatString && this.tickOptions && this.tickOptions.formatString) {
6109              this.tickOptions.formatString = '';
6110          }
6111  
6112          // this._ticks = this.__ticks;
6113      };
6114      
6115      // called with scope of axis
6116      $.jqplot.LinearAxisRenderer.prototype.set = function() { 
6117          var dim = 0;
6118          var temp;
6119          var w = 0;
6120          var h = 0;
6121          var lshow = (this._label == null) ? false : this._label.show;
6122          if (this.show) {
6123              var t = this._ticks;
6124              var tick;
6125              for (var i=0; i<t.length; i++) {
6126                  tick = t[i];
6127                  if (!tick._breakTick && tick.show && tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
6128                      if (this.name == 'xaxis' || this.name == 'x2axis') {
6129                          temp = tick._elem.outerHeight(true);
6130                      }
6131                      else {
6132                          temp = tick._elem.outerWidth(true);
6133                      }
6134                      if (temp > dim) {
6135                          dim = temp;
6136                      }
6137                  }
6138              }
6139              tick = null;
6140              t = null;
6141              
6142              if (lshow) {
6143                  w = this._label._elem.outerWidth(true);
6144                  h = this._label._elem.outerHeight(true); 
6145              }
6146              if (this.name == 'xaxis') {
6147                  dim = dim + h;
6148                  this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'});
6149              }
6150              else if (this.name == 'x2axis') {
6151                  dim = dim + h;
6152                  this._elem.css({'height':dim+'px', left:'0px', top:'0px'});
6153              }
6154              else if (this.name == 'yaxis') {
6155                  dim = dim + w;
6156                  this._elem.css({'width':dim+'px', left:'0px', top:'0px'});
6157                  if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
6158                      this._label._elem.css('width', w+'px');
6159                  }
6160              }
6161              else {
6162                  dim = dim + w;
6163                  this._elem.css({'width':dim+'px', right:'0px', top:'0px'});
6164                  if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
6165                      this._label._elem.css('width', w+'px');
6166                  }
6167              }
6168          }  
6169      };    
6170      
6171      // called with scope of axis
6172      $.jqplot.LinearAxisRenderer.prototype.createTicks = function(plot) {
6173          // we're are operating on an axis here
6174          var ticks = this._ticks;
6175          var userTicks = this.ticks;
6176          var name = this.name;
6177          // databounds were set on axis initialization.
6178          var db = this._dataBounds;
6179          var dim = (this.name.charAt(0) === 'x') ? this._plotDimensions.width : this._plotDimensions.height;
6180          var interval;
6181          var min, max;
6182          var pos1, pos2;
6183          var tt, i;
6184          // get a copy of user's settings for min/max.
6185          var userMin = this.min;
6186          var userMax = this.max;
6187          var userNT = this.numberTicks;
6188          var userTI = this.tickInterval;
6189  
6190          var threshold = 30;
6191          this._scalefact =  (Math.max(dim, threshold+1) - threshold)/300.0;
6192          
6193          // if we already have ticks, use them.
6194          // ticks must be in order of increasing value.
6195          
6196          if (userTicks.length) {
6197              // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed
6198              for (i=0; i<userTicks.length; i++){
6199                  var ut = userTicks[i];
6200                  var t = new this.tickRenderer(this.tickOptions);
6201                  if ($.isArray(ut)) {
6202                      t.value = ut[0];
6203                      if (this.breakPoints) {
6204                          if (ut[0] == this.breakPoints[0]) {
6205                              t.label = this.breakTickLabel;
6206                              t._breakTick = true;
6207                              t.showGridline = false;
6208                              t.showMark = false;
6209                          }
6210                          else if (ut[0] > this.breakPoints[0] && ut[0] <= this.breakPoints[1]) {
6211                              t.show = false;
6212                              t.showGridline = false;
6213                              t.label = ut[1];
6214                          }
6215                          else {
6216                              t.label = ut[1];
6217                          }
6218                      }
6219                      else {
6220                          t.label = ut[1];
6221                      }
6222                      t.setTick(ut[0], this.name);
6223                      this._ticks.push(t);
6224                  }
6225  
6226                  else if ($.isPlainObject(ut)) {
6227                      $.extend(true, t, ut);
6228                      t.axis = this.name;
6229                      this._ticks.push(t);
6230                  }
6231                  
6232                  else {
6233                      t.value = ut;
6234                      if (this.breakPoints) {
6235                          if (ut == this.breakPoints[0]) {
6236                              t.label = this.breakTickLabel;
6237                              t._breakTick = true;
6238                              t.showGridline = false;
6239                              t.showMark = false;
6240                          }
6241                          else if (ut > this.breakPoints[0] && ut <= this.breakPoints[1]) {
6242                              t.show = false;
6243                              t.showGridline = false;
6244                          }
6245                      }
6246                      t.setTick(ut, this.name);
6247                      this._ticks.push(t);
6248                  }
6249              }
6250              this.numberTicks = userTicks.length;
6251              this.min = this._ticks[0].value;
6252              this.max = this._ticks[this.numberTicks-1].value;
6253              this.tickInterval = (this.max - this.min) / (this.numberTicks - 1);
6254          }
6255          
6256          // we don't have any ticks yet, let's make some!
6257          else {
6258              if (name == 'xaxis' || name == 'x2axis') {
6259                  dim = this._plotDimensions.width;
6260              }
6261              else {
6262                  dim = this._plotDimensions.height;
6263              }
6264  
6265              var _numberTicks = this.numberTicks;
6266  
6267              // if aligning this axis, use number of ticks from previous axis.
6268              // Do I need to reset somehow if alignTicks is changed and then graph is replotted??
6269              if (this.alignTicks) {
6270                  if (this.name === 'x2axis' && plot.axes.xaxis.show) {
6271                      _numberTicks = plot.axes.xaxis.numberTicks;
6272                  }
6273                  else if (this.name.charAt(0) === 'y' && this.name !== 'yaxis' && this.name !== 'yMidAxis' && plot.axes.yaxis.show) {
6274                      _numberTicks = plot.axes.yaxis.numberTicks;
6275                  }
6276              }
6277          
6278              min = ((this.min != null) ? this.min : db.min);
6279              max = ((this.max != null) ? this.max : db.max);
6280  
6281              var range = max - min;
6282              var rmin, rmax;
6283              var temp;
6284  
6285              if (this.tickOptions == null || !this.tickOptions.formatString) {
6286                  this._overrideFormatString = true;
6287              }
6288  
6289              // Doing complete autoscaling
6290              if (this.min == null || this.max == null && this.tickInterval == null && !this.autoscale) {
6291                  // Check if user must have tick at 0 or 100 and ensure they are in range.
6292                  // The autoscaling algorithm will always place ticks at 0 and 100 if they are in range.
6293                  if (this.forceTickAt0) {
6294                      if (min > 0) {
6295                          min = 0;
6296                      }
6297                      if (max < 0) {
6298                          max = 0;
6299                      }
6300                  }
6301  
6302                  if (this.forceTickAt100) {
6303                      if (min > 100) {
6304                          min = 100;
6305                      }
6306                      if (max < 100) {
6307                          max = 100;
6308                      }
6309                  }
6310  
6311                  var keepMin = false,
6312                      keepMax = false;
6313  
6314                  if (this.min != null) {
6315                      keepMin = true;
6316                  }
6317  
6318                  else if (this.max != null) {
6319                      keepMax = true;
6320                  }
6321  
6322                  // var threshold = 30;
6323                  // var tdim = Math.max(dim, threshold+1);
6324                  // this._scalefact =  (tdim-threshold)/300.0;
6325                  var ret = $.jqplot.LinearTickGenerator(min, max, this._scalefact, _numberTicks, keepMin, keepMax); 
6326                  // calculate a padded max and min, points should be less than these
6327                  // so that they aren't too close to the edges of the plot.
6328                  // User can adjust how much padding is allowed with pad, padMin and PadMax options. 
6329                  // If min or max is set, don't pad that end of axis.
6330                  var tumin = (this.min != null) ? min : min + range*(this.padMin - 1);
6331                  var tumax = (this.max != null) ? max : max - range*(this.padMax - 1);
6332  
6333                  // if they're equal, we shouldn't have to do anything, right?
6334                  // if (min <=tumin || max >= tumax) {
6335                  if (min <tumin || max > tumax) {
6336                      tumin = (this.min != null) ? min : min - range*(this.padMin - 1);
6337                      tumax = (this.max != null) ? max : max + range*(this.padMax - 1);
6338                      ret = $.jqplot.LinearTickGenerator(tumin, tumax, this._scalefact, _numberTicks, keepMin, keepMax);
6339                  }
6340  
6341                  this.min = ret[0];
6342                  this.max = ret[1];
6343                  // if numberTicks specified, it should return the same.
6344                  this.numberTicks = ret[2];
6345                  this._autoFormatString = ret[3];
6346                  this.tickInterval = ret[4];
6347              }
6348  
6349              // User has specified some axis scale related option, can use auto algorithm
6350              else {
6351                  
6352                  // if min and max are same, space them out a bit
6353                  if (min == max) {
6354                      var adj = 0.05;
6355                      if (min > 0) {
6356                          adj = Math.max(Math.log(min)/Math.LN10, 0.05);
6357                      }
6358                      min -= adj;
6359                      max += adj;
6360                  }
6361                  
6362                  // autoscale.  Can't autoscale if min or max is supplied.
6363                  // Will use numberTicks and tickInterval if supplied.  Ticks
6364                  // across multiple axes may not line up depending on how
6365                  // bars are to be plotted.
6366                  if (this.autoscale && this.min == null && this.max == null) {
6367                      var rrange, ti, margin;
6368                      var forceMinZero = false;
6369                      var forceZeroLine = false;
6370                      var intervals = {min:null, max:null, average:null, stddev:null};
6371                      // if any series are bars, or if any are fill to zero, and if this
6372                      // is the axis to fill toward, check to see if we can start axis at zero.
6373                      for (var i=0; i<this._series.length; i++) {
6374                          var s = this._series[i];
6375                          var faname = (s.fillAxis == 'x') ? s._xaxis.name : s._yaxis.name;
6376                          // check to see if this is the fill axis
6377                          if (this.name == faname) {
6378                              var vals = s._plotValues[s.fillAxis];
6379                              var vmin = vals[0];
6380                              var vmax = vals[0];
6381                              for (var j=1; j<vals.length; j++) {
6382                                  if (vals[j] < vmin) {
6383                                      vmin = vals[j];
6384                                  }
6385                                  else if (vals[j] > vmax) {
6386                                      vmax = vals[j];
6387                                  }
6388                              }
6389                              var dp = (vmax - vmin) / vmax;
6390                              // is this sries a bar?
6391                              if (s.renderer.constructor == $.jqplot.BarRenderer) {
6392                                  // if no negative values and could also check range.
6393                                  if (vmin >= 0 && (s.fillToZero || dp > 0.1)) {
6394                                      forceMinZero = true;
6395                                  }
6396                                  else {
6397                                      forceMinZero = false;
6398                                      if (s.fill && s.fillToZero && vmin < 0 && vmax > 0) {
6399                                          forceZeroLine = true;
6400                                      }
6401                                      else {
6402                                          forceZeroLine = false;
6403                                      }
6404                                  }
6405                              }
6406                              
6407                              // if not a bar and filling, use appropriate method.
6408                              else if (s.fill) {
6409                                  if (vmin >= 0 && (s.fillToZero || dp > 0.1)) {
6410                                      forceMinZero = true;
6411                                  }
6412                                  else if (vmin < 0 && vmax > 0 && s.fillToZero) {
6413                                      forceMinZero = false;
6414                                      forceZeroLine = true;
6415                                  }
6416                                  else {
6417                                      forceMinZero = false;
6418                                      forceZeroLine = false;
6419                                  }
6420                              }
6421                              
6422                              // if not a bar and not filling, only change existing state
6423                              // if it doesn't make sense
6424                              else if (vmin < 0) {
6425                                  forceMinZero = false;
6426                              }
6427                          }
6428                      }
6429                      
6430                      // check if we need make axis min at 0.
6431                      if (forceMinZero) {
6432                          // compute number of ticks
6433                          this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
6434                          this.min = 0;
6435                          userMin = 0;
6436                          // what order is this range?
6437                          // what tick interval does that give us?
6438                          ti = max/(this.numberTicks-1);
6439                          temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10)));
6440                          if (ti/temp == parseInt(ti/temp, 10)) {
6441                              ti += temp;
6442                          }
6443                          this.tickInterval = Math.ceil(ti/temp) * temp;
6444                          this.max = this.tickInterval * (this.numberTicks - 1);
6445                      }
6446                      
6447                      // check if we need to make sure there is a tick at 0.
6448                      else if (forceZeroLine) {
6449                          // compute number of ticks
6450                          this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
6451                          var ntmin = Math.ceil(Math.abs(min)/range*(this.numberTicks-1));
6452                          var ntmax = this.numberTicks - 1  - ntmin;
6453                          ti = Math.max(Math.abs(min/ntmin), Math.abs(max/ntmax));
6454                          temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10)));
6455                          this.tickInterval = Math.ceil(ti/temp) * temp;
6456                          this.max = this.tickInterval * ntmax;
6457                          this.min = -this.tickInterval * ntmin;
6458                      }
6459                      
6460                      // if nothing else, do autoscaling which will try to line up ticks across axes.
6461                      else {  
6462                          if (this.numberTicks == null){
6463                              if (this.tickInterval) {
6464                                  this.numberTicks = 3 + Math.ceil(range / this.tickInterval);
6465                              }
6466                              else {
6467                                  this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
6468                              }
6469                          }
6470                  
6471                          if (this.tickInterval == null) {
6472                              // get a tick interval
6473                              ti = range/(this.numberTicks - 1);
6474  
6475                              if (ti < 1) {
6476                                  temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10)));
6477                              }
6478                              else {
6479                                  temp = 1;
6480                              }
6481                              this.tickInterval = Math.ceil(ti*temp*this.pad)/temp;
6482                          }
6483                          else {
6484                              temp = 1 / this.tickInterval;
6485                          }
6486                          
6487                          // try to compute a nicer, more even tick interval
6488                          // temp = Math.pow(10, Math.floor(Math.log(ti)/Math.LN10));
6489                          // this.tickInterval = Math.ceil(ti/temp) * temp;
6490                          rrange = this.tickInterval * (this.numberTicks - 1);
6491                          margin = (rrange - range)/2;
6492             
6493                          if (this.min == null) {
6494                              this.min = Math.floor(temp*(min-margin))/temp;
6495                          }
6496                          if (this.max == null) {
6497                              this.max = this.min + rrange;
6498                          }
6499                      }
6500  
6501                      // Compute a somewhat decent format string if it is needed.
6502                      // get precision of interval and determine a format string.
6503                      var sf = $.jqplot.getSignificantFigures(this.tickInterval);
6504  
6505                      var fstr;
6506  
6507                      // if we have only a whole number, use integer formatting
6508                      if (sf.digitsLeft >= sf.significantDigits) {
6509                          fstr = '%d';
6510                      }
6511  
6512                      else {
6513                          var temp = Math.max(0, 5 - sf.digitsLeft);
6514                          temp = Math.min(temp, sf.digitsRight);
6515                          fstr = '%.'+ temp + 'f';
6516                      }
6517  
6518                      this._autoFormatString = fstr;
6519                  }
6520                  
6521                  // Use the default algorithm which pads each axis to make the chart
6522                  // centered nicely on the grid.
6523                  else {
6524  
6525                      rmin = (this.min != null) ? this.min : min - range*(this.padMin - 1);
6526                      rmax = (this.max != null) ? this.max : max + range*(this.padMax - 1);
6527                      range = rmax - rmin;
6528          
6529                      if (this.numberTicks == null){
6530                          // if tickInterval is specified by user, we will ignore computed maximum.
6531                          // max will be equal or greater to fit even # of ticks.
6532                          if (this.tickInterval != null) {
6533                              this.numberTicks = Math.ceil((rmax - rmin)/this.tickInterval)+1;
6534                          }
6535                          else if (dim > 100) {
6536                              this.numberTicks = parseInt(3+(dim-100)/75, 10);
6537                          }
6538                          else {
6539                              this.numberTicks = 2;
6540                          }
6541                      }
6542                  
6543                      if (this.tickInterval == null) {
6544                          this.tickInterval = range / (this.numberTicks-1);
6545                      }
6546                      
6547                      if (this.max == null) {
6548                          rmax = rmin + this.tickInterval*(this.numberTicks - 1);
6549                      }        
6550                      if (this.min == null) {
6551                          rmin = rmax - this.tickInterval*(this.numberTicks - 1);
6552                      }
6553  
6554                      // get precision of interval and determine a format string.
6555                      var sf = $.jqplot.getSignificantFigures(this.tickInterval);
6556  
6557                      var fstr;
6558  
6559                      // if we have only a whole number, use integer formatting
6560                      if (sf.digitsLeft >= sf.significantDigits) {
6561                          fstr = '%d';
6562                      }
6563  
6564                      else {
6565                          var temp = Math.max(0, 5 - sf.digitsLeft);
6566                          temp = Math.min(temp, sf.digitsRight);
6567                          fstr = '%.'+ temp + 'f';
6568                      }
6569  
6570  
6571                      this._autoFormatString = fstr;
6572  
6573                      this.min = rmin;
6574                      this.max = rmax;
6575                  }
6576                  
6577                  if (this.renderer.constructor == $.jqplot.LinearAxisRenderer && this._autoFormatString == '') {
6578                      // fix for misleading tick display with small range and low precision.
6579                      range = this.max - this.min;
6580                      // figure out precision
6581                      var temptick = new this.tickRenderer(this.tickOptions);
6582                      // use the tick formatString or, the default.
6583                      var fs = temptick.formatString || $.jqplot.config.defaultTickFormatString; 
6584                      var fs = fs.match($.jqplot.sprintf.regex)[0];
6585                      var precision = 0;
6586                      if (fs) {
6587                          if (fs.search(/[fFeEgGpP]/) > -1) {
6588                              var m = fs.match(/\%\.(\d{0,})?[eEfFgGpP]/);
6589                              if (m) {
6590                                  precision = parseInt(m[1], 10);
6591                              }
6592                              else {
6593                                  precision = 6;
6594                              }
6595                          }
6596                          else if (fs.search(/[di]/) > -1) {
6597                              precision = 0;
6598                          }
6599                          // fact will be <= 1;
6600                          var fact = Math.pow(10, -precision);
6601                          if (this.tickInterval < fact) {
6602                              // need to correct underrange
6603                              if (userNT == null && userTI == null) {
6604                                  this.tickInterval = fact;
6605                                  if (userMax == null && userMin == null) {
6606                                      // this.min = Math.floor((this._dataBounds.min - this.tickInterval)/fact) * fact;
6607                                      this.min = Math.floor(this._dataBounds.min/fact) * fact;
6608                                      if (this.min == this._dataBounds.min) {
6609                                          this.min = this._dataBounds.min - this.tickInterval;
6610                                      }
6611                                      // this.max = Math.ceil((this._dataBounds.max + this.tickInterval)/fact) * fact;
6612                                      this.max = Math.ceil(this._dataBounds.max/fact) * fact;
6613                                      if (this.max == this._dataBounds.max) {
6614                                          this.max = this._dataBounds.max + this.tickInterval;
6615                                      }
6616                                      var n = (this.max - this.min)/this.tickInterval;
6617                                      n = n.toFixed(11);
6618                                      n = Math.ceil(n);
6619                                      this.numberTicks = n + 1;
6620                                  }
6621                                  else if (userMax == null) {
6622                                      // add one tick for top of range.
6623                                      var n = (this._dataBounds.max - this.min) / this.tickInterval;
6624                                      n = n.toFixed(11);
6625                                      this.numberTicks = Math.ceil(n) + 2;
6626                                      this.max = this.min + this.tickInterval * (this.numberTicks-1);
6627                                  }
6628                                  else if (userMin == null) {
6629                                      // add one tick for bottom of range.
6630                                      var n = (this.max - this._dataBounds.min) / this.tickInterval;
6631                                      n = n.toFixed(11);
6632                                      this.numberTicks = Math.ceil(n) + 2;
6633                                      this.min = this.max - this.tickInterval * (this.numberTicks-1);
6634                                  }
6635                                  else {
6636                                      // calculate a number of ticks so max is within axis scale
6637                                      this.numberTicks = Math.ceil((userMax - userMin)/this.tickInterval) + 1;
6638                                      // if user's min and max don't fit evenly in ticks, adjust.
6639                                      // This takes care of cases such as user min set to 0, max set to 3.5 but tick
6640                                      // format string set to %d (integer ticks)
6641                                      this.min =  Math.floor(userMin*Math.pow(10, precision))/Math.pow(10, precision);
6642                                      this.max =  Math.ceil(userMax*Math.pow(10, precision))/Math.pow(10, precision);
6643                                      // this.max = this.min + this.tickInterval*(this.numberTicks-1);
6644                                      this.numberTicks = Math.ceil((this.max - this.min)/this.tickInterval) + 1;
6645                                  }
6646                              }
6647                          }
6648                      }
6649                  }
6650                  
6651              }
6652              
6653              if (this._overrideFormatString && this._autoFormatString != '') {
6654                  this.tickOptions = this.tickOptions || {};
6655                  this.tickOptions.formatString = this._autoFormatString;
6656              }
6657  
6658              var t, to;
6659              for (var i=0; i<this.numberTicks; i++){
6660                  tt = this.min + i * this.tickInterval;
6661                  t = new this.tickRenderer(this.tickOptions);
6662                  // var t = new $.jqplot.AxisTickRenderer(this.tickOptions);
6663  
6664                  t.setTick(tt, this.name);
6665                  this._ticks.push(t);
6666  
6667                  if (i < this.numberTicks - 1) {
6668                      for (var j=0; j<this.minorTicks; j++) {
6669                          tt += this.tickInterval/(this.minorTicks+1);
6670                          to = $.extend(true, {}, this.tickOptions, {name:this.name, value:tt, label:'', isMinorTick:true});
6671                          t = new this.tickRenderer(to);
6672                          this._ticks.push(t);
6673                      }
6674                  }
6675                  t = null;
6676              }
6677          }
6678  
6679          if (this.tickInset) {
6680              this.min = this.min - this.tickInset * this.tickInterval;
6681              this.max = this.max + this.tickInset * this.tickInterval;
6682          }
6683  
6684          ticks = null;
6685      };
6686      
6687      // Used to reset just the values of the ticks and then repack, which will
6688      // recalculate the positioning functions.  It is assuemd that the 
6689      // number of ticks is the same and the values of the new array are at the
6690      // proper interval.
6691      // This method needs to be called with the scope of an axis object, like:
6692      //
6693      // > plot.axes.yaxis.renderer.resetTickValues.call(plot.axes.yaxis, yarr);
6694      //
6695      $.jqplot.LinearAxisRenderer.prototype.resetTickValues = function(opts) {
6696          if ($.isArray(opts) && opts.length == this._ticks.length) {
6697              var t;
6698              for (var i=0; i<opts.length; i++) {
6699                  t = this._ticks[i];
6700                  t.value = opts[i];
6701                  t.label = t.formatter(t.formatString, opts[i]);
6702                  t.label = t.prefix + t.label;
6703                  t._elem.html(t.label);
6704              }
6705              t = null;
6706              this.min = $.jqplot.arrayMin(opts);
6707              this.max = $.jqplot.arrayMax(opts);
6708              this.pack();
6709          }
6710          // Not implemented yet.
6711          // else if ($.isPlainObject(opts)) {
6712          // 
6713          // }
6714      };
6715      
6716      // called with scope of axis
6717      $.jqplot.LinearAxisRenderer.prototype.pack = function(pos, offsets) {
6718          // Add defaults for repacking from resetTickValues function.
6719          pos = pos || {};
6720          offsets = offsets || this._offsets;
6721          
6722          var ticks = this._ticks;
6723          var max = this.max;
6724          var min = this.min;
6725          var offmax = offsets.max;
6726          var offmin = offsets.min;
6727          var lshow = (this._label == null) ? false : this._label.show;
6728          
6729          for (var p in pos) {
6730              this._elem.css(p, pos[p]);
6731          }
6732          
6733          this._offsets = offsets;
6734          // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left.
6735          var pixellength = offmax - offmin;
6736          var unitlength = max - min;
6737          
6738          // point to unit and unit to point conversions references to Plot DOM element top left corner.
6739          if (this.breakPoints) {
6740              unitlength = unitlength - this.breakPoints[1] + this.breakPoints[0];
6741              
6742              this.p2u = function(p){
6743                  return (p - offmin) * unitlength / pixellength + min;
6744              };
6745          
6746              this.u2p = function(u){
6747                  if (u > this.breakPoints[0] && u < this.breakPoints[1]){
6748                      u = this.breakPoints[0];
6749                  }
6750                  if (u <= this.breakPoints[0]) {
6751                      return (u - min) * pixellength / unitlength + offmin;
6752                  }
6753                  else {
6754                      return (u - this.breakPoints[1] + this.breakPoints[0] - min) * pixellength / unitlength + offmin;
6755                  }
6756              };
6757                  
6758              if (this.name.charAt(0) == 'x'){
6759                  this.series_u2p = function(u){
6760                      if (u > this.breakPoints[0] && u < this.breakPoints[1]){
6761                          u = this.breakPoints[0];
6762                      }
6763                      if (u <= this.breakPoints[0]) {
6764                          return (u - min) * pixellength / unitlength;
6765                      }
6766                      else {
6767                          return (u - this.breakPoints[1] + this.breakPoints[0] - min) * pixellength / unitlength;
6768                      }
6769                  };
6770                  this.series_p2u = function(p){
6771                      return p * unitlength / pixellength + min;
6772                  };
6773              }
6774          
6775              else {
6776                  this.series_u2p = function(u){
6777                      if (u > this.breakPoints[0] && u < this.breakPoints[1]){
6778                          u = this.breakPoints[0];
6779                      }
6780                      if (u >= this.breakPoints[1]) {
6781                          return (u - max) * pixellength / unitlength;
6782                      }
6783                      else {
6784                          return (u + this.breakPoints[1] - this.breakPoints[0] - max) * pixellength / unitlength;
6785                      }
6786                  };
6787                  this.series_p2u = function(p){
6788                      return p * unitlength / pixellength + max;
6789                  };
6790              }
6791          }
6792          else {
6793              this.p2u = function(p){
6794                  return (p - offmin) * unitlength / pixellength + min;
6795              };
6796          
6797              this.u2p = function(u){
6798                  return (u - min) * pixellength / unitlength + offmin;
6799              };
6800                  
6801              if (this.name == 'xaxis' || this.name == 'x2axis'){
6802                  this.series_u2p = function(u){
6803                      return (u - min) * pixellength / unitlength;
6804                  };
6805                  this.series_p2u = function(p){
6806                      return p * unitlength / pixellength + min;
6807                  };
6808              }
6809          
6810              else {
6811                  this.series_u2p = function(u){
6812                      return (u - max) * pixellength / unitlength;
6813                  };
6814                  this.series_p2u = function(p){
6815                      return p * unitlength / pixellength + max;
6816                  };
6817              }
6818          }
6819          
6820          if (this.show) {
6821              if (this.name == 'xaxis' || this.name == 'x2axis') {
6822                  for (var i=0; i<ticks.length; i++) {
6823                      var t = ticks[i];
6824                      if (t.show && t.showLabel) {
6825                          var shim;
6826                          
6827                          if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
6828                              // will need to adjust auto positioning based on which axis this is.
6829                              var temp = (this.name == 'xaxis') ? 1 : -1;
6830                              switch (t.labelPosition) {
6831                                  case 'auto':
6832                                      // position at end
6833                                      if (temp * t.angle < 0) {
6834                                          shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
6835                                      }
6836                                      // position at start
6837                                      else {
6838                                          shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
6839                                      }
6840                                      break;
6841                                  case 'end':
6842                                      shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
6843                                      break;
6844                                  case 'start':
6845                                      shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
6846                                      break;
6847                                  case 'middle':
6848                                      shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
6849                                      break;
6850                                  default:
6851                                      shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
6852                                      break;
6853                              }
6854                          }
6855                          else {
6856                              shim = -t.getWidth()/2;
6857                          }
6858                          var val = this.u2p(t.value) + shim + 'px';
6859                          t._elem.css('left', val);
6860                          t.pack();
6861                      }
6862                  }
6863                  if (lshow) {
6864                      var w = this._label._elem.outerWidth(true);
6865                      this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px');
6866                      if (this.name == 'xaxis') {
6867                          this._label._elem.css('bottom', '0px');
6868                      }
6869                      else {
6870                          this._label._elem.css('top', '0px');
6871                      }
6872                      this._label.pack();
6873                  }
6874              }
6875              else {
6876                  for (var i=0; i<ticks.length; i++) {
6877                      var t = ticks[i];
6878                      if (t.show && t.showLabel) {                        
6879                          var shim;
6880                          if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
6881                              // will need to adjust auto positioning based on which axis this is.
6882                              var temp = (this.name == 'yaxis') ? 1 : -1;
6883                              switch (t.labelPosition) {
6884                                  case 'auto':
6885                                      // position at end
6886                                  case 'end':
6887                                      if (temp * t.angle < 0) {
6888                                          shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
6889                                      }
6890                                      else {
6891                                          shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
6892                                      }
6893                                      break;
6894                                  case 'start':
6895                                      if (t.angle > 0) {
6896                                          shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
6897                                      }
6898                                      else {
6899                                          shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
6900                                      }
6901                                      break;
6902                                  case 'middle':
6903                                      // if (t.angle > 0) {
6904                                      //     shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
6905                                      // }
6906                                      // else {
6907                                      //     shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
6908                                      // }
6909                                      shim = -t.getHeight()/2;
6910                                      break;
6911                                  default:
6912                                      shim = -t.getHeight()/2;
6913                                      break;
6914                              }
6915                          }
6916                          else {
6917                              shim = -t.getHeight()/2;
6918                          }
6919                          
6920                          var val = this.u2p(t.value) + shim + 'px';
6921                          t._elem.css('top', val);
6922                          t.pack();
6923                      }
6924                  }
6925                  if (lshow) {
6926                      var h = this._label._elem.outerHeight(true);
6927                      this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px');
6928                      if (this.name == 'yaxis') {
6929                          this._label._elem.css('left', '0px');
6930                      }
6931                      else {
6932                          this._label._elem.css('right', '0px');
6933                      }   
6934                      this._label.pack();
6935                  }
6936              }
6937          }
6938  
6939          ticks = null;
6940      };
6941  
6942  
6943      /**
6944      * The following code was generaously given to me a while back by Scott Prahl.
6945      * He did a good job at computing axes min, max and number of ticks for the 
6946      * case where the user has not set any scale related parameters (tickInterval,
6947      * numberTicks, min or max).  I had ignored this use case for a long time,
6948      * focusing on the more difficult case where user has set some option controlling
6949      * tick generation.  Anyway, about time I got this into jqPlot.
6950      * Thanks Scott!!
6951      */
6952      
6953      /**
6954      * Copyright (c) 2010 Scott Prahl
6955      * The next three routines are currently available for use in all personal 
6956      * or commercial projects under both the MIT and GPL version 2.0 licenses. 
6957      * This means that you can choose the license that best suits your project 
6958      * and use it accordingly. 
6959      */
6960  
6961      // A good format string depends on the interval. If the interval is greater 
6962      // than 1 then there is no need to show any decimal digits. If it is < 1.0, then
6963      // use the magnitude of the interval to determine the number of digits to show.
6964      function bestFormatString (interval)
6965      {
6966          var fstr;
6967          interval = Math.abs(interval);
6968          if (interval >= 10) {
6969              fstr = '%d';
6970          }
6971  
6972          else if (interval > 1) {
6973              if (interval === parseInt(interval, 10)) {
6974                  fstr = '%d';
6975              }
6976              else {
6977                  fstr = '%.1f';
6978              }
6979          }
6980  
6981          else {
6982              var expv = -Math.floor(Math.log(interval)/Math.LN10);
6983              fstr = '%.' + expv + 'f';
6984          }
6985          
6986          return fstr; 
6987      }
6988  
6989      var _factors = [0.1, 0.2, 0.3, 0.4, 0.5, 0.8, 1, 2, 3, 4, 5];
6990  
6991      var _getLowerFactor = function(f) {
6992          var i = _factors.indexOf(f);
6993          if (i > 0) {
6994              return _factors[i-1];
6995          }
6996          else {
6997              return _factors[_factors.length - 1] / 100;
6998          }
6999      };
7000  
7001      var _getHigherFactor = function(f) {
7002          var i = _factors.indexOf(f);
7003          if (i < _factors.length-1) {
7004              return _factors[i+1];
7005          }
7006          else {
7007              return _factors[0] * 100;
7008          }
7009      };
7010  
7011      // Given a fixed minimum and maximum and a target number ot ticks
7012      // figure out the best interval and 
7013      // return min, max, number ticks, format string and tick interval
7014      function bestConstrainedInterval(min, max, nttarget) {
7015          // run through possible number to ticks and see which interval is best
7016          var low = Math.floor(nttarget/2);
7017          var hi = Math.ceil(nttarget*1.5);
7018          var badness = Number.MAX_VALUE;
7019          var r = (max - min);
7020          var temp;
7021          var sd;
7022          var bestNT;
7023          var gsf = $.jqplot.getSignificantFigures;
7024          var fsd;
7025          var fs;
7026          var currentNT;
7027          var bestPrec;
7028  
7029          for (var i=0, l=hi-low+1; i<l; i++) {
7030              currentNT = low + i;
7031              temp = r/(currentNT-1);
7032              sd = gsf(temp);
7033  
7034              temp = Math.abs(nttarget - currentNT) + sd.digitsRight;
7035              if (temp < badness) {
7036                  badness = temp;
7037                  bestNT = currentNT;
7038                  bestPrec = sd.digitsRight;
7039              }
7040              else if (temp === badness) {
7041                  // let nicer ticks trump number ot ticks
7042                  if (sd.digitsRight < bestPrec) {
7043                      bestNT = currentNT;
7044                      bestPrec = sd.digitsRight;
7045                  }
7046              }
7047  
7048          }
7049  
7050          fsd = Math.max(bestPrec, Math.max(gsf(min).digitsRight, gsf(max).digitsRight));
7051          if (fsd === 0) {
7052              fs = '%d';
7053          }
7054          else {
7055              fs = '%.' + fsd + 'f';
7056          }
7057          temp = r / (bestNT - 1);
7058          // min, max, number ticks, format string, tick interval
7059          return [min, max, bestNT, fs, temp];
7060      }
7061  
7062      // This will return an interval of form 2 * 10^n, 5 * 10^n or 10 * 10^n
7063      // it is based soley on the range and number of ticks.  So if user specifies
7064      // number of ticks, use this.
7065      function bestInterval(range, numberTicks) {
7066          numberTicks = numberTicks || 7;
7067          var minimum = range / (numberTicks - 1);
7068          var magnitude = Math.pow(10, Math.floor(Math.log(minimum) / Math.LN10));
7069          var residual = minimum / magnitude;
7070          var interval;
7071          // "nicest" ranges are 1, 2, 5 or powers of these.
7072          // for magnitudes below 1, only allow these. 
7073          if (magnitude < 1) {
7074              if (residual > 5) {
7075                  interval = 10 * magnitude;
7076              }
7077              else if (residual > 2) {
7078                  interval = 5 * magnitude;
7079              }
7080              else if (residual > 1) {
7081                  interval = 2 * magnitude;
7082              }
7083              else {
7084                  interval = magnitude;
7085              }
7086          }
7087          // for large ranges (whole integers), allow intervals like 3, 4 or powers of these.
7088          // this helps a lot with poor choices for number of ticks. 
7089          else {
7090              if (residual > 5) {
7091                  interval = 10 * magnitude;
7092              }
7093              else if (residual > 4) {
7094                  interval = 5 * magnitude;
7095              }
7096              else if (residual > 3) {
7097                  interval = 4 * magnitude;
7098              }
7099              else if (residual > 2) {
7100                  interval = 3 * magnitude;
7101              }
7102              else if (residual > 1) {
7103                  interval = 2 * magnitude;
7104              }
7105              else {
7106                  interval = magnitude;
7107              }
7108          }
7109  
7110          return interval;
7111      }
7112  
7113      // This will return an interval of form 2 * 10^n, 5 * 10^n or 10 * 10^n
7114      // it is based soley on the range of data, number of ticks must be computed later.
7115      function bestLinearInterval(range, scalefact) {
7116          scalefact = scalefact || 1;
7117          var expv = Math.floor(Math.log(range)/Math.LN10);
7118          var magnitude = Math.pow(10, expv);
7119          // 0 < f < 10
7120          var f = range / magnitude;
7121          var fact;
7122          // for large plots, scalefact will decrease f and increase number of ticks.
7123          // for small plots, scalefact will increase f and decrease number of ticks.
7124          f = f/scalefact;
7125  
7126          // for large plots, smaller interval, more ticks.
7127          if (f<=0.38) {
7128              fact = 0.1;
7129          }
7130          else if (f<=1.6) {
7131              fact = 0.2;
7132          }
7133          else if (f<=4.0) {
7134              fact = 0.5;
7135          }
7136          else if (f<=8.0) {
7137              fact = 1.0;
7138          }
7139          // for very small plots, larger interval, less ticks in number ticks
7140          else if (f<=16.0) {
7141              fact = 2;
7142          }
7143          else {
7144              fact = 5;
7145          } 
7146  
7147          return fact*magnitude; 
7148      }
7149  
7150      function bestLinearComponents(range, scalefact) {
7151          var expv = Math.floor(Math.log(range)/Math.LN10);
7152          var magnitude = Math.pow(10, expv);
7153          // 0 < f < 10
7154          var f = range / magnitude;
7155          var interval;
7156          var fact;
7157          // for large plots, scalefact will decrease f and increase number of ticks.
7158          // for small plots, scalefact will increase f and decrease number of ticks.
7159          f = f/scalefact;
7160  
7161          // for large plots, smaller interval, more ticks.
7162          if (f<=0.38) {
7163              fact = 0.1;
7164          }
7165          else if (f<=1.6) {
7166              fact = 0.2;
7167          }
7168          else if (f<=4.0) {
7169              fact = 0.5;
7170          }
7171          else if (f<=8.0) {
7172              fact = 1.0;
7173          }
7174          // for very small plots, larger interval, less ticks in number ticks
7175          else if (f<=16.0) {
7176              fact = 2;
7177          }
7178          // else if (f<=20.0) {
7179          //     fact = 3;
7180          // }
7181          // else if (f<=24.0) {
7182          //     fact = 4;
7183          // }
7184          else {
7185              fact = 5;
7186          } 
7187  
7188          interval = fact * magnitude;
7189  
7190          return [interval, fact, magnitude];
7191      }
7192  
7193      // Given the min and max for a dataset, return suitable endpoints
7194      // for the graphing, a good number for the number of ticks, and a
7195      // format string so that extraneous digits are not displayed.
7196      // returned is an array containing [min, max, nTicks, format]
7197      $.jqplot.LinearTickGenerator = function(axis_min, axis_max, scalefact, numberTicks, keepMin, keepMax) {
7198          // Set to preserve EITHER min OR max.
7199          // If min is preserved, max must be free.
7200          keepMin = (keepMin === null) ? false : keepMin;
7201          keepMax = (keepMax === null || keepMin) ? false : keepMax;
7202          // if endpoints are equal try to include zero otherwise include one
7203          if (axis_min === axis_max) {
7204              axis_max = (axis_max) ? 0 : 1;
7205          }
7206  
7207          scalefact = scalefact || 1.0;
7208  
7209          // make sure range is positive
7210          if (axis_max < axis_min) {
7211              var a = axis_max;
7212              axis_max = axis_min;
7213              axis_min = a;
7214          }
7215  
7216          var r = [];
7217          var ss = bestLinearInterval(axis_max - axis_min, scalefact);
7218  
7219          var gsf = $.jqplot.getSignificantFigures;
7220          
7221          if (numberTicks == null) {
7222  
7223              // Figure out the axis min, max and number of ticks
7224              // the min and max will be some multiple of the tick interval,
7225              // 1*10^n, 2*10^n or 5*10^n.  This gaurantees that, if the
7226              // axis min is negative, 0 will be a tick.
7227              if (!keepMin && !keepMax) {
7228                  r[0] = Math.floor(axis_min / ss) * ss;  // min
7229                  r[1] = Math.ceil(axis_max / ss) * ss;   // max
7230                  r[2] = Math.round((r[1]-r[0])/ss+1.0);  // number of ticks
7231                  r[3] = bestFormatString(ss);            // format string
7232                  r[4] = ss;                              // tick Interval
7233              }
7234  
7235              else if (keepMin) {
7236                  r[0] = axis_min;                                        // min
7237                  r[2] = Math.ceil((axis_max - axis_min) / ss + 1.0);     // number of ticks
7238                  r[1] = axis_min + (r[2] - 1) * ss;                      // max
7239                  var digitsMin = gsf(axis_min).digitsRight;
7240                  var digitsSS = gsf(ss).digitsRight;
7241                  if (digitsMin < digitsSS) {
7242                      r[3] = bestFormatString(ss);                        // format string
7243                  }
7244                  else {
7245                      r[3] = '%.' + digitsMin + 'f';
7246                  }
7247                  r[4] = ss;                                              // tick Interval
7248              }
7249  
7250              else if (keepMax) {
7251                  r[1] = axis_max;                                        // max
7252                  r[2] = Math.ceil((axis_max - axis_min) / ss + 1.0);     // number of ticks
7253                  r[0] = axis_max - (r[2] - 1) * ss;                      // min
7254                  var digitsMax = gsf(axis_max).digitsRight;
7255                  var digitsSS = gsf(ss).digitsRight;
7256                  if (digitsMax < digitsSS) {
7257                      r[3] = bestFormatString(ss);                        // format string
7258                  }
7259                  else {
7260                      r[3] = '%.' + digitsMax + 'f';
7261                  }
7262                  r[4] = ss;                                              // tick Interval
7263              }
7264          }
7265  
7266          else {
7267              var tempr = [];
7268  
7269              // Figure out the axis min, max and number of ticks
7270              // the min and max will be some multiple of the tick interval,
7271              // 1*10^n, 2*10^n or 5*10^n.  This gaurantees that, if the
7272              // axis min is negative, 0 will be a tick.
7273              tempr[0] = Math.floor(axis_min / ss) * ss;  // min
7274              tempr[1] = Math.ceil(axis_max / ss) * ss;   // max
7275              tempr[2] = Math.round((tempr[1]-tempr[0])/ss+1.0);    // number of ticks
7276              tempr[3] = bestFormatString(ss);            // format string
7277              tempr[4] = ss;                              // tick Interval
7278  
7279              // first, see if we happen to get the right number of ticks
7280              if (tempr[2] === numberTicks) {
7281                  r = tempr;
7282              }
7283  
7284              else {
7285  
7286                  var newti = bestInterval(tempr[1] - tempr[0], numberTicks);
7287  
7288                  r[0] = tempr[0];                        // min
7289                  r[2] = numberTicks;                     // number of ticks
7290                  r[4] = newti;                           // tick interval
7291                  r[3] = bestFormatString(newti);         // format string
7292                  r[1] = r[0] + (r[2] - 1) * r[4];        // max
7293              }
7294          }
7295  
7296          return r;
7297      };
7298  
7299      $.jqplot.LinearTickGenerator.bestLinearInterval = bestLinearInterval;
7300      $.jqplot.LinearTickGenerator.bestInterval = bestInterval;
7301      $.jqplot.LinearTickGenerator.bestLinearComponents = bestLinearComponents;
7302      $.jqplot.LinearTickGenerator.bestConstrainedInterval = bestConstrainedInterval;
7303  
7304  
7305      // class: $.jqplot.MarkerRenderer
7306      // The default jqPlot marker renderer, rendering the points on the line.
7307      $.jqplot.MarkerRenderer = function(options){
7308          // Group: Properties
7309          
7310          // prop: show
7311          // wether or not to show the marker.
7312          this.show = true;
7313          // prop: style
7314          // One of diamond, circle, square, x, plus, dash, filledDiamond, filledCircle, filledSquare
7315          this.style = 'filledCircle';
7316          // prop: lineWidth
7317          // size of the line for non-filled markers.
7318          this.lineWidth = 2;
7319          // prop: size
7320          // Size of the marker (diameter or circle, length of edge of square, etc.)
7321          this.size = 9.0;
7322          // prop: color
7323          // color of marker.  Will be set to color of series by default on init.
7324          this.color = '#666666';
7325          // prop: shadow
7326          // wether or not to draw a shadow on the line
7327          this.shadow = true;
7328          // prop: shadowAngle
7329          // Shadow angle in degrees
7330          this.shadowAngle = 45;
7331          // prop: shadowOffset
7332          // Shadow offset from line in pixels
7333          this.shadowOffset = 1;
7334          // prop: shadowDepth
7335          // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
7336          this.shadowDepth = 3;
7337          // prop: shadowAlpha
7338          // Alpha channel transparency of shadow.  0 = transparent.
7339          this.shadowAlpha = '0.07';
7340          // prop: shadowRenderer
7341          // Renderer that will draws the shadows on the marker.
7342          this.shadowRenderer = new $.jqplot.ShadowRenderer();
7343          // prop: shapeRenderer
7344          // Renderer that will draw the marker.
7345          this.shapeRenderer = new $.jqplot.ShapeRenderer();
7346          
7347          $.extend(true, this, options);
7348      };
7349      
7350      $.jqplot.MarkerRenderer.prototype.init = function(options) {
7351          $.extend(true, this, options);
7352          var sdopt = {angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, lineWidth:this.lineWidth, depth:this.shadowDepth, closePath:true};
7353          if (this.style.indexOf('filled') != -1) {
7354              sdopt.fill = true;
7355          }
7356          if (this.style.indexOf('ircle') != -1) {
7357              sdopt.isarc = true;
7358              sdopt.closePath = false;
7359          }
7360          this.shadowRenderer.init(sdopt);
7361          
7362          var shopt = {fill:false, isarc:false, strokeStyle:this.color, fillStyle:this.color, lineWidth:this.lineWidth, closePath:true};
7363          if (this.style.indexOf('filled') != -1) {
7364              shopt.fill = true;
7365          }
7366          if (this.style.indexOf('ircle') != -1) {
7367              shopt.isarc = true;
7368              shopt.closePath = false;
7369          }
7370          this.shapeRenderer.init(shopt);
7371      };
7372      
7373      $.jqplot.MarkerRenderer.prototype.drawDiamond = function(x, y, ctx, fill, options) {
7374          var stretch = 1.2;
7375          var dx = this.size/2/stretch;
7376          var dy = this.size/2*stretch;
7377          var points = [[x-dx, y], [x, y+dy], [x+dx, y], [x, y-dy]];
7378          if (this.shadow) {
7379              this.shadowRenderer.draw(ctx, points);
7380          }
7381          this.shapeRenderer.draw(ctx, points, options);
7382      };
7383      
7384      $.jqplot.MarkerRenderer.prototype.drawPlus = function(x, y, ctx, fill, options) {
7385          var stretch = 1.0;
7386          var dx = this.size/2*stretch;
7387          var dy = this.size/2*stretch;
7388          var points1 = [[x, y-dy], [x, y+dy]];
7389          var points2 = [[x+dx, y], [x-dx, y]];
7390          var opts = $.extend(true, {}, this.options, {closePath:false});
7391          if (this.shadow) {
7392              this.shadowRenderer.draw(ctx, points1, {closePath:false});
7393              this.shadowRenderer.draw(ctx, points2, {closePath:false});
7394          }
7395          this.shapeRenderer.draw(ctx, points1, opts);
7396          this.shapeRenderer.draw(ctx, points2, opts);
7397      };
7398      
7399      $.jqplot.MarkerRenderer.prototype.drawX = function(x, y, ctx, fill, options) {
7400          var stretch = 1.0;
7401          var dx = this.size/2*stretch;
7402          var dy = this.size/2*stretch;
7403          var opts = $.extend(true, {}, this.options, {closePath:false});
7404          var points1 = [[x-dx, y-dy], [x+dx, y+dy]];
7405          var points2 = [[x-dx, y+dy], [x+dx, y-dy]];
7406          if (this.shadow) {
7407              this.shadowRenderer.draw(ctx, points1, {closePath:false});
7408              this.shadowRenderer.draw(ctx, points2, {closePath:false});
7409          }
7410          this.shapeRenderer.draw(ctx, points1, opts);
7411          this.shapeRenderer.draw(ctx, points2, opts);
7412      };
7413      
7414      $.jqplot.MarkerRenderer.prototype.drawDash = function(x, y, ctx, fill, options) {
7415          var stretch = 1.0;
7416          var dx = this.size/2*stretch;
7417          var dy = this.size/2*stretch;
7418          var points = [[x-dx, y], [x+dx, y]];
7419          if (this.shadow) {
7420              this.shadowRenderer.draw(ctx, points);
7421          }
7422          this.shapeRenderer.draw(ctx, points, options);
7423      };
7424      
7425      $.jqplot.MarkerRenderer.prototype.drawLine = function(p1, p2, ctx, fill, options) {
7426          var points = [p1, p2];
7427          if (this.shadow) {
7428              this.shadowRenderer.draw(ctx, points);
7429          }
7430          this.shapeRenderer.draw(ctx, points, options);
7431      };
7432      
7433      $.jqplot.MarkerRenderer.prototype.drawSquare = function(x, y, ctx, fill, options) {
7434          var stretch = 1.0;
7435          var dx = this.size/2/stretch;
7436          var dy = this.size/2*stretch;
7437          var points = [[x-dx, y-dy], [x-dx, y+dy], [x+dx, y+dy], [x+dx, y-dy]];
7438          if (this.shadow) {
7439              this.shadowRenderer.draw(ctx, points);
7440          }
7441          this.shapeRenderer.draw(ctx, points, options);
7442      };
7443      
7444      $.jqplot.MarkerRenderer.prototype.drawCircle = function(x, y, ctx, fill, options) {
7445          var radius = this.size/2;
7446          var end = 2*Math.PI;
7447          var points = [x, y, radius, 0, end, true];
7448          if (this.shadow) {
7449              this.shadowRenderer.draw(ctx, points);
7450          }
7451          this.shapeRenderer.draw(ctx, points, options);
7452      };
7453      
7454      $.jqplot.MarkerRenderer.prototype.draw = function(x, y, ctx, options) {
7455          options = options || {};
7456          // hack here b/c shape renderer uses canvas based color style options
7457          // and marker uses css style names.
7458          if (options.show == null || options.show != false) {
7459              if (options.color && !options.fillStyle) {
7460                  options.fillStyle = options.color;
7461              }
7462              if (options.color && !options.strokeStyle) {
7463                  options.strokeStyle = options.color;
7464              }
7465              switch (this.style) {
7466                  case 'diamond':
7467                      this.drawDiamond(x,y,ctx, false, options);
7468                      break;
7469                  case 'filledDiamond':
7470                      this.drawDiamond(x,y,ctx, true, options);
7471                      break;
7472                  case 'circle':
7473                      this.drawCircle(x,y,ctx, false, options);
7474                      break;
7475                  case 'filledCircle':
7476                      this.drawCircle(x,y,ctx, true, options);
7477                      break;
7478                  case 'square':
7479                      this.drawSquare(x,y,ctx, false, options);
7480                      break;
7481                  case 'filledSquare':
7482                      this.drawSquare(x,y,ctx, true, options);
7483                      break;
7484                  case 'x':
7485                      this.drawX(x,y,ctx, true, options);
7486                      break;
7487                  case 'plus':
7488                      this.drawPlus(x,y,ctx, true, options);
7489                      break;
7490                  case 'dash':
7491                      this.drawDash(x,y,ctx, true, options);
7492                      break;
7493                  case 'line':
7494                      this.drawLine(x, y, ctx, false, options);
7495                      break;
7496                  default:
7497                      this.drawDiamond(x,y,ctx, false, options);
7498                      break;
7499              }
7500          }
7501      };
7502      
7503      // class: $.jqplot.shadowRenderer
7504      // The default jqPlot shadow renderer, rendering shadows behind shapes.
7505      $.jqplot.ShadowRenderer = function(options){ 
7506          // Group: Properties
7507          
7508          // prop: angle
7509          // Angle of the shadow in degrees.  Measured counter-clockwise from the x axis.
7510          this.angle = 45;
7511          // prop: offset
7512          // Pixel offset at the given shadow angle of each shadow stroke from the last stroke.
7513          this.offset = 1;
7514          // prop: alpha
7515          // alpha transparency of shadow stroke.
7516          this.alpha = 0.07;
7517          // prop: lineWidth
7518          // width of the shadow line stroke.
7519          this.lineWidth = 1.5;
7520          // prop: lineJoin
7521          // How line segments of the shadow are joined.
7522          this.lineJoin = 'miter';
7523          // prop: lineCap
7524          // how ends of the shadow line are rendered.
7525          this.lineCap = 'round';
7526          // prop; closePath
7527          // whether line path segment is closed upon itself.
7528          this.closePath = false;
7529          // prop: fill
7530          // whether to fill the shape.
7531          this.fill = false;
7532          // prop: depth
7533          // how many times the shadow is stroked.  Each stroke will be offset by offset at angle degrees.
7534          this.depth = 3;
7535          this.strokeStyle = 'rgba(0,0,0,0.1)';
7536          // prop: isarc
7537          // wether the shadow is an arc or not.
7538          this.isarc = false;
7539          
7540          $.extend(true, this, options);
7541      };
7542      
7543      $.jqplot.ShadowRenderer.prototype.init = function(options) {
7544          $.extend(true, this, options);
7545      };
7546      
7547      // function: draw
7548      // draws an transparent black (i.e. gray) shadow.
7549      //
7550      // ctx - canvas drawing context
7551      // points - array of points or [x, y, radius, start angle (rad), end angle (rad)]
7552      $.jqplot.ShadowRenderer.prototype.draw = function(ctx, points, options) {
7553          ctx.save();
7554          var opts = (options != null) ? options : {};
7555          var fill = (opts.fill != null) ? opts.fill : this.fill;
7556          var fillRect = (opts.fillRect != null) ? opts.fillRect : this.fillRect;
7557          var closePath = (opts.closePath != null) ? opts.closePath : this.closePath;
7558          var offset = (opts.offset != null) ? opts.offset : this.offset;
7559          var alpha = (opts.alpha != null) ? opts.alpha : this.alpha;
7560          var depth = (opts.depth != null) ? opts.depth : this.depth;
7561          var isarc = (opts.isarc != null) ? opts.isarc : this.isarc;
7562          var linePattern = (opts.linePattern != null) ? opts.linePattern : this.linePattern;
7563          ctx.lineWidth = (opts.lineWidth != null) ? opts.lineWidth : this.lineWidth;
7564          ctx.lineJoin = (opts.lineJoin != null) ? opts.lineJoin : this.lineJoin;
7565          ctx.lineCap = (opts.lineCap != null) ? opts.lineCap : this.lineCap;
7566          ctx.strokeStyle = opts.strokeStyle || this.strokeStyle || 'rgba(0,0,0,'+alpha+')';
7567          ctx.fillStyle = opts.fillStyle || this.fillStyle || 'rgba(0,0,0,'+alpha+')';
7568          for (var j=0; j<depth; j++) {
7569              var ctxPattern = $.jqplot.LinePattern(ctx, linePattern);
7570              ctx.translate(Math.cos(this.angle*Math.PI/180)*offset, Math.sin(this.angle*Math.PI/180)*offset);
7571              ctxPattern.beginPath();
7572              if (isarc) {
7573                  ctx.arc(points[0], points[1], points[2], points[3], points[4], true);                
7574              }
7575              else if (fillRect) {
7576                  if (fillRect) {
7577                      ctx.fillRect(points[0], points[1], points[2], points[3]);
7578                  }
7579              }
7580              else if (points && points.length){
7581                  var move = true;
7582                  for (var i=0; i<points.length; i++) {
7583                      // skip to the first non-null point and move to it.
7584                      if (points[i][0] != null && points[i][1] != null) {
7585                          if (move) {
7586                              ctxPattern.moveTo(points[i][0], points[i][1]);
7587                              move = false;
7588                          }
7589                          else {
7590                              ctxPattern.lineTo(points[i][0], points[i][1]);
7591                          }
7592                      }
7593                      else {
7594                          move = true;
7595                      }
7596                  }
7597                  
7598              }
7599              if (closePath) {
7600                  ctxPattern.closePath();
7601              }
7602              if (fill) {
7603                  ctx.fill();
7604              }
7605              else {
7606                  ctx.stroke();
7607              }
7608          }
7609          ctx.restore();
7610      };
7611      
7612      // class: $.jqplot.shapeRenderer
7613      // The default jqPlot shape renderer.  Given a set of points will
7614      // plot them and either stroke a line (fill = false) or fill them (fill = true).
7615      // If a filled shape is desired, closePath = true must also be set to close
7616      // the shape.
7617      $.jqplot.ShapeRenderer = function(options){
7618          
7619          this.lineWidth = 1.5;
7620          // prop: linePattern
7621          // line pattern 'dashed', 'dotted', 'solid', some combination
7622          // of '-' and '.' characters such as '.-.' or a numerical array like 
7623          // [draw, skip, draw, skip, ...] such as [1, 10] to draw a dotted line, 
7624          // [1, 10, 20, 10] to draw a dot-dash line, and so on.
7625          this.linePattern = 'solid';
7626          // prop: lineJoin
7627          // How line segments of the shadow are joined.
7628          this.lineJoin = 'miter';
7629          // prop: lineCap
7630          // how ends of the shadow line are rendered.
7631          this.lineCap = 'round';
7632          // prop; closePath
7633          // whether line path segment is closed upon itself.
7634          this.closePath = false;
7635          // prop: fill
7636          // whether to fill the shape.
7637          this.fill = false;
7638          // prop: isarc
7639          // wether the shadow is an arc or not.
7640          this.isarc = false;
7641          // prop: fillRect
7642          // true to draw shape as a filled rectangle.
7643          this.fillRect = false;
7644          // prop: strokeRect
7645          // true to draw shape as a stroked rectangle.
7646          this.strokeRect = false;
7647          // prop: clearRect
7648          // true to cear a rectangle.
7649          this.clearRect = false;
7650          // prop: strokeStyle
7651          // css color spec for the stoke style
7652          this.strokeStyle = '#999999';
7653          // prop: fillStyle
7654          // css color spec for the fill style.
7655          this.fillStyle = '#999999'; 
7656          
7657          $.extend(true, this, options);
7658      };
7659      
7660      $.jqplot.ShapeRenderer.prototype.init = function(options) {
7661          $.extend(true, this, options);
7662      };
7663      
7664      // function: draw
7665      // draws the shape.
7666      //
7667      // ctx - canvas drawing context
7668      // points - array of points for shapes or 
7669      // [x, y, width, height] for rectangles or
7670      // [x, y, radius, start angle (rad), end angle (rad)] for circles and arcs.
7671      $.jqplot.ShapeRenderer.prototype.draw = function(ctx, points, options) {
7672          ctx.save();
7673          var opts = (options != null) ? options : {};
7674          var fill = (opts.fill != null) ? opts.fill : this.fill;
7675          var closePath = (opts.closePath != null) ? opts.closePath : this.closePath;
7676          var fillRect = (opts.fillRect != null) ? opts.fillRect : this.fillRect;
7677          var strokeRect = (opts.strokeRect != null) ? opts.strokeRect : this.strokeRect;
7678          var clearRect = (opts.clearRect != null) ? opts.clearRect : this.clearRect;
7679          var isarc = (opts.isarc != null) ? opts.isarc : this.isarc;
7680          var linePattern = (opts.linePattern != null) ? opts.linePattern : this.linePattern;
7681          var ctxPattern = $.jqplot.LinePattern(ctx, linePattern);
7682          ctx.lineWidth = opts.lineWidth || this.lineWidth;
7683          ctx.lineJoin = opts.lineJoin || this.lineJoin;
7684          ctx.lineCap = opts.lineCap || this.lineCap;
7685          ctx.strokeStyle = (opts.strokeStyle || opts.color) || this.strokeStyle;
7686          ctx.fillStyle = opts.fillStyle || this.fillStyle;
7687          ctx.beginPath();
7688          if (isarc) {
7689              ctx.arc(points[0], points[1], points[2], points[3], points[4], true);   
7690              if (closePath) {
7691                  ctx.closePath();
7692              }
7693              if (fill) {
7694                  ctx.fill();
7695              }
7696              else {
7697                  ctx.stroke();
7698              }
7699              ctx.restore();
7700              return;
7701          }
7702          else if (clearRect) {
7703              ctx.clearRect(points[0], points[1], points[2], points[3]);
7704              ctx.restore();
7705              return;
7706          }
7707          else if (fillRect || strokeRect) {
7708              if (fillRect) {
7709                  ctx.fillRect(points[0], points[1], points[2], points[3]);
7710              }
7711              if (strokeRect) {
7712                  ctx.strokeRect(points[0], points[1], points[2], points[3]);
7713                  ctx.restore();
7714                  return;
7715              }
7716          }
7717          else if (points && points.length){
7718              var move = true;
7719              for (var i=0; i<points.length; i++) {
7720                  // skip to the first non-null point and move to it.
7721                  if (points[i][0] != null && points[i][1] != null) {
7722                      if (move) {
7723                          ctxPattern.moveTo(points[i][0], points[i][1]);
7724                          move = false;
7725                      }
7726                      else {
7727                          ctxPattern.lineTo(points[i][0], points[i][1]);
7728                      }
7729                  }
7730                  else {
7731                      move = true;
7732                  }
7733              }
7734              if (closePath) {
7735                  ctxPattern.closePath();
7736              }
7737              if (fill) {
7738                  ctx.fill();
7739              }
7740              else {
7741                  ctx.stroke();
7742              }
7743          }
7744          ctx.restore();
7745      };
7746      
7747      // class $.jqplot.TableLegendRenderer
7748      // The default legend renderer for jqPlot.
7749      $.jqplot.TableLegendRenderer = function(){
7750          //
7751      };
7752      
7753      $.jqplot.TableLegendRenderer.prototype.init = function(options) {
7754          $.extend(true, this, options);
7755      };
7756          
7757      $.jqplot.TableLegendRenderer.prototype.addrow = function (label, color, pad, reverse) {
7758          var rs = (pad) ? this.rowSpacing+'px' : '0px';
7759          var tr;
7760          var td;
7761          var elem;
7762          var div0;
7763          var div1;
7764          elem = document.createElement('tr');
7765          tr = $(elem);
7766          tr.addClass('jqplot-table-legend');
7767          elem = null;
7768  
7769          if (reverse){
7770              tr.prependTo(this._elem);
7771          }
7772  
7773          else{
7774              tr.appendTo(this._elem);
7775          }
7776  
7777          if (this.showSwatches) {
7778              td = $(document.createElement('td'));
7779              td.addClass('jqplot-table-legend jqplot-table-legend-swatch');
7780              td.css({textAlign: 'center', paddingTop: rs});
7781  
7782              div0 = $(document.createElement('div'));
7783              div0.addClass('jqplot-table-legend-swatch-outline');
7784              div1 = $(document.createElement('div'));
7785              div1.addClass('jqplot-table-legend-swatch');
7786              div1.css({backgroundColor: color, borderColor: color});
7787  
7788              tr.append(td.append(div0.append(div1)));
7789  
7790              // $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+
7791              // '<div><div class="jqplot-table-legend-swatch" style="background-color:'+color+';border-color:'+color+';"></div>'+
7792              // '</div></td>').appendTo(tr);
7793          }
7794          if (this.showLabels) {
7795              td = $(document.createElement('td'));
7796              td.addClass('jqplot-table-legend jqplot-table-legend-label');
7797              td.css('paddingTop', rs);
7798              tr.append(td);
7799  
7800              // elem = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
7801              // elem.appendTo(tr);
7802              if (this.escapeHtml) {
7803                  td.text(label);
7804              }
7805              else {
7806                  td.html(label);
7807              }
7808          }
7809          td = null;
7810          div0 = null;
7811          div1 = null;
7812          tr = null;
7813          elem = null;
7814      };
7815      
7816      // called with scope of legend
7817      $.jqplot.TableLegendRenderer.prototype.draw = function() {
7818          if (this._elem) {
7819              this._elem.emptyForce();
7820              this._elem = null;
7821          }
7822  
7823          if (this.show) {
7824              var series = this._series;
7825              // make a table.  one line label per row.
7826              var elem = document.createElement('table');
7827              this._elem = $(elem);
7828              this._elem.addClass('jqplot-table-legend');
7829  
7830              var ss = {position:'absolute'};
7831              if (this.background) {
7832                  ss['background'] = this.background;
7833              }
7834              if (this.border) {
7835                  ss['border'] = this.border;
7836              }
7837              if (this.fontSize) {
7838                  ss['fontSize'] = this.fontSize;
7839              }
7840              if (this.fontFamily) {
7841                  ss['fontFamily'] = this.fontFamily;
7842              }
7843              if (this.textColor) {
7844                  ss['textColor'] = this.textColor;
7845              }
7846              if (this.marginTop != null) {
7847                  ss['marginTop'] = this.marginTop;
7848              }
7849              if (this.marginBottom != null) {
7850                  ss['marginBottom'] = this.marginBottom;
7851              }
7852              if (this.marginLeft != null) {
7853                  ss['marginLeft'] = this.marginLeft;
7854              }
7855              if (this.marginRight != null) {
7856                  ss['marginRight'] = this.marginRight;
7857              }
7858              
7859          
7860              var pad = false, 
7861                  reverse = false,
7862                  s;
7863              for (var i = 0; i< series.length; i++) {
7864                  s = series[i];
7865                  if (s._stack || s.renderer.constructor == $.jqplot.BezierCurveRenderer){
7866                      reverse = true;
7867                  }
7868                  if (s.show && s.showLabel) {
7869                      var lt = this.labels[i] || s.label.toString();
7870                      if (lt) {
7871                          var color = s.color;
7872                          if (reverse && i < series.length - 1){
7873                              pad = true;
7874                          }
7875                          else if (reverse && i == series.length - 1){
7876                              pad = false;
7877                          }
7878                          this.renderer.addrow.call(this, lt, color, pad, reverse);
7879                          pad = true;
7880                      }
7881                      // let plugins add more rows to legend.  Used by trend line plugin.
7882                      for (var j=0; j<$.jqplot.addLegendRowHooks.length; j++) {
7883                          var item = $.jqplot.addLegendRowHooks[j].call(this, s);
7884                          if (item) {
7885                              this.renderer.addrow.call(this, item.label, item.color, pad);
7886                              pad = true;
7887                          } 
7888                      }
7889                      lt = null;
7890                  }
7891              }
7892          }
7893          return this._elem;
7894      };
7895      
7896      $.jqplot.TableLegendRenderer.prototype.pack = function(offsets) {
7897          if (this.show) {       
7898              if (this.placement == 'insideGrid') {
7899                  switch (this.location) {
7900                      case 'nw':
7901                          var a = offsets.left;
7902                          var b = offsets.top;
7903                          this._elem.css('left', a);
7904                          this._elem.css('top', b);
7905                          break;
7906                      case 'n':
7907                          var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
7908                          var b = offsets.top;
7909                          this._elem.css('left', a);
7910                          this._elem.css('top', b);
7911                          break;
7912                      case 'ne':
7913                          var a = offsets.right;
7914                          var b = offsets.top;
7915                          this._elem.css({right:a, top:b});
7916                          break;
7917                      case 'e':
7918                          var a = offsets.right;
7919                          var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
7920                          this._elem.css({right:a, top:b});
7921                          break;
7922                      case 'se':
7923                          var a = offsets.right;
7924                          var b = offsets.bottom;
7925                          this._elem.css({right:a, bottom:b});
7926                          break;
7927                      case 's':
7928                          var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
7929                          var b = offsets.bottom;
7930                          this._elem.css({left:a, bottom:b});
7931                          break;
7932                      case 'sw':
7933                          var a = offsets.left;
7934                          var b = offsets.bottom;
7935                          this._elem.css({left:a, bottom:b});
7936                          break;
7937                      case 'w':
7938                          var a = offsets.left;
7939                          var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
7940                          this._elem.css({left:a, top:b});
7941                          break;
7942                      default:  // same as 'se'
7943                          var a = offsets.right;
7944                          var b = offsets.bottom;
7945                          this._elem.css({right:a, bottom:b});
7946                          break;
7947                  }
7948                  
7949              }
7950              else if (this.placement == 'outside'){
7951                  switch (this.location) {
7952                      case 'nw':
7953                          var a = this._plotDimensions.width - offsets.left;
7954                          var b = offsets.top;
7955                          this._elem.css('right', a);
7956                          this._elem.css('top', b);
7957                          break;
7958                      case 'n':
7959                          var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
7960                          var b = this._plotDimensions.height - offsets.top;
7961                          this._elem.css('left', a);
7962                          this._elem.css('bottom', b);
7963                          break;
7964                      case 'ne':
7965                          var a = this._plotDimensions.width - offsets.right;
7966                          var b = offsets.top;
7967                          this._elem.css({left:a, top:b});
7968                          break;
7969                      case 'e':
7970                          var a = this._plotDimensions.width - offsets.right;
7971                          var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
7972                          this._elem.css({left:a, top:b});
7973                          break;
7974                      case 'se':
7975                          var a = this._plotDimensions.width - offsets.right;
7976                          var b = offsets.bottom;
7977                          this._elem.css({left:a, bottom:b});
7978                          break;
7979                      case 's':
7980                          var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
7981                          var b = this._plotDimensions.height - offsets.bottom;
7982                          this._elem.css({left:a, top:b});
7983                          break;
7984                      case 'sw':
7985                          var a = this._plotDimensions.width - offsets.left;
7986                          var b = offsets.bottom;
7987                          this._elem.css({right:a, bottom:b});
7988                          break;
7989                      case 'w':
7990                          var a = this._plotDimensions.width - offsets.left;
7991                          var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
7992                          this._elem.css({right:a, top:b});
7993                          break;
7994                      default:  // same as 'se'
7995                          var a = offsets.right;
7996                          var b = offsets.bottom;
7997                          this._elem.css({right:a, bottom:b});
7998                          break;
7999                  }
8000              }
8001              else {
8002                  switch (this.location) {
8003                      case 'nw':
8004                          this._elem.css({left:0, top:offsets.top});
8005                          break;
8006                      case 'n':
8007                          var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
8008                          this._elem.css({left: a, top:offsets.top});
8009                          break;
8010                      case 'ne':
8011                          this._elem.css({right:0, top:offsets.top});
8012                          break;
8013                      case 'e':
8014                          var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
8015                          this._elem.css({right:offsets.right, top:b});
8016                          break;
8017                      case 'se':
8018                          this._elem.css({right:offsets.right, bottom:offsets.bottom});
8019                          break;
8020                      case 's':
8021                          var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
8022                          this._elem.css({left: a, bottom:offsets.bottom});
8023                          break;
8024                      case 'sw':
8025                          this._elem.css({left:offsets.left, bottom:offsets.bottom});
8026                          break;
8027                      case 'w':
8028                          var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
8029                          this._elem.css({left:offsets.left, top:b});
8030                          break;
8031                      default:  // same as 'se'
8032                          this._elem.css({right:offsets.right, bottom:offsets.bottom});
8033                          break;
8034                  }
8035              }
8036          } 
8037      };
8038  
8039      /**
8040       * Class: $.jqplot.ThemeEngine
8041       * Theme Engine provides a programatic way to change some of the  more
8042       * common jqplot styling options such as fonts, colors and grid options.
8043       * A theme engine instance is created with each plot.  The theme engine
8044       * manages a collection of themes which can be modified, added to, or 
8045       * applied to the plot.
8046       * 
8047       * The themeEngine class is not instantiated directly.
8048       * When a plot is initialized, the current plot options are scanned
8049       * an a default theme named "Default" is created.  This theme is
8050       * used as the basis for other themes added to the theme engine and
8051       * is always available.
8052       * 
8053       * A theme is a simple javascript object with styling parameters for
8054       * various entities of the plot.  A theme has the form:
8055       * 
8056       * 
8057       * > {
8058       * >     _name:f "Default",
8059       * >     target: {
8060       * >         backgroundColor: "transparent"
8061       * >     },
8062       * >     legend: {
8063       * >         textColor: null,
8064       * >         fontFamily: null,
8065       * >         fontSize: null,
8066       * >         border: null,
8067       * >         background: null
8068       * >     },
8069       * >     title: {
8070       * >         textColor: "rgb(102, 102, 102)",
8071       * >         fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif",
8072       * >         fontSize: "19.2px",
8073       * >         textAlign: "center"
8074       * >     },
8075       * >     seriesStyles: {},
8076       * >     series: [{
8077       * >         color: "#4bb2c5",
8078       * >         lineWidth: 2.5,
8079       * >         linePattern: "solid",
8080       * >         shadow: true,
8081       * >         fillColor: "#4bb2c5",
8082       * >         showMarker: true,
8083       * >         markerOptions: {
8084       * >             color: "#4bb2c5",
8085       * >             show: true,
8086       * >             style: 'filledCircle',
8087       * >             lineWidth: 1.5,
8088       * >             size: 4,
8089       * >             shadow: true
8090       * >         }
8091       * >     }],
8092       * >     grid: {
8093       * >         drawGridlines: true,
8094       * >         gridLineColor: "#cccccc",
8095       * >         gridLineWidth: 1,
8096       * >         backgroundColor: "#fffdf6",
8097       * >         borderColor: "#999999",
8098       * >         borderWidth: 2,
8099       * >         shadow: true
8100       * >     },
8101       * >     axesStyles: {
8102       * >         label: {},
8103       * >         ticks: {}
8104       * >     },
8105       * >     axes: {
8106       * >         xaxis: {
8107       * >             borderColor: "#999999",
8108       * >             borderWidth: 2,
8109       * >             ticks: {
8110       * >                 show: true,
8111       * >                 showGridline: true,
8112       * >                 showLabel: true,
8113       * >                 showMark: true,
8114       * >                 size: 4,
8115       * >                 textColor: "",
8116       * >                 whiteSpace: "nowrap",
8117       * >                 fontSize: "12px",
8118       * >                 fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif"
8119       * >             },
8120       * >             label: {
8121       * >                 textColor: "rgb(102, 102, 102)",
8122       * >                 whiteSpace: "normal",
8123       * >                 fontSize: "14.6667px",
8124       * >                 fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif",
8125       * >                 fontWeight: "400"
8126       * >             }
8127       * >         },
8128       * >         yaxis: {
8129       * >             borderColor: "#999999",
8130       * >             borderWidth: 2,
8131       * >             ticks: {
8132       * >                 show: true,
8133       * >                 showGridline: true,
8134       * >                 showLabel: true,
8135       * >                 showMark: true,
8136       * >                 size: 4,
8137       * >                 textColor: "",
8138       * >                 whiteSpace: "nowrap",
8139       * >                 fontSize: "12px",
8140       * >                 fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif"
8141       * >             },
8142       * >             label: {
8143       * >                 textColor: null,
8144       * >                 whiteSpace: null,
8145       * >                 fontSize: null,
8146       * >                 fontFamily: null,
8147       * >                 fontWeight: null
8148       * >             }
8149       * >         },
8150       * >         x2axis: {...
8151       * >         },
8152       * >         ...
8153       * >         y9axis: {...
8154       * >         }
8155       * >     }
8156       * > }
8157       * 
8158       * "seriesStyles" is a style object that will be applied to all series in the plot.
8159       * It will forcibly override any styles applied on the individual series.  "axesStyles" is
8160       * a style object that will be applied to all axes in the plot.  It will also forcibly
8161       * override any styles on the individual axes.
8162       * 
8163       * The example shown above has series options for a line series.  Options for other
8164       * series types are shown below:
8165       * 
8166       * Bar Series:
8167       * 
8168       * > {
8169       * >     color: "#4bb2c5",
8170       * >     seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
8171       * >     lineWidth: 2.5,
8172       * >     shadow: true,
8173       * >     barPadding: 2,
8174       * >     barMargin: 10,
8175       * >     barWidth: 15.09375,
8176       * >     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)"]
8177       * > }
8178       * 
8179       * Pie Series:
8180       * 
8181       * > {
8182       * >     seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
8183       * >     padding: 20,
8184       * >     sliceMargin: 0,
8185       * >     fill: true,
8186       * >     shadow: true,
8187       * >     startAngle: 0,
8188       * >     lineWidth: 2.5,
8189       * >     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)"]
8190       * > }
8191       * 
8192       * Funnel Series:
8193       * 
8194       * > {
8195       * >     color: "#4bb2c5",
8196       * >     lineWidth: 2,
8197       * >     shadow: true,
8198       * >     padding: {
8199       * >         top: 20,
8200       * >         right: 20,
8201       * >         bottom: 20,
8202       * >         left: 20
8203       * >     },
8204       * >     sectionMargin: 6,
8205       * >     seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
8206       * >     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)"]
8207       * > }
8208       * 
8209       */
8210      $.jqplot.ThemeEngine = function(){
8211          // Group: Properties
8212          //
8213          // prop: themes
8214          // hash of themes managed by the theme engine.  
8215          // Indexed by theme name.
8216          this.themes = {};
8217          // prop: activeTheme
8218          // Pointer to currently active theme
8219          this.activeTheme=null;
8220          
8221      };
8222      
8223      // called with scope of plot
8224      $.jqplot.ThemeEngine.prototype.init = function() {
8225          // get the Default theme from the current plot settings.
8226          var th = new $.jqplot.Theme({_name:'Default'});
8227          var n, i, nn;
8228          
8229          for (n in th.target) {
8230              if (n == "textColor") {
8231                  th.target[n] = this.target.css('color');
8232              }
8233              else {
8234                  th.target[n] = this.target.css(n);
8235              }
8236          }
8237          
8238          if (this.title.show && this.title._elem) {
8239              for (n in th.title) {
8240                  if (n == "textColor") {
8241                      th.title[n] = this.title._elem.css('color');
8242                  }
8243                  else {
8244                      th.title[n] = this.title._elem.css(n);
8245                  }
8246              }
8247          }
8248          
8249          for (n in th.grid) {
8250              th.grid[n] = this.grid[n];
8251          }
8252          if (th.grid.backgroundColor == null && this.grid.background != null) {
8253              th.grid.backgroundColor = this.grid.background;
8254          }
8255          if (this.legend.show && this.legend._elem) {
8256              for (n in th.legend) {
8257                  if (n == 'textColor') {
8258                      th.legend[n] = this.legend._elem.css('color');
8259                  }
8260                  else {
8261                      th.legend[n] = this.legend._elem.css(n);
8262                  }
8263              }
8264          }
8265          var s;
8266          
8267          for (i=0; i<this.series.length; i++) {
8268              s = this.series[i];
8269              if (s.renderer.constructor == $.jqplot.LineRenderer) {
8270                  th.series.push(new LineSeriesProperties());
8271              }
8272              else if (s.renderer.constructor == $.jqplot.BarRenderer) {
8273                  th.series.push(new BarSeriesProperties());
8274              }
8275              else if (s.renderer.constructor == $.jqplot.PieRenderer) {
8276                  th.series.push(new PieSeriesProperties());
8277              }
8278              else if (s.renderer.constructor == $.jqplot.DonutRenderer) {
8279                  th.series.push(new DonutSeriesProperties());
8280              }
8281              else if (s.renderer.constructor == $.jqplot.FunnelRenderer) {
8282                  th.series.push(new FunnelSeriesProperties());
8283              }
8284              else if (s.renderer.constructor == $.jqplot.MeterGaugeRenderer) {
8285                  th.series.push(new MeterSeriesProperties());
8286              }
8287              else {
8288                  th.series.push({});
8289              }
8290              for (n in th.series[i]) {
8291                  th.series[i][n] = s[n];
8292              }
8293          }
8294          var a, ax;
8295          for (n in this.axes) {
8296              ax = this.axes[n];
8297              a = th.axes[n] = new AxisProperties();
8298              a.borderColor = ax.borderColor;
8299              a.borderWidth = ax.borderWidth;
8300              if (ax._ticks && ax._ticks[0]) {
8301                  for (nn in a.ticks) {
8302                      if (ax._ticks[0].hasOwnProperty(nn)) {
8303                          a.ticks[nn] = ax._ticks[0][nn];
8304                      }
8305                      else if (ax._ticks[0]._elem){
8306                          a.ticks[nn] = ax._ticks[0]._elem.css(nn);
8307                      }
8308                  }
8309              }
8310              if (ax._label && ax._label.show) {
8311                  for (nn in a.label) {
8312                      // a.label[nn] = ax._label._elem.css(nn);
8313                      if (ax._label[nn]) {
8314                          a.label[nn] = ax._label[nn];
8315                      }
8316                      else if (ax._label._elem){
8317                          if (nn == 'textColor') {
8318                              a.label[nn] = ax._label._elem.css('color');
8319                          }
8320                          else {
8321                              a.label[nn] = ax._label._elem.css(nn);
8322                          }
8323                      }
8324                  }
8325              }
8326          }
8327          this.themeEngine._add(th);
8328          this.themeEngine.activeTheme  = this.themeEngine.themes[th._name];
8329      };
8330      /**
8331       * Group: methods
8332       * 
8333       * method: get
8334       * 
8335       * Get and return the named theme or the active theme if no name given.
8336       * 
8337       * parameter:
8338       * 
8339       * name - name of theme to get.
8340       * 
8341       * returns:
8342       * 
8343       * Theme instance of given name.
8344       */   
8345      $.jqplot.ThemeEngine.prototype.get = function(name) {
8346          if (!name) {
8347              // return the active theme
8348              return this.activeTheme;
8349          }
8350          else {
8351              return this.themes[name];
8352          }
8353      };
8354      
8355      function numericalOrder(a,b) { return a-b; }
8356      
8357      /**
8358       * method: getThemeNames
8359       * 
8360       * Return the list of theme names in this manager in alpha-numerical order.
8361       * 
8362       * parameter:
8363       * 
8364       * None
8365       * 
8366       * returns:
8367       * 
8368       * A the list of theme names in this manager in alpha-numerical order.
8369       */       
8370      $.jqplot.ThemeEngine.prototype.getThemeNames = function() {
8371          var tn = [];
8372          for (var n in this.themes) {
8373              tn.push(n);
8374          }
8375          return tn.sort(numericalOrder);
8376      };
8377  
8378      /**
8379       * method: getThemes
8380       * 
8381       * Return a list of themes in alpha-numerical order by name.
8382       * 
8383       * parameter:
8384       * 
8385       * None
8386       * 
8387       * returns:
8388       * 
8389       * A list of themes in alpha-numerical order by name.
8390       */ 
8391      $.jqplot.ThemeEngine.prototype.getThemes = function() {
8392          var tn = [];
8393          var themes = [];
8394          for (var n in this.themes) {
8395              tn.push(n);
8396          }
8397          tn.sort(numericalOrder);
8398          for (var i=0; i<tn.length; i++) {
8399              themes.push(this.themes[tn[i]]);
8400          }
8401          return themes;
8402      };
8403      
8404      $.jqplot.ThemeEngine.prototype.activate = function(plot, name) {
8405          // sometimes need to redraw whole plot.
8406          var redrawPlot = false;
8407          if (!name && this.activeTheme && this.activeTheme._name) {
8408              name = this.activeTheme._name;
8409          }
8410          if (!this.themes.hasOwnProperty(name)) {
8411              throw new Error("No theme of that name");
8412          }
8413          else {
8414              var th = this.themes[name];
8415              this.activeTheme = th;
8416              var val, checkBorderColor = false, checkBorderWidth = false;
8417              var arr = ['xaxis', 'x2axis', 'yaxis', 'y2axis'];
8418              
8419              for (i=0; i<arr.length; i++) {
8420                  var ax = arr[i];
8421                  if (th.axesStyles.borderColor != null) {
8422                      plot.axes[ax].borderColor = th.axesStyles.borderColor;
8423                  }
8424                  if (th.axesStyles.borderWidth != null) {
8425                      plot.axes[ax].borderWidth = th.axesStyles.borderWidth;
8426                  }
8427              }
8428              
8429              for (var axname in plot.axes) {
8430                  var axis = plot.axes[axname];
8431                  if (axis.show) {
8432                      var thaxis = th.axes[axname] || {};
8433                      var thaxstyle = th.axesStyles;
8434                      var thax = $.jqplot.extend(true, {}, thaxis, thaxstyle);
8435                      val = (th.axesStyles.borderColor != null) ? th.axesStyles.borderColor : thax.borderColor;
8436                      if (thax.borderColor != null) {
8437                          axis.borderColor = thax.borderColor;
8438                          redrawPlot = true;
8439                      }
8440                      val = (th.axesStyles.borderWidth != null) ? th.axesStyles.borderWidth : thax.borderWidth;
8441                      if (thax.borderWidth != null) {
8442                          axis.borderWidth = thax.borderWidth;
8443                          redrawPlot = true;
8444                      }
8445                      if (axis._ticks && axis._ticks[0]) {
8446                          for (var nn in thax.ticks) {
8447                              // val = null;
8448                              // if (th.axesStyles.ticks && th.axesStyles.ticks[nn] != null) {
8449                              //     val = th.axesStyles.ticks[nn];
8450                              // }
8451                              // else if (thax.ticks[nn] != null){
8452                              //     val = thax.ticks[nn]
8453                              // }
8454                              val = thax.ticks[nn];
8455                              if (val != null) {
8456                                  axis.tickOptions[nn] = val;
8457                                  axis._ticks = [];
8458                                  redrawPlot = true;
8459                              }
8460                          }
8461                      }
8462                      if (axis._label && axis._label.show) {
8463                          for (var nn in thax.label) {
8464                              // val = null;
8465                              // if (th.axesStyles.label && th.axesStyles.label[nn] != null) {
8466                              //     val = th.axesStyles.label[nn];
8467                              // }
8468                              // else if (thax.label && thax.label[nn] != null){
8469                              //     val = thax.label[nn]
8470                              // }
8471                              val = thax.label[nn];
8472                              if (val != null) {
8473                                  axis.labelOptions[nn] = val;
8474                                  redrawPlot = true;
8475                              }
8476                          }
8477                      }
8478                      
8479                  }
8480              }            
8481              
8482              for (var n in th.grid) {
8483                  if (th.grid[n] != null) {
8484                      plot.grid[n] = th.grid[n];
8485                  }
8486              }
8487              if (!redrawPlot) {
8488                  plot.grid.draw();
8489              }
8490              
8491              if (plot.legend.show) { 
8492                  for (n in th.legend) {
8493                      if (th.legend[n] != null) {
8494                          plot.legend[n] = th.legend[n];
8495                      }
8496                  }
8497              }
8498              if (plot.title.show) {
8499                  for (n in th.title) {
8500                      if (th.title[n] != null) {
8501                          plot.title[n] = th.title[n];
8502                      }
8503                  }
8504              }
8505              
8506              var i;
8507              for (i=0; i<th.series.length; i++) {
8508                  var opts = {};
8509                  var redrawSeries = false;
8510                  for (n in th.series[i]) {
8511                      val = (th.seriesStyles[n] != null) ? th.seriesStyles[n] : th.series[i][n];
8512                      if (val != null) {
8513                          opts[n] = val;
8514                          if (n == 'color') {
8515                              plot.series[i].renderer.shapeRenderer.fillStyle = val;
8516                              plot.series[i].renderer.shapeRenderer.strokeStyle = val;
8517                              plot.series[i][n] = val;
8518                          }
8519                          else if ((n == 'lineWidth') || (n == 'linePattern')) {
8520                              plot.series[i].renderer.shapeRenderer[n] = val;
8521                              plot.series[i][n] = val;
8522                          }
8523                          else if (n == 'markerOptions') {
8524                              merge (plot.series[i].markerOptions, val);
8525                              merge (plot.series[i].markerRenderer, val);
8526                          }
8527                          else {
8528                              plot.series[i][n] = val;
8529                          }
8530                          redrawPlot = true;
8531                      }
8532                  }
8533              }
8534              
8535              if (redrawPlot) {
8536                  plot.target.empty();
8537                  plot.draw();
8538              }
8539              
8540              for (n in th.target) {
8541                  if (th.target[n] != null) {
8542                      plot.target.css(n, th.target[n]);
8543                  }
8544              }
8545          }
8546          
8547      };
8548      
8549      $.jqplot.ThemeEngine.prototype._add = function(theme, name) {
8550          if (name) {
8551              theme._name = name;
8552          }
8553          if (!theme._name) {
8554              theme._name = Date.parse(new Date());
8555          }
8556          if (!this.themes.hasOwnProperty(theme._name)) {
8557              this.themes[theme._name] = theme;
8558          }
8559          else {
8560              throw new Error("jqplot.ThemeEngine Error: Theme already in use");
8561          }
8562      };
8563      
8564      // method remove
8565      // Delete the named theme, return true on success, false on failure.
8566      
8567  
8568      /**
8569       * method: remove
8570       * 
8571       * Remove the given theme from the themeEngine.
8572       * 
8573       * parameters:
8574       * 
8575       * name - name of the theme to remove.
8576       * 
8577       * returns:
8578       * 
8579       * true on success, false on failure.
8580       */
8581      $.jqplot.ThemeEngine.prototype.remove = function(name) {
8582          if (name == 'Default') {
8583              return false;
8584          }
8585          return delete this.themes[name];
8586      };
8587  
8588      /**
8589       * method: newTheme
8590       * 
8591       * Create a new theme based on the default theme, adding it the themeEngine.
8592       * 
8593       * parameters:
8594       * 
8595       * name - name of the new theme.
8596       * obj - optional object of styles to be applied to this new theme.
8597       * 
8598       * returns:
8599       * 
8600       * new Theme object.
8601       */
8602      $.jqplot.ThemeEngine.prototype.newTheme = function(name, obj) {
8603          if (typeof(name) == 'object') {
8604              obj = obj || name;
8605              name = null;
8606          }
8607          if (obj && obj._name) {
8608              name = obj._name;
8609          }
8610          else {
8611              name = name || Date.parse(new Date());
8612          }
8613          // var th = new $.jqplot.Theme(name);
8614          var th = this.copy(this.themes['Default']._name, name);
8615          $.jqplot.extend(th, obj);
8616          return th;
8617      };
8618      
8619      // function clone(obj) {
8620      //     return eval(obj.toSource());
8621      // }
8622      
8623      function clone(obj){
8624          if(obj == null || typeof(obj) != 'object'){
8625              return obj;
8626          }
8627      
8628          var temp = new obj.constructor();
8629          for(var key in obj){
8630              temp[key] = clone(obj[key]);
8631          }   
8632          return temp;
8633      }
8634      
8635      $.jqplot.clone = clone;
8636      
8637      function merge(obj1, obj2) {
8638          if (obj2 ==  null || typeof(obj2) != 'object') {
8639              return;
8640          }
8641          for (var key in obj2) {
8642              if (key == 'highlightColors') {
8643                  obj1[key] = clone(obj2[key]);
8644              }
8645              if (obj2[key] != null && typeof(obj2[key]) == 'object') {
8646                  if (!obj1.hasOwnProperty(key)) {
8647                      obj1[key] = {};
8648                  }
8649                  merge(obj1[key], obj2[key]);
8650              }
8651              else {
8652                  obj1[key] = obj2[key];
8653              }
8654          }
8655      }
8656      
8657      $.jqplot.merge = merge;
8658      
8659          // Use the jQuery 1.3.2 extend function since behaviour in jQuery 1.4 seems problematic
8660      $.jqplot.extend = function() {
8661          // copy reference to target object
8662          var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
8663  
8664          // Handle a deep copy situation
8665          if ( typeof target === "boolean" ) {
8666              deep = target;
8667              target = arguments[1] || {};
8668              // skip the boolean and the target
8669              i = 2;
8670          }
8671  
8672          // Handle case when target is a string or something (possible in deep copy)
8673          if ( typeof target !== "object" && !toString.call(target) === "[object Function]" ) {
8674              target = {};
8675          }
8676  
8677          for ( ; i < length; i++ ){
8678              // Only deal with non-null/undefined values
8679              if ( (options = arguments[ i ]) != null ) {
8680                  // Extend the base object
8681                  for ( var name in options ) {
8682                      var src = target[ name ], copy = options[ name ];
8683  
8684                      // Prevent never-ending loop
8685                      if ( target === copy ) {
8686                          continue;
8687                      }
8688  
8689                      // Recurse if we're merging object values
8690                      if ( deep && copy && typeof copy === "object" && !copy.nodeType ) {
8691                          target[ name ] = $.jqplot.extend( deep, 
8692                              // Never move original objects, clone them
8693                              src || ( copy.length != null ? [ ] : { } )
8694                          , copy );
8695                      }
8696                      // Don't bring in undefined values
8697                      else if ( copy !== undefined ) {
8698                          target[ name ] = copy;
8699                      }
8700                  }
8701              }
8702          }
8703          // Return the modified object
8704          return target;
8705      };
8706  
8707      /**
8708       * method: rename
8709       * 
8710       * Rename a theme.
8711       * 
8712       * parameters:
8713       * 
8714       * oldName - current name of the theme.
8715       * newName - desired name of the theme.
8716       * 
8717       * returns:
8718       * 
8719       * new Theme object.
8720       */
8721      $.jqplot.ThemeEngine.prototype.rename = function (oldName, newName) {
8722          if (oldName == 'Default' || newName == 'Default') {
8723              throw new Error ("jqplot.ThemeEngine Error: Cannot rename from/to Default");
8724          }
8725          if (this.themes.hasOwnProperty(newName)) {
8726              throw new Error ("jqplot.ThemeEngine Error: New name already in use.");
8727          }
8728          else if (this.themes.hasOwnProperty(oldName)) {
8729              var th = this.copy (oldName, newName);
8730              this.remove(oldName);
8731              return th;
8732          }
8733          throw new Error("jqplot.ThemeEngine Error: Old name or new name invalid");
8734      };
8735  
8736      /**
8737       * method: copy
8738       * 
8739       * Create a copy of an existing theme in the themeEngine, adding it the themeEngine.
8740       * 
8741       * parameters:
8742       * 
8743       * sourceName - name of the existing theme.
8744       * targetName - name of the copy.
8745       * obj - optional object of style parameter to apply to the new theme.
8746       * 
8747       * returns:
8748       * 
8749       * new Theme object.
8750       */
8751      $.jqplot.ThemeEngine.prototype.copy = function (sourceName, targetName, obj) {
8752          if (targetName == 'Default') {
8753              throw new Error ("jqplot.ThemeEngine Error: Cannot copy over Default theme");
8754          }
8755          if (!this.themes.hasOwnProperty(sourceName)) {
8756              var s = "jqplot.ThemeEngine Error: Source name invalid";
8757              throw new Error(s);
8758          }
8759          if (this.themes.hasOwnProperty(targetName)) {
8760              var s = "jqplot.ThemeEngine Error: Target name invalid";
8761              throw new Error(s);
8762          }
8763          else {
8764              var th = clone(this.themes[sourceName]);
8765              th._name = targetName;
8766              $.jqplot.extend(true, th, obj);
8767              this._add(th);
8768              return th;
8769          }
8770      };
8771      
8772      
8773      $.jqplot.Theme = function(name, obj) {
8774          if (typeof(name) == 'object') {
8775              obj = obj || name;
8776              name = null;
8777          }
8778          name = name || Date.parse(new Date());
8779          this._name = name;
8780          this.target = {
8781              backgroundColor: null
8782          };
8783          this.legend = {
8784              textColor: null,
8785              fontFamily: null,
8786              fontSize: null,
8787              border: null,
8788              background: null
8789          };
8790          this.title = {
8791              textColor: null,
8792              fontFamily: null,
8793              fontSize: null,
8794              textAlign: null
8795          };
8796          this.seriesStyles = {};
8797          this.series = [];
8798          this.grid = {
8799              drawGridlines: null,
8800              gridLineColor: null,
8801              gridLineWidth: null,
8802              backgroundColor: null,
8803              borderColor: null,
8804              borderWidth: null,
8805              shadow: null
8806          };
8807          this.axesStyles = {label:{}, ticks:{}};
8808          this.axes = {};
8809          if (typeof(obj) == 'string') {
8810              this._name = obj;
8811          }
8812          else if(typeof(obj) == 'object') {
8813              $.jqplot.extend(true, this, obj);
8814          }
8815      };
8816      
8817      var AxisProperties = function() {
8818          this.borderColor = null;
8819          this.borderWidth = null;
8820          this.ticks = new AxisTicks();
8821          this.label = new AxisLabel();
8822      };
8823      
8824      var AxisTicks = function() {
8825          this.show = null;
8826          this.showGridline = null;
8827          this.showLabel = null;
8828          this.showMark = null;
8829          this.size = null;
8830          this.textColor = null;
8831          this.whiteSpace = null;
8832          this.fontSize = null;
8833          this.fontFamily = null;
8834      };
8835      
8836      var AxisLabel = function() {
8837          this.textColor = null;
8838          this.whiteSpace = null;
8839          this.fontSize = null;
8840          this.fontFamily = null;
8841          this.fontWeight = null;
8842      };
8843      
8844      var LineSeriesProperties = function() {
8845          this.color=null;
8846          this.lineWidth=null;
8847          this.linePattern=null;
8848          this.shadow=null;
8849          this.fillColor=null;
8850          this.showMarker=null;
8851          this.markerOptions = new MarkerOptions();
8852      };
8853      
8854      var MarkerOptions = function() {
8855          this.show = null;
8856          this.style = null;
8857          this.lineWidth = null;
8858          this.size = null;
8859          this.color = null;
8860          this.shadow = null;
8861      };
8862      
8863      var BarSeriesProperties = function() {
8864          this.color=null;
8865          this.seriesColors=null;
8866          this.lineWidth=null;
8867          this.shadow=null;
8868          this.barPadding=null;
8869          this.barMargin=null;
8870          this.barWidth=null;
8871          this.highlightColors=null;
8872      };
8873      
8874      var PieSeriesProperties = function() {
8875          this.seriesColors=null;
8876          this.padding=null;
8877          this.sliceMargin=null;
8878          this.fill=null;
8879          this.shadow=null;
8880          this.startAngle=null;
8881          this.lineWidth=null;
8882          this.highlightColors=null;
8883      };
8884      
8885      var DonutSeriesProperties = function() {
8886          this.seriesColors=null;
8887          this.padding=null;
8888          this.sliceMargin=null;
8889          this.fill=null;
8890          this.shadow=null;
8891          this.startAngle=null;
8892          this.lineWidth=null;
8893          this.innerDiameter=null;
8894          this.thickness=null;
8895          this.ringMargin=null;
8896          this.highlightColors=null;
8897      };
8898      
8899      var FunnelSeriesProperties = function() {
8900          this.color=null;
8901          this.lineWidth=null;
8902          this.shadow=null;
8903          this.padding=null;
8904          this.sectionMargin=null;
8905          this.seriesColors=null;
8906          this.highlightColors=null;
8907      };
8908      
8909      var MeterSeriesProperties = function() {
8910          this.padding=null;
8911          this.backgroundColor=null;
8912          this.ringColor=null;
8913          this.tickColor=null;
8914          this.ringWidth=null;
8915          this.intervalColors=null;
8916          this.intervalInnerRadius=null;
8917          this.intervalOuterRadius=null;
8918          this.hubRadius=null;
8919          this.needleThickness=null;
8920          this.needlePad=null;
8921      };
8922          
8923  
8924  
8925  
8926      $.fn.jqplotChildText = function() {
8927          return $(this).contents().filter(function() {
8928              return this.nodeType == 3;  // Node.TEXT_NODE not defined in I7
8929          }).text();
8930      };
8931  
8932      // Returns font style as abbreviation for "font" property.
8933      $.fn.jqplotGetComputedFontStyle = function() {
8934          var css = window.getComputedStyle ?  window.getComputedStyle(this[0], "") : this[0].currentStyle;
8935          var attrs = css['font-style'] ? ['font-style', 'font-weight', 'font-size', 'font-family'] : ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily'];
8936          var style = [];
8937  
8938          for (var i=0 ; i < attrs.length; ++i) {
8939              var attr = String(css[attrs[i]]);
8940  
8941              if (attr && attr != 'normal') {
8942                  style.push(attr);
8943              }
8944          }
8945          return style.join(' ');
8946      };
8947  
8948      /**
8949       * Namespace: $.fn
8950       * jQuery namespace to attach functions to jQuery elements.
8951       *  
8952       */
8953  
8954      $.fn.jqplotToImageCanvas = function(options) {
8955  
8956          options = options || {};
8957          var x_offset = (options.x_offset == null) ? 0 : options.x_offset;
8958          var y_offset = (options.y_offset == null) ? 0 : options.y_offset;
8959          var backgroundColor = (options.backgroundColor == null) ? 'rgb(255,255,255)' : options.backgroundColor;
8960  
8961          if ($(this).width() == 0 || $(this).height() == 0) {
8962              return null;
8963          }
8964  
8965          // excanvas and hence IE < 9 do not support toDataURL and cannot export images.
8966          if ($.jqplot.use_excanvas) {
8967              return null;
8968          }
8969          
8970          var newCanvas = document.createElement("canvas");
8971          var h = $(this).outerHeight(true);
8972          var w = $(this).outerWidth(true);
8973          var offs = $(this).offset();
8974          var plotleft = offs.left;
8975          var plottop = offs.top;
8976          var transx = 0, transy = 0;
8977  
8978          // have to check if any elements are hanging outside of plot area before rendering,
8979          // since changing width of canvas will erase canvas.
8980  
8981          var clses = ['jqplot-table-legend', 'jqplot-xaxis-tick', 'jqplot-x2axis-tick', 'jqplot-yaxis-tick', 'jqplot-y2axis-tick', 'jqplot-y3axis-tick', 
8982          'jqplot-y4axis-tick', 'jqplot-y5axis-tick', 'jqplot-y6axis-tick', 'jqplot-y7axis-tick', 'jqplot-y8axis-tick', 'jqplot-y9axis-tick',
8983          'jqplot-xaxis-label', 'jqplot-x2axis-label', 'jqplot-yaxis-label', 'jqplot-y2axis-label', 'jqplot-y3axis-label', 'jqplot-y4axis-label', 
8984          'jqplot-y5axis-label', 'jqplot-y6axis-label', 'jqplot-y7axis-label', 'jqplot-y8axis-label', 'jqplot-y9axis-label' ];
8985  
8986          var temptop, templeft, tempbottom, tempright;
8987  
8988          for (var i = 0; i < clses.length; i++) {
8989              $(this).find('.'+clses[i]).each(function() {
8990                  temptop = $(this).offset().top - plottop;
8991                  templeft = $(this).offset().left - plotleft;
8992                  tempright = templeft + $(this).outerWidth(true) + transx;
8993                  tempbottom = temptop + $(this).outerHeight(true) + transy;
8994                  if (templeft < -transx) {
8995                      w = w - transx - templeft;
8996                      transx = -templeft;
8997                  }
8998                  if (temptop < -transy) {
8999                      h = h - transy - temptop;
9000                      transy = - temptop;
9001                  }
9002                  if (tempright > w) {
9003                      w = tempright;
9004                  }
9005                  if (tempbottom > h) {
9006                      h =  tempbottom;
9007                  }
9008              });
9009          }
9010  
9011          newCanvas.width = w + Number(x_offset);
9012          newCanvas.height = h + Number(y_offset);
9013  
9014          var newContext = newCanvas.getContext("2d"); 
9015  
9016          newContext.save();
9017          newContext.fillStyle = backgroundColor;
9018          newContext.fillRect(0,0, newCanvas.width, newCanvas.height);
9019          newContext.restore();
9020  
9021          newContext.translate(transx, transy);
9022          newContext.textAlign = 'left';
9023          newContext.textBaseline = 'top';
9024  
9025          function getLineheight(el) {
9026              var lineheight = parseInt($(el).css('line-height'), 10);
9027  
9028              if (isNaN(lineheight)) {
9029                  lineheight = parseInt($(el).css('font-size'), 10) * 1.2;
9030              }
9031              return lineheight;
9032          }
9033  
9034          function writeWrappedText (el, context, text, left, top, canvasWidth) {
9035              var lineheight = getLineheight(el);
9036              var tagwidth = $(el).innerWidth();
9037              var tagheight = $(el).innerHeight();
9038              var words = text.split(/\s+/);
9039              var wl = words.length;
9040              var w = '';
9041              var breaks = [];
9042              var temptop = top;
9043              var templeft = left;
9044  
9045              for (var i=0; i<wl; i++) {
9046                  w += words[i];
9047                  if (context.measureText(w).width > tagwidth) {
9048                      breaks.push(i);
9049                      w = '';
9050                      i--;
9051                  }   
9052              }
9053              if (breaks.length === 0) {
9054                  // center text if necessary
9055                  if ($(el).css('textAlign') === 'center') {
9056                      templeft = left + (canvasWidth - context.measureText(w).width)/2  - transx;
9057                  }
9058                  context.fillText(text, templeft, top);
9059              }
9060              else {
9061                  w = words.slice(0, breaks[0]).join(' ');
9062                  // center text if necessary
9063                  if ($(el).css('textAlign') === 'center') {
9064                      templeft = left + (canvasWidth - context.measureText(w).width)/2  - transx;
9065                  }
9066                  context.fillText(w, templeft, temptop);
9067                  temptop += lineheight;
9068                  for (var i=1, l=breaks.length; i<l; i++) {
9069                      w = words.slice(breaks[i-1], breaks[i]).join(' ');
9070                      // center text if necessary
9071                      if ($(el).css('textAlign') === 'center') {
9072                          templeft = left + (canvasWidth - context.measureText(w).width)/2  - transx;
9073                      }
9074                      context.fillText(w, templeft, temptop);
9075                      temptop += lineheight;
9076                  }
9077                  w = words.slice(breaks[i-1], words.length).join(' ');
9078                  // center text if necessary
9079                  if ($(el).css('textAlign') === 'center') {
9080                      templeft = left + (canvasWidth - context.measureText(w).width)/2  - transx;
9081                  }
9082                  context.fillText(w, templeft, temptop);
9083              }
9084  
9085          }
9086  
9087          function _jqpToImage(el, x_offset, y_offset) {
9088              var tagname = el.tagName.toLowerCase();
9089              var p = $(el).position();
9090              var css = window.getComputedStyle ?  window.getComputedStyle(el, "") : el.currentStyle; // for IE < 9
9091              var left = x_offset + p.left + parseInt(css.marginLeft, 10) + parseInt(css.borderLeftWidth, 10) + parseInt(css.paddingLeft, 10);
9092              var top = y_offset + p.top + parseInt(css.marginTop, 10) + parseInt(css.borderTopWidth, 10)+ parseInt(css.paddingTop, 10);
9093              var w = newCanvas.width;
9094              // var left = x_offset + p.left + $(el).css('marginLeft') + $(el).css('borderLeftWidth') 
9095  
9096              // somehow in here, for divs within divs, the width of the inner div should be used instead of the canvas.
9097  
9098              if ((tagname == 'div' || tagname == 'span') && !$(el).hasClass('jqplot-highlighter-tooltip')) {
9099                  $(el).children().each(function() {
9100                      _jqpToImage(this, left, top);
9101                  });
9102                  var text = $(el).jqplotChildText();
9103  
9104                  if (text) {
9105                      newContext.font = $(el).jqplotGetComputedFontStyle();
9106                      newContext.fillStyle = $(el).css('color');
9107  
9108                      writeWrappedText(el, newContext, text, left, top, w);
9109                  }
9110              }
9111  
9112              // handle the standard table legend
9113  
9114              else if (tagname === 'table' && $(el).hasClass('jqplot-table-legend')) {
9115                  newContext.strokeStyle = $(el).css('border-top-color');
9116                  newContext.fillStyle = $(el).css('background-color');
9117                  newContext.fillRect(left, top, $(el).innerWidth(), $(el).innerHeight());
9118                  if (parseInt($(el).css('border-top-width'), 10) > 0) {
9119                      newContext.strokeRect(left, top, $(el).innerWidth(), $(el).innerHeight());
9120                  }
9121  
9122                  // find all the swatches
9123                  $(el).find('div.jqplot-table-legend-swatch-outline').each(function() {
9124                      // get the first div and stroke it
9125                      var elem = $(this);
9126                      newContext.strokeStyle = elem.css('border-top-color');
9127                      var l = left + elem.position().left;
9128                      var t = top + elem.position().top;
9129                      newContext.strokeRect(l, t, elem.innerWidth(), elem.innerHeight());
9130  
9131                      // now fill the swatch
9132                      
9133                      l += parseInt(elem.css('padding-left'), 10);
9134                      t += parseInt(elem.css('padding-top'), 10);
9135                      var h = elem.innerHeight() - 2 * parseInt(elem.css('padding-top'), 10);
9136                      var w = elem.innerWidth() - 2 * parseInt(elem.css('padding-left'), 10);
9137  
9138                      var swatch = elem.children('div.jqplot-table-legend-swatch');
9139                      newContext.fillStyle = swatch.css('background-color');
9140                      newContext.fillRect(l, t, w, h);
9141                  });
9142  
9143                  // now add text
9144  
9145                  $(el).find('td.jqplot-table-legend-label').each(function(){
9146                      var elem = $(this);
9147                      var l = left + elem.position().left;
9148                      var t = top + elem.position().top + parseInt(elem.css('padding-top'), 10);
9149                      newContext.font = elem.jqplotGetComputedFontStyle();
9150                      newContext.fillStyle = elem.css('color');
9151                      writeWrappedText(elem, newContext, elem.text(), l, t, w);
9152                  });
9153  
9154                  var elem = null;
9155              }
9156  
9157              else if (tagname == 'canvas') {
9158                  newContext.drawImage(el, left, top);
9159              }
9160          }
9161          $(this).children().each(function() {
9162              _jqpToImage(this, x_offset, y_offset);
9163          });
9164          return newCanvas;
9165      };
9166  
9167      // return the raw image data string.
9168      // Should work on canvas supporting browsers.
9169      $.fn.jqplotToImageStr = function(options) {
9170          var imgCanvas = $(this).jqplotToImageCanvas(options);
9171          if (imgCanvas) {
9172              return imgCanvas.toDataURL("image/png");
9173          }
9174          else {
9175              return null;
9176          }
9177      };
9178  
9179      // return a DOM <img> element and return it.
9180      // Should work on canvas supporting browsers.
9181      $.fn.jqplotToImageElem = function(options) {
9182          var elem = document.createElement("img");
9183          var str = $(this).jqplotToImageStr(options);
9184          elem.src = str;
9185          return elem;
9186      };
9187  
9188      // return a string for an <img> element and return it.
9189      // Should work on canvas supporting browsers.
9190      $.fn.jqplotToImageElemStr = function(options) {
9191          var str = '<img src='+$(this).jqplotToImageStr(options)+' />';
9192          return str;
9193      };
9194  
9195      // Not gauranteed to work, even on canvas supporting browsers due to 
9196      // limitations with location.href and browser support.
9197      $.fn.jqplotSaveImage = function() {
9198          var imgData = $(this).jqplotToImageStr({});
9199          if (imgData) {
9200              window.location.href = imgData.replace("image/png", "image/octet-stream");
9201          }
9202  
9203      };
9204  
9205      // Not gauranteed to work, even on canvas supporting browsers due to
9206      // limitations with window.open and arbitrary data.
9207      $.fn.jqplotViewImage = function() {
9208          var imgStr = $(this).jqplotToImageElemStr({});
9209          var imgData = $(this).jqplotToImageStr({});
9210          if (imgStr) {
9211              var w = window.open('');
9212              w.document.open("image/png");
9213              w.document.write(imgStr);
9214              w.document.close();
9215              w = null;
9216          }
9217      };
9218      
9219  
9220  
9221  
9222      /** 
9223       * @description
9224       * <p>Object with extended date parsing and formatting capabilities.
9225       * This library borrows many concepts and ideas from the Date Instance 
9226       * Methods by Ken Snyder along with some parts of Ken's actual code.</p>
9227       *
9228       * <p>jsDate takes a different approach by not extending the built-in 
9229       * Date Object, improving date parsing, allowing for multiple formatting 
9230       * syntaxes and multiple and more easily expandable localization.</p>
9231       * 
9232       * @author Chris Leonello
9233       * @date #date#
9234       * @version #VERSION#
9235       * @copyright (c) 2010 Chris Leonello
9236       * jsDate is currently available for use in all personal or commercial projects 
9237       * under both the MIT and GPL version 2.0 licenses. This means that you can 
9238       * choose the license that best suits your project and use it accordingly.
9239       * 
9240       * <p>Ken's origianl Date Instance Methods and copyright notice:</p>
9241       * <pre>
9242       * Ken Snyder (ken d snyder at gmail dot com)
9243       * 2008-09-10
9244       * version 2.0.2 (http://kendsnyder.com/sandbox/date/)     
9245       * Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/)
9246       * </pre>
9247       * 
9248       * @class
9249       * @name jsDate
9250       * @param  {String | Number | Array | Date&nbsp;Object | Options&nbsp;Object} arguments Optional arguments, either a parsable date/time string,
9251       * a JavaScript timestamp, an array of numbers of form [year, month, day, hours, minutes, seconds, milliseconds],
9252       * a Date object, or an options object of form {syntax: "perl", date:some Date} where all options are optional.
9253       */
9254       
9255      var jsDate = function () {
9256      
9257          this.syntax = jsDate.config.syntax;
9258          this._type = "jsDate";
9259          this.proxy = new Date();
9260          this.options = {};
9261          this.locale = jsDate.regional.getLocale();
9262          this.formatString = '';
9263          this.defaultCentury = jsDate.config.defaultCentury;
9264  
9265          switch ( arguments.length ) {
9266              case 0:
9267                  break;
9268              case 1:
9269                  // other objects either won't have a _type property or,
9270                  // if they do, it shouldn't be set to "jsDate", so
9271                  // assume it is an options argument.
9272                  if (get_type(arguments[0]) == "[object Object]" && arguments[0]._type != "jsDate") {
9273                      var opts = this.options = arguments[0];
9274                      this.syntax = opts.syntax || this.syntax;
9275                      this.defaultCentury = opts.defaultCentury || this.defaultCentury;
9276                      this.proxy = jsDate.createDate(opts.date);
9277                  }
9278                  else {
9279                      this.proxy = jsDate.createDate(arguments[0]);
9280                  }
9281                  break;
9282              default:
9283                  var a = [];
9284                  for ( var i=0; i<arguments.length; i++ ) {
9285                      a.push(arguments[i]);
9286                  }
9287                  // this should be the current date/time?
9288                  this.proxy = new Date();
9289                  this.proxy.setFullYear.apply( this.proxy, a.slice(0,3) );
9290                  if ( a.slice(3).length ) {
9291                      this.proxy.setHours.apply( this.proxy, a.slice(3) );
9292                  }
9293                  break;
9294          }
9295      };
9296      
9297      /**
9298       * @namespace Configuration options that will be used as defaults for all instances on the page.
9299       * @property {String} defaultLocale The default locale to use [en].
9300       * @property {String} syntax The default syntax to use [perl].
9301       * @property {Number} defaultCentury The default centry for 2 digit dates.
9302       */
9303      jsDate.config = {
9304          defaultLocale: 'en',
9305          syntax: 'perl',
9306          defaultCentury: 1900
9307      };
9308          
9309      /**
9310       * Add an arbitrary amount to the currently stored date
9311       * 
9312       * @param {Number} number      
9313       * @param {String} unit
9314       * @returns {jsDate}       
9315       */
9316       
9317      jsDate.prototype.add = function(number, unit) {
9318          var factor = multipliers[unit] || multipliers.day;
9319          if (typeof factor == 'number') {
9320              this.proxy.setTime(this.proxy.getTime() + (factor * number));
9321          } else {
9322              factor.add(this, number);
9323          }
9324          return this;
9325      };
9326          
9327      /**
9328       * Create a new jqplot.date object with the same date
9329       * 
9330       * @returns {jsDate}
9331       */  
9332       
9333      jsDate.prototype.clone = function() {
9334              return new jsDate(this.proxy.getTime());
9335      };
9336  
9337      /**
9338       * Get the UTC TimeZone Offset of this date in milliseconds.
9339       *
9340       * @returns {Number}
9341       */
9342  
9343      jsDate.prototype.getUtcOffset = function() {
9344          return this.proxy.getTimezoneOffset() * 60000;
9345      };
9346  
9347      /**
9348       * Find the difference between this jsDate and another date.
9349       * 
9350       * @param {String| Number| Array| jsDate&nbsp;Object| Date&nbsp;Object} dateObj
9351       * @param {String} unit
9352       * @param {Boolean} allowDecimal
9353       * @returns {Number} Number of units difference between dates.
9354       */
9355       
9356      jsDate.prototype.diff = function(dateObj, unit, allowDecimal) {
9357          // ensure we have a Date object
9358          dateObj = new jsDate(dateObj);
9359          if (dateObj === null) {
9360              return null;
9361          }
9362          // get the multiplying factor integer or factor function
9363          var factor = multipliers[unit] || multipliers.day;
9364          if (typeof factor == 'number') {
9365              // multiply
9366              var unitDiff = (this.proxy.getTime() - dateObj.proxy.getTime()) / factor;
9367          } else {
9368              // run function
9369              var unitDiff = factor.diff(this.proxy, dateObj.proxy);
9370          }
9371          // if decimals are not allowed, round toward zero
9372          return (allowDecimal ? unitDiff : Math[unitDiff > 0 ? 'floor' : 'ceil'](unitDiff));          
9373      };
9374      
9375      /**
9376       * Get the abbreviated name of the current week day
9377       * 
9378       * @returns {String}
9379       */   
9380       
9381      jsDate.prototype.getAbbrDayName = function() {
9382          return jsDate.regional[this.locale]["dayNamesShort"][this.proxy.getDay()];
9383      };
9384      
9385      /**
9386       * Get the abbreviated name of the current month
9387       * 
9388       * @returns {String}
9389       */
9390       
9391      jsDate.prototype.getAbbrMonthName = function() {
9392          return jsDate.regional[this.locale]["monthNamesShort"][this.proxy.getMonth()];
9393      };
9394      
9395      /**
9396       * Get UPPER CASE AM or PM for the current time
9397       * 
9398       * @returns {String}
9399       */
9400       
9401      jsDate.prototype.getAMPM = function() {
9402          return this.proxy.getHours() >= 12 ? 'PM' : 'AM';
9403      };
9404      
9405      /**
9406       * Get lower case am or pm for the current time
9407       * 
9408       * @returns {String}
9409       */
9410       
9411      jsDate.prototype.getAmPm = function() {
9412          return this.proxy.getHours() >= 12 ? 'pm' : 'am';
9413      };
9414      
9415      /**
9416       * Get the century (19 for 20th Century)
9417       *
9418       * @returns {Integer} Century (19 for 20th century).
9419       */
9420      jsDate.prototype.getCentury = function() { 
9421          return parseInt(this.proxy.getFullYear()/100, 10);
9422      };
9423      
9424      /**
9425       * Implements Date functionality
9426       */
9427      jsDate.prototype.getDate = function() {
9428          return this.proxy.getDate();
9429      };
9430      
9431      /**
9432       * Implements Date functionality
9433       */
9434      jsDate.prototype.getDay = function() {
9435          return this.proxy.getDay();
9436      };
9437      
9438      /**
9439       * Get the Day of week 1 (Monday) thru 7 (Sunday)
9440       * 
9441       * @returns {Integer} Day of week 1 (Monday) thru 7 (Sunday)
9442       */
9443      jsDate.prototype.getDayOfWeek = function() { 
9444          var dow = this.proxy.getDay(); 
9445          return dow===0?7:dow; 
9446      };
9447      
9448      /**
9449       * Get the day of the year
9450       * 
9451       * @returns {Integer} 1 - 366, day of the year
9452       */
9453      jsDate.prototype.getDayOfYear = function() {
9454          var d = this.proxy;
9455          var ms = d - new Date('' + d.getFullYear() + '/1/1 GMT');
9456          ms += d.getTimezoneOffset()*60000;
9457          d = null;
9458          return parseInt(ms/60000/60/24, 10)+1;
9459      };
9460      
9461      /**
9462       * Get the name of the current week day
9463       * 
9464       * @returns {String}
9465       */  
9466       
9467      jsDate.prototype.getDayName = function() {
9468          return jsDate.regional[this.locale]["dayNames"][this.proxy.getDay()];
9469      };
9470      
9471      /**
9472       * Get the week number of the given year, starting with the first Sunday as the first week
9473       * @returns {Integer} Week number (13 for the 13th full week of the year).
9474       */
9475      jsDate.prototype.getFullWeekOfYear = function() {
9476          var d = this.proxy;
9477          var doy = this.getDayOfYear();
9478          var rdow = 6-d.getDay();
9479          var woy = parseInt((doy+rdow)/7, 10);
9480          return woy;
9481      };
9482      
9483      /**
9484       * Implements Date functionality
9485       */
9486      jsDate.prototype.getFullYear = function() {
9487          return this.proxy.getFullYear();
9488      };
9489      
9490      /**
9491       * Get the GMT offset in hours and minutes (e.g. +06:30)
9492       * 
9493       * @returns {String}
9494       */
9495       
9496      jsDate.prototype.getGmtOffset = function() {
9497          // divide the minutes offset by 60
9498          var hours = this.proxy.getTimezoneOffset() / 60;
9499          // decide if we are ahead of or behind GMT
9500          var prefix = hours < 0 ? '+' : '-';
9501          // remove the negative sign if any
9502          hours = Math.abs(hours);
9503          // add the +/- to the padded number of hours to : to the padded minutes
9504          return prefix + addZeros(Math.floor(hours), 2) + ':' + addZeros((hours % 1) * 60, 2);
9505      };
9506      
9507      /**
9508       * Implements Date functionality
9509       */
9510      jsDate.prototype.getHours = function() {
9511          return this.proxy.getHours();
9512      };
9513      
9514      /**
9515       * Get the current hour on a 12-hour scheme
9516       * 
9517       * @returns {Integer}
9518       */
9519       
9520      jsDate.prototype.getHours12  = function() {
9521          var hours = this.proxy.getHours();
9522          return hours > 12 ? hours - 12 : (hours == 0 ? 12 : hours);
9523      };
9524      
9525      
9526      jsDate.prototype.getIsoWeek = function() {
9527          var d = this.proxy;
9528          var woy = d.getWeekOfYear();
9529          var dow1_1 = (new Date('' + d.getFullYear() + '/1/1')).getDay();
9530          // First week is 01 and not 00 as in the case of %U and %W,
9531          // so we add 1 to the final result except if day 1 of the year
9532          // is a Monday (then %W returns 01).
9533          // We also need to subtract 1 if the day 1 of the year is 
9534          // Friday-Sunday, so the resulting equation becomes:
9535          var idow = woy + (dow1_1 > 4 || dow1_1 <= 1 ? 0 : 1);
9536          if(idow == 53 && (new Date('' + d.getFullYear() + '/12/31')).getDay() < 4)
9537          {
9538              idow = 1;
9539          }
9540          else if(idow === 0)
9541          {
9542              d = new jsDate(new Date('' + (d.getFullYear()-1) + '/12/31'));
9543              idow = d.getIsoWeek();
9544          }
9545          d = null;
9546          return idow;
9547      };
9548      
9549      /**
9550       * Implements Date functionality
9551       */
9552      jsDate.prototype.getMilliseconds = function() {
9553          return this.proxy.getMilliseconds();
9554      };
9555      
9556      /**
9557       * Implements Date functionality
9558       */
9559      jsDate.prototype.getMinutes = function() {
9560          return this.proxy.getMinutes();
9561      };
9562      
9563      /**
9564       * Implements Date functionality
9565       */
9566      jsDate.prototype.getMonth = function() {
9567          return this.proxy.getMonth();
9568      };
9569      
9570      /**
9571       * Get the name of the current month
9572       * 
9573       * @returns {String}
9574       */
9575       
9576      jsDate.prototype.getMonthName = function() {
9577          return jsDate.regional[this.locale]["monthNames"][this.proxy.getMonth()];
9578      };
9579      
9580      /**
9581       * Get the number of the current month, 1-12
9582       * 
9583       * @returns {Integer}
9584       */
9585       
9586      jsDate.prototype.getMonthNumber = function() {
9587          return this.proxy.getMonth() + 1;
9588      };
9589      
9590      /**
9591       * Implements Date functionality
9592       */
9593      jsDate.prototype.getSeconds = function() {
9594          return this.proxy.getSeconds();
9595      };
9596      
9597      /**
9598       * Return a proper two-digit year integer
9599       * 
9600       * @returns {Integer}
9601       */
9602       
9603      jsDate.prototype.getShortYear = function() {
9604          return this.proxy.getYear() % 100;
9605      };
9606      
9607      /**
9608       * Implements Date functionality
9609       */
9610      jsDate.prototype.getTime = function() {
9611          return this.proxy.getTime();
9612      };
9613      
9614      /**
9615       * Get the timezone abbreviation
9616       *
9617       * @returns {String} Abbreviation for the timezone
9618       */
9619      jsDate.prototype.getTimezoneAbbr = function() {
9620          return this.proxy.toString().replace(/^.*\(([^)]+)\)$/, '$1'); 
9621      };
9622      
9623      /**
9624       * Get the browser-reported name for the current timezone (e.g. MDT, Mountain Daylight Time)
9625       * 
9626       * @returns {String}
9627       */
9628      jsDate.prototype.getTimezoneName = function() {
9629          var match = /(?:\((.+)\)$| ([A-Z]{3}) )/.exec(this.toString());
9630          return match[1] || match[2] || 'GMT' + this.getGmtOffset();
9631      }; 
9632      
9633      /**
9634       * Implements Date functionality
9635       */
9636      jsDate.prototype.getTimezoneOffset = function() {
9637          return this.proxy.getTimezoneOffset();
9638      };
9639      
9640      
9641      /**
9642       * Get the week number of the given year, starting with the first Monday as the first week
9643       * @returns {Integer} Week number (13 for the 13th week of the year).
9644       */
9645      jsDate.prototype.getWeekOfYear = function() {
9646          var doy = this.getDayOfYear();
9647          var rdow = 7 - this.getDayOfWeek();
9648          var woy = parseInt((doy+rdow)/7, 10);
9649          return woy;
9650      };
9651      
9652      /**
9653       * Get the current date as a Unix timestamp
9654       * 
9655       * @returns {Integer}
9656       */
9657       
9658      jsDate.prototype.getUnix = function() {
9659          return Math.round(this.proxy.getTime() / 1000, 0);
9660      }; 
9661      
9662      /**
9663       * Implements Date functionality
9664       */
9665      jsDate.prototype.getYear = function() {
9666          return this.proxy.getYear();
9667      };
9668      
9669      /**
9670       * Return a date one day ahead (or any other unit)
9671       * 
9672       * @param {String} unit Optional, year | month | day | week | hour | minute | second | millisecond
9673       * @returns {jsDate}
9674       */
9675       
9676      jsDate.prototype.next = function(unit) {
9677          unit = unit || 'day';
9678          return this.clone().add(1, unit);
9679      };
9680      
9681      /**
9682       * Set the jsDate instance to a new date.
9683       *
9684       * @param  {String | Number | Array | Date Object | jsDate Object | Options Object} arguments Optional arguments, 
9685       * either a parsable date/time string,
9686       * a JavaScript timestamp, an array of numbers of form [year, month, day, hours, minutes, seconds, milliseconds],
9687       * a Date object, jsDate Object or an options object of form {syntax: "perl", date:some Date} where all options are optional.
9688       */
9689      jsDate.prototype.set = function() {
9690          switch ( arguments.length ) {
9691              case 0:
9692                  this.proxy = new Date();
9693                  break;
9694              case 1:
9695                  // other objects either won't have a _type property or,
9696                  // if they do, it shouldn't be set to "jsDate", so
9697                  // assume it is an options argument.
9698                  if (get_type(arguments[0]) == "[object Object]" && arguments[0]._type != "jsDate") {
9699                      var opts = this.options = arguments[0];
9700                      this.syntax = opts.syntax || this.syntax;
9701                      this.defaultCentury = opts.defaultCentury || this.defaultCentury;
9702                      this.proxy = jsDate.createDate(opts.date);
9703                  }
9704                  else {
9705                      this.proxy = jsDate.createDate(arguments[0]);
9706                  }
9707                  break;
9708              default:
9709                  var a = [];
9710                  for ( var i=0; i<arguments.length; i++ ) {
9711                      a.push(arguments[i]);
9712                  }
9713                  // this should be the current date/time
9714                  this.proxy = new Date();
9715                  this.proxy.setFullYear.apply( this.proxy, a.slice(0,3) );
9716                  if ( a.slice(3).length ) {
9717                      this.proxy.setHours.apply( this.proxy, a.slice(3) );
9718                  }
9719                  break;
9720          }
9721          return this;
9722      };
9723      
9724      /**
9725       * Sets the day of the month for a specified date according to local time.
9726       * @param {Integer} dayValue An integer from 1 to 31, representing the day of the month. 
9727       */
9728      jsDate.prototype.setDate = function(n) {
9729          this.proxy.setDate(n);
9730          return this;
9731      };
9732      
9733      /**
9734       * Sets the full year for a specified date according to local time.
9735       * @param {Integer} yearValue The numeric value of the year, for example, 1995.  
9736       * @param {Integer} monthValue Optional, between 0 and 11 representing the months January through December.  
9737       * @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. 
9738       */
9739      jsDate.prototype.setFullYear = function() {
9740          this.proxy.setFullYear.apply(this.proxy, arguments);
9741          return this;
9742      };
9743      
9744      /**
9745       * Sets the hours for a specified date according to local time.
9746       * 
9747       * @param {Integer} hoursValue An integer between 0 and 23, representing the hour.  
9748       * @param {Integer} minutesValue Optional, An integer between 0 and 59, representing the minutes.  
9749       * @param {Integer} secondsValue Optional, An integer between 0 and 59, representing the seconds. 
9750       * If you specify the secondsValue parameter, you must also specify the minutesValue.  
9751       * @param {Integer} msValue Optional, A number between 0 and 999, representing the milliseconds. 
9752       * If you specify the msValue parameter, you must also specify the minutesValue and secondsValue. 
9753       */
9754      jsDate.prototype.setHours = function() {
9755          this.proxy.setHours.apply(this.proxy, arguments);
9756          return this;
9757      };
9758      
9759      /**
9760       * Implements Date functionality
9761       */ 
9762      jsDate.prototype.setMilliseconds = function(n) {
9763          this.proxy.setMilliseconds(n);
9764          return this;
9765      };
9766      
9767      /**
9768       * Implements Date functionality
9769       */ 
9770      jsDate.prototype.setMinutes = function() {
9771          this.proxy.setMinutes.apply(this.proxy, arguments);
9772          return this;
9773      };
9774      
9775      /**
9776       * Implements Date functionality
9777       */ 
9778      jsDate.prototype.setMonth = function() {
9779          this.proxy.setMonth.apply(this.proxy, arguments);
9780          return this;
9781      };
9782      
9783      /**
9784       * Implements Date functionality
9785       */ 
9786      jsDate.prototype.setSeconds = function() {
9787          this.proxy.setSeconds.apply(this.proxy, arguments);
9788          return this;
9789      };
9790      
9791      /**
9792       * Implements Date functionality
9793       */ 
9794      jsDate.prototype.setTime = function(n) {
9795          this.proxy.setTime(n);
9796          return this;
9797      };
9798      
9799      /**
9800       * Implements Date functionality
9801       */ 
9802      jsDate.prototype.setYear = function() {
9803          this.proxy.setYear.apply(this.proxy, arguments);
9804          return this;
9805      };
9806      
9807      /**
9808       * Provide a formatted string representation of this date.
9809       * 
9810       * @param {String} formatString A format string.  
9811       * See: {@link jsDate.formats}.
9812       * @returns {String} Date String.
9813       */
9814              
9815      jsDate.prototype.strftime = function(formatString) {
9816          formatString = formatString || this.formatString || jsDate.regional[this.locale]['formatString'];
9817          return jsDate.strftime(this, formatString, this.syntax);
9818      };
9819          
9820      /**
9821       * Return a String representation of this jsDate object.
9822       * @returns {String} Date string.
9823       */
9824      
9825      jsDate.prototype.toString = function() {
9826          return this.proxy.toString();
9827      };
9828          
9829      /**
9830       * Convert the current date to an 8-digit integer (%Y%m%d)
9831       * 
9832       * @returns {Integer}
9833       */
9834       
9835      jsDate.prototype.toYmdInt = function() {
9836          return (this.proxy.getFullYear() * 10000) + (this.getMonthNumber() * 100) + this.proxy.getDate();
9837      };
9838      
9839      /**
9840       * @namespace Holds localizations for month/day names.
9841       * <p>jsDate attempts to detect locale when loaded and defaults to 'en'.
9842       * If a localization is detected which is not available, jsDate defaults to 'en'.
9843       * Additional localizations can be added after jsDate loads.  After adding a localization,
9844       * call the jsDate.regional.getLocale() method.  Currently, en, fr and de are defined.</p>
9845       * 
9846       * <p>Localizations must be an object and have the following properties defined:  monthNames, monthNamesShort, dayNames, dayNamesShort and Localizations are added like:</p>
9847       * <pre class="code">
9848       * jsDate.regional['en'] = {
9849       * monthNames      : 'January February March April May June July August September October November December'.split(' '),
9850       * monthNamesShort : 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' '),
9851       * dayNames        : 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' '),
9852       * dayNamesShort   : 'Sun Mon Tue Wed Thu Fri Sat'.split(' ')
9853       * };
9854       * </pre>
9855       * <p>After adding localizations, call <code>jsDate.regional.getLocale();</code> to update the locale setting with the
9856       * new localizations.</p>
9857       */
9858       
9859      jsDate.regional = {
9860          'en': {
9861              monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'],
9862              monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun','Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
9863              dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
9864              dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
9865              formatString: '%Y-%m-%d %H:%M:%S'
9866          },
9867          
9868          'fr': {
9869              monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre'],
9870              monthNamesShort: ['Jan','Fév','Mar','Avr','Mai','Jun','Jul','Aoû','Sep','Oct','Nov','Déc'],
9871              dayNames: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'],
9872              dayNamesShort: ['Dim','Lun','Mar','Mer','Jeu','Ven','Sam'],
9873              formatString: '%Y-%m-%d %H:%M:%S'
9874          },
9875          
9876          'de': {
9877              monthNames: ['Januar','Februar','März','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember'],
9878              monthNamesShort: ['Jan','Feb','Mär','Apr','Mai','Jun','Jul','Aug','Sep','Okt','Nov','Dez'],
9879              dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'],
9880              dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'],
9881              formatString: '%Y-%m-%d %H:%M:%S'
9882          },
9883          
9884          'es': {
9885              monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio', 'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'],
9886              monthNamesShort: ['Ene','Feb','Mar','Abr','May','Jun', 'Jul','Ago','Sep','Oct','Nov','Dic'],
9887              dayNames: ['Domingo','Lunes','Martes','Mi&eacute;rcoles','Jueves','Viernes','S&aacute;bado'],
9888              dayNamesShort: ['Dom','Lun','Mar','Mi&eacute;','Juv','Vie','S&aacute;b'],
9889              formatString: '%Y-%m-%d %H:%M:%S'
9890          },
9891          
9892          'ru': {
9893              monthNames: ['Январь','Февраль','Март','Апрель','Май','Июнь','Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'],
9894              monthNamesShort: ['Янв','Фев','Мар','Апр','Май','Июн','Июл','Авг','Сен','Окт','Ноя','Дек'],
9895              dayNames: ['воскресенье','понедельник','вторник','среда','четверг','пятница','суббота'],
9896              dayNamesShort: ['вск','пнд','втр','срд','чтв','птн','сбт'],
9897              formatString: '%Y-%m-%d %H:%M:%S'
9898          },
9899          
9900          'ar': {
9901              monthNames: ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'آذار', 'حزيران','تموز', 'آب', 'أيلول',   'تشرين الأول', 'تشرين الثاني', 'كانون الأول'],
9902              monthNamesShort: ['1','2','3','4','5','6','7','8','9','10','11','12'],
9903              dayNames: ['السبت', 'الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة'],
9904              dayNamesShort: ['سبت', 'أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة'],
9905              formatString: '%Y-%m-%d %H:%M:%S'
9906          },
9907          
9908          'pt': {
9909              monthNames: ['Janeiro','Fevereiro','Mar&ccedil;o','Abril','Maio','Junho','Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'],
9910              monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'],
9911              dayNames: ['Domingo','Segunda-feira','Ter&ccedil;a-feira','Quarta-feira','Quinta-feira','Sexta-feira','S&aacute;bado'],
9912              dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','S&aacute;b'],
9913              formatString: '%Y-%m-%d %H:%M:%S'   
9914          },
9915          
9916          'pt-BR': {
9917              monthNames: ['Janeiro','Fevereiro','Mar&ccedil;o','Abril','Maio','Junho', 'Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'],
9918              monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'],
9919              dayNames: ['Domingo','Segunda-feira','Ter&ccedil;a-feira','Quarta-feira','Quinta-feira','Sexta-feira','S&aacute;bado'],
9920              dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','S&aacute;b'],
9921              formatString: '%Y-%m-%d %H:%M:%S'
9922          }
9923          
9924      
9925      };
9926      
9927      // Set english variants to 'en'
9928      jsDate.regional['en-US'] = jsDate.regional['en-GB'] = jsDate.regional['en'];
9929      
9930      /**
9931       * Try to determine the users locale based on the lang attribute of the html page.  Defaults to 'en'
9932       * if it cannot figure out a locale of if the locale does not have a localization defined.
9933       * @returns {String} locale
9934       */
9935       
9936      jsDate.regional.getLocale = function () {
9937          var l = jsDate.config.defaultLocale;
9938          
9939          if ( document && document.getElementsByTagName('html') && document.getElementsByTagName('html')[0].lang ) {
9940              l = document.getElementsByTagName('html')[0].lang;
9941              if (!jsDate.regional.hasOwnProperty(l)) {
9942                  l = jsDate.config.defaultLocale;
9943              }
9944          }
9945          
9946          return l;
9947      };
9948      
9949      // ms in day
9950      var day = 24 * 60 * 60 * 1000;
9951      
9952      // padd a number with zeros
9953      var addZeros = function(num, digits) {
9954          num = String(num);
9955          var i = digits - num.length;
9956          var s = String(Math.pow(10, i)).slice(1);
9957          return s.concat(num);
9958      };
9959  
9960      // representations used for calculating differences between dates.
9961      // This borrows heavily from Ken Snyder's work.
9962      var multipliers = {
9963          millisecond: 1,
9964          second: 1000,
9965          minute: 60 * 1000,
9966          hour: 60 * 60 * 1000,
9967          day: day,
9968          week: 7 * day,
9969          month: {
9970              // add a number of months
9971              add: function(d, number) {
9972                  // add any years needed (increments of 12)
9973                  multipliers.year.add(d, Math[number > 0 ? 'floor' : 'ceil'](number / 12));
9974                  // ensure that we properly wrap betwen December and January
9975                  // 11 % 12 = 11
9976                  // 12 % 12 = 0
9977                  var prevMonth = d.getMonth() + (number % 12);
9978                  if (prevMonth == 12) {
9979                      prevMonth = 0;
9980                      d.setYear(d.getFullYear() + 1);
9981                  } else if (prevMonth == -1) {
9982                      prevMonth = 11;
9983                      d.setYear(d.getFullYear() - 1);
9984                  }
9985                  d.setMonth(prevMonth);
9986              },
9987              // get the number of months between two Date objects (decimal to the nearest day)
9988              diff: function(d1, d2) {
9989                  // get the number of years
9990                  var diffYears = d1.getFullYear() - d2.getFullYear();
9991                  // get the number of remaining months
9992                  var diffMonths = d1.getMonth() - d2.getMonth() + (diffYears * 12);
9993                  // get the number of remaining days
9994                  var diffDays = d1.getDate() - d2.getDate();
9995                  // return the month difference with the days difference as a decimal
9996                  return diffMonths + (diffDays / 30);
9997              }
9998          },
9999          year: {
10000              // add a number of years
10001              add: function(d, number) {
10002                  d.setYear(d.getFullYear() + Math[number > 0 ? 'floor' : 'ceil'](number));
10003              },
10004              // get the number of years between two Date objects (decimal to the nearest day)
10005              diff: function(d1, d2) {
10006                  return multipliers.month.diff(d1, d2) / 12;
10007              }
10008          }        
10009      };
10010      //
10011      // Alias each multiplier with an 's' to allow 'year' and 'years' for example.
10012      // This comes from Ken Snyders work.
10013      //
10014      for (var unit in multipliers) {
10015          if (unit.substring(unit.length - 1) != 's') { // IE will iterate newly added properties :|
10016              multipliers[unit + 's'] = multipliers[unit];
10017          }
10018      }
10019      
10020      //
10021      // take a jsDate instance and a format code and return the formatted value.
10022      // This is a somewhat modified version of Ken Snyder's method.
10023      //
10024      var format = function(d, code, syntax) {
10025          // if shorcut codes are used, recursively expand those.
10026          if (jsDate.formats[syntax]["shortcuts"][code]) {
10027              return jsDate.strftime(d, jsDate.formats[syntax]["shortcuts"][code], syntax);
10028          } else {
10029              // get the format code function and addZeros() argument
10030              var getter = (jsDate.formats[syntax]["codes"][code] || '').split('.');
10031              var nbr = d['get' + getter[0]] ? d['get' + getter[0]]() : '';
10032              if (getter[1]) {
10033                  nbr = addZeros(nbr, getter[1]);
10034              }
10035              return nbr;
10036          }       
10037      };
10038      
10039      /**
10040       * @static
10041       * Static function for convert a date to a string according to a given format.  Also acts as namespace for strftime format codes.
10042       * <p>strftime formatting can be accomplished without creating a jsDate object by calling jsDate.strftime():</p>
10043       * <pre class="code">
10044       * var formattedDate = jsDate.strftime('Feb 8, 2006 8:48:32', '%Y-%m-%d %H:%M:%S');
10045       * </pre>
10046       * @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.
10047       * @param {String} formatString String with embedded date formatting codes.  
10048       * See: {@link jsDate.formats}. 
10049       * @param {String} syntax Optional syntax to use [default perl].
10050       * @param {String} locale Optional locale to use.
10051       * @returns {String} Formatted representation of the date.
10052      */
10053      //
10054      // Logic as implemented here is very similar to Ken Snyder's Date Instance Methods.
10055      //
10056      jsDate.strftime = function(d, formatString, syntax, locale) {
10057          var syn = 'perl';
10058          var loc = jsDate.regional.getLocale();
10059          
10060          // check if syntax and locale are available or reversed
10061          if (syntax && jsDate.formats.hasOwnProperty(syntax)) {
10062              syn = syntax;
10063          }
10064          else if (syntax && jsDate.regional.hasOwnProperty(syntax)) {
10065              loc = syntax;
10066          }
10067          
10068          if (locale && jsDate.formats.hasOwnProperty(locale)) {
10069              syn = locale;
10070          }
10071          else if (locale && jsDate.regional.hasOwnProperty(locale)) {
10072              loc = locale;
10073          }
10074          
10075          if (get_type(d) != "[object Object]" || d._type != "jsDate") {
10076              d = new jsDate(d);
10077              d.locale = loc;
10078          }
10079          if (!formatString) {
10080              formatString = d.formatString || jsDate.regional[loc]['formatString'];
10081          }
10082          // default the format string to year-month-day
10083          var source = formatString || '%Y-%m-%d', 
10084              result = '', 
10085              match;
10086          // replace each format code
10087          while (source.length > 0) {
10088              if (match = source.match(jsDate.formats[syn].codes.matcher)) {
10089                  result += source.slice(0, match.index);
10090                  result += (match[1] || '') + format(d, match[2], syn);
10091                  source = source.slice(match.index + match[0].length);
10092              } else {
10093                  result += source;
10094                  source = '';
10095              }
10096          }
10097          return result;
10098      };
10099      
10100      /**
10101       * @namespace
10102       * Namespace to hold format codes and format shortcuts.  "perl" and "php" format codes 
10103       * and shortcuts are defined by default.  Additional codes and shortcuts can be
10104       * added like:
10105       * 
10106       * <pre class="code">
10107       * jsDate.formats["perl"] = {
10108       *     "codes": {
10109       *         matcher: /someregex/,
10110       *         Y: "fullYear",  // name of "get" method without the "get",
10111       *         ...,            // more codes
10112       *     },
10113       *     "shortcuts": {
10114       *         F: '%Y-%m-%d',
10115       *         ...,            // more shortcuts
10116       *     }
10117       * };
10118       * </pre>
10119       * 
10120       * <p>Additionally, ISO and SQL shortcuts are defined and can be accesses via:
10121       * <code>jsDate.formats.ISO</code> and <code>jsDate.formats.SQL</code>
10122       */
10123      
10124      jsDate.formats = {
10125          ISO:'%Y-%m-%dT%H:%M:%S.%N%G',
10126          SQL:'%Y-%m-%d %H:%M:%S'
10127      };
10128      
10129      /**
10130       * Perl format codes and shortcuts for strftime.
10131       * 
10132       * A hash (object) of codes where each code must be an array where the first member is 
10133       * the name of a Date.prototype or jsDate.prototype function to call
10134       * and optionally a second member indicating the number to pass to addZeros()
10135       * 
10136       * <p>The following format codes are defined:</p>
10137       * 
10138       * <pre class="code">
10139       * Code    Result                    Description
10140       * == Years ==           
10141       * %Y      2008                      Four-digit year
10142       * %y      08                        Two-digit year
10143       * 
10144       * == Months ==          
10145       * %m      09                        Two-digit month
10146       * %#m     9                         One or two-digit month
10147       * %B      September                 Full month name
10148       * %b      Sep                       Abbreviated month name
10149       * 
10150       * == Days ==            
10151       * %d      05                        Two-digit day of month
10152       * %#d     5                         One or two-digit day of month
10153       * %e      5                         One or two-digit day of month
10154       * %A      Sunday                    Full name of the day of the week
10155       * %a      Sun                       Abbreviated name of the day of the week
10156       * %w      0                         Number of the day of the week (0 = Sunday, 6 = Saturday)
10157       * 
10158       * == Hours ==           
10159       * %H      23                        Hours in 24-hour format (two digits)
10160       * %#H     3                         Hours in 24-hour integer format (one or two digits)
10161       * %I      11                        Hours in 12-hour format (two digits)
10162       * %#I     3                         Hours in 12-hour integer format (one or two digits)
10163       * %p      PM                        AM or PM
10164       * 
10165       * == Minutes ==         
10166       * %M      09                        Minutes (two digits)
10167       * %#M     9                         Minutes (one or two digits)
10168       * 
10169       * == Seconds ==         
10170       * %S      02                        Seconds (two digits)
10171       * %#S     2                         Seconds (one or two digits)
10172       * %s      1206567625723             Unix timestamp (Seconds past 1970-01-01 00:00:00)
10173       * 
10174       * == Milliseconds ==    
10175       * %N      008                       Milliseconds (three digits)
10176       * %#N     8                         Milliseconds (one to three digits)
10177       * 
10178       * == Timezone ==        
10179       * %O      360                       difference in minutes between local time and GMT
10180       * %Z      Mountain Standard Time    Name of timezone as reported by browser
10181       * %G      06:00                     Hours and minutes between GMT
10182       * 
10183       * == Shortcuts ==       
10184       * %F      2008-03-26                %Y-%m-%d
10185       * %T      05:06:30                  %H:%M:%S
10186       * %X      05:06:30                  %H:%M:%S
10187       * %x      03/26/08                  %m/%d/%y
10188       * %D      03/26/08                  %m/%d/%y
10189       * %#c     Wed Mar 26 15:31:00 2008  %a %b %e %H:%M:%S %Y
10190       * %v      3-Sep-2008                %e-%b-%Y
10191       * %R      15:31                     %H:%M
10192       * %r      03:31:00 PM               %I:%M:%S %p
10193       * 
10194       * == Characters ==      
10195       * %n      \n                        Newline
10196       * %t      \t                        Tab
10197       * %%      %                         Percent Symbol
10198       * </pre>
10199       * 
10200       * <p>Formatting shortcuts that will be translated into their longer version.
10201       * Be sure that format shortcuts do not refer to themselves: this will cause an infinite loop.</p>
10202       * 
10203       * <p>Format codes and format shortcuts can be redefined after the jsDate
10204       * module is imported.</p>
10205       * 
10206       * <p>Note that if you redefine the whole hash (object), you must supply a "matcher"
10207       * regex for the parser.  The default matcher is:</p>
10208       * 
10209       * <code>/()%(#?(%|[a-z]))/i</code>
10210       * 
10211       * <p>which corresponds to the Perl syntax used by default.</p>
10212       * 
10213       * <p>By customizing the matcher and format codes, nearly any strftime functionality is possible.</p>
10214       */
10215       
10216      jsDate.formats.perl = {
10217          codes: {
10218              //
10219              // 2-part regex matcher for format codes
10220              //
10221              // first match must be the character before the code (to account for escaping)
10222              // second match must be the format code character(s)
10223              //
10224              matcher: /()%(#?(%|[a-z]))/i,
10225              // year
10226              Y: 'FullYear',
10227              y: 'ShortYear.2',
10228              // month
10229              m: 'MonthNumber.2',
10230              '#m': 'MonthNumber',
10231              B: 'MonthName',
10232              b: 'AbbrMonthName',
10233              // day
10234              d: 'Date.2',
10235              '#d': 'Date',
10236              e: 'Date',
10237              A: 'DayName',
10238              a: 'AbbrDayName',
10239              w: 'Day',
10240              // hours
10241              H: 'Hours.2',
10242              '#H': 'Hours',
10243              I: 'Hours12.2',
10244              '#I': 'Hours12',
10245              p: 'AMPM',
10246              // minutes
10247              M: 'Minutes.2',
10248              '#M': 'Minutes',
10249              // seconds
10250              S: 'Seconds.2',
10251              '#S': 'Seconds',
10252              s: 'Unix',
10253              // milliseconds
10254              N: 'Milliseconds.3',
10255              '#N': 'Milliseconds',
10256              // timezone
10257              O: 'TimezoneOffset',
10258              Z: 'TimezoneName',
10259              G: 'GmtOffset'  
10260          },
10261          
10262          shortcuts: {
10263              // date
10264              F: '%Y-%m-%d',
10265              // time
10266              T: '%H:%M:%S',
10267              X: '%H:%M:%S',
10268              // local format date
10269              x: '%m/%d/%y',
10270              D: '%m/%d/%y',
10271              // local format extended
10272              '#c': '%a %b %e %H:%M:%S %Y',
10273              // local format short
10274              v: '%e-%b-%Y',
10275              R: '%H:%M',
10276              r: '%I:%M:%S %p',
10277              // tab and newline
10278              t: '\t',
10279              n: '\n',
10280              '%': '%'
10281          }
10282      };
10283      
10284      /**
10285       * PHP format codes and shortcuts for strftime.
10286       * 
10287       * A hash (object) of codes where each code must be an array where the first member is 
10288       * the name of a Date.prototype or jsDate.prototype function to call
10289       * and optionally a second member indicating the number to pass to addZeros()
10290       * 
10291       * <p>The following format codes are defined:</p>
10292       * 
10293       * <pre class="code">
10294       * Code    Result                    Description
10295       * === Days ===        
10296       * %a      Sun through Sat           An abbreviated textual representation of the day
10297       * %A      Sunday - Saturday         A full textual representation of the day
10298       * %d      01 to 31                  Two-digit day of the month (with leading zeros)
10299       * %e      1 to 31                   Day of the month, with a space preceding single digits.
10300       * %j      001 to 366                Day of the year, 3 digits with leading zeros
10301       * %u      1 - 7 (Mon - Sun)         ISO-8601 numeric representation of the day of the week
10302       * %w      0 - 6 (Sun - Sat)         Numeric representation of the day of the week
10303       *                                  
10304       * === Week ===                     
10305       * %U      13                        Full Week number, starting with the first Sunday as the first week
10306       * %V      01 through 53             ISO-8601:1988 week number, starting with the first week of the year 
10307       *                                   with at least 4 weekdays, with Monday being the start of the week
10308       * %W      46                        A numeric representation of the week of the year, 
10309       *                                   starting with the first Monday as the first week
10310       * === Month ===                    
10311       * %b      Jan through Dec           Abbreviated month name, based on the locale
10312       * %B      January - December        Full month name, based on the locale
10313       * %h      Jan through Dec           Abbreviated month name, based on the locale (an alias of %b)
10314       * %m      01 - 12 (Jan - Dec)       Two digit representation of the month
10315       * 
10316       * === Year ===                     
10317       * %C      19                        Two digit century (year/100, truncated to an integer)
10318       * %y      09 for 2009               Two digit year
10319       * %Y      2038                      Four digit year
10320       * 
10321       * === Time ===                     
10322       * %H      00 through 23             Two digit representation of the hour in 24-hour format
10323       * %I      01 through 12             Two digit representation of the hour in 12-hour format
10324       * %l      1 through 12              Hour in 12-hour format, with a space preceeding single digits
10325       * %M      00 through 59             Two digit representation of the minute
10326       * %p      AM/PM                     UPPER-CASE 'AM' or 'PM' based on the given time
10327       * %P      am/pm                     lower-case 'am' or 'pm' based on the given time
10328       * %r      09:34:17 PM               Same as %I:%M:%S %p
10329       * %R      00:35                     Same as %H:%M
10330       * %S      00 through 59             Two digit representation of the second
10331       * %T      21:34:17                  Same as %H:%M:%S
10332       * %X      03:59:16                  Preferred time representation based on locale, without the date
10333       * %z      -0500 or EST              Either the time zone offset from UTC or the abbreviation
10334       * %Z      -0500 or EST              The time zone offset/abbreviation option NOT given by %z
10335       * 
10336       * === Time and Date ===            
10337       * %D      02/05/09                  Same as %m/%d/%y
10338       * %F      2009-02-05                Same as %Y-%m-%d (commonly used in database datestamps)
10339       * %s      305815200                 Unix Epoch Time timestamp (same as the time() function)
10340       * %x      02/05/09                  Preferred date representation, without the time
10341       * 
10342       * === Miscellaneous ===            
10343       * %n        ---                     A newline character (\n)
10344       * %t        ---                     A Tab character (\t)
10345       * %%        ---                     A literal percentage character (%)
10346       * </pre>
10347       */
10348   
10349      jsDate.formats.php = {
10350          codes: {
10351              //
10352              // 2-part regex matcher for format codes
10353              //
10354              // first match must be the character before the code (to account for escaping)
10355              // second match must be the format code character(s)
10356              //
10357              matcher: /()%((%|[a-z]))/i,
10358              // day
10359              a: 'AbbrDayName',
10360              A: 'DayName',
10361              d: 'Date.2',
10362              e: 'Date',
10363              j: 'DayOfYear.3',
10364              u: 'DayOfWeek',
10365              w: 'Day',
10366              // week
10367              U: 'FullWeekOfYear.2',
10368              V: 'IsoWeek.2',
10369              W: 'WeekOfYear.2',
10370              // month
10371              b: 'AbbrMonthName',
10372              B: 'MonthName',
10373              m: 'MonthNumber.2',
10374              h: 'AbbrMonthName',
10375              // year
10376              C: 'Century.2',
10377              y: 'ShortYear.2',
10378              Y: 'FullYear',
10379              // time
10380              H: 'Hours.2',
10381              I: 'Hours12.2',
10382              l: 'Hours12',
10383              p: 'AMPM',
10384              P: 'AmPm',
10385              M: 'Minutes.2',
10386              S: 'Seconds.2',
10387              s: 'Unix',
10388              O: 'TimezoneOffset',
10389              z: 'GmtOffset',
10390              Z: 'TimezoneAbbr'
10391          },
10392          
10393          shortcuts: {
10394              D: '%m/%d/%y',
10395              F: '%Y-%m-%d',
10396              T: '%H:%M:%S',
10397              X: '%H:%M:%S',
10398              x: '%m/%d/%y',
10399              R: '%H:%M',
10400              r: '%I:%M:%S %p',
10401              t: '\t',
10402              n: '\n',
10403              '%': '%'
10404          }
10405      };   
10406      //
10407      // Conceptually, the logic implemented here is similar to Ken Snyder's Date Instance Methods.
10408      // I use his idea of a set of parsers which can be regular expressions or functions,
10409      // iterating through those, and then seeing if Date.parse() will create a date.
10410      // The parser expressions and functions are a little different and some bugs have been
10411      // worked out.  Also, a lot of "pre-parsing" is done to fix implementation
10412      // variations of Date.parse() between browsers.
10413      //
10414      jsDate.createDate = function(date) {
10415          // if passing in multiple arguments, try Date constructor
10416          if (date == null) {
10417              return new Date();
10418          }
10419          // If the passed value is already a date object, return it
10420          if (date instanceof Date) {
10421              return date;
10422          }
10423          // if (typeof date == 'number') return new Date(date * 1000);
10424          // If the passed value is an integer, interpret it as a javascript timestamp
10425          if (typeof date == 'number') {
10426              return new Date(date);
10427          }
10428          
10429          // Before passing strings into Date.parse(), have to normalize them for certain conditions.
10430          // If strings are not formatted staccording to the EcmaScript spec, results from Date parse will be implementation dependent.  
10431          // 
10432          // For example: 
10433          //  * FF and Opera assume 2 digit dates are pre y2k, Chome assumes <50 is pre y2k, 50+ is 21st century.  
10434          //  * Chrome will correctly parse '1984-1-25' into localtime, FF and Opera will not parse.
10435          //  * Both FF, Chrome and Opera will parse '1984/1/25' into localtime.
10436          
10437          // remove leading and trailing spaces
10438          var parsable = String(date).replace(/^\s*(.+)\s*$/g, '$1');
10439          
10440          // replace dahses (-) with slashes (/) in dates like n[nnn]/n[n]/n[nnn]
10441          parsable = parsable.replace(/^([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,4})/, "$1/$2/$3");
10442          
10443          /////////
10444          // Need to check for '15-Dec-09' also.
10445          // FF will not parse, but Chrome will.
10446          // Chrome will set date to 2009 as well.
10447          /////////
10448          
10449          // first check for 'dd-mmm-yyyy' or 'dd/mmm/yyyy' like '15-Dec-2010'
10450          parsable = parsable.replace(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{4})/i, "$1 $2 $3");
10451          
10452          // Now check for 'dd-mmm-yy' or 'dd/mmm/yy' and normalize years to default century.
10453          var match = parsable.match(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{2})\D*/i);
10454          if (match && match.length > 3) {
10455              var m3 = parseFloat(match[3]);
10456              var ny = jsDate.config.defaultCentury + m3;
10457              ny = String(ny);
10458              
10459              // now replace 2 digit year with 4 digit year
10460              parsable = parsable.replace(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{2})\D*/i, match[1] +' '+ match[2] +' '+ ny);
10461              
10462          }
10463          
10464          // Check for '1/19/70 8:14PM'
10465          // where starts with mm/dd/yy or yy/mm/dd and have something after
10466          // Check if 1st postiion is greater than 31, assume it is year.
10467          // Assme all 2 digit years are 1900's.
10468          // Finally, change them into US style mm/dd/yyyy representations.
10469          match = parsable.match(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})[^0-9]/);
10470          
10471          function h1(parsable, match) {
10472              var m1 = parseFloat(match[1]);
10473              var m2 = parseFloat(match[2]);
10474              var m3 = parseFloat(match[3]);
10475              var cent = jsDate.config.defaultCentury;
10476              var ny, nd, nm, str;
10477              
10478              if (m1 > 31) { // first number is a year
10479                  nd = m3;
10480                  nm = m2;
10481                  ny = cent + m1;
10482              }
10483              
10484              else { // last number is the year
10485                  nd = m2;
10486                  nm = m1;
10487                  ny = cent + m3;
10488              }
10489              
10490              str = nm+'/'+nd+'/'+ny;
10491              
10492              // now replace 2 digit year with 4 digit year
10493              return  parsable.replace(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})/, str);
10494          
10495          }
10496          
10497          if (match && match.length > 3) {
10498              parsable = h1(parsable, match);
10499          }
10500          
10501          // Now check for '1/19/70' with nothing after and do as above
10502          var match = parsable.match(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})$/);
10503          
10504          if (match && match.length > 3) {
10505              parsable = h1(parsable, match);
10506          }
10507                  
10508          
10509          var i = 0;
10510          var length = jsDate.matchers.length;
10511          var pattern,
10512              ms,
10513              current = parsable,
10514              obj;
10515          while (i < length) {
10516              ms = Date.parse(current);
10517              if (!isNaN(ms)) {
10518                  return new Date(ms);
10519              }
10520              pattern = jsDate.matchers[i];
10521              if (typeof pattern == 'function') {
10522                  obj = pattern.call(jsDate, current);
10523                  if (obj instanceof Date) {
10524                      return obj;
10525                  }
10526              } else {
10527                  current = parsable.replace(pattern[0], pattern[1]);
10528              }
10529              i++;
10530          }
10531          return NaN;
10532      };
10533      
10534  
10535      /**
10536       * @static
10537       * Handy static utility function to return the number of days in a given month.
10538       * @param {Integer} year Year
10539       * @param {Integer} month Month (1-12)
10540       * @returns {Integer} Number of days in the month.
10541      */
10542      //
10543      // handy utility method Borrowed right from Ken Snyder's Date Instance Mehtods.
10544      // 
10545      jsDate.daysInMonth = function(year, month) {
10546          if (month == 2) {
10547              return new Date(year, 1, 29).getDate() == 29 ? 29 : 28;
10548          }
10549          return [undefined,31,undefined,31,30,31,30,31,31,30,31,30,31][month];
10550      };
10551  
10552  
10553      //
10554      // An Array of regular expressions or functions that will attempt to match the date string.
10555      // Functions are called with scope of a jsDate instance.
10556      //
10557      jsDate.matchers = [
10558          // convert dd.mmm.yyyy to mm/dd/yyyy (world date to US date).
10559          [/(3[01]|[0-2]\d)\s*\.\s*(1[0-2]|0\d)\s*\.\s*([1-9]\d{3})/, '$2/$1/$3'],
10560          // convert yyyy-mm-dd to mm/dd/yyyy (ISO date to US date).
10561          [/([1-9]\d{3})\s*-\s*(1[0-2]|0\d)\s*-\s*(3[01]|[0-2]\d)/, '$2/$3/$1'],
10562          // Handle 12 hour or 24 hour time with milliseconds am/pm and optional date part.
10563          function(str) { 
10564              var match = str.match(/^(?:(.+)\s+)?([012]?\d)(?:\s*\:\s*(\d\d))?(?:\s*\:\s*(\d\d(\.\d*)?))?\s*(am|pm)?\s*$/i);
10565              //                   opt. date      hour       opt. minute     opt. second       opt. msec   opt. am or pm
10566              if (match) {
10567                  if (match[1]) {
10568                      var d = this.createDate(match[1]);
10569                      if (isNaN(d)) {
10570                          return;
10571                      }
10572                  } else {
10573                      var d = new Date();
10574                      d.setMilliseconds(0);
10575                  }
10576                  var hour = parseFloat(match[2]);
10577                  if (match[6]) {
10578                      hour = match[6].toLowerCase() == 'am' ? (hour == 12 ? 0 : hour) : (hour == 12 ? 12 : hour + 12);
10579                  }
10580                  d.setHours(hour, parseInt(match[3] || 0, 10), parseInt(match[4] || 0, 10), ((parseFloat(match[5] || 0)) || 0)*1000);
10581                  return d;
10582              }
10583              else {
10584                  return str;
10585              }
10586          },
10587          // Handle ISO timestamp with time zone.
10588          function(str) {
10589              var match = str.match(/^(?:(.+))[T|\s+]([012]\d)(?:\:(\d\d))(?:\:(\d\d))(?:\.\d+)([\+\-]\d\d\:\d\d)$/i);
10590              if (match) {
10591                  if (match[1]) {
10592                      var d = this.createDate(match[1]);
10593                      if (isNaN(d)) {
10594                          return;
10595                      }
10596                  } else {
10597                      var d = new Date();
10598                      d.setMilliseconds(0);
10599                  }
10600                  var hour = parseFloat(match[2]);
10601                  d.setHours(hour, parseInt(match[3], 10), parseInt(match[4], 10), parseFloat(match[5])*1000);
10602                  return d;
10603              }
10604              else {
10605                      return str;
10606              }
10607          },
10608          // Try to match ambiguous strings like 12/8/22.
10609          // Use FF date assumption that 2 digit years are 20th century (i.e. 1900's).
10610          // This may be redundant with pre processing of date already performed.
10611          function(str) {
10612              var match = str.match(/^([0-3]?\d)\s*[-\/.\s]{1}\s*([a-zA-Z]{3,9})\s*[-\/.\s]{1}\s*([0-3]?\d)$/);
10613              if (match) {
10614                  var d = new Date();
10615                  var cent = jsDate.config.defaultCentury;
10616                  var m1 = parseFloat(match[1]);
10617                  var m3 = parseFloat(match[3]);
10618                  var ny, nd, nm;
10619                  if (m1 > 31) { // first number is a year
10620                      nd = m3;
10621                      ny = cent + m1;
10622                  }
10623                  
10624                  else { // last number is the year
10625                      nd = m1;
10626                      ny = cent + m3;
10627                  }
10628                  
10629                  var nm = inArray(match[2], jsDate.regional[jsDate.regional.getLocale()]["monthNamesShort"]);
10630                  
10631                  if (nm == -1) {
10632                      nm = inArray(match[2], jsDate.regional[jsDate.regional.getLocale()]["monthNames"]);
10633                  }
10634              
10635                  d.setFullYear(ny, nm, nd);
10636                  d.setHours(0,0,0,0);
10637                  return d;
10638              }
10639              
10640              else {
10641                  return str;
10642              }
10643          }      
10644      ];
10645  
10646      //
10647      // I think John Reisig published this method on his blog, ejohn.
10648      //
10649      function inArray( elem, array ) {
10650          if ( array.indexOf ) {
10651              return array.indexOf( elem );
10652          }
10653  
10654          for ( var i = 0, length = array.length; i < length; i++ ) {
10655              if ( array[ i ] === elem ) {
10656                  return i;
10657              }
10658          }
10659  
10660          return -1;
10661      }
10662      
10663      //
10664      // Thanks to Kangax, Christian Sciberras and Stack Overflow for this method.
10665      //
10666      function get_type(thing){
10667          if(thing===null) return "[object Null]"; // special case
10668          return Object.prototype.toString.call(thing);
10669      }
10670      
10671      $.jsDate = jsDate;
10672  
10673        
10674      /**
10675       * JavaScript printf/sprintf functions.
10676       * 
10677       * This code has been adapted from the publicly available sprintf methods
10678       * by Ash Searle. His original header follows:
10679       *
10680       *     This code is unrestricted: you are free to use it however you like.
10681       *     
10682       *     The functions should work as expected, performing left or right alignment,
10683       *     truncating strings, outputting numbers with a required precision etc.
10684       *
10685       *     For complex cases, these functions follow the Perl implementations of
10686       *     (s)printf, allowing arguments to be passed out-of-order, and to set the
10687       *     precision or length of the output based on arguments instead of fixed
10688       *     numbers.
10689       *
10690       *     See http://perldoc.perl.org/functions/sprintf.html for more information.
10691       *
10692       *     Implemented:
10693       *     - zero and space-padding
10694       *     - right and left-alignment,
10695       *     - base X prefix (binary, octal and hex)
10696       *     - positive number prefix
10697       *     - (minimum) width
10698       *     - precision / truncation / maximum width
10699       *     - out of order arguments
10700       *
10701       *     Not implemented (yet):
10702       *     - vector flag
10703       *     - size (bytes, words, long-words etc.)
10704       *     
10705       *     Will not implement:
10706       *     - %n or %p (no pass-by-reference in JavaScript)
10707       *
10708       *     @version 2007.04.27
10709       *     @author Ash Searle 
10710       * 
10711       * You can see the original work and comments on his blog:
10712       * http://hexmen.com/blog/2007/03/printf-sprintf/
10713       * http://hexmen.com/js/sprintf.js
10714       */
10715       
10716       /**
10717        * @Modifications 2009.05.26
10718        * @author Chris Leonello
10719        * 
10720        * Added %p %P specifier
10721        * Acts like %g or %G but will not add more significant digits to the output than present in the input.
10722        * Example:
10723        * Format: '%.3p', Input: 0.012, Output: 0.012
10724        * Format: '%.3g', Input: 0.012, Output: 0.0120
10725        * Format: '%.4p', Input: 12.0, Output: 12.0
10726        * Format: '%.4g', Input: 12.0, Output: 12.00
10727        * Format: '%.4p', Input: 4.321e-5, Output: 4.321e-5
10728        * Format: '%.4g', Input: 4.321e-5, Output: 4.3210e-5
10729        * 
10730        * Example:
10731        * >>> $.jqplot.sprintf('%.2f, %d', 23.3452, 43.23)
10732        * "23.35, 43"
10733        * >>> $.jqplot.sprintf("no value: %n, decimal with thousands separator: %'d", 23.3452, 433524)
10734        * "no value: , decimal with thousands separator: 433,524"
10735        */
10736      $.jqplot.sprintf = function() {
10737          function pad(str, len, chr, leftJustify) {
10738              var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr);
10739              return leftJustify ? str + padding : padding + str;
10740  
10741          }
10742  
10743          function thousand_separate(value) {
10744              var value_str = new String(value);
10745              for (var i=10; i>0; i--) {
10746                  if (value_str == (value_str = value_str.replace(/^(\d+)(\d{3})/, "$1"+$.jqplot.sprintf.thousandsSeparator+"$2"))) break;
10747              }
10748              return value_str; 
10749          }
10750  
10751          function justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace) {
10752              var diff = minWidth - value.length;
10753              if (diff > 0) {
10754                  var spchar = ' ';
10755                  if (htmlSpace) { spchar = '&nbsp;'; }
10756                  if (leftJustify || !zeroPad) {
10757                      value = pad(value, minWidth, spchar, leftJustify);
10758                  } else {
10759                      value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length);
10760                  }
10761              }
10762              return value;
10763          }
10764  
10765          function formatBaseX(value, base, prefix, leftJustify, minWidth, precision, zeroPad, htmlSpace) {
10766              // Note: casts negative numbers to positive ones
10767              var number = value >>> 0;
10768              prefix = prefix && number && {'2': '0b', '8': '0', '16': '0x'}[base] || '';
10769              value = prefix + pad(number.toString(base), precision || 0, '0', false);
10770              return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace);
10771          }
10772  
10773          function formatString(value, leftJustify, minWidth, precision, zeroPad, htmlSpace) {
10774              if (precision != null) {
10775                  value = value.slice(0, precision);
10776              }
10777              return justify(value, '', leftJustify, minWidth, zeroPad, htmlSpace);
10778          }
10779  
10780          var a = arguments, i = 0, format = a[i++];
10781  
10782          return format.replace($.jqplot.sprintf.regex, function(substring, valueIndex, flags, minWidth, _, precision, type) {
10783              if (substring == '%%') { return '%'; }
10784  
10785              // parse flags
10786              var leftJustify = false, positivePrefix = '', zeroPad = false, prefixBaseX = false, htmlSpace = false, thousandSeparation = false;
10787              for (var j = 0; flags && j < flags.length; j++) switch (flags.charAt(j)) {
10788                  case ' ': positivePrefix = ' '; break;
10789                  case '+': positivePrefix = '+'; break;
10790                  case '-': leftJustify = true; break;
10791                  case '0': zeroPad = true; break;
10792                  case '#': prefixBaseX = true; break;
10793                  case '&': htmlSpace = true; break;
10794                  case '\'': thousandSeparation = true; break;
10795              }
10796  
10797              // parameters may be null, undefined, empty-string or real valued
10798              // we want to ignore null, undefined and empty-string values
10799  
10800              if (!minWidth) {
10801                  minWidth = 0;
10802              } 
10803              else if (minWidth == '*') {
10804                  minWidth = +a[i++];
10805              } 
10806              else if (minWidth.charAt(0) == '*') {
10807                  minWidth = +a[minWidth.slice(1, -1)];
10808              } 
10809              else {
10810                  minWidth = +minWidth;
10811              }
10812  
10813              // Note: undocumented perl feature:
10814              if (minWidth < 0) {
10815                  minWidth = -minWidth;
10816                  leftJustify = true;
10817              }
10818  
10819              if (!isFinite(minWidth)) {
10820                  throw new Error('$.jqplot.sprintf: (minimum-)width must be finite');
10821              }
10822  
10823              if (!precision) {
10824                  precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : void(0);
10825              } 
10826              else if (precision == '*') {
10827                  precision = +a[i++];
10828              } 
10829              else if (precision.charAt(0) == '*') {
10830                  precision = +a[precision.slice(1, -1)];
10831              } 
10832              else {
10833                  precision = +precision;
10834              }
10835  
10836              // grab value using valueIndex if required?
10837              var value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++];
10838  
10839              switch (type) {
10840              case 's': {
10841                  if (value == null) {
10842                      return '';
10843                  }
10844                  return formatString(String(value), leftJustify, minWidth, precision, zeroPad, htmlSpace);
10845              }
10846              case 'c': return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad, htmlSpace);
10847              case 'b': return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad,htmlSpace);
10848              case 'o': return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace);
10849              case 'x': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace);
10850              case 'X': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace).toUpperCase();
10851              case 'u': return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace);
10852              case 'i': {
10853                var number = parseInt(+value, 10);
10854                if (isNaN(number)) {
10855                  return '';
10856                }
10857                var prefix = number < 0 ? '-' : positivePrefix;
10858                var number_str = thousandSeparation ? thousand_separate(String(Math.abs(number))): String(Math.abs(number));
10859                value = prefix + pad(number_str, precision, '0', false);
10860                //value = prefix + pad(String(Math.abs(number)), precision, '0', false);
10861                return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace);
10862                    }
10863              case 'd': {
10864                var number = Math.round(+value);
10865                if (isNaN(number)) {
10866                  return '';
10867                }
10868                var prefix = number < 0 ? '-' : positivePrefix;
10869                var number_str = thousandSeparation ? thousand_separate(String(Math.abs(number))): String(Math.abs(number));
10870                value = prefix + pad(number_str, precision, '0', false);
10871                return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace);
10872                    }
10873              case 'e':
10874              case 'E':
10875              case 'f':
10876              case 'F':
10877              case 'g':
10878              case 'G':
10879                        {
10880                        var number = +value;
10881                        if (isNaN(number)) {
10882                            return '';
10883                        }
10884                        var prefix = number < 0 ? '-' : positivePrefix;
10885                        var method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())];
10886                        var textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2];
10887                        var number_str = Math.abs(number)[method](precision);
10888                        number_str = thousandSeparation ? thousand_separate(number_str): number_str;
10889                        value = prefix + number_str;
10890                        var justified = justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace)[textTransform]();
10891  
10892                        if ($.jqplot.sprintf.decimalMark !== '.' && $.jqplot.sprintf.decimalMark !== $.jqplot.sprintf.thousandsSeparator) {
10893                            return justified.replace(/\./, $.jqplot.sprintf.decimalMark);
10894                        } else {
10895                            return justified;
10896                        }
10897                    }
10898              case 'p':
10899              case 'P':
10900              {
10901                  // make sure number is a number
10902                  var number = +value;
10903                  if (isNaN(number)) {
10904                      return '';
10905                  }
10906                  var prefix = number < 0 ? '-' : positivePrefix;
10907  
10908                  var parts = String(Number(Math.abs(number)).toExponential()).split(/e|E/);
10909                  var sd = (parts[0].indexOf('.') != -1) ? parts[0].length - 1 : parts[0].length;
10910                  var zeros = (parts[1] < 0) ? -parts[1] - 1 : 0;
10911                  
10912                  if (Math.abs(number) < 1) {
10913                      if (sd + zeros  <= precision) {
10914                          value = prefix + Math.abs(number).toPrecision(sd);
10915                      }
10916                      else {
10917                          if (sd  <= precision - 1) {
10918                              value = prefix + Math.abs(number).toExponential(sd-1);
10919                          }
10920                          else {
10921                              value = prefix + Math.abs(number).toExponential(precision-1);
10922                          }
10923                      }
10924                  }
10925                  else {
10926                      var prec = (sd <= precision) ? sd : precision;
10927                      value = prefix + Math.abs(number).toPrecision(prec);
10928                  }
10929                  var textTransform = ['toString', 'toUpperCase']['pP'.indexOf(type) % 2];
10930                  return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace)[textTransform]();
10931              }
10932              case 'n': return '';
10933              default: return substring;
10934              }
10935          });
10936      };
10937  
10938      $.jqplot.sprintf.thousandsSeparator = ',';
10939      // Specifies the decimal mark for floating point values. By default a period '.'
10940      // is used. If you change this value to for example a comma be sure to also
10941      // change the thousands separator or else this won't work since a simple String
10942      // replace is used (replacing all periods with the mark specified here).
10943      $.jqplot.sprintf.decimalMark = '.';
10944      
10945      $.jqplot.sprintf.regex = /%%|%(\d+\$)?([-+#0&\' ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([nAscboxXuidfegpEGP])/g;
10946  
10947      $.jqplot.getSignificantFigures = function(number) {
10948          var parts = String(Number(Math.abs(number)).toExponential()).split(/e|E/);
10949          // total significant digits
10950          var sd = (parts[0].indexOf('.') != -1) ? parts[0].length - 1 : parts[0].length;
10951          var zeros = (parts[1] < 0) ? -parts[1] - 1 : 0;
10952          // exponent
10953          var expn = parseInt(parts[1], 10);
10954          // digits to the left of the decimal place
10955          var dleft = (expn + 1 > 0) ? expn + 1 : 0;
10956          // digits to the right of the decimal place
10957          var dright = (sd <= dleft) ? 0 : sd - expn - 1;
10958          return {significantDigits: sd, digitsLeft: dleft, digitsRight: dright, zeros: zeros, exponent: expn} ;
10959      };
10960  
10961      $.jqplot.getPrecision = function(number) {
10962          return $.jqplot.getSignificantFigures(number).digitsRight;
10963      };
10964  
10965  })(jQuery);  
10966  
10967  
10968      var backCompat = $.uiBackCompat !== false;
10969  
10970      $.jqplot.effects = {
10971          effect: {}
10972      };
10973  
10974      // prefix used for storing data on .data()
10975      var dataSpace = "jqplot.storage.";
10976  
10977      /******************************************************************************/
10978      /*********************************** EFFECTS **********************************/
10979      /******************************************************************************/
10980  
10981      $.extend( $.jqplot.effects, {
10982          version: "1.9pre",
10983  
10984          // Saves a set of properties in a data storage
10985          save: function( element, set ) {
10986              for( var i=0; i < set.length; i++ ) {
10987                  if ( set[ i ] !== null ) {
10988                      element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
10989                  }
10990              }
10991          },
10992  
10993          // Restores a set of previously saved properties from a data storage
10994          restore: function( element, set ) {
10995              for( var i=0; i < set.length; i++ ) {
10996                  if ( set[ i ] !== null ) {
10997                      element.css( set[ i ], element.data( dataSpace + set[ i ] ) );
10998                  }
10999              }
11000          },
11001  
11002          setMode: function( el, mode ) {
11003              if (mode === "toggle") {
11004                  mode = el.is( ":hidden" ) ? "show" : "hide";
11005              }
11006              return mode;
11007          },
11008  
11009          // Wraps the element around a wrapper that copies position properties
11010          createWrapper: function( element ) {
11011  
11012              // if the element is already wrapped, return it
11013              if ( element.parent().is( ".ui-effects-wrapper" )) {
11014                  return element.parent();
11015              }
11016  
11017              // wrap the element
11018              var props = {
11019                      width: element.outerWidth(true),
11020                      height: element.outerHeight(true),
11021                      "float": element.css( "float" )
11022                  },
11023                  wrapper = $( "<div></div>" )
11024                      .addClass( "ui-effects-wrapper" )
11025                      .css({
11026                          fontSize: "100%",
11027                          background: "transparent",
11028                          border: "none",
11029                          margin: 0,
11030                          padding: 0
11031                      }),
11032                  // Store the size in case width/height are defined in % - Fixes #5245
11033                  size = {
11034                      width: element.width(),
11035                      height: element.height()
11036                  },
11037                  active = document.activeElement;
11038  
11039              element.wrap( wrapper );
11040  
11041              // Fixes #7595 - Elements lose focus when wrapped.
11042              if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
11043                  $( active ).focus();
11044              }
11045  
11046              wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually loose the reference to the wrapped element
11047  
11048              // transfer positioning properties to the wrapper
11049              if ( element.css( "position" ) === "static" ) {
11050                  wrapper.css({ position: "relative" });
11051                  element.css({ position: "relative" });
11052              } else {
11053                  $.extend( props, {
11054                      position: element.css( "position" ),
11055                      zIndex: element.css( "z-index" )
11056                  });
11057                  $.each([ "top", "left", "bottom", "right" ], function(i, pos) {
11058                      props[ pos ] = element.css( pos );
11059                      if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
11060                          props[ pos ] = "auto";
11061                      }
11062                  });
11063                  element.css({
11064                      position: "relative",
11065                      top: 0,
11066                      left: 0,
11067                      right: "auto",
11068                      bottom: "auto"
11069                  });
11070              }
11071              element.css(size);
11072  
11073              return wrapper.css( props ).show();
11074          },
11075  
11076          removeWrapper: function( element ) {
11077              var active = document.activeElement;
11078  
11079              if ( element.parent().is( ".ui-effects-wrapper" ) ) {
11080                  element.parent().replaceWith( element );
11081  
11082                  // Fixes #7595 - Elements lose focus when wrapped.
11083                  if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
11084                      $( active ).focus();
11085                  }
11086              }
11087  
11088  
11089              return element;
11090          }
11091      });
11092  
11093      // return an effect options object for the given parameters:
11094      function _normalizeArguments( effect, options, speed, callback ) {
11095  
11096          // short path for passing an effect options object:
11097          if ( $.isPlainObject( effect ) ) {
11098              return effect;
11099          }
11100  
11101          // convert to an object
11102          effect = { effect: effect };
11103  
11104          // catch (effect)
11105          if ( options === undefined ) {
11106              options = {};
11107          }
11108  
11109          // catch (effect, callback)
11110          if ( $.isFunction( options ) ) {
11111              callback = options;
11112              speed = null;
11113              options = {};
11114          }
11115  
11116          // catch (effect, speed, ?)
11117          if ( $.type( options ) === "number" || $.fx.speeds[ options ]) {
11118              callback = speed;
11119              speed = options;
11120              options = {};
11121          }
11122  
11123          // catch (effect, options, callback)
11124          if ( $.isFunction( speed ) ) {
11125              callback = speed;
11126              speed = null;
11127          }
11128  
11129          // add options to effect
11130          if ( options ) {
11131              $.extend( effect, options );
11132          }
11133  
11134          speed = speed || options.duration;
11135          effect.duration = $.fx.off ? 0 : typeof speed === "number"
11136              ? speed : speed in $.fx.speeds ? $.fx.speeds[ speed ] : $.fx.speeds._default;
11137  
11138          effect.complete = callback || options.complete;
11139  
11140          return effect;
11141      }
11142  
11143      function standardSpeed( speed ) {
11144          // valid standard speeds
11145          if ( !speed || typeof speed === "number" || $.fx.speeds[ speed ] ) {
11146              return true;
11147          }
11148  
11149          // invalid strings - treat as "normal" speed
11150          if ( typeof speed === "string" && !$.jqplot.effects.effect[ speed ] ) {
11151              // TODO: remove in 2.0 (#7115)
11152              if ( backCompat && $.jqplot.effects[ speed ] ) {
11153                  return false;
11154              }
11155              return true;
11156          }
11157  
11158          return false;
11159      }
11160  
11161      $.fn.extend({
11162          jqplotEffect: function( effect, options, speed, callback ) {
11163              var args = _normalizeArguments.apply( this, arguments ),
11164                  mode = args.mode,
11165                  queue = args.queue,
11166                  effectMethod = $.jqplot.effects.effect[ args.effect ],
11167  
11168                  // DEPRECATED: remove in 2.0 (#7115)
11169                  oldEffectMethod = !effectMethod && backCompat && $.jqplot.effects[ args.effect ];
11170  
11171              if ( $.fx.off || !( effectMethod || oldEffectMethod ) ) {
11172                  // delegate to the original method (e.g., .show()) if possible
11173                  if ( mode ) {
11174                      return this[ mode ]( args.duration, args.complete );
11175                  } else {
11176                      return this.each( function() {
11177                          if ( args.complete ) {
11178                              args.complete.call( this );
11179                          }
11180                      });
11181                  }
11182              }
11183  
11184              function run( next ) {
11185                  var elem = $( this ),
11186                      complete = args.complete,
11187                      mode = args.mode;
11188  
11189                  function done() {
11190                      if ( $.isFunction( complete ) ) {
11191                          complete.call( elem[0] );
11192                      }
11193                      if ( $.isFunction( next ) ) {
11194                          next();
11195                      }
11196                  }
11197  
11198                  // if the element is hiddden and mode is hide,
11199                  // or element is visible and mode is show
11200                  if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
11201                      done();
11202                  } else {
11203                      effectMethod.call( elem[0], args, done );
11204                  }
11205              }
11206  
11207              // TODO: remove this check in 2.0, effectMethod will always be true
11208              if ( effectMethod ) {
11209                  return queue === false ? this.each( run ) : this.queue( queue || "fx", run );
11210              } else {
11211                  // DEPRECATED: remove in 2.0 (#7115)
11212                  return oldEffectMethod.call(this, {
11213                      options: args,
11214                      duration: args.duration,
11215                      callback: args.complete,
11216                      mode: args.mode
11217                  });
11218              }
11219          }
11220      });
11221  
11222  
11223  
11224  
11225      var rvertical = /up|down|vertical/,
11226          rpositivemotion = /up|left|vertical|horizontal/;
11227  
11228      $.jqplot.effects.effect.blind = function( o, done ) {
11229          // Create element
11230          var el = $( this ),
11231              props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
11232              mode = $.jqplot.effects.setMode( el, o.mode || "hide" ),
11233              direction = o.direction || "up",
11234              vertical = rvertical.test( direction ),
11235              ref = vertical ? "height" : "width",
11236              ref2 = vertical ? "top" : "left",
11237              motion = rpositivemotion.test( direction ),
11238              animation = {},
11239              show = mode === "show",
11240              wrapper, distance, top;
11241  
11242          // // if already wrapped, the wrapper's properties are my property. #6245
11243          if ( el.parent().is( ".ui-effects-wrapper" ) ) {
11244              $.jqplot.effects.save( el.parent(), props );
11245          } else {
11246              $.jqplot.effects.save( el, props );
11247          }
11248          el.show();
11249          top = parseInt(el.css('top'), 10);
11250          wrapper = $.jqplot.effects.createWrapper( el ).css({
11251              overflow: "hidden"
11252          });
11253  
11254          distance = vertical ? wrapper[ ref ]() + top : wrapper[ ref ]();
11255  
11256          animation[ ref ] = show ? String(distance) : '0';
11257          if ( !motion ) {
11258              el
11259                  .css( vertical ? "bottom" : "right", 0 )
11260                  .css( vertical ? "top" : "left", "" )
11261                  .css({ position: "absolute" });
11262              animation[ ref2 ] = show ? '0' : String(distance);
11263          }
11264  
11265          // // start at 0 if we are showing
11266          if ( show ) {
11267              wrapper.css( ref, 0 );
11268              if ( ! motion ) {
11269                  wrapper.css( ref2, distance );
11270              }
11271          }
11272  
11273          // // Animate
11274          wrapper.animate( animation, {
11275              duration: o.duration,
11276              easing: o.easing,
11277              queue: false,
11278              complete: function() {
11279                  if ( mode === "hide" ) {
11280                      el.hide();
11281                  }
11282                  $.jqplot.effects.restore( el, props );
11283                  $.jqplot.effects.removeWrapper( el );
11284                  done();
11285              }
11286          });
11287  
11288      };
11289  
11290  


Generated: Fri Nov 28 20:08:37 2014 Cross-referenced by PHPXref 0.7.1