r20473 - gnucash/trunk - Add javascript jqplot 0.9.7r635 module so that it can be used in reports.

Christian Stimming cstim at code.gnucash.org
Thu Mar 24 17:30:09 EDT 2011


Author: cstim
Date: 2011-03-24 17:30:09 -0400 (Thu, 24 Mar 2011)
New Revision: 20473
Trac: http://svn.gnucash.org/trac/changeset/20473

Added:
   gnucash/trunk/src/report/jqplot/
   gnucash/trunk/src/report/jqplot/Makefile.am
   gnucash/trunk/src/report/jqplot/gpl-2.0.txt
   gnucash/trunk/src/report/jqplot/jquery-1.4.2.min.js
   gnucash/trunk/src/report/jqplot/jquery.jqplot.css
   gnucash/trunk/src/report/jqplot/jquery.jqplot.js
   gnucash/trunk/src/report/jqplot/plugins/
   gnucash/trunk/src/report/jqplot/plugins/jqplot.BezierCurveRenderer.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.barRenderer.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.blockRenderer.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.bubbleRenderer.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.canvasAxisLabelRenderer.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.canvasAxisTickRenderer.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.canvasTextRenderer.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.categoryAxisRenderer.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.ciParser.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.cursor.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.dateAxisRenderer.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.donutRenderer.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.dragable.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.enhancedLegendRenderer.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.funnelRenderer.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.highlighter.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.json2.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.logAxisRenderer.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.mekkoAxisRenderer.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.mekkoRenderer.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.meterGaugeRenderer.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.ohlcRenderer.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.pieRenderer.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.pointLabels.js
   gnucash/trunk/src/report/jqplot/plugins/jqplot.trendline.js
Modified:
   gnucash/trunk/configure.ac
   gnucash/trunk/src/report/Makefile.am
Log:
Add javascript jqplot 0.9.7r635 module so that it can be used in reports.

From: Andy Clayton <q3aiml at gmail.com>

Modified: gnucash/trunk/configure.ac
===================================================================
--- gnucash/trunk/configure.ac	2011-03-24 20:10:57 UTC (rev 20472)
+++ gnucash/trunk/configure.ac	2011-03-24 21:30:09 UTC (rev 20473)
@@ -1412,6 +1412,7 @@
   src/report/stylesheets/test/Makefile
   src/report/utility-reports/Makefile
   src/report/utility-reports/test/Makefile
+  src/report/jqplot/Makefile
   src/scm/Makefile
   src/scm/gnumeric/Makefile
   src/tax/Makefile

Modified: gnucash/trunk/src/report/Makefile.am
===================================================================
--- gnucash/trunk/src/report/Makefile.am	2011-03-24 20:10:57 UTC (rev 20472)
+++ gnucash/trunk/src/report/Makefile.am	2011-03-24 21:30:09 UTC (rev 20473)
@@ -3,7 +3,8 @@
   standard-reports \
   utility-reports \
   business-reports \
-  locale-specific
+  locale-specific \
+  jqplot
 
 GUI_SUBDIRS = \
   report-gnome \

Added: gnucash/trunk/src/report/jqplot/Makefile.am
===================================================================
--- gnucash/trunk/src/report/jqplot/Makefile.am	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/Makefile.am	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,35 @@
+SUBDIRS = . 
+
+gncjqplotdir = ${GNC_SHAREDIR}/jqplot/
+gncjqplot_DATA = \
+  jquery-1.4.2.min.js \
+  jquery.jqplot.js \
+  jquery.jqplot.css \
+  plugins/jqplot.barRenderer.js \
+  plugins/jqplot.BezierCurveRenderer.js \
+  plugins/jqplot.blockRenderer.js \
+  plugins/jqplot.bubbleRenderer.js \
+  plugins/jqplot.canvasAxisLabelRenderer.js \
+  plugins/jqplot.canvasAxisTickRenderer.js \
+  plugins/jqplot.canvasTextRenderer.js \
+  plugins/jqplot.categoryAxisRenderer.js \
+  plugins/jqplot.ciParser.js \
+  plugins/jqplot.cursor.js \
+  plugins/jqplot.dateAxisRenderer.js \
+  plugins/jqplot.donutRenderer.js \
+  plugins/jqplot.dragable.js \
+  plugins/jqplot.enhancedLegendRenderer.js \
+  plugins/jqplot.funnelRenderer.js \
+  plugins/jqplot.highlighter.js \
+  plugins/jqplot.json2.js \
+  plugins/jqplot.logAxisRenderer.js \
+  plugins/jqplot.mekkoAxisRenderer.js \
+  plugins/jqplot.mekkoRenderer.js \
+  plugins/jqplot.meterGaugeRenderer.js \
+  plugins/jqplot.ohlcRenderer.js \
+  plugins/jqplot.pieRenderer.js \
+  plugins/jqplot.pointLabels.js \
+  plugins/jqplot.trendline.js
+
+
+EXTRA_DIST = ${gncjqplot_DATA}

Added: gnucash/trunk/src/report/jqplot/gpl-2.0.txt
===================================================================
--- gnucash/trunk/src/report/jqplot/gpl-2.0.txt	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/gpl-2.0.txt	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,280 @@
+Title: GPL Version 2
+
+           GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/jquery-1.4.2.min.js
===================================================================
--- gnucash/trunk/src/report/jqplot/jquery-1.4.2.min.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/jquery-1.4.2.min.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,154 @@
+/*!
+ * jQuery JavaScript Library v1.4.2
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Sat Feb 13 22:33:48 2010 -0500
+ */
+(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
+e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
+j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
+"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
+true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
+Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
+(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
+a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
+"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
+function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
+c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
+L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
+"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
+d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
+a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
+!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
+true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML="   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
+parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
+false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
+s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
+applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
+else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
+a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
+w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
+cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
+i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
+" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
+this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
+e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
+c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
+a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
+function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
+k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
+C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
+null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
+e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
+f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
+if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
+d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
+"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
+a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
+isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
+{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
+if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
+e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
+"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
+d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
+!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
+toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
+u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
+function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
+if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
+t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
+g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
+for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
+1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
+relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
+l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
+h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
+CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
+g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
+text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
+setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
+h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
+m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
+"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
+h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
+!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
+h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
+q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
+if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
+(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
+function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
+gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
+c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
+{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
+"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
+d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
+a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
+1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
+a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
+c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
+wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
+prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
+this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
+return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
+""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
+this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
+u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
+1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
+return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
+""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
+c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
+c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
+function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
+Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
+"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
+a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
+a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
+"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
+serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
+function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
+global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
+e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
+"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
+false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
+false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
+c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
+d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
+g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
+1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
+"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
+if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
+this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
+"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
+animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
+j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
+this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
+"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
+c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
+this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
+this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
+e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
+c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
+function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
+this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
+k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
+f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
+c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
+d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
+f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
+"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
+e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);

Added: gnucash/trunk/src/report/jqplot/jquery.jqplot.css
===================================================================
--- gnucash/trunk/src/report/jqplot/jquery.jqplot.css	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/jquery.jqplot.css	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,212 @@
+/*rules for the plot target div.  These will be cascaded down to all plot elements according to css rules*/
+.jqplot-target {
+    position: relative;
+    color: #666666;
+    font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
+    font-size: 1em;
+/*    height: 300px;
+    width: 400px;*/
+}
+
+/*rules applied to all axes*/
+.jqplot-axis {
+    font-size: 0.75em;
+}
+
+.jqplot-xaxis {
+    margin-top: 10px;
+}
+
+.jqplot-x2axis {
+    margin-bottom: 10px;
+}
+
+.jqplot-yaxis {
+    margin-right: 10px;
+}
+
+.jqplot-y2axis, .jqplot-y3axis, .jqplot-y4axis, .jqplot-y5axis, .jqplot-y6axis, .jqplot-y7axis, .jqplot-y8axis, .jqplot-y9axis {
+    margin-left: 10px;
+    margin-right: 10px;
+}
+
+/*rules applied to all axis tick divs*/
+.jqplot-axis-tick, .jqplot-xaxis-tick, .jqplot-yaxis-tick, .jqplot-x2axis-tick, .jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick {
+    position: absolute;
+}
+
+
+.jqplot-xaxis-tick {
+    top: 0px;
+    /* initial position untill tick is drawn in proper place */
+    left: 15px;
+/*    padding-top: 10px;*/
+    vertical-align: top;
+}
+
+.jqplot-x2axis-tick {
+    bottom: 0px;
+    /* initial position untill tick is drawn in proper place */
+    left: 15px;
+/*    padding-bottom: 10px;*/
+    vertical-align: bottom;
+}
+
+.jqplot-yaxis-tick {
+    right: 0px;
+    /* initial position untill tick is drawn in proper place */
+    top: 15px;
+/*    padding-right: 10px;*/
+    text-align: right;
+}
+
+.jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick {
+    left: 0px;
+    /* initial position untill tick is drawn in proper place */
+    top: 15px;
+/*    padding-left: 10px;*/
+/*    padding-right: 15px;*/
+    text-align: left;
+}
+
+.jqplot-meterGauge-tick {
+    font-size: 0.75em;
+    color: #999999;
+}
+
+.jqplot-meterGauge-label {
+    font-size: 1em;
+    color: #999999;
+}
+.jqplot-xaxis-label {
+    margin-top: 10px;
+    font-size: 11pt;
+    position: absolute;
+}
+
+.jqplot-x2axis-label {
+    margin-bottom: 10px;
+    font-size: 11pt;
+    position: absolute;
+}
+
+.jqplot-yaxis-label {
+    margin-right: 10px;
+/*    text-align: center;*/
+    font-size: 11pt;
+    position: absolute;
+}
+
+.jqplot-y2axis-label, .jqplot-y3axis-label, .jqplot-y4axis-label, .jqplot-y5axis-label, .jqplot-y6axis-label, .jqplot-y7axis-label, .jqplot-y8axis-label, .jqplot-y9axis-label {
+/*    text-align: center;*/
+    font-size: 11pt;
+    position: absolute;
+}
+
+table.jqplot-table-legend {
+    margin-top: 12px;
+    margin-bottom: 12px;
+    margin-left: 12px;
+    margin-right: 12px;
+}
+
+table.jqplot-table-legend, table.jqplot-cursor-legend {
+    background-color: rgba(255,255,255,0.6);
+    border: 1px solid #cccccc;
+    position: absolute;
+    font-size: 0.75em;
+}
+
+td.jqplot-table-legend {
+    vertical-align:middle;
+}
+
+td.jqplot-seriesToggle:hover, td.jqplot-seriesToggle:active {
+    cursor: pointer;
+}
+
+td.jqplot-table-legend > div {
+    border: 1px solid #cccccc;
+    padding:1px;
+}
+
+div.jqplot-table-legend-swatch {
+    width:0px;
+    height:0px;
+    border-top-width: 5px;
+    border-bottom-width: 5px;
+    border-left-width: 6px;
+    border-right-width: 6px;
+    border-top-style: solid;
+    border-bottom-style: solid;
+    border-left-style: solid;
+    border-right-style: solid;
+}
+
+.jqplot-title {
+    top: 0px;
+    left: 0px;
+    padding-bottom: 0.5em;
+    font-size: 1.2em;
+}
+
+table.jqplot-cursor-tooltip {
+    border: 1px solid #cccccc;
+    font-size: 0.75em;
+}
+
+
+.jqplot-cursor-tooltip {
+    border: 1px solid #cccccc;
+    font-size: 0.75em;
+    white-space: nowrap;
+    background: rgba(208,208,208,0.5);
+    padding: 1px;
+}
+
+.jqplot-highlighter-tooltip {
+    border: 1px solid #cccccc;
+    font-size: 0.75em;
+    white-space: nowrap;
+    background: rgba(208,208,208,0.5);
+    padding: 1px;
+}
+
+.jqplot-point-label {
+    font-size: 0.75em;
+    z-index: 2;
+}
+      
+td.jqplot-cursor-legend-swatch {
+vertical-align:middle;
+text-align:center;
+}
+
+div.jqplot-cursor-legend-swatch {
+width:1.2em;
+height:0.7em;
+}
+
+.jqplot-error {
+/*   Styles added to the plot target container when there is an error go here.*/
+    text-align: center;
+}
+
+.jqplot-error-message {
+/*    Styling of the custom error message div goes here.*/
+    position: relative;
+    top: 46%;
+    display: inline-block;
+}
+
+div.jqplot-bubble-label {
+    font-size: 0.8em;
+/*    background: rgba(90%, 90%, 90%, 0.15);*/
+    padding-left: 2px;
+    padding-right: 2px;
+    color: rgb(20%, 20%, 20%);
+}
+
+div.jqplot-bubble-label.jqplot-bubble-label-highlight {
+    background: rgba(90%, 90%, 90%, 0.7);
+}
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/jquery.jqplot.js
===================================================================
--- gnucash/trunk/src/report/jqplot/jquery.jqplot.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/jquery.jqplot.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,6983 @@
+/**
+ * Title: jqPlot Charts
+ * 
+ * Pure JavaScript plotting plugin for jQuery.
+ * 
+ * About: Version
+ * 
+ * 0.9.7r635 
+ * 
+ * About: Copyright & License
+ * 
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly.
+ * 
+ * See <GPL Version 2> and <MIT License> contained within this distribution for further information. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php.  This is, of course, not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php.
+ * 
+ * jqPlot includes `date instance methods and printf/sprintf functions by other authors:
+ * 
+ * Date instance methods:
+ *
+ *     author Ken Snyder (ken d snyder at gmail dot com)
+ *     date 2008-09-10
+ *     version 2.0.2 (http://kendsnyder.com/sandbox/date/)     
+ *     license Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/)
+ *
+ * JavaScript printf/sprintf functions:
+ *
+ *     version 2007.04.27
+ *     author Ash Searle
+ *     http://hexmen.com/blog/2007/03/printf-sprintf/
+ *     http://hexmen.com/js/sprintf.js
+ *     The author (Ash Searle) has placed this code in the public domain:
+ *     "This code is unrestricted: you are free to use it however you like."
+ * 
+ * 
+ * About: Introduction
+ * 
+ * jqPlot requires jQuery (1.4+ required for certain features). jQuery 1.4.1 is included in the distribution.  
+ * To use jqPlot include jQuery, the jqPlot jQuery plugin, the jqPlot css file and optionally 
+ * the excanvas script for IE support in your web page:
+ * 
+ * > <!--[if IE]><script language="javascript" type="text/javascript" src="excanvas.js"></script><![endif]-->
+ * > <script language="javascript" type="text/javascript" src="jquery-1.4.2.min.js"></script>
+ * > <script language="javascript" type="text/javascript" src="jquery.jqplot.min.js"></script>
+ * > <link rel="stylesheet" type="text/css" href="jquery.jqplot.css" />
+ * 
+ * jqPlot can be customized by overriding the defaults of any of the objects which make
+ * up the plot. The general usage of jqplot is:
+ * 
+ * > chart = $.jqplot('targetElemId', [dataArray,...], {optionsObject});
+ * 
+ * The options available to jqplot are detailed in <jqPlot Options> in the jqPlotOptions.txt file.
+ * 
+ * An actual call to $.jqplot() may look like the 
+ * examples below:
+ * 
+ * > chart = $.jqplot('chartdiv',  [[[1, 2],[3,5.12],[5,13.1],[7,33.6],[9,85.9],[11,219.9]]]);
+ * 
+ * or
+ * 
+ * > dataArray = [34,12,43,55,77];
+ * > chart = $.jqplot('targetElemId', [dataArray, ...], {title:'My Plot', axes:{yaxis:{min:20, max:100}}});
+ * 
+ * For more inforrmation, see <jqPlot Usage>.
+ * 
+ * About: Usage
+ * 
+ * See <jqPlot Usage>
+ * 
+ * About: Available Options 
+ * 
+ * See <jqPlot Options> for a list of options available thorugh the options object (not complete yet!)
+ * 
+ * About: Options Usage
+ * 
+ * See <Options Tutorial>
+ * 
+ * About: Changes
+ * 
+ * See <Change Log>
+ * 
+ */
+
+(function($) {
+    // make sure undefined is undefined
+    var undefined;
+
+    /**
+     * Class: $.jqplot
+     * jQuery function called by the user to create a plot.
+     *  
+     * Parameters:
+     * target - ID of target element to render the plot into.
+     * data - an array of data series.
+     * options - user defined options object.  See the individual classes for available options.
+     * 
+     * Properties:
+     * config - object to hold configuration information for jqPlot plot object.
+     * 
+     * attributes:
+     * enablePlugins - False to disable plugins by default.  Plugins must then be explicitly 
+     *   enabled in the individual plot options.  Default: false.
+     *   This property sets the "show" property of certain plugins to true or false.
+     *   Only plugins that can be immediately active upon loading are affected.  This includes
+     *   non-renderer plugins like cursor, dragable, highlighter, and trendline.
+     * defaultHeight - Default height for plots where no css height specification exists.  This
+     *   is a jqplot wide default.
+     * defaultWidth - Default height for plots where no css height specification exists.  This
+     *   is a jqplot wide default.
+     */
+
+    $.jqplot = function(target, data, options) {
+        var _data, _options;
+        
+        if (options == null) {
+            if (data instanceof Array) {
+                _data = data;
+                _options = null;   
+            }
+            
+            else if (data.constructor == Object) {
+                _data = null;
+                _options = data;
+            }
+        }
+        else {
+            _data = data;
+            _options = options;
+        }
+        var plot = new jqPlot();
+        // remove any error class that may be stuck on target.
+        $('#'+target).removeClass('jqplot-error');
+        
+        if ($.jqplot.config.catchErrors) {
+            try {
+                plot.init(target, _data, _options);
+                plot.draw();
+                plot.themeEngine.init.call(plot);
+                return plot;
+            }
+            catch(e) {
+                var msg = $.jqplot.config.errorMessage || e.message;
+                $('#'+target).append('<div class="jqplot-error-message">'+msg+'</div>');
+                $('#'+target).addClass('jqplot-error');
+                document.getElementById(target).style.background = $.jqplot.config.errorBackground;
+                document.getElementById(target).style.border = $.jqplot.config.errorBorder;
+                document.getElementById(target).style.fontFamily = $.jqplot.config.errorFontFamily;
+                document.getElementById(target).style.fontSize = $.jqplot.config.errorFontSize;
+                document.getElementById(target).style.fontStyle = $.jqplot.config.errorFontStyle;
+                document.getElementById(target).style.fontWeight = $.jqplot.config.errorFontWeight;
+            }
+        }
+        else {        
+            plot.init(target, _data, _options);
+            plot.draw();
+            plot.themeEngine.init.call(plot);
+            return plot;
+        }
+    };
+        
+    $.jqplot.debug = 1;
+    $.jqplot.config = {
+        debug:1,
+        enablePlugins:false,
+        defaultHeight:300,
+        defaultWidth:400,
+        UTCAdjust:false,
+        timezoneOffset: new Date(new Date().getTimezoneOffset() * 60000),
+        errorMessage: '',
+        errorBackground: '',
+        errorBorder: '',
+        errorFontFamily: '',
+        errorFontSize: '',
+        errorFontStyle: '',
+        errorFontWeight: '',
+        catchErrors: false,
+        defaultTickFormatString: "%.1f"
+    };
+    
+    $.jqplot.enablePlugins = $.jqplot.config.enablePlugins;
+    
+    // canvas related tests taken from modernizer:
+    // Copyright © 2009–2010 Faruk Ates.
+    // http://www.modernizr.com
+    
+    $.jqplot.support_canvas = function() {
+        return !!document.createElement('canvas').getContext;
+    };
+            
+    $.jqplot.support_canvas_text = function() {
+        return !!(document.createElement('canvas').getContext && typeof document.createElement('canvas').getContext('2d').fillText == 'function');
+    };
+    
+    $.jqplot.use_excanvas = ($.browser.msie && !$.jqplot.support_canvas()) ? true : false;
+    
+    /**
+     * 
+     * Hooks: jqPlot Pugin Hooks
+     * 
+     * $.jqplot.preInitHooks - called before initialization.
+     * $.jqplot.postInitHooks - called after initialization.
+     * $.jqplot.preParseOptionsHooks - called before user options are parsed.
+     * $.jqplot.postParseOptionsHooks - called after user options are parsed.
+     * $.jqplot.preDrawHooks - called before plot draw.
+     * $.jqplot.postDrawHooks - called after plot draw.
+     * $.jqplot.preDrawSeriesHooks - called before each series is drawn.
+     * $.jqplot.postDrawSeriesHooks - called after each series is drawn.
+     * $.jqplot.preDrawLegendHooks - called before the legend is drawn.
+     * $.jqplot.addLegendRowHooks - called at the end of legend draw, so plugins
+     *     can add rows to the legend table.
+     * $.jqplot.preSeriesInitHooks - called before series is initialized.
+     * $.jqplot.postSeriesInitHooks - called after series is initialized.
+     * $.jqplot.preParseSeriesOptionsHooks - called before series related options
+     *     are parsed.
+     * $.jqplot.postParseSeriesOptionsHooks - called after series related options
+     *     are parsed.
+     * $.jqplot.eventListenerHooks - called at the end of plot drawing, binds
+     *     listeners to the event canvas which lays on top of the grid area.
+     * $.jqplot.preDrawSeriesShadowHooks - called before series shadows are drawn.
+     * $.jqplot.postDrawSeriesShadowHooks - called after series shadows are drawn.
+     * 
+     */
+    
+    $.jqplot.preInitHooks = [];
+    $.jqplot.postInitHooks = [];
+    $.jqplot.preParseOptionsHooks = [];
+    $.jqplot.postParseOptionsHooks = [];
+    $.jqplot.preDrawHooks = [];
+    $.jqplot.postDrawHooks = [];
+    $.jqplot.preDrawSeriesHooks = [];
+    $.jqplot.postDrawSeriesHooks = [];
+    $.jqplot.preDrawLegendHooks = [];
+    $.jqplot.addLegendRowHooks = [];
+    $.jqplot.preSeriesInitHooks = [];
+    $.jqplot.postSeriesInitHooks = [];
+    $.jqplot.preParseSeriesOptionsHooks = [];
+    $.jqplot.postParseSeriesOptionsHooks = [];
+    $.jqplot.eventListenerHooks = [];
+    $.jqplot.preDrawSeriesShadowHooks = [];
+    $.jqplot.postDrawSeriesShadowHooks = [];
+
+    // A superclass holding some common properties and methods.
+    $.jqplot.ElemContainer = function() {
+        this._elem;
+        this._plotWidth;
+        this._plotHeight;
+        this._plotDimensions = {height:null, width:null};
+    };
+    
+    $.jqplot.ElemContainer.prototype.createElement = function(el, offsets, clss, cssopts, attrib) {
+        this._offsets = offsets;
+        var klass = clss || 'jqplot';
+        var elem = document.createElement(el);
+        this._elem = $(elem);
+        this._elem.addClass(klass);
+        this._elem.css(cssopts);
+        this._elem.attr(attrib);
+        return this._elem;
+    };
+    
+    $.jqplot.ElemContainer.prototype.getWidth = function() {
+        if (this._elem) {
+            return this._elem.outerWidth(true);
+        }
+        else {
+            return null;
+        }
+    };
+    
+    $.jqplot.ElemContainer.prototype.getHeight = function() {
+        if (this._elem) {
+            return this._elem.outerHeight(true);
+        }
+        else {
+            return null;
+        }
+    };
+    
+    $.jqplot.ElemContainer.prototype.getPosition = function() {
+        if (this._elem) {
+            return this._elem.position();
+        }
+        else {
+            return {top:null, left:null, bottom:null, right:null};
+        }
+    };
+    
+    $.jqplot.ElemContainer.prototype.getTop = function() {
+        return this.getPosition().top;
+    };
+    
+    $.jqplot.ElemContainer.prototype.getLeft = function() {
+        return this.getPosition().left;
+    };
+    
+    $.jqplot.ElemContainer.prototype.getBottom = function() {
+        return this._elem.css('bottom');
+    };
+    
+    $.jqplot.ElemContainer.prototype.getRight = function() {
+        return this._elem.css('right');
+    };
+    
+
+    /**
+     * Class: Axis
+     * An individual axis object.  Cannot be instantiated directly, but created
+     * by the Plot oject.  Axis properties can be set or overriden by the 
+     * options passed in from the user.
+     * 
+     */
+    function Axis(name) {
+        $.jqplot.ElemContainer.call(this);
+        // Group: Properties
+        //
+        // Axes options are specified within an axes object at the top level of the 
+        // plot options like so:
+        // > {
+        // >    axes: {
+        // >        xaxis: {min: 5},
+        // >        yaxis: {min: 2, max: 8, numberTicks:4},
+        // >        x2axis: {pad: 1.5},
+        // >        y2axis: {ticks:[22, 44, 66, 88]}
+        // >        }
+        // > }
+        // There are 4 axes, 'xaxis', 'yaxis', 'x2axis', 'y2axis'.  Any or all of 
+        // which may be specified.
+        this.name = name;
+        this._series = [];
+        // prop: show
+        // Wether to display the axis on the graph.
+        this.show = false;
+        // prop: tickRenderer
+        // A class of a rendering engine for creating the ticks labels displayed on the plot, 
+        // See <$.jqplot.AxisTickRenderer>.
+        this.tickRenderer = $.jqplot.AxisTickRenderer;
+        // prop: tickOptions
+        // Options that will be passed to the tickRenderer, see <$.jqplot.AxisTickRenderer> options.
+        this.tickOptions = {};
+        // prop: labelRenderer
+        // A class of a rendering engine for creating an axis label.
+        this.labelRenderer = $.jqplot.AxisLabelRenderer;
+        // prop: labelOptions
+        // Options passed to the label renderer.
+        this.labelOptions = {};
+        // prop: label
+        // Label for the axis
+        this.label = null;
+        // prop: showLabel
+        // true to show the axis label.
+        this.showLabel = true;
+        // prop: min
+        // minimum value of the axis (in data units, not pixels).
+        this.min=null;
+        // prop: max
+        // maximum value of the axis (in data units, not pixels).
+        this.max=null;
+        // prop: autoscale
+        // Autoscale the axis min and max values to provide sensible tick spacing.
+        // If axis min or max are set, autoscale will be turned off.
+        // The numberTicks, tickInterval and pad options do work with 
+        // autoscale, although tickInterval has not been tested yet.
+        // padMin and padMax do nothing when autoscale is on.
+        this.autoscale = false;
+        // prop: pad
+        // Padding to extend the range above and below the data bounds.
+        // The data range is multiplied by this factor to determine minimum and maximum axis bounds.
+        // A value of 0 will be interpreted to mean no padding, and pad will be set to 1.0.
+        this.pad = 1.2;
+        // prop: padMax
+        // Padding to extend the range above data bounds.
+        // The top of the data range is multiplied by this factor to determine maximum axis bounds.
+        // A value of 0 will be interpreted to mean no padding, and padMax will be set to 1.0.
+        this.padMax = null;
+        // prop: padMin
+        // Padding to extend the range below data bounds.
+        // The bottom of the data range is multiplied by this factor to determine minimum axis bounds.
+        // A value of 0 will be interpreted to mean no padding, and padMin will be set to 1.0.
+        this.padMin = null;
+        // prop: ticks
+        // 1D [val, val, ...] or 2D [[val, label], [val, label], ...] array of ticks for the axis.
+        // If no label is specified, the value is formatted into an appropriate label.
+        this.ticks = [];
+        // prop: numberTicks
+        // Desired number of ticks.  Default is to compute automatically.
+        this.numberTicks;
+        // prop: tickInterval
+        // number of units between ticks.  Mutually exclusive with numberTicks.
+        this.tickInterval;
+        // prop: renderer
+        // A class of a rendering engine that handles tick generation, 
+        // scaling input data to pixel grid units and drawing the axis element.
+        this.renderer = $.jqplot.LinearAxisRenderer;
+        // prop: rendererOptions
+        // renderer specific options.  See <$.jqplot.LinearAxisRenderer> for options.
+        this.rendererOptions = {};
+        // prop: showTicks
+        // Wether to show the ticks (both marks and labels) or not.
+        // Will not override showMark and showLabel options if specified on the ticks themselves.
+        this.showTicks = true;
+        // prop: showTickMarks
+        // Wether to show the tick marks (line crossing grid) or not.
+        // Overridden by showTicks and showMark option of tick itself.
+        this.showTickMarks = true;
+        // prop: showMinorTicks
+        // Wether or not to show minor ticks.  This is renderer dependent.
+        // The default <$.jqplot.LinearAxisRenderer> does not have minor ticks.
+        this.showMinorTicks = true;
+        // prop: useSeriesColor
+        // Use the color of the first series associated with this axis for the
+        // tick marks and line bordering this axis.
+        this.useSeriesColor = false;
+        // prop: borderWidth
+        // width of line stroked at the border of the axis.  Defaults
+        // to the width of the grid boarder.
+        this.borderWidth = null;
+        // prop: borderColor
+        // color of the border adjacent to the axis.  Defaults to grid border color.
+        this.borderColor = null;
+        // minimum and maximum values on the axis.
+        this._dataBounds = {min:null, max:null};
+        // pixel position from the top left of the min value and max value on the axis.
+        this._offsets = {min:null, max:null};
+        this._ticks=[];
+        this._label = null;
+        // prop: syncTicks
+        // true to try and synchronize tick spacing across multiple axes so that ticks and
+        // grid lines line up.  This has an impact on autoscaling algorithm, however.
+        // In general, autoscaling an individual axis will work better if it does not
+        // have to sync ticks.
+        this.syncTicks = null;
+        // prop: tickSpacing
+        // Approximate pixel spacing between ticks on graph.  Used during autoscaling.
+        // This number will be an upper bound, actual spacing will be less.
+        this.tickSpacing = 75;
+        // Properties to hold the original values for min, max, ticks, tickInterval and numberTicks
+        // so they can be restored if altered by plugins.
+        this._min = null;
+        this._max = null;
+        this._tickInterval = null;
+        this._numberTicks = null;
+        this.__ticks = null;
+    }
+    
+    Axis.prototype = new $.jqplot.ElemContainer();
+    Axis.prototype.constructor = Axis;
+    
+    Axis.prototype.init = function() {
+        this.renderer = new this.renderer();
+        // set the axis name
+        this.tickOptions.axis = this.name;
+        // if showMark or showLabel tick options not specified, use value of axis option.
+        // showTicks overrides showTickMarks.
+        if (this.tickOptions.showMark == null) {
+            this.tickOptions.showMark = this.showTicks;
+        }
+        if (this.tickOptions.showMark == null) {
+            this.tickOptions.showMark = this.showTickMarks;
+        }
+        if (this.tickOptions.showLabel == null) {
+            this.tickOptions.showLabel = this.showTicks;
+        }
+        
+        if (this.label == null || this.label == '') {
+            this.showLabel = false;
+        }
+        else {
+            this.labelOptions.label = this.label;
+        }
+        if (this.showLabel == false) {
+            this.labelOptions.show = false;
+        }
+        // set the default padMax, padMin if not specified
+        // special check, if no padding desired, padding
+        // should be set to 1.0
+        if (this.pad == 0) {
+            this.pad = 1.0;
+        }
+        if (this.padMax == 0) {
+            this.padMax = 1.0;
+        }
+        if (this.padMin == 0) {
+            this.padMin = 1.0;
+        }
+        if (this.padMax == null) {
+            this.padMax = (this.pad-1)/2 + 1;
+        }
+        if (this.padMin == null) {
+            this.padMin = (this.pad-1)/2 + 1;
+        }
+        // now that padMin and padMax are correctly set, reset pad in case user has supplied 
+        // padMin and/or padMax
+        this.pad = this.padMax + this.padMin - 1;
+        if (this.min != null || this.max != null) {
+            this.autoscale = false;
+        }
+        // if not set, sync ticks for y axes but not x by default.
+        if (this.syncTicks == null && this.name.indexOf('y') > -1) {
+            this.syncTicks = true;
+        }
+        else if (this.syncTicks == null){
+            this.syncTicks = false;
+        }
+        this.renderer.init.call(this, this.rendererOptions);
+        
+    };
+    
+    Axis.prototype.draw = function(ctx) {
+        return this.renderer.draw.call(this, ctx);
+        
+    };
+    
+    Axis.prototype.set = function() {
+        this.renderer.set.call(this);
+    };
+    
+    Axis.prototype.pack = function(pos, offsets) {
+        if (this.show) {
+            this.renderer.pack.call(this, pos, offsets);
+        }
+        // these properties should all be available now.
+        if (this._min == null) {
+            this._min = this.min;
+            this._max = this.max;
+            this._tickInterval = this.tickInterval;
+            this._numberTicks = this.numberTicks;
+            this.__ticks = this._ticks;
+        }
+    };
+    
+    // reset the axis back to original values if it has been scaled, zoomed, etc.
+    Axis.prototype.reset = function() {
+        this.renderer.reset.call(this);
+    };
+    
+    Axis.prototype.resetScale = function() {
+        this.min = null;
+        this.max = null;
+        this.numberTicks = null;
+        this.tickInterval = null;
+    };
+
+    /**
+     * Class: Legend
+     * Legend object.  Cannot be instantiated directly, but created
+     * by the Plot oject.  Legend properties can be set or overriden by the 
+     * options passed in from the user.
+     */
+    function Legend(options) {
+        $.jqplot.ElemContainer.call(this);
+        // Group: Properties
+        
+        // prop: show
+        // Wether to display the legend on the graph.
+        this.show = false;
+        // prop: location
+        // Placement of the legend.  one of the compass directions: nw, n, ne, e, se, s, sw, w
+        this.location = 'ne';
+        // prop: labels
+        // Array of labels to use.  By default the renderer will look for labels on the series.
+        // Labels specified in this array will override labels specified on the series.
+        this.labels = [];
+        // prop: showLabels
+        // true to show the label text on the  legend.
+        this.showLabels = true;
+        // prop: showSwatch
+        // true to show the color swatches on the legend.
+        this.showSwatches = true;
+        // prop: placement
+        // "insideGrid" places legend inside the grid area of the plot.
+        // "outsideGrid" places the legend outside the grid but inside the plot container, 
+        // shrinking the grid to accomodate the legend.
+        // "inside" synonym for "insideGrid", 
+        // "outside" places the legend ouside the grid area, but does not shrink the grid which
+        // can cause the legend to overflow the plot container.
+        this.placement = "insideGrid";
+        // prop: xoffset
+        // DEPRECATED.  Set the margins on the legend using the marginTop, marginLeft, etc. 
+        // properties or via CSS margin styling of the .jqplot-table-legend class.
+        this.xoffset = 0;
+        // prop: yoffset
+        // DEPRECATED.  Set the margins on the legend using the marginTop, marginLeft, etc. 
+        // properties or via CSS margin styling of the .jqplot-table-legend class.
+        this.yoffset = 0;
+        // prop: border
+        // css spec for the border around the legend box.
+        this.border;
+        // prop: background
+        // css spec for the background of the legend box.
+        this.background;
+        // prop: textColor
+        // css color spec for the legend text.
+        this.textColor;
+        // prop: fontFamily
+        // css font-family spec for the legend text.
+        this.fontFamily; 
+        // prop: fontSize
+        // css font-size spec for the legend text.
+        this.fontSize ;
+        // prop: rowSpacing
+        // css padding-top spec for the rows in the legend.
+        this.rowSpacing = '0.5em';
+        // renderer
+        // A class that will create a DOM object for the legend,
+        // see <$.jqplot.TableLegendRenderer>.
+        this.renderer = $.jqplot.TableLegendRenderer;
+        // prop: rendererOptions
+        // renderer specific options passed to the renderer.
+        this.rendererOptions = {};
+        // prop: predraw
+        // Wether to draw the legend before the series or not.
+        // Used with series specific legend renderers for pie, donut, mekko charts, etc.
+        this.preDraw = false;
+        // prop: marginTop
+        // CSS margin for the legend DOM element. This will set an element 
+        // CSS style for the margin which will override any style sheet setting.
+        // The default will be taken from the stylesheet.
+        this.marginTop = null;
+        // prop: marginRight
+        // CSS margin for the legend DOM element. This will set an element 
+        // CSS style for the margin which will override any style sheet setting.
+        // The default will be taken from the stylesheet.
+        this.marginRight = null;
+        // prop: marginBottom
+        // CSS margin for the legend DOM element. This will set an element 
+        // CSS style for the margin which will override any style sheet setting.
+        // The default will be taken from the stylesheet.
+        this.marginBottom = null;
+        // prop: marginLeft
+        // CSS margin for the legend DOM element. This will set an element 
+        // CSS style for the margin which will override any style sheet setting.
+        // The default will be taken from the stylesheet.
+        this.marginLeft = null;
+        
+        this.escapeHtml = false;
+        this._series = [];
+        
+        $.extend(true, this, options);
+    }
+    
+    Legend.prototype = new $.jqplot.ElemContainer();
+    Legend.prototype.constructor = Legend;
+    
+    Legend.prototype.setOptions = function(options) {
+        $.extend(true, this, options);
+        
+        // Try to emulate deprecated behaviour
+        // if user has specified xoffset or yoffset, copy these to
+        // the margin properties.
+        
+        if (this.placement ==  'inside') this.placement = 'insideGrid';
+        
+        if (this.xoffset >0) {
+            if (this.placement == 'insideGrid') {
+                switch (this.location) {
+                    case 'nw':
+                    case 'w':
+                    case 'sw':
+                        if (this.marginLeft == null) {
+                            this.marginLeft = this.xoffset + 'px';
+                        }
+                        this.marginRight = '0px';
+                        break;
+                    case 'ne':
+                    case 'e':
+                    case 'se':
+                    default:
+                        if (this.marginRight == null) {
+                            this.marginRight = this.xoffset + 'px';
+                        }
+                        this.marginLeft = '0px';
+                        break;
+                }
+            }
+            else if (this.placement == 'outside') {
+                switch (this.location) {
+                    case 'nw':
+                    case 'w':
+                    case 'sw':
+                        if (this.marginRight == null) {
+                            this.marginRight = this.xoffset + 'px';
+                        }
+                        this.marginLeft = '0px';
+                        break;
+                    case 'ne':
+                    case 'e':
+                    case 'se':
+                    default:
+                        if (this.marginLeft == null) {
+                            this.marginLeft = this.xoffset + 'px';
+                        }
+                        this.marginRight = '0px';
+                        break;
+                }
+            }
+            this.xoffset = 0;
+        }
+        
+        if (this.yoffset >0) {
+            if (this.placement == 'outside') {
+                switch (this.location) {
+                    case 'sw':
+                    case 's':
+                    case 'se':
+                        if (this.marginTop == null) {
+                            this.marginTop = this.yoffset + 'px';
+                        }
+                        this.marginBottom = '0px';
+                        break;
+                    case 'ne':
+                    case 'n':
+                    case 'nw':
+                    default:
+                        if (this.marginBottom == null) {
+                            this.marginBottom = this.yoffset + 'px';
+                        }
+                        this.marginTop = '0px';
+                        break;
+                }
+            }
+            else if (this.placement == 'insideGrid') {
+                switch (this.location) {
+                    case 'sw':
+                    case 's':
+                    case 'se':
+                        if (this.marginBottom == null) {
+                            this.marginBottom = this.yoffset + 'px';
+                        }
+                        this.marginTop = '0px';
+                        break;
+                    case 'ne':
+                    case 'n':
+                    case 'nw':
+                    default:
+                        if (this.marginTop == null) {
+                            this.marginTop = this.yoffset + 'px';
+                        }
+                        this.marginBottom = '0px';
+                        break;
+                }
+            }
+            this.yoffset = 0;
+        }
+        
+        // TO-DO:
+        // Handle case where offsets are < 0.
+        //
+    };
+    
+    Legend.prototype.init = function() {
+        this.renderer = new this.renderer();
+        this.renderer.init.call(this, this.rendererOptions);
+    };
+    
+    Legend.prototype.draw = function(offsets) {
+        for (var i=0; i<$.jqplot.preDrawLegendHooks.length; i++){
+            $.jqplot.preDrawLegendHooks[i].call(this, offsets);
+        }
+        return this.renderer.draw.call(this, offsets);
+    };
+    
+    Legend.prototype.pack = function(offsets) {
+        this.renderer.pack.call(this, offsets);
+    };
+
+    /**
+     * Class: Title
+     * Plot Title object.  Cannot be instantiated directly, but created
+     * by the Plot oject.  Title properties can be set or overriden by the 
+     * options passed in from the user.
+     * 
+     * Parameters:
+     * text - text of the title.
+     */
+    function Title(text) {
+        $.jqplot.ElemContainer.call(this);
+        // Group: Properties
+        
+        // prop: text
+        // text of the title;
+        this.text = text;
+        // prop: show
+        // wether or not to show the title
+        this.show = true;
+        // prop: fontFamily
+        // css font-family spec for the text.
+        this.fontFamily;
+        // prop: fontSize
+        // css font-size spec for the text.
+        this.fontSize ;
+        // prop: textAlign
+        // css text-align spec for the text.
+        this.textAlign;
+        // prop: textColor
+        // css color spec for the text.
+        this.textColor;
+        // prop: renderer
+        // A class for creating a DOM element for the title,
+        // see <$.jqplot.DivTitleRenderer>.
+        this.renderer = $.jqplot.DivTitleRenderer;
+        // prop: rendererOptions
+        // renderer specific options passed to the renderer.
+        this.rendererOptions = {};   
+    }
+    
+    Title.prototype = new $.jqplot.ElemContainer();
+    Title.prototype.constructor = Title;
+    
+    Title.prototype.init = function() {
+        this.renderer = new this.renderer();
+        this.renderer.init.call(this, this.rendererOptions);
+    };
+    
+    Title.prototype.draw = function(width) {
+        return this.renderer.draw.call(this, width);
+    };
+    
+    Title.prototype.pack = function() {
+        this.renderer.pack.call(this);
+    };
+
+
+    /**
+     * Class: Series
+     * An individual data series object.  Cannot be instantiated directly, but created
+     * by the Plot oject.  Series properties can be set or overriden by the 
+     * options passed in from the user.
+     */
+    function Series() {
+        $.jqplot.ElemContainer.call(this);
+        // Group: Properties
+        // Properties will be assigned from a series array at the top level of the
+        // options.  If you had two series and wanted to change the color and line
+        // width of the first and set the second to use the secondary y axis with
+        // no shadow and supply custom labels for each:
+        // > {
+        // >    series:[
+        // >        {color: '#ff4466', lineWidth: 5, label:'good line'},
+        // >        {yaxis: 'y2axis', shadow: false, label:'bad line'}
+        // >    ]
+        // > }
+        
+        // prop: show
+        // wether or not to draw the series.
+        this.show = true;
+        // prop: xaxis
+        // which x axis to use with this series, either 'xaxis' or 'x2axis'.
+        this.xaxis = 'xaxis';
+        this._xaxis;
+        // prop: yaxis
+        // which y axis to use with this series, either 'yaxis' or 'y2axis'.
+        this.yaxis = 'yaxis';
+        this._yaxis;
+        this.gridBorderWidth = 2.0;
+        // prop: renderer
+        // A class of a renderer which will draw the series, 
+        // see <$.jqplot.LineRenderer>.
+        this.renderer = $.jqplot.LineRenderer;
+        // prop: rendererOptions
+        // Options to pass on to the renderer.
+        this.rendererOptions = {};
+        this.data = [];
+        this.gridData = [];
+        // prop: label
+        // Line label to use in the legend.
+        this.label = '';
+        // prop: showLabel
+        // true to show label for this series in the legend.
+        this.showLabel = true;
+        // prop: color
+        // css color spec for the series
+        this.color;
+        // prop: lineWidth
+        // width of the line in pixels.  May have different meanings depending on renderer.
+        this.lineWidth = 2.5;
+        // prop: shadow
+        // wether or not to draw a shadow on the line
+        this.shadow = true;
+        // prop: shadowAngle
+        // Shadow angle in degrees
+        this.shadowAngle = 45;
+        // prop: shadowOffset
+        // Shadow offset from line in pixels
+        this.shadowOffset = 1.25;
+        // prop: shadowDepth
+        // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
+        this.shadowDepth = 3;
+        // prop: shadowAlpha
+        // Alpha channel transparency of shadow.  0 = transparent.
+        this.shadowAlpha = '0.1';
+        // prop: breakOnNull
+        // Not implemented. wether line segments should be be broken at null value.
+        // False will join point on either side of line.
+        this.breakOnNull = false;
+        // prop: markerRenderer
+        // A class of a renderer which will draw marker (e.g. circle, square, ...) at the data points,
+        // see <$.jqplot.MarkerRenderer>.
+        this.markerRenderer = $.jqplot.MarkerRenderer;
+        // prop: markerOptions
+        // renderer specific options to pass to the markerRenderer,
+        // see <$.jqplot.MarkerRenderer>.
+        this.markerOptions = {};
+        // prop: showLine
+        // wether to actually draw the line or not.  Series will still be renderered, even if no line is drawn.
+        this.showLine = true;
+        // prop: showMarker
+        // wether or not to show the markers at the data points.
+        this.showMarker = true;
+        // prop: index
+        // 0 based index of this series in the plot series array.
+        this.index;
+        // prop: fill
+        // true or false, wether to fill under lines or in bars.
+        // May not be implemented in all renderers.
+        this.fill = false;
+        // prop: fillColor
+        // CSS color spec to use for fill under line.  Defaults to line color.
+        this.fillColor;
+        // prop: fillAlpha
+        // Alpha transparency to apply to the fill under the line.
+        // Use this to adjust alpha separate from fill color.
+        this.fillAlpha;
+        // prop: fillAndStroke
+        // If true will stroke the line (with color this.color) as well as fill under it.
+        // Applies only when fill is true.
+        this.fillAndStroke = false;
+        // prop: disableStack
+        // true to not stack this series with other series in the plot.
+        // To render properly, non-stacked series must come after any stacked series
+        // in the plot's data series array.  So, the plot's data series array would look like:
+        // > [stackedSeries1, stackedSeries2, ..., nonStackedSeries1, nonStackedSeries2, ...]
+        // disableStack will put a gap in the stacking order of series, and subsequent
+        // stacked series will not fill down through the non-stacked series and will
+        // most likely not stack properly on top of the non-stacked series.
+        this.disableStack = false;
+        // _stack is set by the Plot if the plot is a stacked chart.
+        // will stack lines or bars on top of one another to build a "mountain" style chart.
+        // May not be implemented in all renderers.
+        this._stack = false;
+        // prop: neighborThreshold
+        // how close or far (in pixels) the cursor must be from a point marker to detect the point.
+        this.neighborThreshold = 4;
+        // prop: fillToZero
+        // true will force bar and filled series to fill toward zero on the fill Axis.
+        this.fillToZero = false;
+        // prop: fillToValue
+        // fill a filled series to this value on the fill axis.
+        // Works in conjunction with fillToZero, so that must be true.
+        this.fillToValue = 0;
+        // prop: fillAxis
+        // Either 'x' or 'y'.  Which axis to fill the line toward if fillToZero is true.
+        // 'y' means fill up/down to 0 on the y axis for this series.
+        this.fillAxis = 'y';
+        // prop: useNegativeColors
+        // true to color negative values differently in filled and bar charts.
+        this.useNegativeColors = true;
+        this._stackData = [];
+        // _plotData accounts for stacking.  If plots not stacked, _plotData and data are same.  If
+        // stacked, _plotData is accumulation of stacking data.
+        this._plotData = [];
+        // _plotValues hold the individual x and y values that will be plotted for this series.
+        this._plotValues = {x:[], y:[]};
+        // statistics about the intervals between data points.  Used for auto scaling.
+        this._intervals = {x:{}, y:{}};
+        // data from the previous series, for stacked charts.
+        this._prevPlotData = [];
+        this._prevGridData = [];
+        this._stackAxis = 'y';
+        this._primaryAxis = '_xaxis';
+        // give each series a canvas to draw on.  This should allow for redrawing speedups.
+        this.canvas = new $.jqplot.GenericCanvas();
+        this.shadowCanvas = new $.jqplot.GenericCanvas();
+        this.plugins = {};
+        // sum of y values in this series.
+        this._sumy = 0;
+        this._sumx = 0;
+    }
+    
+    Series.prototype = new $.jqplot.ElemContainer();
+    Series.prototype.constructor = Series;
+    
+    Series.prototype.init = function(index, gridbw, plot) {
+        // weed out any null values in the data.
+        this.index = index;
+        this.gridBorderWidth = gridbw;
+        var d = this.data;
+        var temp = [], i;
+        for (i=0; i<d.length; i++) {
+            if (! this.breakOnNull) {
+                if (d[i] == null || d[i][0] == null || d[i][1] == null) {
+                    continue;
+                }
+                else {
+                    temp.push(d[i]);
+                }
+            }
+            else {
+                // TODO: figure out what to do with null values
+                // probably involve keeping nulls in data array
+                // and then updating renderers to break line
+                // when it hits null value.
+                // For now, just keep value.
+                temp.push(d[i]);
+            }
+        }
+        this.data = temp;
+        if (!this.fillColor) {
+            this.fillColor = this.color;
+        }
+        if (this.fillAlpha) {
+            var comp = $.jqplot.normalize2rgb(this.fillColor);
+            var comp = $.jqplot.getColorComponents(comp);
+            this.fillColor = 'rgba('+comp[0]+','+comp[1]+','+comp[2]+','+this.fillAlpha+')';
+        }
+        this.renderer = new this.renderer();
+        this.renderer.init.call(this, this.rendererOptions, plot);
+        this.markerRenderer = new this.markerRenderer();
+        if (!this.markerOptions.color) {
+            this.markerOptions.color = this.color;
+        }
+        if (this.markerOptions.show == null) {
+            this.markerOptions.show = this.showMarker;
+        }
+        this.showMarker = this.markerOptions.show;
+        // the markerRenderer is called within it's own scaope, don't want to overwrite series options!!
+        this.markerRenderer.init(this.markerOptions);
+    };
+    
+    // data - optional data point array to draw using this series renderer
+    // gridData - optional grid data point array to draw using this series renderer
+    // stackData - array of cumulative data for stacked plots.
+    Series.prototype.draw = function(sctx, opts, plot) {
+        var options = (opts == undefined) ? {} : opts;
+        sctx = (sctx == undefined) ? this.canvas._ctx : sctx;
+        // hooks get called even if series not shown
+        // we don't clear canvas here, it would wipe out all other series as well.
+        for (var j=0; j<$.jqplot.preDrawSeriesHooks.length; j++) {
+            $.jqplot.preDrawSeriesHooks[j].call(this, sctx, options);
+        }
+        if (this.show) {
+            this.renderer.setGridData.call(this, plot);
+            if (!options.preventJqPlotSeriesDrawTrigger) {
+                $(sctx.canvas).trigger('jqplotSeriesDraw', [this.data, this.gridData]);
+            }
+            var data = [];
+            if (options.data) {
+                data = options.data;
+            }
+            else if (!this._stack) {
+                data = this.data;
+            }
+            else {
+                data = this._plotData;
+            }
+            var gridData = options.gridData || this.renderer.makeGridData.call(this, data, plot);
+            this.renderer.draw.call(this, sctx, gridData, options, plot);
+        }
+        
+        for (var j=0; j<$.jqplot.postDrawSeriesHooks.length; j++) {
+            $.jqplot.postDrawSeriesHooks[j].call(this, sctx, options);
+        }
+    };
+    
+    Series.prototype.drawShadow = function(sctx, opts, plot) {
+        var options = (opts == undefined) ? {} : opts;
+        sctx = (sctx == undefined) ? this.shadowCanvas._ctx : sctx;
+        // hooks get called even if series not shown
+        // we don't clear canvas here, it would wipe out all other series as well.
+        for (var j=0; j<$.jqplot.preDrawSeriesShadowHooks.length; j++) {
+            $.jqplot.preDrawSeriesShadowHooks[j].call(this, sctx, options);
+        }
+        if (this.shadow) {
+            this.renderer.setGridData.call(this, plot);
+
+            var data = [];
+            if (options.data) {
+                data = options.data;
+            }
+            else if (!this._stack) {
+                data = this.data;
+            }
+            else {
+                data = this._plotData;
+            }
+            var gridData = options.gridData || this.renderer.makeGridData.call(this, data, plot);
+        
+            this.renderer.drawShadow.call(this, sctx, gridData, options);
+        }
+        
+        for (var j=0; j<$.jqplot.postDrawSeriesShadowHooks.length; j++) {
+            $.jqplot.postDrawSeriesShadowHooks[j].call(this, sctx, options);
+        }
+        
+    };
+    
+    // toggles series display on plot, e.g. show/hide series
+    Series.prototype.toggleDisplay = function(ev) {
+        var s, speed;
+        if (ev.data.series) {
+            s = ev.data.series;
+        }
+        else {
+            s = this;
+        }
+        if (ev.data.speed) {
+            speed = ev.data.speed;
+        }
+        if (speed) {
+            if (s.canvas._elem.is(':hidden')) {
+                if (s.shadowCanvas._elem) {
+                    s.shadowCanvas._elem.fadeIn(speed);
+                }
+                s.canvas._elem.fadeIn(speed);
+                s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).fadeIn(speed);
+            }
+            else {
+                if (s.shadowCanvas._elem) {
+                    s.shadowCanvas._elem.fadeOut(speed);
+                }
+                s.canvas._elem.fadeOut(speed);
+                s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).fadeOut(speed);
+            }
+        }
+        else {
+            if (s.canvas._elem.is(':hidden')) {
+                if (s.shadowCanvas._elem) {
+                    s.shadowCanvas._elem.show();
+                }
+                s.canvas._elem.show();
+                s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).show();
+            }
+            else {
+                if (s.shadowCanvas._elem) {
+                    s.shadowCanvas._elem.hide();
+                }
+                s.canvas._elem.hide();
+                s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).hide();
+            }
+        }
+    };
+    
+
+
+    /**
+     * Class: Grid
+     * 
+     * Object representing the grid on which the plot is drawn.  The grid in this
+     * context is the area bounded by the axes, the area which will contain the series.
+     * Note, the series are drawn on their own canvas.
+     * The Grid object cannot be instantiated directly, but is created by the Plot oject.  
+     * Grid properties can be set or overriden by the options passed in from the user.
+     */
+    function Grid() {
+        $.jqplot.ElemContainer.call(this);
+        // Group: Properties
+        
+        // prop: drawGridlines
+        // wether to draw the gridlines on the plot.
+        this.drawGridlines = true;
+        // prop: gridLineColor
+        // color of the grid lines.
+        this.gridLineColor = '#cccccc';
+        // prop: gridLineWidth
+        // width of the grid lines.
+        this.gridLineWidth = 1.0;
+        // prop: background
+        // css spec for the background color.
+        this.background = '#fffdf6';
+        // prop: borderColor
+        // css spec for the color of the grid border.
+        this.borderColor = '#999999';
+        // prop: borderWidth
+        // width of the border in pixels.
+        this.borderWidth = 2.0;
+        // prop: drawBorder
+        // True to draw border around grid.
+        this.drawBorder = true;
+        // prop: shadow
+        // wether to show a shadow behind the grid.
+        this.shadow = true;
+        // prop: shadowAngle
+        // shadow angle in degrees
+        this.shadowAngle = 45;
+        // prop: shadowOffset
+        // Offset of each shadow stroke from the border in pixels
+        this.shadowOffset = 1.5;
+        // prop: shadowWidth
+        // width of the stoke for the shadow
+        this.shadowWidth = 3;
+        // prop: shadowDepth
+        // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
+        this.shadowDepth = 3;
+        // prop: shadowColor
+        // an optional css color spec for the shadow in 'rgba(n, n, n, n)' form
+        this.shadowColor = null;
+        // prop: shadowAlpha
+        // Alpha channel transparency of shadow.  0 = transparent.
+        this.shadowAlpha = '0.07';
+        this._left;
+        this._top;
+        this._right;
+        this._bottom;
+        this._width;
+        this._height;
+        this._axes = [];
+        // prop: renderer
+        // Instance of a renderer which will actually render the grid,
+        // see <$.jqplot.CanvasGridRenderer>.
+        this.renderer = $.jqplot.CanvasGridRenderer;
+        // prop: rendererOptions
+        // Options to pass on to the renderer,
+        // see <$.jqplot.CanvasGridRenderer>.
+        this.rendererOptions = {};
+        this._offsets = {top:null, bottom:null, left:null, right:null};
+    }
+    
+    Grid.prototype = new $.jqplot.ElemContainer();
+    Grid.prototype.constructor = Grid;
+    
+    Grid.prototype.init = function() {
+        this.renderer = new this.renderer();
+        this.renderer.init.call(this, this.rendererOptions);
+    };
+    
+    Grid.prototype.createElement = function(offsets) {
+        this._offsets = offsets;
+        return this.renderer.createElement.call(this);
+    };
+    
+    Grid.prototype.draw = function() {
+        this.renderer.draw.call(this);
+    };
+    
+    $.jqplot.GenericCanvas = function() {
+        $.jqplot.ElemContainer.call(this);
+        this._ctx;  
+    };
+    
+    $.jqplot.GenericCanvas.prototype = new $.jqplot.ElemContainer();
+    $.jqplot.GenericCanvas.prototype.constructor = $.jqplot.GenericCanvas;
+    
+    $.jqplot.GenericCanvas.prototype.createElement = function(offsets, clss, plotDimensions) {
+        this._offsets = offsets;
+        var klass = 'jqplot';
+        if (clss != undefined) {
+            klass = clss;
+        }
+        var elem;
+        // if this canvas already has a dom element, don't make a new one.
+        if (this._elem) {
+            elem = this._elem.get(0);
+        }
+        else {
+            elem = document.createElement('canvas');
+        }
+        // if new plotDimensions supplied, use them.
+        if (plotDimensions != undefined) {
+            this._plotDimensions = plotDimensions;
+        }
+        
+        elem.width = this._plotDimensions.width - this._offsets.left - this._offsets.right;
+        elem.height = this._plotDimensions.height - this._offsets.top - this._offsets.bottom;
+        this._elem = $(elem);
+        this._elem.css({ position: 'absolute', left: this._offsets.left, top: this._offsets.top });
+        
+        this._elem.addClass(klass);
+        if ($.jqplot.use_excanvas) {
+            window.G_vmlCanvasManager.init_(document);
+            elem = window.G_vmlCanvasManager.initElement(elem);
+        }
+        return this._elem;
+    };
+    
+    $.jqplot.GenericCanvas.prototype.setContext = function() {
+        this._ctx = this._elem.get(0).getContext("2d");
+        return this._ctx;
+    };
+    
+    $.jqplot.HooksManager = function () {
+        this.hooks =[];
+    };
+    
+    $.jqplot.HooksManager.prototype.addOnce = function(fn) {
+        var havehook = false, i;
+        for (i=0; i<this.hooks.length; i++) {
+            if (this.hooks[i][0] == fn) {
+                havehook = true;
+            }
+        }
+        if (!havehook) {
+            this.hooks.push(fn);
+        }
+    };
+    
+    $.jqplot.HooksManager.prototype.add = function(fn) {
+        this.hooks.push(fn);
+    };
+    
+    $.jqplot.EventListenerManager = function () {
+        this.hooks =[];
+    };
+    
+    $.jqplot.EventListenerManager.prototype.addOnce = function(ev, fn) {
+        var havehook = false, h, i;
+        for (i=0; i<this.hooks.length; i++) {
+            h = this.hooks[i];
+            if (h[0] == ev && h[1] == fn) {
+                havehook = true;
+            }
+        }
+        if (!havehook) {
+            this.hooks.push([ev, fn]);
+        }
+    };
+    
+    $.jqplot.EventListenerManager.prototype.add = function(ev, fn) {
+        this.hooks.push([ev, fn]);
+    };
+
+    /**
+     * Class: jqPlot
+     * Plot object returned by call to $.jqplot.  Handles parsing user options,
+     * creating sub objects (Axes, legend, title, series) and rendering the plot.
+     */
+    function jqPlot() {
+        // Group: Properties
+        // These properties are specified at the top of the options object
+        // like so:
+        // > {
+        // >     axesDefaults:{min:0},
+        // >     series:[{color:'#6633dd'}],
+        // >     title: 'A Plot'
+        // > }
+        //
+        // prop: data
+        // user's data.  Data should *NOT* be specified in the options object,
+        // but be passed in as the second argument to the $.jqplot() function.
+        // The data property is described here soley for reference. 
+        // The data should be in the form of an array of 2D or 1D arrays like
+        // > [ [[x1, y1], [x2, y2],...], [y1, y2, ...] ].
+        this.data = [];
+        // prop dataRenderer
+        // A callable which can be used to preprocess data passed into the plot.
+        // Will be called with 2 arguments, the plot data and a reference to the plot.
+        this.dataRenderer;
+        // prop dataRendererOptions
+        // Options that will be passed to the dataRenderer.
+        // Can be of any type.
+        this.dataRendererOptions;
+        // The id of the dom element to render the plot into
+        this.targetId = null;
+        // the jquery object for the dom target.
+        this.target = null; 
+        this.defaults = {
+            // prop: axesDefaults
+            // default options that will be applied to all axes.
+            // see <Axis> for axes options.
+            axesDefaults: {},
+            axes: {xaxis:{}, yaxis:{}, x2axis:{}, y2axis:{}, y3axis:{}, y4axis:{}, y5axis:{}, y6axis:{}, y7axis:{}, y8axis:{}, y9axis:{}},
+            // prop: seriesDefaults
+            // default options that will be applied to all series.
+            // see <Series> for series options.
+            seriesDefaults: {},
+            gridPadding: {top:10, right:10, bottom:23, left:10},
+            series:[]
+        };
+        // prop: series
+        // Array of series object options.
+        // see <Series> for series specific options.
+        this.series = [];
+        // prop: axes
+        // up to 4 axes are supported, each with it's own options, 
+        // See <Axis> for axis specific options.
+        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')};
+        // prop: grid
+        // See <Grid> for grid specific options.
+        this.grid = new Grid();
+        // prop: legend
+        // see <$.jqplot.TableLegendRenderer>
+        this.legend = new Legend();
+        this.baseCanvas = new $.jqplot.GenericCanvas();
+        // array of series indicies. Keep track of order
+        // which series canvases are displayed, lowest
+        // to highest, back to front.
+        this.seriesStack = [];
+        this.previousSeriesStack = [];
+        this.eventCanvas = new $.jqplot.GenericCanvas();
+        this._width = null;
+        this._height = null; 
+        this._plotDimensions = {height:null, width:null};
+        this._gridPadding = {top:10, right:10, bottom:10, left:10};
+        // a shortcut for axis syncTicks options.  Not implemented yet.
+        this.syncXTicks = true;
+        // a shortcut for axis syncTicks options.  Not implemented yet.
+        this.syncYTicks = true;
+        // prop: seriesColors
+        // Ann array of CSS color specifications that will be applied, in order,
+        // to the series in the plot.  Colors will wrap around so, if their
+        // are more series than colors, colors will be reused starting at the
+        // beginning.  For pie charts, this specifies the colors of the slices.
+        this.seriesColors = [ "#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"];
+        this.negativeSeriesColors = [ "#498991", "#C08840", "#9F9274", "#546D61", "#646C4A", "#6F6621", "#6E3F5F", "#4F64B0", "#A89050", "#C45923", "#187399", "#945381", "#959E5C", "#C7AF7B", "#478396", "#907294"];
+        // prop: sortData
+        // false to not sort the data passed in by the user.
+        // Many bar, stakced and other graphs as well as many plugins depend on
+        // having sorted data.
+        this.sortData = true;
+        var seriesColorsIndex = 0;
+        // prop textColor
+        // css spec for the css color attribute.  Default for the entire plot.
+        this.textColor;
+        // prop; fontFamily
+        // css spec for the font-family attribute.  Default for the entire plot.
+        this.fontFamily;
+        // prop: fontSize
+        // css spec for the font-size attribute.  Default for the entire plot.
+        this.fontSize;
+        // prop: title
+        // Title object.  See <Title> for specific options.  As a shortcut, you
+        // can specify the title option as just a string like: title: 'My Plot'
+        // and this will create a new title object with the specified text.
+        this.title = new Title();
+        // container to hold all of the merged options.  Convienence for plugins.
+        this.options = {};
+        // prop: stackSeries
+        // true or false, creates a stack or "mountain" plot.
+        // Not all series renderers may implement this option.
+        this.stackSeries = false;
+        // prop: defaultAxisStart
+        // 1-D data series are internally converted into 2-D [x,y] data point arrays
+        // by jqPlot.  This is the default starting value for the missing x or y value.
+        // The added data will be a monotonically increasing series (e.g. [1, 2, 3, ...])
+        // starting at this value.
+        this.defaultAxisStart = 1;
+        // array to hold the cumulative stacked series data.
+        // used to ajust the individual series data, which won't have access to other
+        // series data.
+        this._stackData = [];
+        // array that holds the data to be plotted. This will be the series data
+        // merged with the the appropriate data from _stackData according to the stackAxis.
+        this._plotData = [];
+        // Namespece to hold plugins.  Generally non-renderer plugins add themselves to here.
+        this.plugins = {};
+        // Count how many times the draw method has been called while the plot is visible.
+        // Mostly used to test if plot has never been dran (=0), has been successfully drawn
+        // into a visible container once (=1) or draw more than once into a visible container.
+        // Can use this in tests to see if plot has been visibly drawn at least one time.
+        // After plot has been visibly drawn once, it generally doesn't need redrawn if its
+        // container is hidden and shown.
+        this._drawCount = 0;
+        // this.doCustomEventBinding = true;
+        // prop: drawIfHidden
+        // True to execute the draw method even if the plot target is hidden.
+        // Generally, this should be false.  Most plot elements will not be sized/
+        // positioned correclty if renderered into a hidden container.  To render into
+        // a hidden container, call the replot method when the container is shown.
+        this.drawIfHidden = false;
+        // true to intercept right click events and fire a 'jqplotRightClick' event.
+        // this will also block the context menu.
+        this.captureRightClick = false;
+        this.themeEngine = new $.jqplot.ThemeEngine();
+        // sum of y values for all series in plot.
+        // used in mekko chart.
+        this._sumy = 0;
+        this._sumx = 0;
+        this.preInitHooks = new $.jqplot.HooksManager();
+        this.postInitHooks = new $.jqplot.HooksManager();
+        this.preParseOptionsHooks = new $.jqplot.HooksManager();
+        this.postParseOptionsHooks = new $.jqplot.HooksManager();
+        this.preDrawHooks = new $.jqplot.HooksManager();
+        this.postDrawHooks = new $.jqplot.HooksManager();
+        this.preDrawSeriesHooks = new $.jqplot.HooksManager();
+        this.postDrawSeriesHooks = new $.jqplot.HooksManager();
+        this.preDrawLegendHooks = new $.jqplot.HooksManager();
+        this.addLegendRowHooks = new $.jqplot.HooksManager();
+        this.preSeriesInitHooks = new $.jqplot.HooksManager();
+        this.postSeriesInitHooks = new $.jqplot.HooksManager();
+        this.preParseSeriesOptionsHooks = new $.jqplot.HooksManager();
+        this.postParseSeriesOptionsHooks = new $.jqplot.HooksManager();
+        this.eventListenerHooks = new $.jqplot.EventListenerManager();
+        this.preDrawSeriesShadowHooks = new $.jqplot.HooksManager();
+        this.postDrawSeriesShadowHooks = new $.jqplot.HooksManager();
+        
+        this.colorGenerator = $.jqplot.ColorGenerator;
+        
+        // Group: methods
+        //
+        // method: init
+        // sets the plot target, checks data and applies user
+        // options to plot.
+        this.init = function(target, data, options) {
+            for (var i=0; i<$.jqplot.preInitHooks.length; i++) {
+                $.jqplot.preInitHooks[i].call(this, target, data, options);
+            }
+
+            for (var i=0; i<this.preInitHooks.hooks.length; i++) {
+                this.preInitHooks.hooks[i].call(this, target, data, options);
+            }
+            
+            this.targetId = '#'+target;
+            this.target = $('#'+target);
+            // remove any error class that may be stuck on target.
+            this.target.removeClass('jqplot-error');
+            if (!this.target.get(0)) {
+                throw "No plot target specified";
+            }
+            
+            // make sure the target is positioned by some means and set css
+            if (this.target.css('position') == 'static') {
+                this.target.css('position', 'relative');
+            }
+            if (!this.target.hasClass('jqplot-target')) {
+                this.target.addClass('jqplot-target');
+            }
+            
+            // if no height or width specified, use a default.
+            if (!this.target.height()) {
+                var h;
+                if (options && options.height) {
+                    h = parseInt(options.height, 10);
+                }
+                else if (this.target.attr('data-height')) {
+                    h = parseInt(this.target.attr('data-height'), 10);
+                }
+                else {
+                    h = parseInt($.jqplot.config.defaultHeight, 10);
+                }
+                this._height = h;
+                this.target.css('height', h+'px');
+            }
+            else {
+                this._height = this.target.height();
+            }
+            if (!this.target.width()) {
+                var w;
+                if (options && options.width) {
+                    w = parseInt(options.width, 10);
+                }
+                else if (this.target.attr('data-width')) {
+                    w = parseInt(this.target.attr('data-width'), 10);
+                }
+                else {
+                    w = parseInt($.jqplot.config.defaultWidth, 10);
+                }
+                this._width = w;
+                this.target.css('width', w+'px');
+            }
+            else {
+                this._width = this.target.width();
+            }
+            
+            this._plotDimensions.height = this._height;
+            this._plotDimensions.width = this._width;
+            this.grid._plotDimensions = this._plotDimensions;
+            this.title._plotDimensions = this._plotDimensions;
+            this.baseCanvas._plotDimensions = this._plotDimensions;
+            this.eventCanvas._plotDimensions = this._plotDimensions;
+            this.legend._plotDimensions = this._plotDimensions;
+            if (this._height <=0 || this._width <=0 || !this._height || !this._width) {
+                throw "Canvas dimension not set";
+            }
+            
+            if (options.dataRenderer && typeof(options.dataRenderer) == "function") {
+                if (options.dataRendererOptions) {
+                    this.dataRendererOptions = options.dataRendererOptions;
+                }
+                this.dataRenderer = options.dataRenderer;
+                data = this.dataRenderer(data, this, this.dataRendererOptions);
+            }
+            
+            if (data == null) {
+                throw{
+                    name: "DataError",
+                    message: "No data to plot."
+                };
+            }
+            
+            if (data.constructor != Array || data.length == 0 || data[0].constructor != Array || data[0].length == 0) {
+                throw{
+                    name: "DataError",
+                    message: "No data to plot."
+                };
+            }
+            
+            this.data = data;
+            
+            this.parseOptions(options);
+            
+            if (this.textColor) {
+                this.target.css('color', this.textColor);
+            }
+            if (this.fontFamily) {
+                this.target.css('font-family', this.fontFamily);
+            }
+            if (this.fontSize) {
+                this.target.css('font-size', this.fontSize);
+            }
+            
+            this.title.init();
+            this.legend.init();
+            this._sumy = 0;
+            this._sumx = 0;
+            for (var i=0; i<this.series.length; i++) {
+                // set default stacking order for series canvases
+                this.seriesStack.push(i);
+                this.previousSeriesStack.push(i);
+                this.series[i].shadowCanvas._plotDimensions = this._plotDimensions;
+                this.series[i].canvas._plotDimensions = this._plotDimensions;
+                for (var j=0; j<$.jqplot.preSeriesInitHooks.length; j++) {
+                    $.jqplot.preSeriesInitHooks[j].call(this.series[i], target, data, this.options.seriesDefaults, this.options.series[i], this);
+                }
+                for (var j=0; j<this.preSeriesInitHooks.hooks.length; j++) {
+                    this.preSeriesInitHooks.hooks[j].call(this.series[i], target, data, this.options.seriesDefaults, this.options.series[i], this);
+                }
+                this.populatePlotData(this.series[i], i);
+                this.series[i]._plotDimensions = this._plotDimensions;
+                this.series[i].init(i, this.grid.borderWidth, this);
+                for (var j=0; j<$.jqplot.postSeriesInitHooks.length; j++) {
+                    $.jqplot.postSeriesInitHooks[j].call(this.series[i], target, data, this.options.seriesDefaults, this.options.series[i], this);
+                }
+                for (var j=0; j<this.postSeriesInitHooks.hooks.length; j++) {
+                    this.postSeriesInitHooks.hooks[j].call(this.series[i], target, data, this.options.seriesDefaults, this.options.series[i], this);
+                }
+                this._sumy += this.series[i]._sumy;
+                this._sumx += this.series[i]._sumx;
+            }
+
+            for (var name in this.axes) {
+                this.axes[name]._plotDimensions = this._plotDimensions;
+                this.axes[name].init();
+            }
+            
+            if (this.sortData) {
+                sortData(this.series);
+            }
+            this.grid.init();
+            this.grid._axes = this.axes;
+            
+            this.legend._series = this.series;
+
+            for (var i=0; i<$.jqplot.postInitHooks.length; i++) {
+                $.jqplot.postInitHooks[i].call(this, target, data, options);
+            }
+
+            for (var i=0; i<this.postInitHooks.hooks.length; i++) {
+                this.postInitHooks.hooks[i].call(this, target, data, options);
+            }
+        };  
+        
+        // method: resetAxesScale
+        // Reset the specified axes min, max, numberTicks and tickInterval properties to null
+        // or reset these properties on all axes if no list of axes is provided.
+        //
+        // Parameters:
+        // axes - Boolean to reset or not reset all axes or an array or object of axis names to reset.
+        this.resetAxesScale = function(axes) {
+            var ax = (axes != undefined) ? axes : this.axes;
+            if (ax === true) {
+                ax = this.axes;
+            }
+            if (ax.constructor === Array) {
+                for (var i = 0; i < ax.length; i++) {
+                    this.axes[ax[i]].resetScale();
+                }
+            }
+            else if (ax.constructor === Object) {
+                for (var name in ax) {
+                    this.axes[name].resetScale();
+                }
+            }
+        };
+        // method: reInitialize
+        // reinitialize plot for replotting.
+        // not called directly.
+        this.reInitialize = function () {
+            // Plot should be visible and have a height and width.
+            // If plot doesn't have height and width for some
+            // reason, set it by other means.  Plot must not have
+            // a display:none attribute, however.
+            if (!this.target.height()) {
+                var h;
+                if (options && options.height) {
+                    h = parseInt(options.height, 10);
+                }
+                else if (this.target.attr('data-height')) {
+                    h = parseInt(this.target.attr('data-height'), 10);
+                }
+                else {
+                    h = parseInt($.jqplot.config.defaultHeight, 10);
+                }
+                this._height = h;
+                this.target.css('height', h+'px');
+            }
+            else {
+                this._height = this.target.height();
+            }
+            if (!this.target.width()) {
+                var w;
+                if (options && options.width) {
+                    w = parseInt(options.width, 10);
+                }
+                else if (this.target.attr('data-width')) {
+                    w = parseInt(this.target.attr('data-width'), 10);
+                }
+                else {
+                    w = parseInt($.jqplot.config.defaultWidth, 10);
+                }
+                this._width = w;
+                this.target.css('width', w+'px');
+            }
+            else {
+                this._width = this.target.width();
+            }
+            
+            if (this._height <=0 || this._width <=0 || !this._height || !this._width) {
+                throw "Target dimension not set";
+            }
+            
+            this._plotDimensions.height = this._height;
+            this._plotDimensions.width = this._width;
+            this.grid._plotDimensions = this._plotDimensions;
+            this.title._plotDimensions = this._plotDimensions;
+            this.baseCanvas._plotDimensions = this._plotDimensions;
+            this.eventCanvas._plotDimensions = this._plotDimensions;
+            this.legend._plotDimensions = this._plotDimensions;
+            
+            for (var n in this.axes) {
+                this.axes[n]._plotWidth = this._width;
+                this.axes[n]._plotHeight = this._height;
+            }
+            
+            this.title._plotWidth = this._width;
+            
+            if (this.textColor) {
+                this.target.css('color', this.textColor);
+            }
+            if (this.fontFamily) {
+                this.target.css('font-family', this.fontFamily);
+            }
+            if (this.fontSize) {
+                this.target.css('font-size', this.fontSize);
+            }
+            
+            this._sumy = 0;
+            this._sumx = 0;
+            for (var i=0; i<this.series.length; i++) {
+                this.populatePlotData(this.series[i], i);
+                this.series[i]._plotDimensions = this._plotDimensions;
+                this.series[i].canvas._plotDimensions = this._plotDimensions;
+                //this.series[i].init(i, this.grid.borderWidth);
+                this._sumy += this.series[i]._sumy;
+                this._sumx += this.series[i]._sumx;
+            }
+            
+            for (var name in this.axes) {
+                this.axes[name]._plotDimensions = this._plotDimensions;
+                this.axes[name]._ticks = [];
+                this.axes[name].renderer.init.call(this.axes[name], {});
+            }
+            
+            if (this.sortData) {
+                sortData(this.series);
+            }
+            
+            this.grid._axes = this.axes;
+            
+            this.legend._series = this.series;
+        };
+        
+        // sort the series data in increasing order.
+        function sortData(series) {
+            var d, sd, pd, ppd, ret;
+            for (var i=0; i<series.length; i++) {
+                // d = series[i].data;
+                // sd = series[i]._stackData;
+                // pd = series[i]._plotData;
+                // ppd = series[i]._prevPlotData;
+                var check;
+                var bat = [series[i].data, series[i]._stackData, series[i]._plotData, series[i]._prevPlotData];
+                for (var n=0; n<4; n++) {
+                    check = true;
+                    d = bat[n];
+                    if (series[i]._stackAxis == 'x') {
+                        for (var j = 0; j < d.length; j++) {
+                            if (typeof(d[j][1]) != "number") {
+                                check = false;
+                                break;
+                            }
+                        }
+                        if (check) {
+                            d.sort(function(a,b) { return a[1] - b[1]; });
+                            // sd.sort(function(a,b) { return a[1] - b[1]; });
+                            // pd.sort(function(a,b) { return a[1] - b[1]; });
+                            // ppd.sort(function(a,b) { return a[1] - b[1]; });
+                        }
+                    }
+                    else {
+                        for (var j = 0; j < d.length; j++) {
+                            if (typeof(d[j][0]) != "number") {
+                                check = false;
+                                break;
+                            }
+                        }
+                        if (check) {
+                            d.sort(function(a,b) { return a[0] - b[0]; });
+                            // sd.sort(function(a,b) { return a[0] - b[0]; });
+                            // pd.sort(function(a,b) { return a[0] - b[0]; });
+                            // ppd.sort(function(a,b) { return a[0] - b[0]; });
+                        }
+                    }
+                }
+               
+            }
+        }
+        
+        // populate the _stackData and _plotData arrays for the plot and the series.
+        this.populatePlotData = function(series, index) {
+            // if a stacked chart, compute the stacked data
+            this._plotData = [];
+            this._stackData = [];
+            series._stackData = [];
+            series._plotData = [];
+            var plotValues = {x:[], y:[]};
+            if (this.stackSeries && !series.disableStack) {
+                series._stack = true;
+                var sidx = series._stackAxis == 'x' ? 0 : 1;
+                var idx = sidx ? 0 : 1;
+                // push the current data into stackData
+                //this._stackData.push(this.series[i].data);
+                var temp = $.extend(true, [], series.data);
+                // create the data that will be plotted for this series
+                var plotdata = $.extend(true, [], series.data);
+                // for first series, nothing to add to stackData.
+                for (var j=0; j<index; j++) {
+                    var cd = this.series[j].data;
+                    for (var k=0; k<cd.length; k++) {
+                        temp[k][0] += cd[k][0];
+                        temp[k][1] += cd[k][1];
+                        // only need to sum up the stack axis column of data
+                        plotdata[k][sidx] += cd[k][sidx];
+                    }
+                }
+                for (var i=0; i<plotdata.length; i++) {
+                    plotValues.x.push(plotdata[i][0]);
+                    plotValues.y.push(plotdata[i][1]);
+                }
+                this._plotData.push(plotdata);
+                this._stackData.push(temp);
+                series._stackData = temp;
+                series._plotData = plotdata;
+                series._plotValues = plotValues;
+            }
+            else {
+                for (var i=0; i<series.data.length; i++) {
+                    plotValues.x.push(series.data[i][0]);
+                    plotValues.y.push(series.data[i][1]);
+                }
+                this._stackData.push(series.data);
+                this.series[index]._stackData = series.data;
+                this._plotData.push(series.data);
+                series._plotData = series.data;
+                series._plotValues = plotValues;
+            }
+            if (index>0) {
+                series._prevPlotData = this.series[index-1]._plotData;
+            }
+            series._sumy = 0;
+            series._sumx = 0;
+            for (i=series.data.length-1; i>-1; i--) {
+                series._sumy += series.data[i][1];
+                series._sumx += series.data[i][0];
+            }
+        };
+        
+        // function to safely return colors from the color array and wrap around at the end.
+        this.getNextSeriesColor = (function(t) {
+            var idx = 0;
+            var sc = t.seriesColors;
+            
+            return function () { 
+                if (idx < sc.length) {
+                    return sc[idx++];
+                }
+                else {
+                    idx = 0;
+                    return sc[idx++];
+                }
+            };
+        })(this);
+    
+        this.parseOptions = function(options){
+            for (var i=0; i<this.preParseOptionsHooks.hooks.length; i++) {
+                this.preParseOptionsHooks.hooks[i].call(this, options);
+            }
+            for (var i=0; i<$.jqplot.preParseOptionsHooks.length; i++) {
+                $.jqplot.preParseOptionsHooks[i].call(this, options);
+            }
+            this.options = $.extend(true, {}, this.defaults, options);
+            this.stackSeries = this.options.stackSeries;
+            if (this.options.seriesColors) {
+                this.seriesColors = this.options.seriesColors;
+            }
+            if (this.options.negativeSeriesColors) {
+                this.negativeSeriesColors = this.options.negativeSeriesColors;
+            }
+            if (this.options.captureRightClick) {
+                this.captureRightClick = this.options.captureRightClick;
+            }
+            this.defaultAxisStart = (options && options.defaultAxisStart != null) ? options.defaultAxisStart : this.defaultAxisStart;
+            var cg = new this.colorGenerator(this.seriesColors);
+            // this._gridPadding = this.options.gridPadding;
+            $.extend(true, this._gridPadding, this.options.gridPadding);
+            this.sortData = (this.options.sortData != null) ? this.options.sortData : this.sortData;
+            for (var n in this.axes) {
+                var axis = this.axes[n];
+                $.extend(true, axis, this.options.axesDefaults, this.options.axes[n]);
+                axis._plotWidth = this._width;
+                axis._plotHeight = this._height;
+            }
+            if (this.data.length == 0) {
+                this.data = [];
+                for (var i=0; i<this.options.series.length; i++) {
+                    this.data.push(this.options.series.data);
+                }    
+            }
+                
+            var normalizeData = function(data, dir, start) {
+                // return data as an array of point arrays,
+                // in form [[x1,y1...], [x2,y2...], ...]
+                var temp = [];
+                var i;
+                dir = dir || 'vertical';
+                if (!(data[0] instanceof Array)) {
+                    // we have a series of scalars.  One line with just y values.
+                    // turn the scalar list of data into a data array of form:
+                    // [[1, data[0]], [2, data[1]], ...]
+                    for (i=0; i<data.length; i++) {
+                        if (dir == 'vertical') {
+                            temp.push([start + i, data[i]]);   
+                        }
+                        else {
+                            temp.push([data[i], start+i]);
+                        }
+                    }
+                }            
+                else {
+                    // we have a properly formatted data series, copy it.
+                    $.extend(true, temp, data);
+                }
+                return temp;
+            };
+
+            for (var i=0; i<this.data.length; i++) { 
+                var temp = new Series();
+                for (var j=0; j<$.jqplot.preParseSeriesOptionsHooks.length; j++) {
+                    $.jqplot.preParseSeriesOptionsHooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]);
+                }
+                for (var j=0; j<this.preParseSeriesOptionsHooks.hooks.length; j++) {
+                    this.preParseSeriesOptionsHooks.hooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]);
+                }
+                $.extend(true, temp, {seriesColors:this.seriesColors, negativeSeriesColors:this.negativeSeriesColors}, this.options.seriesDefaults, this.options.series[i]);
+                var dir = 'vertical';
+                if (temp.renderer.constructor == $.jqplot.barRenderer && temp.rendererOptions && temp.rendererOptions.barDirection == 'horizontal') {
+                    dir = 'horizontal';
+                }
+                temp.data = normalizeData(this.data[i], dir, this.defaultAxisStart);
+                switch (temp.xaxis) {
+                    case 'xaxis':
+                        temp._xaxis = this.axes.xaxis;
+                        break;
+                    case 'x2axis':
+                        temp._xaxis = this.axes.x2axis;
+                        break;
+                    default:
+                        break;
+                }
+                temp._yaxis = this.axes[temp.yaxis];
+                temp._xaxis._series.push(temp);
+                temp._yaxis._series.push(temp);
+                if (temp.show) {
+                    temp._xaxis.show = true;
+                    temp._yaxis.show = true;
+                }
+
+                // parse the renderer options and apply default colors if not provided
+                if (!temp.color && temp.show != false) {
+                    temp.color = cg.next();
+                }
+                if (!temp.label) {
+                    temp.label = 'Series '+ (i+1).toString();
+                }
+                // temp.rendererOptions.show = temp.show;
+                // $.extend(true, temp.renderer, {color:this.seriesColors[i]}, this.rendererOptions);
+                this.series.push(temp);  
+                for (var j=0; j<$.jqplot.postParseSeriesOptionsHooks.length; j++) {
+                    $.jqplot.postParseSeriesOptionsHooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]);
+                }
+                for (var j=0; j<this.postParseSeriesOptionsHooks.hooks.length; j++) {
+                    this.postParseSeriesOptionsHooks.hooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]);
+                }
+            }
+            
+            // copy the grid and title options into this object.
+            $.extend(true, this.grid, this.options.grid);
+            // if axis border properties aren't set, set default.
+            for (var n in this.axes) {
+                var axis = this.axes[n];
+                if (axis.borderWidth == null) {
+                    axis.borderWidth =this.grid.borderWidth;
+                }
+                if (axis.borderColor == null) {
+                    if (n != 'xaxis' && n != 'x2axis' && axis.useSeriesColor === true && axis.show) {
+                        axis.borderColor = axis._series[0].color;
+                    }
+                    else {
+                        axis.borderColor = this.grid.borderColor;
+                    }
+                }
+            }
+            
+            if (typeof this.options.title == 'string') {
+                this.title.text = this.options.title;
+            }
+            else if (typeof this.options.title == 'object') {
+                $.extend(true, this.title, this.options.title);
+            }
+            this.title._plotWidth = this._width;
+            this.legend.setOptions(this.options.legend);
+            
+            for (var i=0; i<$.jqplot.postParseOptionsHooks.length; i++) {
+                $.jqplot.postParseOptionsHooks[i].call(this, options);
+            }
+            for (var i=0; i<this.postParseOptionsHooks.hooks.length; i++) {
+                this.postParseOptionsHooks.hooks[i].call(this, options);
+            }
+        };
+        
+        // method: replot
+        // Does a reinitialization of the plot followed by
+        // a redraw.  Method could be used to interactively
+        // change plot characteristics and then replot.
+        //
+        // Parameters:
+        // options - Options used for replotting.
+        //
+        // Properties:
+        // clear - false to not clear (empty) the plot container before replotting (default: true).
+        // resetAxes - true to reset all axes min, max, numberTicks and tickInterval setting so axes will rescale themselves.
+        //             optionally pass in list of axes to reset (e.g. ['xaxis', 'y2axis']) (default: false).
+        this.replot = function(options) {
+            var opts = (options != undefined) ? options : {};
+            var clear = (opts.clear != undefined) ? opts.clear : true;
+            var resetAxes = (opts.resetAxes != undefined) ? opts.resetAxes : false;
+            this.target.trigger('jqplotPreReplot');
+            if (clear) {
+                this.target.empty();
+            }
+            if (resetAxes) {
+                this.resetAxesScale(resetAxes);
+            }
+            this.reInitialize();
+            this.draw();
+            this.target.trigger('jqplotPostReplot');
+        };
+        
+        // method: redraw
+        // Empties the plot target div and redraws the plot.
+        // This enables plot data and properties to be changed
+        // and then to comletely clear the plot and redraw.
+        // redraw *will not* reinitialize any plot elements.
+        // That is, axes will not be autoscaled and defaults
+        // will not be reapplied to any plot elements.  redraw
+        // is used primarily with zooming. 
+        //
+        // Parameters:
+        // clear - false to not clear (empty) the plot container before redrawing (default: true).
+        this.redraw = function(clear) {
+            clear = (clear != null) ? clear : true;
+            this.target.trigger('jqplotPreRedraw');
+            if (clear) {
+                this.target.empty();
+            }
+             for (var ax in this.axes) {
+                this.axes[ax]._ticks = [];
+            }
+            for (var i=0; i<this.series.length; i++) {
+                this.populatePlotData(this.series[i], i);
+            }
+            this._sumy = 0;
+            this._sumx = 0;
+            for (i=0; i<this.series.length; i++) {
+                this._sumy += this.series[i]._sumy;
+                this._sumx += this.series[i]._sumx;
+            }
+            this.draw();
+            this.target.trigger('jqplotPostRedraw');
+        };
+        
+        // method: draw
+        // Draws all elements of the plot into the container.
+        // Does not clear the container before drawing.
+        this.draw = function(){
+            if (this.drawIfHidden || this.target.is(':visible')) {
+                this.target.trigger('jqplotPreDraw');
+                var i, j;
+                for (i=0; i<$.jqplot.preDrawHooks.length; i++) {
+                    $.jqplot.preDrawHooks[i].call(this);
+                }
+                for (i=0; i<this.preDrawHooks.hooks.length; i++) {
+                    this.preDrawHooks.hooks[i].call(this);
+                }
+                // create an underlying canvas to be used for special features.
+                this.target.append(this.baseCanvas.createElement({left:0, right:0, top:0, bottom:0}, 'jqplot-base-canvas'));
+                this.baseCanvas.setContext();
+                this.target.append(this.title.draw());
+                this.title.pack({top:0, left:0});
+                
+                // make room  for the legend between the grid and the edge.
+                var legendElem = this.legend.draw();
+                
+                var gridPadding = {top:0, left:0, bottom:0, right:0};
+                
+                if (this.legend.placement == "outsideGrid") {
+                    // temporarily append the legend to get dimensions
+                    this.target.append(legendElem);
+                    switch (this.legend.location) {
+                        case 'n':
+                            gridPadding.top += this.legend.getHeight();
+                            break;
+                        case 's':
+                            gridPadding.bottom += this.legend.getHeight();
+                            break;
+                        case 'ne':
+                        case 'e':
+                        case 'se':
+                            gridPadding.right += this.legend.getWidth();
+                            break;
+                        case 'nw':
+                        case 'w':
+                        case 'sw':
+                            gridPadding.left += this.legend.getWidth();
+                            break;
+                        default:  // same as 'ne'
+                            gridPadding.right += this.legend.getWidth();
+                            break;
+                    }
+                    legendElem = legendElem.detach();
+                }
+                
+                var ax = this.axes;
+                for (var name in ax) {
+                    this.target.append(ax[name].draw(this.baseCanvas._ctx));
+                    ax[name].set();
+                }
+                if (ax.yaxis.show) {
+                    gridPadding.left += ax.yaxis.getWidth();
+                }
+                var ra = ['y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis'];
+                var rapad = [0, 0, 0, 0, 0, 0, 0, 0];
+                var gpr = 0;
+                var n;
+                for (n=0; n<8; n++) {
+                    if (ax[ra[n]].show) {
+                        gpr += ax[ra[n]].getWidth();
+                        rapad[n] = gpr;
+                    }
+                }
+                gridPadding.right += gpr;
+                if (ax.x2axis.show) {
+                    gridPadding.top += ax.x2axis.getHeight();
+                }
+                if (this.title.show) {
+                    gridPadding.top += this.title.getHeight();
+                }
+                if (ax.xaxis.show) {
+                    gridPadding.bottom += ax.xaxis.getHeight();
+                }
+                
+                // end of gridPadding adjustments.
+                var arr = ['top', 'bottom', 'left', 'right'];
+                for (var n in arr) {
+                    if (gridPadding[arr[n]]) {
+                        this._gridPadding[arr[n]] = gridPadding[arr[n]];
+                    }
+                }
+                
+                var legendPadding = (this.legend.placement == 'outsideGrid') ? {top:this.title.getHeight(), left: 0, right: 0, bottom: 0} : this._gridPadding;
+            
+                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});
+                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});
+                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});
+                for (i=8; i>0; i--) {
+                    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});
+                }
+                // ax.y2axis.pack({position:'absolute', top:0, right:0}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top});
+            
+                this.target.append(this.grid.createElement(this._gridPadding));
+                this.grid.draw();
+                
+                // put the shadow canvases behind the series canvases so shadows don't overlap on stacked bars.
+                for (i=0; i<this.series.length; i++) {
+                    // draw series in order of stacking.  This affects only
+                    // order in which canvases are added to dom.
+                    j = this.seriesStack[i];
+                    this.target.append(this.series[j].shadowCanvas.createElement(this._gridPadding, 'jqplot-series-shadowCanvas'));
+                    this.series[j].shadowCanvas.setContext();
+                    this.series[j].shadowCanvas._elem.data('seriesIndex', j);
+                }
+                
+                for (i=0; i<this.series.length; i++) {
+                    // draw series in order of stacking.  This affects only
+                    // order in which canvases are added to dom.
+                    j = this.seriesStack[i];
+                    this.target.append(this.series[j].canvas.createElement(this._gridPadding, 'jqplot-series-canvas'));
+                    this.series[j].canvas.setContext();
+                    this.series[j].canvas._elem.data('seriesIndex', j);
+                }
+                // Need to use filled canvas to capture events in IE.
+                // Also, canvas seems to block selection of other elements in document on FF.
+                this.target.append(this.eventCanvas.createElement(this._gridPadding, 'jqplot-event-canvas'));
+                this.eventCanvas.setContext();
+                this.eventCanvas._ctx.fillStyle = 'rgba(0,0,0,0)';
+                this.eventCanvas._ctx.fillRect(0,0,this.eventCanvas._ctx.canvas.width, this.eventCanvas._ctx.canvas.height);
+            
+                // bind custom event handlers to regular events.
+                this.bindCustomEvents();
+            
+                // draw legend before series if the series needs to know the legend dimensions.
+                if (this.legend.preDraw) {  
+                    this.eventCanvas._elem.before(legendElem);
+                    this.legend.pack(legendPadding);
+                    if (this.legend._elem) {
+                        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}});
+                    }
+                    else {
+                        this.drawSeries();
+                    }
+                }
+                else {  // draw series before legend
+                    this.drawSeries();
+                    $(this.series[this.series.length-1].canvas._elem).after(legendElem);
+                    this.legend.pack(legendPadding);                
+                }
+            
+                // register event listeners on the overlay canvas
+                for (var i=0; i<$.jqplot.eventListenerHooks.length; i++) {
+                    // in the handler, this will refer to the eventCanvas dom element.
+                    // make sure there are references back into plot objects.
+                    this.eventCanvas._elem.bind($.jqplot.eventListenerHooks[i][0], {plot:this}, $.jqplot.eventListenerHooks[i][1]);
+                }
+            
+                // register event listeners on the overlay canvas
+                for (var i=0; i<this.eventListenerHooks.hooks.length; i++) {
+                    // in the handler, this will refer to the eventCanvas dom element.
+                    // make sure there are references back into plot objects.
+                    this.eventCanvas._elem.bind(this.eventListenerHooks.hooks[i][0], {plot:this}, this.eventListenerHooks.hooks[i][1]);
+                }
+
+                for (var i=0; i<$.jqplot.postDrawHooks.length; i++) {
+                    $.jqplot.postDrawHooks[i].call(this);
+                }
+
+                for (var i=0; i<this.postDrawHooks.hooks.length; i++) {
+                    this.postDrawHooks.hooks[i].call(this);
+                }
+            
+                if (this.target.is(':visible')) {
+                    this._drawCount += 1;
+                }
+            
+                this.target.trigger('jqplotPostDraw', [this]);
+            }
+        };
+        
+        this.bindCustomEvents = function() {
+            this.eventCanvas._elem.bind('click', {plot:this}, this.onClick);
+            this.eventCanvas._elem.bind('dblclick', {plot:this}, this.onDblClick);
+            this.eventCanvas._elem.bind('mousedown', {plot:this}, this.onMouseDown);
+            this.eventCanvas._elem.bind('mousemove', {plot:this}, this.onMouseMove);
+            this.eventCanvas._elem.bind('mouseenter', {plot:this}, this.onMouseEnter);
+            this.eventCanvas._elem.bind('mouseleave', {plot:this}, this.onMouseLeave);
+            if (this.captureRightClick) {
+                this.eventCanvas._elem.bind('mouseup', {plot:this}, this.onRightClick);
+                this.eventCanvas._elem.get(0).oncontextmenu = function() {
+					return false;
+				};
+            }
+            else {
+                this.eventCanvas._elem.bind('mouseup', {plot:this}, this.onMouseUp);
+            }
+        };
+        
+        function getEventPosition(ev) {
+            var plot = ev.data.plot;
+            var go = plot.eventCanvas._elem.offset();
+            var gridPos = {x:ev.pageX - go.left, y:ev.pageY - go.top};
+            var dataPos = {xaxis:null, yaxis:null, x2axis:null, y2axis:null, y3axis:null, y4axis:null, y5axis:null, y6axis:null, y7axis:null, y8axis:null, y9axis:null};
+            var an = ['xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis'];
+            var ax = plot.axes;
+            var n, axis;
+            for (n=11; n>0; n--) {
+                axis = an[n-1];
+                if (ax[axis].show) {
+                    dataPos[axis] = ax[axis].series_p2u(gridPos[axis.charAt(0)]);
+                }
+            }
+
+            return {offsets:go, gridPos:gridPos, dataPos:dataPos};
+        }
+        
+        
+        // function to check if event location is over a area area
+        function checkIntersection(gridpos, plot) {
+            var series = plot.series;
+            var i, j, k, s, r, x, y, theta, sm, sa, minang, maxang;
+            var d0, d, p, pp, points, bw;
+            var threshold, t;
+            for (k=plot.seriesStack.length-1; k>=0; k--) {
+                i = plot.seriesStack[k];
+                s = series[i];
+                switch (s.renderer.constructor) {
+                    case $.jqplot.BarRenderer:
+                        x = gridpos.x;
+                        y = gridpos.y;
+                        for (j=s.gridData.length-1; j>=0; j--) {
+                            points = s._barPoints[j];
+                            if (x>points[0][0] && x<points[2][0] && y>points[2][1] && y<points[0][1]) {
+                                return {seriesIndex:s.index, pointIndex:j, gridData:p, data:s.data[j], points:s._barPoints[j]};
+                            }
+                        }
+                        break;
+                    
+                    case $.jqplot.DonutRenderer:
+                        sa = s.startAngle/180*Math.PI;
+                        x = gridpos.x - s._center[0];
+                        y = gridpos.y - s._center[1];
+                        r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
+                        if (x > 0 && -y >= 0) {
+                            theta = 2*Math.PI - Math.atan(-y/x);
+                        }
+                        else if (x > 0 && -y < 0) {
+                            theta = -Math.atan(-y/x);
+                        }
+                        else if (x < 0) {
+                            theta = Math.PI - Math.atan(-y/x);
+                        }
+                        else if (x == 0 && -y > 0) {
+                            theta = 3*Math.PI/2;
+                        }
+                        else if (x == 0 && -y < 0) {
+                            theta = Math.PI/2;
+                        }
+                        else if (x == 0 && y == 0) {
+                            theta = 0;
+                        }
+                        if (sa) {
+                            theta -= sa;
+                            if (theta < 0) {
+                                theta += 2*Math.PI;
+                            }
+                            else if (theta > 2*Math.PI) {
+                                theta -= 2*Math.PI;
+                            }
+                        }
+            
+                        sm = s.sliceMargin/180*Math.PI;
+                        if (r < s._radius && r > s._innerRadius) {
+                            for (j=0; j<s.gridData.length; j++) {
+                                minang = (j>0) ? s.gridData[j-1][1]+sm : sm;
+                                maxang = s.gridData[j][1];
+                                if (theta > minang && theta < maxang) {
+                                    return {seriesIndex:s.index, pointIndex:j, gridData:s.gridData[j], data:s.data[j]};
+                                }
+                            }
+                        }
+                        break;
+                        
+                    case $.jqplot.PieRenderer:
+                        sa = s.startAngle/180*Math.PI;
+                        x = gridpos.x - s._center[0];
+                        y = gridpos.y - s._center[1];
+                        r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
+                        if (x > 0 && -y >= 0) {
+                            theta = 2*Math.PI - Math.atan(-y/x);
+                        }
+                        else if (x > 0 && -y < 0) {
+                            theta = -Math.atan(-y/x);
+                        }
+                        else if (x < 0) {
+                            theta = Math.PI - Math.atan(-y/x);
+                        }
+                        else if (x == 0 && -y > 0) {
+                            theta = 3*Math.PI/2;
+                        }
+                        else if (x == 0 && -y < 0) {
+                            theta = Math.PI/2;
+                        }
+                        else if (x == 0 && y == 0) {
+                            theta = 0;
+                        }
+                        if (sa) {
+                            theta -= sa;
+                            if (theta < 0) {
+                                theta += 2*Math.PI;
+                            }
+                            else if (theta > 2*Math.PI) {
+                                theta -= 2*Math.PI;
+                            }
+                        }
+            
+                        sm = s.sliceMargin/180*Math.PI;
+                        if (r < s._radius) {
+                            for (j=0; j<s.gridData.length; j++) {
+                                minang = (j>0) ? s.gridData[j-1][1]+sm : sm;
+                                maxang = s.gridData[j][1];
+                                if (theta > minang && theta < maxang) {
+                                    return {seriesIndex:s.index, pointIndex:j, gridData:s.gridData[j], data:s.data[j]};
+                                }
+                            }
+                        }
+                        break;
+                        
+                    case $.jqplot.BubbleRenderer:
+                        x = gridpos.x;
+                        y = gridpos.y;
+                        var ret = null;
+                        
+                        if (s.show) {
+                            for (var j=0; j<s.gridData.length; j++) {
+                                p = s.gridData[j];
+                                d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) );
+                                if (d <= p[2] && (d <= d0 || d0 == null)) {
+                                   d0 = d;
+                                   ret = {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
+                                }
+                            }
+                            if (ret != null) return ret;
+                        }
+                        break;
+                        
+                    case $.jqplot.FunnelRenderer:
+                        x = gridpos.x;
+                        y = gridpos.y;
+                        var v = s._vertices,
+                            vfirst = v[0],
+                            vlast = v[v.length-1],
+                            lex,
+                            rex;
+    
+                        // equations of right and left sides, returns x, y values given height of section (y value and 2 points)
+    
+                        function findedge (l, p1 , p2) {
+                            var m = (p1[1] - p2[1])/(p1[0] - p2[0]);
+                            var b = p1[1] - m*p1[0];
+                            var y = l + p1[1];
+        
+                            return [(y - b)/m, y];
+                        }
+    
+                        // check each section
+                        lex = findedge(y, vfirst[0], vlast[3]);
+                        rex = findedge(y, vfirst[1], vlast[2]);
+                        for (j=0; j<v.length; j++) {
+                            cv = v[j];
+                            if (y >= cv[0][1] && y <= cv[3][1] && x >= lex[0] && x <= rex[0]) {
+                                return {seriesIndex:s.index, pointIndex:j, gridData:null, data:s.data[j]};
+                            }
+                        }         
+                        break;           
+                    
+                    case $.jqplot.LineRenderer:
+                        x = gridpos.x;
+                        y = gridpos.y;
+                        r = s.renderer;
+                        if (s.show) {
+                            if (s.fill) {
+                                // first check if it is in bounding box
+                                var inside = false;
+                                if (x>s._boundingBox[0][0] && x<s._boundingBox[1][0] && y>s._boundingBox[1][1] && y<s._boundingBox[0][1]) { 
+                                    // now check the crossing number   
+                                    
+                                    var numPoints = s._areaPoints.length;
+                                    var ii;
+                                    var j = numPoints-1;
+
+                                    for(var ii=0; ii < numPoints; ii++) { 
+                                    	var vertex1 = [s._areaPoints[ii][0], s._areaPoints[ii][1]];
+                                    	var vertex2 = [s._areaPoints[j][0], s._areaPoints[j][1]];
+
+                                    	if (vertex1[1] < y && vertex2[1] >= y || vertex2[1] < y && vertex1[1] >= y)	 {
+                                    		if (vertex1[0] + (y - vertex1[1]) / (vertex2[1] - vertex1[1]) * (vertex2[0] - vertex1[0]) < x) {
+                                    			inside = !inside;
+                                    		}
+                                    	}
+
+                                    	j = ii;
+                                    }        
+                                }
+                                if (inside) {
+                                    return {seriesIndex:i, pointIndex:null, gridData:s.gridData, data:s.data, points:s._areaPoints};
+                                }
+                                break;
+                                
+                            }
+                            else {
+                                t = s.markerRenderer.size/2+s.neighborThreshold;
+                                threshold = (t > 0) ? t : 0;
+                                for (var j=0; j<s.gridData.length; j++) {
+                                    p = s.gridData[j];
+                                    // neighbor looks different to OHLC chart.
+                                    if (r.constructor == $.jqplot.OHLCRenderer) {
+                                        if (r.candleStick) {
+                                            var yp = s._yaxis.series_u2p;
+                                            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])) {
+                                                return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
+                                            }
+                                        }
+                                        // if an open hi low close chart
+                                        else if (!r.hlc){
+                                            var yp = s._yaxis.series_u2p;
+                                            if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) {
+                                                return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
+                                            }
+                                        }
+                                        // a hi low close chart
+                                        else {
+                                            var yp = s._yaxis.series_u2p;
+                                            if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) {
+                                                return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
+                                            }
+                                        }
+                            
+                                    }
+                                    else if (p[0] != null && p[1] != null){
+                                        d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) );
+                                        if (d <= threshold && (d <= d0 || d0 == null)) {
+                                           d0 = d;
+                                           return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
+                                        }
+                                    }
+                                } 
+                            }
+                        }
+                        break;
+                        
+                    default:
+                        x = gridpos.x;
+                        y = gridpos.y;
+                        r = s.renderer;
+                        if (s.show) {
+                            t = s.markerRenderer.size/2+s.neighborThreshold;
+                            threshold = (t > 0) ? t : 0;
+                            for (var j=0; j<s.gridData.length; j++) {
+                                p = s.gridData[j];
+                                // neighbor looks different to OHLC chart.
+                                if (r.constructor == $.jqplot.OHLCRenderer) {
+                                    if (r.candleStick) {
+                                        var yp = s._yaxis.series_u2p;
+                                        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])) {
+                                            return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
+                                        }
+                                    }
+                                    // if an open hi low close chart
+                                    else if (!r.hlc){
+                                        var yp = s._yaxis.series_u2p;
+                                        if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) {
+                                            return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
+                                        }
+                                    }
+                                    // a hi low close chart
+                                    else {
+                                        var yp = s._yaxis.series_u2p;
+                                        if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) {
+                                            return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
+                                        }
+                                    }
+                            
+                                }
+                                else {
+                                    d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) );
+                                    if (d <= threshold && (d <= d0 || d0 == null)) {
+                                       d0 = d;
+                                       return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]};
+                                    }
+                                }
+                            } 
+                        }
+                        break;
+                }
+            }
+            
+            return null;
+        }
+        
+        
+        
+        this.onClick = function(ev) {
+            // Event passed in is normalized and will have data attribute.
+            // Event passed out is unnormalized.
+            var positions = getEventPosition(ev);
+            var p = ev.data.plot;
+            var neighbor = checkIntersection(positions.gridPos, p);
+            var evt = jQuery.Event('jqplotClick');
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
+        };
+        
+        this.onDblClick = function(ev) {
+            // Event passed in is normalized and will have data attribute.
+            // Event passed out is unnormalized.
+            var positions = getEventPosition(ev);
+            var p = ev.data.plot;
+            var neighbor = checkIntersection(positions.gridPos, p);
+            var evt = jQuery.Event('jqplotDblClick');
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
+        };
+        
+        this.onMouseDown = function(ev) {
+            var positions = getEventPosition(ev);
+            var p = ev.data.plot;
+            var neighbor = checkIntersection(positions.gridPos, p);
+            var evt = jQuery.Event('jqplotMouseDown');
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
+        };
+        
+        this.onMouseUp = function(ev) {
+            var positions = getEventPosition(ev);
+            var evt = jQuery.Event('jqplotMouseUp');
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, ev.data.plot]);
+        };
+        
+        this.onRightClick = function(ev) {
+            var positions = getEventPosition(ev);
+            var p = ev.data.plot;
+            var neighbor = checkIntersection(positions.gridPos, p);
+            if (p.captureRightClick) {
+                if (ev.which == 3) {
+                var evt = jQuery.Event('jqplotRightClick');
+                evt.pageX = ev.pageX;
+                evt.pageY = ev.pageY;
+                    $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
+                }
+                else {
+                var evt = jQuery.Event('jqplotMouseUp');
+                evt.pageX = ev.pageX;
+                evt.pageY = ev.pageY;
+                    $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
+                }
+            }
+        };
+        
+        this.onMouseMove = function(ev) {
+            var positions = getEventPosition(ev);
+            var p = ev.data.plot;
+            var neighbor = checkIntersection(positions.gridPos, p);
+            var evt = jQuery.Event('jqplotMouseMove');
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
+        };
+        
+        this.onMouseEnter = function(ev) {
+            var positions = getEventPosition(ev);
+            var p = ev.data.plot;
+            var evt = jQuery.Event('jqplotMouseEnter');
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, p]);
+        };
+        
+        this.onMouseLeave = function(ev) {
+            var positions = getEventPosition(ev);
+            var p = ev.data.plot;
+            var evt = jQuery.Event('jqplotMouseLeave');
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, p]);
+        };
+        
+        // method: drawSeries
+        // Redraws all or just one series on the plot.  No axis scaling
+        // is performed and no other elements on the plot are redrawn.
+        // options is an options object to pass on to the series renderers.
+        // It can be an empty object {}.  idx is the series index
+        // to redraw if only one series is to be redrawn.
+        this.drawSeries = function(options, idx){
+            var i, series, ctx;
+            // if only one argument passed in and it is a number, use it ad idx.
+            idx = (typeof(options) == "number" && idx == null) ? options : idx;
+            options = (typeof(options) == "object") ? options : {};
+            // draw specified series
+            if (idx != undefined) {
+                series = this.series[idx];
+                ctx = series.shadowCanvas._ctx;
+                ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+                series.drawShadow(ctx, options, this);
+                ctx = series.canvas._ctx;
+                ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+                series.draw(ctx, options, this);
+                if (series.renderer.constructor == $.jqplot.BezierCurveRenderer) {
+                    if (idx < this.series.length - 1) {
+                        this.drawSeries(idx+1); 
+                    }
+                }
+            }
+            
+            else {
+                // if call series drawShadow method first, in case all series shadows
+                // should be drawn before any series.  This will ensure, like for 
+                // stacked bar plots, that shadows don't overlap series.
+                for (i=0; i<this.series.length; i++) {
+                    // first clear the canvas
+                    series = this.series[i];
+                    ctx = series.shadowCanvas._ctx;
+                    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+                    series.drawShadow(ctx, options, this);
+                    ctx = series.canvas._ctx;
+                    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+                    series.draw(ctx, options, this);
+                }
+            }
+        };
+        
+        // method: moveSeriesToFront
+        // This method requires jQuery 1.4+
+        // Moves the specified series canvas in front of all other series canvases.
+        // This effectively "draws" the specified series on top of all other series,
+        // although it is performed through DOM manipulation, no redrawing is performed.
+        //
+        // Parameters:
+        // idx - 0 based index of the series to move.  This will be the index of the series
+        // as it was first passed into the jqplot function.
+        this.moveSeriesToFront = function (idx) { 
+            idx = parseInt(idx, 10);
+            var stackIndex = $.inArray(idx, this.seriesStack);
+            // if already in front, return
+            if (stackIndex == -1) {
+                return;
+            }
+            if (stackIndex == this.seriesStack.length -1) {
+                this.previousSeriesStack = this.seriesStack.slice(0);
+                return;
+            }
+            var opidx = this.seriesStack[this.seriesStack.length -1];
+            var serelem = this.series[idx].canvas._elem.detach();
+            var shadelem = this.series[idx].shadowCanvas._elem.detach();
+            this.series[opidx].shadowCanvas._elem.after(shadelem);
+            this.series[opidx].canvas._elem.after(serelem);
+            this.previousSeriesStack = this.seriesStack.slice(0);
+            this.seriesStack.splice(stackIndex, 1);
+            this.seriesStack.push(idx);
+        };
+        
+        // method: moveSeriesToBack
+        // This method requires jQuery 1.4+
+        // Moves the specified series canvas behind all other series canvases.
+        //
+        // Parameters:
+        // idx - 0 based index of the series to move.  This will be the index of the series
+        // as it was first passed into the jqplot function.
+        this.moveSeriesToBack = function (idx) {
+            idx = parseInt(idx, 10);
+            var stackIndex = $.inArray(idx, this.seriesStack);
+            // if already in back, return
+            if (stackIndex == 0 || stackIndex == -1) {
+                return;
+            }
+            var opidx = this.seriesStack[0];
+            var serelem = this.series[idx].canvas._elem.detach();
+            var shadelem = this.series[idx].shadowCanvas._elem.detach();
+            this.series[opidx].shadowCanvas._elem.before(shadelem);
+            this.series[opidx].canvas._elem.before(serelem);
+            this.previousSeriesStack = this.seriesStack.slice(0);
+            this.seriesStack.splice(stackIndex, 1);
+            this.seriesStack.unshift(idx);
+        };
+        
+        // method: restorePreviousSeriesOrder
+        // This method requires jQuery 1.4+
+        // Restore the series canvas order to its previous state.
+        // Useful to put a series back where it belongs after moving
+        // it to the front.
+        this.restorePreviousSeriesOrder = function () {
+            var i, j, serelem, shadelem, temp;
+            // if no change, return.
+            if (this.seriesStack == this.previousSeriesStack) {
+                return;
+            }
+            for (i=1; i<this.previousSeriesStack.length; i++) {
+                move = this.previousSeriesStack[i];
+                keep = this.previousSeriesStack[i-1];
+                serelem = this.series[move].canvas._elem.detach();
+                shadelem = this.series[move].shadowCanvas._elem.detach();
+                this.series[keep].shadowCanvas._elem.after(shadelem);
+                this.series[keep].canvas._elem.after(serelem);
+            }
+            temp = this.seriesStack.slice(0);
+            this.seriesStack = this.previousSeriesStack.slice(0);
+            this.previousSeriesStack = temp;
+        };
+        
+        // method: restoreOriginalSeriesOrder
+        // This method requires jQuery 1.4+
+        // Restore the series canvas order to its original order
+        // when the plot was created.
+        this.restoreOriginalSeriesOrder = function () {
+            var i, j, arr=[];
+            for (i=0; i<this.series.length; i++) {
+                arr.push(i);
+            }
+            if (this.seriesStack == arr) {
+                return;
+            }
+            this.previousSeriesStack = this.seriesStack.slice(0);
+            this.seriesStack = arr;
+            for (i=1; i<this.seriesStack.length; i++) {
+                serelem = this.series[i].canvas._elem.detach();
+                shadelem = this.series[i].shadowCanvas._elem.detach();
+                this.series[i-1].shadowCanvas._elem.after(shadelem);
+                this.series[i-1].canvas._elem.after(serelem);
+            }
+        };
+        
+        this.activateTheme = function (name) {
+            this.themeEngine.activate(this, name);
+        };
+    }
+    
+    
+    // conpute a highlight color or array of highlight colors from given colors.
+    $.jqplot.computeHighlightColors  = function(colors) {
+        var ret;
+        if (typeof(colors) == "array") {
+            ret = [];
+            for (var i=0; i<colors.length; i++){
+                var rgba = $.jqplot.getColorComponents(colors[i]);
+                var newrgb = [rgba[0], rgba[1], rgba[2]];
+                var sum = newrgb[0] + newrgb[1] + newrgb[2];
+                for (var j=0; j<3; j++) {
+                    // when darkening, lowest color component can be is 60.
+                    newrgb[j] = (sum > 570) ?  newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]);
+                    newrgb[j] = parseInt(newrgb[j], 10);
+                }
+                ret.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')');
+            }
+        }
+        else {
+            var rgba = $.jqplot.getColorComponents(colors);
+            var newrgb = [rgba[0], rgba[1], rgba[2]];
+            var sum = newrgb[0] + newrgb[1] + newrgb[2];
+            for (var j=0; j<3; j++) {
+                // when darkening, lowest color component can be is 60.
+                newrgb[j] = (sum > 570) ?  newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]);
+                newrgb[j] = parseInt(newrgb[j], 10);
+            }
+            ret = 'rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')';
+        }
+        return ret;
+    };
+        
+   $.jqplot.ColorGenerator = function(colors) {
+        var idx = 0;
+        
+        this.next = function () { 
+            if (idx < colors.length) {
+                return colors[idx++];
+            }
+            else {
+                idx = 0;
+                return colors[idx++];
+            }
+        };
+        
+        this.previous = function () { 
+            if (idx > 0) {
+                return colors[idx--];
+            }
+            else {
+                idx = colors.length-1;
+                return colors[idx];
+            }
+        };
+        
+        // get a color by index without advancing pointer.
+        this.get = function(i) {
+            var idx = i - colors.length * Math.floor(i/colors.length);
+            return colors[idx];
+        };
+        
+        this.setColors = function(c) {
+            colors = c;
+        };
+        
+        this.reset = function() {
+            idx = 0;
+        };
+    };
+
+    // convert a hex color string to rgb string.
+    // h - 3 or 6 character hex string, with or without leading #
+    // a - optional alpha
+    $.jqplot.hex2rgb = function(h, a) {
+        h = h.replace('#', '');
+        if (h.length == 3) {
+            h = h[0]+h[0]+h[1]+h[1]+h[2]+h[2];
+        }
+        var rgb;
+        rgb = 'rgba('+parseInt(h.slice(0,2), 16)+', '+parseInt(h.slice(2,4), 16)+', '+parseInt(h.slice(4,6), 16);
+        if (a) {
+            rgb += ', '+a;
+        }
+        rgb += ')';
+        return rgb;
+    };
+    
+    // convert an rgb color spec to a hex spec.  ignore any alpha specification.
+    $.jqplot.rgb2hex = function(s) {
+        var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *(?:, *[0-9.]*)?\)/;
+        var m = s.match(pat);
+        var h = '#';
+        for (i=1; i<4; i++) {
+            var temp;
+            if (m[i].search(/%/) != -1) {
+                temp = parseInt(255*m[i]/100, 10).toString(16);
+                if (temp.length == 1) {
+                    temp = '0'+temp;
+                }
+            }
+            else {
+                temp = parseInt(m[i], 10).toString(16);
+                if (temp.length == 1) {
+                    temp = '0'+temp;
+                }
+            }
+            h += temp;
+        }
+        return h;
+    };
+    
+    // given a css color spec, return an rgb css color spec
+    $.jqplot.normalize2rgb = function(s, a) {
+        if (s.search(/^ *rgba?\(/) != -1) {
+            return s; 
+        }
+        else if (s.search(/^ *#?[0-9a-fA-F]?[0-9a-fA-F]/) != -1) {
+            return $.jqplot.hex2rgb(s, a);
+        }
+        else {
+            throw 'invalid color spec';
+        }
+    };
+    
+    // extract the r, g, b, a color components out of a css color spec.
+    $.jqplot.getColorComponents = function(s) {
+        // check to see if a color keyword.
+        s = $.jqplot.colorKeywordMap[s] || s;
+        var rgb = $.jqplot.normalize2rgb(s);
+        var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *,? *([0-9.]* *)?\)/;
+        var m = rgb.match(pat);
+        var ret = [];
+        for (i=1; i<4; i++) {
+            if (m[i].search(/%/) != -1) {
+                ret[i-1] = parseInt(255*m[i]/100, 10);
+            }
+            else {
+                ret[i-1] = parseInt(m[i], 10);
+            }
+        }
+        ret[3] = parseFloat(m[4]) ? parseFloat(m[4]) : 1.0;
+        return ret;
+    };
+    
+    $.jqplot.colorKeywordMap = {aliceblue: 'rgb(240, 248, 255)', antiquewhite: 'rgb(250, 235, 215)', aqua: 'rgb( 0, 255, 255)', aquamarine: 'rgb(127, 255, 212)', azure: 'rgb(240, 255, 255)', beige: 'rgb(245, 245, 220)', bisque: 'rgb(255, 228, 196)', black: 'rgb( 0, 0, 0)', blanchedalmond: 'rgb(255, 235, 205)', blue: 'rgb( 0, 0, 255)', blueviolet: 'rgb(138, 43, 226)', brown: 'rgb(165, 42, 42)', burlywood: 'rgb(222, 184, 135)', cadetblue: 'rgb( 95, 158, 160)', chartreuse: 'rgb(127, 255, 0)', chocolate: 'rgb(210, 105, 30)', coral: 'rgb(255, 127, 80)', cornflowerblue: 'rgb(100, 149, 237)', cornsilk: 'rgb(255, 248, 220)', crimson: 'rgb(220, 20, 60)', cyan: 'rgb( 0, 255, 255)', darkblue: 'rgb( 0, 0, 139)', darkcyan: 'rgb( 0, 139, 139)', darkgoldenrod: 'rgb(184, 134, 11)', darkgray: 'rgb(169, 169, 169)', darkgreen: 'rgb( 0, 100, 0)', darkgrey: 'rgb(169, 169, 169)', darkkhaki: 'rgb(189, 183, 107)', darkmagenta: 'rgb(139, 0, 139)', darkolivegreen: 'rgb( 85, 107, 47)', darkorange: 'rgb(255, 140, 0)', darkorchid: 'rgb(153, 50, 204)', darkred: 'rgb(139, 0, 0)', darksalmon: 'rgb(233, 150, 122)', darkseagreen: 'rgb(143, 188, 143)', darkslateblue: 'rgb( 72, 61, 139)', darkslategray: 'rgb( 47, 79, 79)', darkslategrey: 'rgb( 47, 79, 79)', darkturquoise: 'rgb( 0, 206, 209)', darkviolet: 'rgb(148, 0, 211)', deeppink: 'rgb(255, 20, 147)', deepskyblue: 'rgb( 0, 191, 255)', dimgray: 'rgb(105, 105, 105)', dimgrey: 'rgb(105, 105, 105)', dodgerblue: 'rgb( 30, 144, 255)', firebrick: 'rgb(178, 34, 34)', floralwhite: 'rgb(255, 250, 240)', forestgreen: 'rgb( 34, 139, 34)', fuchsia: 'rgb(255, 0, 255)', gainsboro: 'rgb(220, 220, 220)', ghostwhite: 'rgb(248, 248, 255)', gold: 'rgb(255, 215, 0)', goldenrod: 'rgb(218, 165, 32)', gray: 'rgb(128, 128, 128)', grey: 'rgb(128, 128, 128)', green: 'rgb( 0, 128, 0)', greenyellow: 'rgb(173, 255, 47)', honeydew: 'rgb(240, 255, 240)', hotpink: 'rgb(255, 105, 180)', indianred: 'rgb(205, 92, 92)', indigo: 'rgb( 75, 0, 130)', ivory: 'rgb(255, 255, 240)', khaki: 'rgb(240, 230, 140)', lavender: 'rgb(23!
 0, 230, 250)', lavenderblush: 'rgb(255, 240, 245)', lawngreen: 'rgb(124, 252, 0)', lemonchiffon: 'rgb(255, 250, 205)', lightblue: 'rgb(173, 216, 230)', lightcoral: 'rgb(240, 128, 128)', lightcyan: 'rgb(224, 255, 255)', lightgoldenrodyellow: 'rgb(250, 250, 210)', lightgray: 'rgb(211, 211, 211)', lightgreen: 'rgb(144, 238, 144)', lightgrey: 'rgb(211, 211, 211)', lightpink: 'rgb(255, 182, 193)', lightsalmon: 'rgb(255, 160, 122)', lightseagreen: 'rgb( 32, 178, 170)', lightskyblue: 'rgb(135, 206, 250)', lightslategray: 'rgb(119, 136, 153)', lightslategrey: 'rgb(119, 136, 153)', lightsteelblue: 'rgb(176, 196, 222)', lightyellow: 'rgb(255, 255, 224)', lime: 'rgb( 0, 255, 0)', limegreen: 'rgb( 50, 205, 50)', linen: 'rgb(250, 240, 230)', magenta: 'rgb(255, 0, 255)', maroon: 'rgb(128, 0, 0)', mediumaquamarine: 'rgb(102, 205, 170)', mediumblue: 'rgb( 0, 0, 205)', mediumorchid: 'rgb(186, 85, 211)', mediumpurple: 'rgb(147, 112, 219)', mediumseagreen: 'rgb( 60, 179, 113)', mediumslateblue: 'rgb(123, 104, 238)', mediumspringgreen: 'rgb( 0, 250, 154)', mediumturquoise: 'rgb( 72, 209, 204)', mediumvioletred: 'rgb(199, 21, 133)', midnightblue: 'rgb( 25, 25, 112)', mintcream: 'rgb(245, 255, 250)', mistyrose: 'rgb(255, 228, 225)', moccasin: 'rgb(255, 228, 181)', navajowhite: 'rgb(255, 222, 173)', navy: 'rgb( 0, 0, 128)', oldlace: 'rgb(253, 245, 230)', olive: 'rgb(128, 128, 0)', olivedrab: 'rgb(107, 142, 35)', orange: 'rgb(255, 165, 0)', orangered: 'rgb(255, 69, 0)', orchid: 'rgb(218, 112, 214)', palegoldenrod: 'rgb(238, 232, 170)', palegreen: 'rgb(152, 251, 152)', paleturquoise: 'rgb(175, 238, 238)', palevioletred: 'rgb(219, 112, 147)', papayawhip: 'rgb(255, 239, 213)', peachpuff: 'rgb(255, 218, 185)', peru: 'rgb(205, 133, 63)', pink: 'rgb(255, 192, 203)', plum: 'rgb(221, 160, 221)', powderblue: 'rgb(176, 224, 230)', purple: 'rgb(128, 0, 128)', red: 'rgb(255, 0, 0)', rosybrown: 'rgb(188, 143, 143)', royalblue: 'rgb( 65, 105, 225)', saddlebrown: 'rgb(139, 69, 19)', salmon: 'rgb(250, 128, 114)', sandybrown: 'rgb(244, 164, 96)', seagr!
 een: 'rgb( 46, 139, 87)', seashell: 'rgb(255, 245, 238)', sienna: 'rgb(160, 82, 45)', silver: 'rgb(192, 192, 192)', skyblue: 'rgb(135, 206, 235)', slateblue: 'rgb(106, 90, 205)', slategray: 'rgb(112, 128, 144)', slategrey: 'rgb(112, 128, 144)', snow: 'rgb(255, 250, 250)', springgreen: 'rgb( 0, 255, 127)', steelblue: 'rgb( 70, 130, 180)', tan: 'rgb(210, 180, 140)', teal: 'rgb( 0, 128, 128)', thistle: 'rgb(216, 191, 216)', tomato: 'rgb(255, 99, 71)', turquoise: 'rgb( 64, 224, 208)', violet: 'rgb(238, 130, 238)', wheat: 'rgb(245, 222, 179)', white: 'rgb(255, 255, 255)', whitesmoke: 'rgb(245, 245, 245)', yellow: 'rgb(255, 255, 0)', yellowgreen: 'rgb(154, 205, 50)'};
+        
+    // Convienence function that won't hang IE.
+    $.jqplot.log = function() {
+        if (window.console && $.jqplot.debug) {
+           if (arguments.length == 1) {
+               console.log (arguments[0]);
+            }
+           else {
+               console.log(arguments);
+            }
+        }
+    };
+    var log = $.jqplot.log;
+    
+
+    // class: $.jqplot.AxisLabelRenderer
+    // Renderer to place labels on the axes.
+    $.jqplot.AxisLabelRenderer = function(options) {
+        // Group: Properties
+        $.jqplot.ElemContainer.call(this);
+        // name of the axis associated with this tick
+        this.axis;
+        // prop: show
+        // wether or not to show the tick (mark and label).
+        this.show = true;
+        // prop: label
+        // The text or html for the label.
+        this.label = '';
+        this.fontFamily = null;
+        this.fontSize = null;
+        this.textColor = null;
+        this._elem;
+        // prop: escapeHTML
+        // true to escape HTML entities in the label.
+        this.escapeHTML = false;
+        
+        $.extend(true, this, options);
+    };
+    
+    $.jqplot.AxisLabelRenderer.prototype = new $.jqplot.ElemContainer();
+    $.jqplot.AxisLabelRenderer.prototype.constructor = $.jqplot.AxisLabelRenderer;
+    
+    $.jqplot.AxisLabelRenderer.prototype.init = function(options) {
+        $.extend(true, this, options);
+    };
+    
+    $.jqplot.AxisLabelRenderer.prototype.draw = function() {
+        this._elem = $('<div style="position:absolute;" class="jqplot-'+this.axis+'-label"></div>');
+        
+        if (Number(this.label)) {
+            this._elem.css('white-space', 'nowrap');
+        }
+        
+        if (!this.escapeHTML) {
+            this._elem.html(this.label);
+        }
+        else {
+            this._elem.text(this.label);
+        }
+        if (this.fontFamily) {
+            this._elem.css('font-family', this.fontFamily);
+        }
+        if (this.fontSize) {
+            this._elem.css('font-size', this.fontSize);
+        }
+        if (this.textColor) {
+            this._elem.css('color', this.textColor);
+        }
+        
+        return this._elem;
+    };
+    
+    $.jqplot.AxisLabelRenderer.prototype.pack = function() {
+    };
+
+    // class: $.jqplot.AxisTickRenderer
+    // A "tick" object showing the value of a tick/gridline on the plot.
+    $.jqplot.AxisTickRenderer = function(options) {
+        // Group: Properties
+        $.jqplot.ElemContainer.call(this);
+        // prop: mark
+        // tick mark on the axis.  One of 'inside', 'outside', 'cross', '' or null.
+        this.mark = 'outside';
+        // name of the axis associated with this tick
+        this.axis;
+        // prop: showMark
+        // wether or not to show the mark on the axis.
+        this.showMark = true;
+        // prop: showGridline
+        // wether or not to draw the gridline on the grid at this tick.
+        this.showGridline = true;
+        // prop: isMinorTick
+        // if this is a minor tick.
+        this.isMinorTick = false;
+        // prop: size
+        // Length of the tick beyond the grid in pixels.
+        // DEPRECATED: This has been superceeded by markSize
+        this.size = 4;
+        // prop:  markSize
+        // Length of the tick marks in pixels.  For 'cross' style, length
+        // will be stoked above and below axis, so total length will be twice this.
+        this.markSize = 6;
+        // prop: show
+        // wether or not to show the tick (mark and label).
+        // Setting this to false requires more testing.  It is recommended
+        // to set showLabel and showMark to false instead.
+        this.show = true;
+        // prop: showLabel
+        // wether or not to show the label.
+        this.showLabel = true;
+        this.label = '';
+        this.value = null;
+        this._styles = {};
+        // prop: formatter
+        // A class of a formatter for the tick text.  sprintf by default.
+        this.formatter = $.jqplot.DefaultTickFormatter;
+        // prop: prefix
+        // string appended to the tick label if no formatString is specified.
+        this.prefix = '';
+        // prop: formatString
+        // string passed to the formatter.
+        this.formatString = '';
+        // prop: fontFamily
+        // css spec for the font-family css attribute.
+        this.fontFamily;
+        // prop: fontSize
+        // css spec for the font-size css attribute.
+        this.fontSize;
+        // prop: textColor
+        // css spec for the color attribute.
+        this.textColor;
+        this._elem;
+        
+        $.extend(true, this, options);
+    };
+    
+    $.jqplot.AxisTickRenderer.prototype.init = function(options) {
+        $.extend(true, this, options);
+    };
+    
+    $.jqplot.AxisTickRenderer.prototype = new $.jqplot.ElemContainer();
+    $.jqplot.AxisTickRenderer.prototype.constructor = $.jqplot.AxisTickRenderer;
+    
+    $.jqplot.AxisTickRenderer.prototype.setTick = function(value, axisName, isMinor) {
+        this.value = value;
+        this.axis = axisName;
+        if (isMinor) {
+            this.isMinorTick = true;
+        }
+        return this;
+    };
+    
+    $.jqplot.AxisTickRenderer.prototype.draw = function() {
+        if (!this.label) {
+            this.label = this.formatter(this.formatString, this.value);
+        }
+        // add prefix if needed
+        if (this.prefix && !this.formatString) {
+            this.label = this.prefix + this.label;
+        }
+        style ='style="position:absolute;';
+        if (Number(this.label)) {
+            style +='white-space:nowrap;';
+        }
+        style += '"';
+        this._elem = $('<div '+style+' class="jqplot-'+this.axis+'-tick">'+this.label+'</div>');
+        for (var s in this._styles) {
+            this._elem.css(s, this._styles[s]);
+        }
+        if (this.fontFamily) {
+            this._elem.css('font-family', this.fontFamily);
+        }
+        if (this.fontSize) {
+            this._elem.css('font-size', this.fontSize);
+        }
+        if (this.textColor) {
+            this._elem.css('color', this.textColor);
+        }
+        return this._elem;
+    };
+        
+    $.jqplot.DefaultTickFormatter = function (format, val) {
+        if (typeof val == 'number') {
+            if (!format) {
+                format = $.jqplot.config.defaultTickFormatString;
+            }
+            return $.jqplot.sprintf(format, val);
+        }
+        else {
+            return String(val);
+        }
+    };
+    
+    $.jqplot.AxisTickRenderer.prototype.pack = function() {
+    };
+     
+    // Class: $.jqplot.CanvasGridRenderer
+    // The default jqPlot grid renderer, creating a grid on a canvas element.
+    // The renderer has no additional options beyond the <Grid> class.
+    $.jqplot.CanvasGridRenderer = function(){
+        this.shadowRenderer = new $.jqplot.ShadowRenderer();
+    };
+    
+    // called with context of Grid object
+    $.jqplot.CanvasGridRenderer.prototype.init = function(options) {
+        this._ctx;
+        $.extend(true, this, options);
+        // set the shadow renderer options
+        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};
+        this.renderer.shadowRenderer.init(sopts);
+    };
+    
+    // called with context of Grid.
+    $.jqplot.CanvasGridRenderer.prototype.createElement = function() {
+        var elem = document.createElement('canvas');
+        var w = this._plotDimensions.width;
+        var h = this._plotDimensions.height;
+        elem.width = w;
+        elem.height = h;
+        this._elem = $(elem);
+        this._elem.addClass('jqplot-grid-canvas');
+        this._elem.css({ position: 'absolute', left: 0, top: 0 });
+        if ($.jqplot.use_excanvas) {
+            window.G_vmlCanvasManager.init_(document);
+        }
+        if ($.jqplot.use_excanvas) {
+            elem = window.G_vmlCanvasManager.initElement(elem);
+        }
+        this._top = this._offsets.top;
+        this._bottom = h - this._offsets.bottom;
+        this._left = this._offsets.left;
+        this._right = w - this._offsets.right;
+        this._width = this._right - this._left;
+        this._height = this._bottom - this._top;
+        return this._elem;
+    };
+    
+    $.jqplot.CanvasGridRenderer.prototype.draw = function() {
+        this._ctx = this._elem.get(0).getContext("2d");
+        var ctx = this._ctx;
+        var axes = this._axes;
+        // Add the grid onto the grid canvas.  This is the bottom most layer.
+        ctx.save();
+        ctx.clearRect(0, 0, this._plotDimensions.width, this._plotDimensions.height);
+        ctx.fillStyle = this.backgroundColor || this.background;
+        ctx.fillRect(this._left, this._top, this._width, this._height);
+        
+        if (this.drawGridlines) {
+            ctx.save();
+            ctx.lineJoin = 'miter';
+            ctx.lineCap = 'butt';
+            ctx.lineWidth = this.gridLineWidth;
+            ctx.strokeStyle = this.gridLineColor;
+            var b, e;
+            var ax = ['xaxis', 'yaxis', 'x2axis', 'y2axis'];
+            for (var i=4; i>0; i--) {
+                var name = ax[i-1];
+                var axis = axes[name];
+                var ticks = axis._ticks;
+                if (axis.show) {
+                    for (var j=ticks.length; j>0; j--) {
+                        var t = ticks[j-1];
+                        if (t.show) {
+                            var pos = Math.round(axis.u2p(t.value)) + 0.5;
+                            switch (name) {
+                                case 'xaxis':
+                                    // draw the grid line
+                                    if (t.showGridline) {
+                                        drawLine(pos, this._top, pos, this._bottom);
+                                    }
+                                    
+                                    // draw the mark
+                                    if (t.showMark && t.mark) {
+                                        s = t.markSize;
+                                        m = t.mark;
+                                        var pos = Math.round(axis.u2p(t.value)) + 0.5;
+                                        switch (m) {
+                                            case 'outside':
+                                                b = this._bottom;
+                                                e = this._bottom+s;
+                                                break;
+                                            case 'inside':
+                                                b = this._bottom-s;
+                                                e = this._bottom;
+                                                break;
+                                            case 'cross':
+                                                b = this._bottom-s;
+                                                e = this._bottom+s;
+                                                break;
+                                            default:
+                                                b = this._bottom;
+                                                e = this._bottom+s;
+                                                break;
+                                        }
+                                        // draw the shadow
+                                        if (this.shadow) {
+                                            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});
+                                        }
+                                        // draw the line
+                                        drawLine(pos, b, pos, e);
+                                    }
+                                    break;
+                                case 'yaxis':
+                                    // draw the grid line
+                                    if (t.showGridline) {
+                                        drawLine(this._right, pos, this._left, pos);
+                                    }
+                                    // draw the mark
+                                    if (t.showMark && t.mark) {
+                                        s = t.markSize;
+                                        m = t.mark;
+                                        var pos = Math.round(axis.u2p(t.value)) + 0.5;
+                                        switch (m) {
+                                            case 'outside':
+                                                b = this._left-s;
+                                                e = this._left;
+                                                break;
+                                            case 'inside':
+                                                b = this._left;
+                                                e = this._left+s;
+                                                break;
+                                            case 'cross':
+                                                b = this._left-s;
+                                                e = this._left+s;
+                                                break;
+                                            default:
+                                                b = this._left-s;
+                                                e = this._left;
+                                                break;
+                                                }
+                                        // draw the shadow
+                                        if (this.shadow) {
+                                            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});
+                                        }
+                                        drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
+                                    }
+                                    break;
+                                case 'x2axis':
+                                    // draw the grid line
+                                    if (t.showGridline) {
+                                        drawLine(pos, this._bottom, pos, this._top);
+                                    }
+                                    // draw the mark
+                                    if (t.showMark && t.mark) {
+                                        s = t.markSize;
+                                        m = t.mark;
+                                        var pos = Math.round(axis.u2p(t.value)) + 0.5;
+                                        switch (m) {
+                                            case 'outside':
+                                                b = this._top-s;
+                                                e = this._top;
+                                                break;
+                                            case 'inside':
+                                                b = this._top;
+                                                e = this._top+s;
+                                                break;
+                                            case 'cross':
+                                                b = this._top-s;
+                                                e = this._top+s;
+                                                break;
+                                            default:
+                                                b = this._top-s;
+                                                e = this._top;
+                                                break;
+                                                }
+                                        // draw the shadow
+                                        if (this.shadow) {
+                                            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});
+                                        }
+                                        drawLine(pos, b, pos, e);
+                                    }
+                                    break;
+                                case 'y2axis':
+                                    // draw the grid line
+                                    if (t.showGridline) {
+                                        drawLine(this._left, pos, this._right, pos);
+                                    }
+                                    // draw the mark
+                                    if (t.showMark && t.mark) {
+                                        s = t.markSize;
+                                        m = t.mark;
+                                        var pos = Math.round(axis.u2p(t.value)) + 0.5;
+                                        switch (m) {
+                                            case 'outside':
+                                                b = this._right;
+                                                e = this._right+s;
+                                                break;
+                                            case 'inside':
+                                                b = this._right-s;
+                                                e = this._right;
+                                                break;
+                                            case 'cross':
+                                                b = this._right-s;
+                                                e = this._right+s;
+                                                break;
+                                            default:
+                                                b = this._right;
+                                                e = this._right+s;
+                                                break;
+                                                }
+                                        // draw the shadow
+                                        if (this.shadow) {
+                                            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});
+                                        }
+                                        drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
+                                    }
+                                    break;
+                                default:
+                                    break;
+                            }
+                        }
+                    }
+                }
+            }
+            // Now draw grid lines for additional y axes
+            ax = ['y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis'];
+            for (var i=7; i>0; i--) {
+                var axis = axes[ax[i-1]];
+                var ticks = axis._ticks;
+                if (axis.show) {
+                    var tn = ticks[axis.numberTicks-1];
+                    var t0 = ticks[0];
+                    var left = axis.getLeft();
+                    var points = [[left, tn.getTop() + tn.getHeight()/2], [left, t0.getTop() + t0.getHeight()/2 + 1.0]];
+                    // draw the shadow
+                    if (this.shadow) {
+                        this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', fill:false, closePath:false});
+                    }
+                    // draw the line
+                    drawLine(points[0][0], points[0][1], points[1][0], points[1][1], {lineCap:'butt', strokeStyle:axis.borderColor, lineWidth:axis.borderWidth});
+                    // draw the tick marks
+                    for (var j=ticks.length; j>0; j--) {
+                        var t = ticks[j-1];
+                        s = t.markSize;
+                        m = t.mark;
+                        var pos = Math.round(axis.u2p(t.value)) + 0.5;
+                        if (t.showMark && t.mark) {
+                            switch (m) {
+                                case 'outside':
+                                    b = left;
+                                    e = left+s;
+                                    break;
+                                case 'inside':
+                                    b = left-s;
+                                    e = left;
+                                    break;
+                                case 'cross':
+                                    b = left-s;
+                                    e = left+s;
+                                    break;
+                                default:
+                                    b = left;
+                                    e = left+s;
+                                    break;
+                            }
+                            points = [[b,pos], [e,pos]];
+                            // draw the shadow
+                            if (this.shadow) {
+                                this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false});
+                            }
+                            // draw the line
+                            drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
+                        }
+                    }
+                }
+            }
+            
+            ctx.restore();
+        }
+        
+        function drawLine(bx, by, ex, ey, opts) {
+            ctx.save();
+            opts = opts || {};
+            if (opts.lineWidth == null || opts.lineWidth != 0){
+                $.extend(true, ctx, opts);
+                ctx.beginPath();
+                ctx.moveTo(bx, by);
+                ctx.lineTo(ex, ey);
+                ctx.stroke();
+                ctx.restore();
+            }
+        }
+        
+        if (this.shadow) {
+            var points = [[this._left, this._bottom], [this._right, this._bottom], [this._right, this._top]];
+            this.renderer.shadowRenderer.draw(ctx, points);
+        }
+        // Now draw border around grid.  Use axis border definitions. start at
+        // upper left and go clockwise.
+        if (this.borderWidth != 0 && this.drawBorder) {
+            drawLine (this._left, this._top, this._right, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth});
+            drawLine (this._right, this._top, this._right, this._bottom, {lineCap:'round', strokeStyle:axes.y2axis.borderColor, lineWidth:axes.y2axis.borderWidth});
+            drawLine (this._right, this._bottom, this._left, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth});
+            drawLine (this._left, this._bottom, this._left, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth});
+        }
+        // ctx.lineWidth = this.borderWidth;
+        // ctx.strokeStyle = this.borderColor;
+        // ctx.strokeRect(this._left, this._top, this._width, this._height);
+        
+    
+        ctx.restore();
+    };
+   
+    /**
+     * Date instance methods
+     *
+     * @author Ken Snyder (ken d snyder at gmail dot com)
+     * @date 2008-09-10
+     * @version 2.0.2 (http://kendsnyder.com/sandbox/date/)     
+     * @license Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/)
+     *
+     * @contributions Chris Leonello
+     * @comment Bug fix to 12 hour time and additions to handle milliseconds and 
+     * @comment 24 hour time without am/pm suffix
+     *
+     */
+ 
+    // begin by creating a scope for utility variables
+    
+    //
+    // pre-calculate the number of milliseconds in a day
+    //  
+    
+    var day = 24 * 60 * 60 * 1000;
+    //
+    // function to add leading zeros
+    //
+    var zeroPad = function(number, digits) {
+        number = String(number);
+        while (number.length < digits) {
+            number = '0' + number;
+        }
+        return number;
+    };
+    //
+    // set up integers and functions for adding to a date or subtracting two dates
+    //
+    var multipliers = {
+        millisecond: 1,
+        second: 1000,
+        minute: 60 * 1000,
+        hour: 60 * 60 * 1000,
+        day: day,
+        week: 7 * day,
+        month: {
+            // add a number of months
+            add: function(d, number) {
+                // add any years needed (increments of 12)
+                multipliers.year.add(d, Math[number > 0 ? 'floor' : 'ceil'](number / 12));
+                // ensure that we properly wrap betwen December and January
+                var prevMonth = d.getMonth() + (number % 12);
+                if (prevMonth == 12) {
+                    prevMonth = 0;
+                    d.setYear(d.getFullYear() + 1);
+                } else if (prevMonth == -1) {
+                    prevMonth = 11;
+                    d.setYear(d.getFullYear() - 1);
+                }
+                d.setMonth(prevMonth);
+            },
+            // get the number of months between two Date objects (decimal to the nearest day)
+            diff: function(d1, d2) {
+                // get the number of years
+                var diffYears = d1.getFullYear() - d2.getFullYear();
+                // get the number of remaining months
+                var diffMonths = d1.getMonth() - d2.getMonth() + (diffYears * 12);
+                // get the number of remaining days
+                var diffDays = d1.getDate() - d2.getDate();
+                // return the month difference with the days difference as a decimal
+                return diffMonths + (diffDays / 30);
+            }
+        },
+        year: {
+            // add a number of years
+            add: function(d, number) {
+                d.setYear(d.getFullYear() + Math[number > 0 ? 'floor' : 'ceil'](number));
+            },
+            // get the number of years between two Date objects (decimal to the nearest day)
+            diff: function(d1, d2) {
+                return multipliers.month.diff(d1, d2) / 12;
+            }
+        }        
+    };
+    //
+    // alias each multiplier with an 's' to allow 'year' and 'years' for example
+    //
+    for (var unit in multipliers) {
+        if (unit.substring(unit.length - 1) != 's') { // IE will iterate newly added properties :|
+            multipliers[unit + 's'] = multipliers[unit];
+        }
+    }
+    //
+    // take a date instance and a format code and return the formatted value
+    //
+    var format = function(d, code) {
+            if (Date.prototype.strftime.formatShortcuts[code]) {
+                    // process any shortcuts recursively
+                    return d.strftime(Date.prototype.strftime.formatShortcuts[code]);
+            } else {
+                    // get the format code function and toPaddedString() argument
+                    var getter = (Date.prototype.strftime.formatCodes[code] || '').split('.');
+                    var nbr = d['get' + getter[0]] ? d['get' + getter[0]]() : '';
+                    // run toPaddedString() if specified
+                    if (getter[1]) {
+                        nbr = zeroPad(nbr, getter[1]);
+                    }
+                    // prepend the leading character
+                    return nbr;
+            }       
+    };
+    //
+    // Add methods to Date instances
+    //
+    var instanceMethods = {
+        //
+        // Return a date one day ahead (or any other unit)
+        //
+        // @param string unit
+        // units: year | month | day | week | hour | minute | second | millisecond
+        // @return object Date
+        //
+        succ: function(unit) {
+            return this.clone().add(1, unit);
+        },
+        //
+        // Add an arbitrary amount to the currently stored date
+        //
+        // @param integer/float number      
+        // @param string unit
+        // @return object Date (chainable)      
+        //
+        add: function(number, unit) {
+            var factor = multipliers[unit] || multipliers.day;
+            if (typeof factor == 'number') {
+                this.setTime(this.getTime() + (factor * number));
+            } else {
+                factor.add(this, number);
+            }
+            return this;
+        },
+        //
+        // Find the difference between the current and another date
+        //
+        // @param string/object dateObj
+        // @param string unit
+        // @param boolean allowDecimal
+        // @return integer/float
+        //
+        diff: function(dateObj, unit, allowDecimal) {
+            // ensure we have a Date object
+            dateObj = Date.create(dateObj);
+            if (dateObj === null) {
+                return null;
+            }
+            // get the multiplying factor integer or factor function
+            var factor = multipliers[unit] || multipliers.day;
+            if (typeof factor == 'number') {
+                // multiply
+                var unitDiff = (this.getTime() - dateObj.getTime()) / factor;
+            } else {
+                // run function
+                var unitDiff = factor.diff(this, dateObj);
+            }
+            // if decimals are not allowed, round toward zero
+            return (allowDecimal ? unitDiff : Math[unitDiff > 0 ? 'floor' : 'ceil'](unitDiff));          
+        },
+        //
+        // Convert a date to a string using traditional strftime format codes
+        //
+        // @param string formatStr
+        // @return string
+        //
+        strftime: function(formatStr) {
+            // default the format string to year-month-day
+            var source = formatStr || '%Y-%m-%d', result = '', match;
+            // Account for display of time in local time or as UTC time
+            // var val = ($.jqplot.comfig.convertUTCtoLocaltime) ? this : 
+            // replace each format code
+            while (source.length > 0) {
+                if (match = source.match(Date.prototype.strftime.formatCodes.matcher)) {
+                    result += source.slice(0, match.index);
+                    result += (match[1] || '') + format(this, match[2]);
+                    source = source.slice(match.index + match[0].length);
+                } else {
+                    result += source;
+                    source = '';
+                }
+            }
+            return result;
+        },
+        //
+        // Return a proper two-digit year integer
+        //
+        // @return integer
+        //
+        getShortYear: function() {
+            return this.getYear() % 100;
+        },
+        //
+        // Get the number of the current month, 1-12
+        //
+        // @return integer
+        //
+        getMonthNumber: function() {
+            return this.getMonth() + 1;
+        },
+        //
+        // Get the name of the current month
+        //
+        // @return string
+        //
+        getMonthName: function() {
+            return Date.MONTHNAMES[this.getMonth()];
+        },
+        //
+        // Get the abbreviated name of the current month
+        //
+        // @return string
+        //
+        getAbbrMonthName: function() {
+            return Date.ABBR_MONTHNAMES[this.getMonth()];
+        },
+        //
+        // Get the name of the current week day
+        //
+        // @return string
+        //      
+        getDayName: function() {
+            return Date.DAYNAMES[this.getDay()];
+        },
+        //
+        // Get the abbreviated name of the current week day
+        //
+        // @return string
+        //      
+        getAbbrDayName: function() {
+            return Date.ABBR_DAYNAMES[this.getDay()];
+        },
+        //
+        // Get the ordinal string associated with the day of the month (i.e. st, nd, rd, th)
+        //
+        // @return string
+        //      
+        getDayOrdinal: function() {
+            return Date.ORDINALNAMES[this.getDate() % 10];
+        },
+        //
+        // Get the current hour on a 12-hour scheme
+        //
+        // @return integer
+        //
+        getHours12: function() {
+            var hours = this.getHours();
+            return hours > 12 ? hours - 12 : (hours == 0 ? 12 : hours);
+        },
+        //
+        // Get the AM or PM for the current time
+        //
+        // @return string
+        //
+        getAmPm: function() {
+            return this.getHours() >= 12 ? 'PM' : 'AM';
+        },
+        //
+        // Get the current date as a Unix timestamp
+        //
+        // @return integer
+        //
+        getUnix: function() {
+            return Math.round(this.getTime() / 1000, 0);
+        },
+        //
+        // Get the GMT offset in hours and minutes (e.g. +06:30)
+        //
+        // @return string
+        //
+        getGmtOffset: function() {
+            // divide the minutes offset by 60
+            var hours = this.getTimezoneOffset() / 60;
+            // decide if we are ahead of or behind GMT
+            var prefix = hours < 0 ? '+' : '-';
+            // remove the negative sign if any
+            hours = Math.abs(hours);
+            // add the +/- to the padded number of hours to : to the padded minutes
+            return prefix + zeroPad(Math.floor(hours), 2) + ':' + zeroPad((hours % 1) * 60, 2);
+        },
+        //
+        // Get the browser-reported name for the current timezone (e.g. MDT, Mountain Daylight Time)
+        //
+        // @return string
+        //
+        getTimezoneName: function() {
+            var match = /(?:\((.+)\)$| ([A-Z]{3}) )/.exec(this.toString());
+            return match[1] || match[2] || 'GMT' + this.getGmtOffset();
+        },
+        //
+        // Convert the current date to an 8-digit integer (%Y%m%d)
+        //
+        // @return int
+        //
+        toYmdInt: function() {
+            return (this.getFullYear() * 10000) + (this.getMonthNumber() * 100) + this.getDate();
+        },  
+        //
+        // Create a copy of a date object
+        //
+        // @return object
+        //       
+        clone: function() {
+                return new Date(this.getTime());
+        }
+    };
+    for (var name in instanceMethods) {
+        Date.prototype[name] = instanceMethods[name];
+    }
+    //
+    // Add static methods to the date object
+    //
+    var staticMethods = {
+        //
+        // The heart of the date functionality: returns a date object if given a convertable value
+        //
+        // @param string/object/integer date
+        // @return object Date
+        //
+        create: function(date) {
+            // If the passed value is already a date object, return it
+            if (date instanceof Date) {
+                return date;
+            }
+            // if (typeof date == 'number') return new Date(date);
+            // If the passed value is an integer, interpret it as a javascript timestamp
+            if (typeof date == 'number') {
+                return new Date(date);
+            }
+            // If the passed value is a string, attempt to parse it using Date.parse()
+            var parsable = String(date).replace(/^\s*(.+)\s*$/, '$1'), i = 0, length = Date.create.patterns.length, pattern;
+            var current = parsable;
+            while (i < length) {
+                ms = Date.parse(current);
+                if (!isNaN(ms)) {
+                    return new Date(ms);
+                }
+                pattern = Date.create.patterns[i];
+                if (typeof pattern == 'function') {
+                    obj = pattern(current);
+                    if (obj instanceof Date) {
+                        return obj;
+                    }
+                } else {
+                    current = parsable.replace(pattern[0], pattern[1]);
+                }
+                i++;
+            }
+            return NaN;
+        },
+        //
+        // constants representing month names, day names, and ordinal names
+        // (same names as Ruby Date constants)
+        //
+        MONTHNAMES          : 'January February March April May June July August September October November December'.split(' '),
+        ABBR_MONTHNAMES : 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' '),
+        DAYNAMES                : 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' '),
+        ABBR_DAYNAMES       : 'Sun Mon Tue Wed Thu Fri Sat'.split(' '),
+        ORDINALNAMES        : 'th st nd rd th th th th th th'.split(' '),
+        //
+        // Shortcut for full ISO-8601 date conversion
+        //
+        ISO: '%Y-%m-%dT%H:%M:%S.%N%G',
+        //
+        // Shortcut for SQL-type formatting
+        //
+        SQL: '%Y-%m-%d %H:%M:%S',
+        //
+        // Setter method for month, day, and ordinal names for i18n
+        //
+        // @param object newNames
+        //
+        daysInMonth: function(year, month) {
+            if (month == 2) {
+                return new Date(year, 1, 29).getDate() == 29 ? 29 : 28;
+            }
+            return [undefined,31,undefined,31,30,31,30,31,31,30,31,30,31][month];
+        }
+    };
+    for (var name in staticMethods) {
+        Date[name] = staticMethods[name];
+    }
+    //
+    // format codes for strftime
+    //
+    // each code must be an array where the first member is the name of a Date.prototype function
+    // and optionally a second member indicating the number to pass to Number#toPaddedString()
+    //
+    Date.prototype.strftime.formatCodes = {
+        //
+        // 2-part regex matcher for format codes
+        //
+        // first match must be the character before the code (to account for escaping)
+        // second match must be the format code character(s)
+        //
+        matcher: /()%(#?(%|[a-z]))/i,
+        // year
+        Y: 'FullYear',
+        y: 'ShortYear.2',
+        // month
+        m: 'MonthNumber.2',
+        '#m': 'MonthNumber',
+        B: 'MonthName',
+        b: 'AbbrMonthName',
+        // day
+        d: 'Date.2',
+        '#d': 'Date',
+        e: 'Date',
+        A: 'DayName',
+        a: 'AbbrDayName',
+        w: 'Day',
+        o: 'DayOrdinal',
+        // hours
+        H: 'Hours.2',
+        '#H': 'Hours',
+        I: 'Hours12.2',
+        '#I': 'Hours12',
+        p: 'AmPm',
+        // minutes
+        M: 'Minutes.2',
+        '#M': 'Minutes',
+        // seconds
+        S: 'Seconds.2',
+        '#S': 'Seconds',
+        s: 'Unix',
+        // milliseconds
+        N: 'Milliseconds.3',
+        '#N': 'Milliseconds',
+        // timezone
+        O: 'TimezoneOffset',
+        Z: 'TimezoneName',
+        G: 'GmtOffset'  
+    };
+    //
+    // shortcuts that will be translated into their longer version
+    //
+    // be sure that format shortcuts do not refer to themselves: this will cause an infinite loop
+    //
+    Date.prototype.strftime.formatShortcuts = {
+        // date
+        F: '%Y-%m-%d',
+        // time
+        T: '%H:%M:%S',
+        X: '%H:%M:%S',
+        // local format date
+        x: '%m/%d/%y',
+        D: '%m/%d/%y',
+        // local format extended
+        '#c': '%a %b %e %H:%M:%S %Y',
+        // local format short
+        v: '%e-%b-%Y',
+        R: '%H:%M',
+        r: '%I:%M:%S %p',
+        // tab and newline
+        t: '\t',
+        n: '\n',
+        '%': '%'
+    };
+    //
+    // A list of conversion patterns (array arguments sent directly to gsub)
+    // Add, remove or splice a patterns to customize date parsing ability
+    //
+    Date.create.patterns = [
+        [/-/g, '/'], // US-style time with dashes => Parsable US-style time
+        [/st|nd|rd|th/g, ''], // remove st, nd, rd and th        
+        [/(3[01]|[0-2]\d)\s*\.\s*(1[0-2]|0\d)\s*\.\s*([1-9]\d{3})/, '$2/$1/$3'], // World time => Parsable US-style time
+        [/([1-9]\d{3})\s*-\s*(1[0-2]|0\d)\s*-\s*(3[01]|[0-2]\d)/, '$2/$3/$1'], // ISO-style time => Parsable US-style time
+        function(str) { // 12-hour or 24 hour time with milliseconds
+            // var match = str.match(/^(?:(.+)\s+)?([1-9]|1[012])(?:\s*\:\s*(\d\d))?(?:\s*\:\s*(\d\d))?\s*(am|pm)\s*$/i);
+            var match = str.match(/^(?:(.+)\s+)?([012]?\d)(?:\s*\:\s*(\d\d))?(?:\s*\:\s*(\d\d(\.\d*)?))?\s*(am|pm)?\s*$/i);
+            //                   opt. date      hour       opt. minute     opt. second       opt. msec   opt. am or pm
+            if (match) {
+                if (match[1]) {
+                    var d = Date.create(match[1]);
+                    if (isNaN(d)) {
+                        return;
+                    }
+                } else {
+                    var d = new Date();
+                    d.setMilliseconds(0);
+                }
+                var hour = parseFloat(match[2]);
+                if (match[6]) {
+                    hour = match[6].toLowerCase() == 'am' ? (hour == 12 ? 0 : hour) : (hour == 12 ? 12 : hour + 12);
+                }
+                d.setHours(hour, parseInt(match[3] || 0, 10), parseInt(match[4] || 0, 10), ((parseFloat(match[5] || 0)) || 0)*1000);
+                return d;
+            }
+            else {
+                return str;
+            }
+        },
+        function(str) { // ISO timestamp with time zone.
+            var match = str.match(/^(?:(.+))[T|\s+]([012]\d)(?:\:(\d\d))(?:\:(\d\d))(?:\.\d+)([\+\-]\d\d\:\d\d)$/i);
+            if (match) {
+                if (match[1]) {
+                    var d = Date.create(match[1]);
+                    if (isNaN(d)) {
+                        return;
+                    }
+                } else {
+                    var d = new Date();
+                    d.setMilliseconds(0);
+                }
+                var hour = parseFloat(match[2]);
+                d.setHours(hour, parseInt(match[3], 10), parseInt(match[4], 10), parseFloat(match[5])*1000);
+                return d;
+            }
+            else {
+                    return str;
+            }
+        },
+        function(str) {
+            var match = str.match(/^([0-3]?\d)\s*[-\/.\s]{1}\s*([a-zA-Z]{3,9})\s*[-\/.\s]{1}\s*([0-3]?\d)$/);
+            if (match) {
+                var d = new Date();
+                var y = parseFloat(String(d.getFullYear()).slice(2,4));
+                var cent = parseInt(String(d.getFullYear())/100, 10)*100;
+                var centoffset = 1;
+                var m1 = parseFloat(match[1]);
+                var m3 = parseFloat(match[3]);
+                var ny, nd, nm;
+                if (m1 > 31) { // first number is a year
+                    nd = match[3];
+                    if (m1 < y+centoffset) { // if less than 1 year out, assume it is this century.
+                        ny = cent + m1;
+                    }
+                    else {
+                        ny = cent - 100 + m1;
+                    }
+                }
+                
+                else { // last number is the year
+                    nd = match[1];
+                    if (m3 < y+centoffset) { // if less than 1 year out, assume it is this century.
+                        ny = cent + m3;
+                    }
+                    else {
+                        ny = cent - 100 + m3;
+                    }
+                }
+                
+                var nm = $.inArray(match[2], Date.ABBR_MONTHNAMES);
+                
+                if (nm == -1) {
+                    nm = $.inArray(match[2], Date.MONTHNAMES);
+                }
+            
+                d.setFullYear(ny, nm, nd);
+                d.setHours(0,0,0,0);
+                return d;
+            }
+            
+            else {
+                return str;
+            }
+        }        
+    ];
+    
+    if ($.jqplot.config.debug) {
+        $.date = Date.create;
+    }
+
+    // Class: $.jqplot.DivTitleRenderer
+    // The default title renderer for jqPlot.  This class has no options beyond the <Title> class. 
+    $.jqplot.DivTitleRenderer = function() {
+    };
+    
+    $.jqplot.DivTitleRenderer.prototype.init = function(options) {
+        $.extend(true, this, options);
+    };
+    
+    $.jqplot.DivTitleRenderer.prototype.draw = function() {
+        var r = this.renderer;
+        if (!this.text) {
+            this.show = false;
+            this._elem = $('<div class="jqplot-title" style="height:0px;width:0px;"></div>');
+        }
+        else if (this.text) {
+            var color;
+            if (this.color) {
+                color = this.color;
+            }
+            else if (this.textColor) {
+                color = this.textColor;
+            }
+            // don't trust that a stylesheet is present, set the position.
+            var styletext = 'position:absolute;top:0px;left:0px;';
+            styletext += (this._plotWidth) ? 'width:'+this._plotWidth+'px;' : '';
+            styletext += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
+            styletext += (this.textAlign) ? 'text-align:'+this.textAlign+';' : 'text-align:center;';
+            styletext += (color) ? 'color:'+color+';' : '';
+            styletext += (this.paddingBottom) ? 'padding-bottom:'+this.paddingBottom+';' : '';
+            this._elem = $('<div class="jqplot-title" style="'+styletext+'">'+this.text+'</div>');
+            if (this.fontFamily) {
+                this._elem.css('font-family', this.fontFamily);
+            }
+        }
+        
+        return this._elem;
+    };
+    
+    $.jqplot.DivTitleRenderer.prototype.pack = function() {
+        // nothing to do here
+    };
+  
+    // Class: $.jqplot.LineRenderer
+    // The default line renderer for jqPlot, this class has no options beyond the <Series> class.
+    // Draws series as a line.
+    $.jqplot.LineRenderer = function(){
+        this.shapeRenderer = new $.jqplot.ShapeRenderer();
+        this.shadowRenderer = new $.jqplot.ShadowRenderer();
+    };
+    
+    // called with scope of series.
+    $.jqplot.LineRenderer.prototype.init = function(options, plot) {
+        options = options || {};
+        var lopts = {highlightMouseOver: options.highlightMouseOver, highlightMouseDown: options.highlightMouseDown, highlightColor: options.highlightColor};
+        
+        delete (options.highlightMouseOver);
+        delete (options.highlightMouseDown);
+        delete (options.highlightColor);
+        
+        $.extend(true, this.renderer, options);
+        // set the shape renderer options
+        var opts = {lineJoin:'round', lineCap:'round', fill:this.fill, isarc:false, strokeStyle:this.color, fillStyle:this.fillColor, lineWidth:this.lineWidth, closePath:this.fill};
+        this.renderer.shapeRenderer.init(opts);
+        // set the shadow renderer options
+        // scale the shadowOffset to the width of the line.
+        if (this.lineWidth > 2.5) {
+            var shadow_offset = this.shadowOffset* (1 + (Math.atan((this.lineWidth/2.5))/0.785398163 - 1)*0.6);
+            // var shadow_offset = this.shadowOffset;
+        }
+        // for skinny lines, don't make such a big shadow.
+        else {
+            var shadow_offset = this.shadowOffset*Math.atan((this.lineWidth/2.5))/0.785398163;
+        }
+        var sopts = {lineJoin:'round', lineCap:'round', fill:this.fill, isarc:false, angle:this.shadowAngle, offset:shadow_offset, alpha:this.shadowAlpha, depth:this.shadowDepth, lineWidth:this.lineWidth, closePath:this.fill};
+        this.renderer.shadowRenderer.init(sopts);
+        this._areaPoints = [];
+        this._boundingBox = [[],[]];
+        
+        if (!this.isTrendline && this.fill) {
+        
+            // prop: highlightMouseOver
+            // True to highlight area on a filled plot when moused over.
+            // This must be false to enable highlightMouseDown to highlight when clicking on an area on a filled plot.
+            this.highlightMouseOver = true;
+            // prop: highlightMouseDown
+            // True to highlight when a mouse button is pressed over an area on a filled plot.
+            // This will be disabled if highlightMouseOver is true.
+            this.highlightMouseDown = false;
+            // prop: highlightColor
+            // color to use when highlighting an area on a filled plot.
+            this.highlightColor = null;
+            // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
+            if (lopts.highlightMouseDown && lopts.highlightMouseOver == null) {
+                lopts.highlightMouseOver = false;
+            }
+        
+            $.extend(true, this, {highlightMouseOver: lopts.highlightMouseOver, highlightMouseDown: lopts.highlightMouseDown, highlightColor: lopts.highlightColor});
+            
+            if (!this.highlightColor) {
+                this.highlightColor = $.jqplot.computeHighlightColors(this.fillColor);
+            }
+            // turn off traditional highlighter
+            if (this.highlighter) {
+                this.highlighter.show = false;
+            }
+            plot.postInitHooks.addOnce(postInit);
+            plot.postDrawHooks.addOnce(postPlotDraw);
+            plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
+            plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown);
+            plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp);
+            plot.eventListenerHooks.addOnce('jqplotClick', handleClick);
+            plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick);
+        }
+
+    };
+    
+    // Method: setGridData
+    // converts the user data values to grid coordinates and stores them
+    // in the gridData array.
+    // Called with scope of a series.
+    $.jqplot.LineRenderer.prototype.setGridData = function(plot) {
+        // recalculate the grid data
+        var xp = this._xaxis.series_u2p;
+        var yp = this._yaxis.series_u2p;
+        var data = this._plotData;
+        var pdata = this._prevPlotData;
+        this.gridData = [];
+        this._prevGridData = [];
+        for (var i=0; i<this.data.length; i++) {
+            // if not a line series or if no nulls in data, push the converted point onto the array.
+            if (data[i][0] != null && data[i][1] != null) {
+                this.gridData.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]);
+            }
+            // else if there is a null, preserve it.
+            else if (data[i][0] == null) {
+                this.gridData.push([null, yp.call(this._yaxis, data[i][1])]);
+            }
+            else if (data[i][1] == null) {
+                this.gridData.push([xp.call(this._xaxis, data[i][0]), null]);
+            }
+            // if not a line series or if no nulls in data, push the converted point onto the array.
+            if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] != null) {
+                this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), yp.call(this._yaxis, pdata[i][1])]);
+            }
+            // else if there is a null, preserve it.
+            else if (pdata[i] != null && pdata[i][0] == null) {
+                this._prevGridData.push([null, yp.call(this._yaxis, pdata[i][1])]);
+            }  
+            else if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] == null) {
+                this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), null]);
+            }
+        }
+    };
+    
+    // Method: makeGridData
+    // converts any arbitrary data values to grid coordinates and
+    // returns them.  This method exists so that plugins can use a series'
+    // linerenderer to generate grid data points without overwriting the
+    // grid data associated with that series.
+    // Called with scope of a series.
+    $.jqplot.LineRenderer.prototype.makeGridData = function(data, plot) {
+        // recalculate the grid data
+        var xp = this._xaxis.series_u2p;
+        var yp = this._yaxis.series_u2p;
+        var gd = [];
+        var pgd = [];
+        for (var i=0; i<data.length; i++) {
+            // if not a line series or if no nulls in data, push the converted point onto the array.
+            if (data[i][0] != null && data[i][1] != null) {
+                gd.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]);
+            }
+            // else if there is a null, preserve it.
+            else if (data[i][0] == null) {
+                gd.push([null, yp.call(this._yaxis, data[i][1])]);
+            }
+            else if (data[i][1] == null) {
+                gd.push([xp.call(this._xaxis, data[i][0]), null]);
+            }
+        }
+        return gd;
+    };
+    
+
+    // called within scope of series.
+    $.jqplot.LineRenderer.prototype.draw = function(ctx, gd, options) {
+        var i;
+        var opts = (options != undefined) ? options : {};
+        var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
+        var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
+        var fill = (opts.fill != undefined) ? opts.fill : this.fill;
+        var fillAndStroke = (opts.fillAndStroke != undefined) ? opts.fillAndStroke : this.fillAndStroke;
+        var xmin, ymin, xmax, ymax;
+        ctx.save();
+        if (gd.length) {
+            if (showLine) {
+                // if we fill, we'll have to add points to close the curve.
+                if (fill) {
+                    if (this.fillToZero) {
+                        // have to break line up into shapes at axis crossings
+                        var negativeColors = new $.jqplot.ColorGenerator(this.negativeSeriesColors);
+                        var negativeColor = negativeColors.get(this.index);
+                        if (! this.useNegativeColors) {
+                            negativeColor = opts.fillStyle;
+                        }
+                        var isnegative = false;
+                        var posfs = opts.fillStyle;
+                    
+                        // if stoking line as well as filling, get a copy of line data.
+                        if (fillAndStroke) {
+                            var fasgd = gd.slice(0);
+                        }
+                        // if not stacked, fill down to axis
+                        if (this.index == 0 || !this._stack) {
+                        
+                            var tempgd = [];
+                            this._areaPoints = [];
+                            var pyzero = this._yaxis.series_u2p(this.fillToValue);
+                            var pxzero = this._xaxis.series_u2p(this.fillToValue);
+                            
+                            if (this.fillAxis == 'y') {
+                                tempgd.push([gd[0][0], pyzero]);
+                                this._areaPoints.push([gd[0][0], pyzero]);
+                                
+                                for (var i=0; i<gd.length-1; i++) {
+                                    tempgd.push(gd[i]);
+                                    this._areaPoints.push(gd[i]);
+                                    // do we have an axis crossing?
+                                    if (this._plotData[i][1] * this._plotData[i+1][1] < 0) {
+                                        if (this._plotData[i][1] < 0) {
+                                            isnegative = true;
+                                            opts.fillStyle = negativeColor;
+                                        }
+                                        else {
+                                            isnegative = false;
+                                            opts.fillStyle = posfs;
+                                        }
+                                        
+                                        var xintercept = gd[i][0] + (gd[i+1][0] - gd[i][0]) * (pyzero-gd[i][1])/(gd[i+1][1] - gd[i][1]);
+                                        tempgd.push([xintercept, pyzero]);
+                                        this._areaPoints.push([xintercept, pyzero]);
+                                        // now draw this shape and shadow.
+                                        if (shadow) {
+                                            this.renderer.shadowRenderer.draw(ctx, tempgd, opts);
+                                        }
+                                        this.renderer.shapeRenderer.draw(ctx, tempgd, opts);
+                                        // now empty temp array and continue
+                                        tempgd = [[xintercept, pyzero]];
+                                        // this._areaPoints = [[xintercept, pyzero]];
+                                    }   
+                                }
+                                if (this._plotData[gd.length-1][1] < 0) {
+                                    isnegative = true;
+                                    opts.fillStyle = negativeColor;
+                                }
+                                else {
+                                    isnegative = false;
+                                    opts.fillStyle = posfs;
+                                }
+                                tempgd.push(gd[gd.length-1]);
+                                this._areaPoints.push(gd[gd.length-1]);
+                                tempgd.push([gd[gd.length-1][0], pyzero]); 
+                                this._areaPoints.push([gd[gd.length-1][0], pyzero]); 
+                            }
+                            // now draw this shape and shadow.
+                            if (shadow) {
+                                this.renderer.shadowRenderer.draw(ctx, tempgd, opts);
+                            }
+                            this.renderer.shapeRenderer.draw(ctx, tempgd, opts);
+                            
+                            
+                            // var gridymin = this._yaxis.series_u2p(0);
+                            // // IE doesn't return new length on unshift
+                            // gd.unshift([gd[0][0], gridymin]);
+                            // len = gd.length;
+                            // gd.push([gd[len - 1][0], gridymin]);                   
+                        }
+                        // if stacked, fill to line below 
+                        else {
+                            var prev = this._prevGridData;
+                            for (var i=prev.length; i>0; i--) {
+                                gd.push(prev[i-1]);
+                                // this._areaPoints.push(prev[i-1]);
+                            }
+                            if (shadow) {
+                                this.renderer.shadowRenderer.draw(ctx, gd, opts);
+                            }
+                            this._areaPoints = gd;
+                            this.renderer.shapeRenderer.draw(ctx, gd, opts);
+                        }
+                    }
+                    /////////////////////////
+                    // Not filled to zero
+                    ////////////////////////
+                    else {                    
+                        // if stoking line as well as filling, get a copy of line data.
+                        if (fillAndStroke) {
+                            var fasgd = gd.slice(0);
+                        }
+                        // if not stacked, fill down to axis
+                        if (this.index == 0 || !this._stack) {
+                            // var gridymin = this._yaxis.series_u2p(this._yaxis.min) - this.gridBorderWidth / 2;
+                            var gridymin = ctx.canvas.height;
+                            // IE doesn't return new length on unshift
+                            gd.unshift([gd[0][0], gridymin]);
+                            len = gd.length;
+                            gd.push([gd[len - 1][0], gridymin]);                   
+                        }
+                        // if stacked, fill to line below 
+                        else {
+                            var prev = this._prevGridData;
+                            for (var i=prev.length; i>0; i--) {
+                                gd.push(prev[i-1]);
+                            }
+                        }
+                        this._areaPoints = gd;
+                        
+                        if (shadow) {
+                            this.renderer.shadowRenderer.draw(ctx, gd, opts);
+                        }
+            
+                        this.renderer.shapeRenderer.draw(ctx, gd, opts);                        
+                    }
+                    if (fillAndStroke) {
+                        var fasopts = $.extend(true, {}, opts, {fill:false, closePath:false});
+                        this.renderer.shapeRenderer.draw(ctx, fasgd, fasopts);
+                        //////////
+                        // TODO: figure out some way to do shadows nicely
+                        // if (shadow) {
+                        //     this.renderer.shadowRenderer.draw(ctx, fasgd, fasopts);
+                        // }
+                        // now draw the markers
+                        if (this.markerRenderer.show) {
+                            for (i=0; i<fasgd.length; i++) {
+                                this.markerRenderer.draw(fasgd[i][0], fasgd[i][1], ctx, opts.markerOptions);
+                            }
+                        }
+                    }
+                }
+                else {
+                    if (shadow) {
+                        this.renderer.shadowRenderer.draw(ctx, gd, opts);
+                    }
+    
+                    this.renderer.shapeRenderer.draw(ctx, gd, opts);
+                }
+            }
+            // calculate the bounding box
+            var xmin = xmax = ymin = ymax = null;
+            for (i=0; i<this._areaPoints.length; i++) {
+                var p = this._areaPoints[i];
+                if (xmin > p[0] || xmin == null) {
+                    xmin = p[0];
+                }
+                if (ymax < p[1] || ymax == null) {
+                    ymax = p[1];
+                }
+                if (xmax < p[0] || xmax == null) {
+                    xmax = p[0];
+                }
+                if (ymin > p[1] || ymin == null) {
+                    ymin = p[1];
+                }
+            }
+            this._boundingBox = [[xmin, ymax], [xmax, ymin]];
+        
+            // now draw the markers
+            if (this.markerRenderer.show && !fill) {
+                for (i=0; i<gd.length; i++) {
+                    if (gd[i][0] != null && gd[i][1] != null) {
+                        this.markerRenderer.draw(gd[i][0], gd[i][1], ctx, opts.markerOptions);
+                    }
+                }
+            }
+        }
+        
+        ctx.restore();
+    };  
+    
+    $.jqplot.LineRenderer.prototype.drawShadow = function(ctx, gd, options) {
+        // This is a no-op, shadows drawn with lines.
+    };
+    
+    // called with scope of plot.
+    // make sure to not leave anything highlighted.
+    function postInit(target, data, options) {
+        for (i=0; i<this.series.length; i++) {
+            if (this.series[i].renderer.constructor == $.jqplot.LineRenderer) {
+                // don't allow mouseover and mousedown at same time.
+                if (this.series[i].highlightMouseOver) {
+                    this.series[i].highlightMouseDown = false;
+                }
+            }
+        }
+        this.target.bind('mouseout', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
+    }  
+    
+    // called within context of plot
+    // create a canvas which we can draw on.
+    // insert it before the eventCanvas, so eventCanvas will still capture events.
+    function postPlotDraw() {
+        this.plugins.lineRenderer = {highlightedSeriesIndex:null};
+        this.plugins.lineRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
+        
+        this.eventCanvas._elem.before(this.plugins.lineRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-lineRenderer-highlight-canvas', this._plotDimensions));
+        var hctx = this.plugins.lineRenderer.highlightCanvas.setContext();
+    } 
+    
+    function highlight (plot, sidx, pidx, points) {
+        var s = plot.series[sidx];
+        var canvas = plot.plugins.lineRenderer.highlightCanvas;
+        canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height);
+        s._highlightedPoint = pidx;
+        plot.plugins.lineRenderer.highlightedSeriesIndex = sidx;
+        var opts = {fillStyle: s.highlightColor};
+        s.renderer.shapeRenderer.draw(canvas._ctx, points, opts);
+    }
+    
+    function unhighlight (plot) {
+        var canvas = plot.plugins.lineRenderer.highlightCanvas;
+        canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
+        for (var i=0; i<plot.series.length; i++) {
+            plot.series[i]._highlightedPoint = null;
+        }
+        plot.plugins.lineRenderer.highlightedSeriesIndex = null;
+        plot.target.trigger('jqplotDataUnhighlight');
+    }
+    
+    
+    function handleMove(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var evt1 = jQuery.Event('jqplotDataMouseOver');
+            evt1.pageX = ev.pageX;
+            evt1.pageY = ev.pageY;
+            plot.target.trigger(evt1, ins);
+            if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.lineRenderer.highlightedSeriesIndex)) {
+                var evt = jQuery.Event('jqplotDataHighlight');
+                evt.pageX = ev.pageX;
+                evt.pageY = ev.pageY;
+                plot.target.trigger(evt, ins);
+                highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    }
+    
+    function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.lineRenderer.highlightedSeriesIndex)) {
+                var evt = jQuery.Event('jqplotDataHighlight');
+                evt.pageX = ev.pageX;
+                evt.pageY = ev.pageY;
+                plot.target.trigger(evt, ins);
+                highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    }
+    
+    function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
+        var idx = plot.plugins.lineRenderer.highlightedSeriesIndex;
+        if (idx != null && plot.series[idx].highlightMouseDown) {
+            unhighlight(plot);
+        }
+    }
+    
+    function handleClick(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var evt = jQuery.Event('jqplotDataClick');
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            plot.target.trigger(evt, ins);
+        }
+    }
+    
+    function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var idx = plot.plugins.lineRenderer.highlightedSeriesIndex;
+            if (idx != null && plot.series[idx].highlightMouseDown) {
+                unhighlight(plot);
+            }
+            var evt = jQuery.Event('jqplotDataRightClick');
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            plot.target.trigger(evt, ins);
+        }
+    }
+    
+    
+    // class: $.jqplot.LinearAxisRenderer
+    // The default jqPlot axis renderer, creating a numeric axis.
+    // The renderer has no additional options beyond the <Axis> object.
+    $.jqplot.LinearAxisRenderer = function() {
+    };
+    
+    // called with scope of axis object.
+    $.jqplot.LinearAxisRenderer.prototype.init = function(options){
+        $.extend(true, this, options);
+        var db = this._dataBounds;
+        // Go through all the series attached to this axis and find
+        // the min/max bounds for this axis.
+        for (var i=0; i<this._series.length; i++) {
+            var s = this._series[i];
+            var d = s._plotData;
+            
+            for (var j=0; j<d.length; j++) { 
+                if (this.name == 'xaxis' || this.name == 'x2axis') {
+                    if ((d[j][0] != null && d[j][0] < db.min) || db.min == null) {
+                        db.min = d[j][0];
+                    }
+                    if ((d[j][0] != null && d[j][0] > db.max) || db.max == null) {
+                        db.max = d[j][0];
+                    }
+                }              
+                else {
+                    if ((d[j][1] != null && d[j][1] < db.min) || db.min == null) {
+                        db.min = d[j][1];
+                    }
+                    if ((d[j][1] != null && d[j][1] > db.max) || db.max == null) {
+                        db.max = d[j][1];
+                    }
+                }              
+            }
+        }
+    };
+    
+    // called with scope of axis
+    $.jqplot.LinearAxisRenderer.prototype.draw = function(ctx) {
+        if (this.show) {
+            // populate the axis label and value properties.
+            // createTicks is a method on the renderer, but
+            // call it within the scope of the axis.
+            this.renderer.createTicks.call(this);
+            // fill a div with axes labels in the right direction.
+            // Need to pregenerate each axis to get it's bounds and
+            // position it and the labels correctly on the plot.
+            var dim=0;
+            var temp;
+            // Added for theming.
+            if (this._elem) {
+                this._elem.empty();
+            }
+            
+            this._elem = $('<div class="jqplot-axis jqplot-'+this.name+'" style="position:absolute;"></div>');
+            
+            if (this.name == 'xaxis' || this.name == 'x2axis') {
+                this._elem.width(this._plotDimensions.width);
+            }
+            else {
+                this._elem.height(this._plotDimensions.height);
+            }
+            
+            // create a _label object.
+            this.labelOptions.axis = this.name;
+            this._label = new this.labelRenderer(this.labelOptions);
+            if (this._label.show) {
+                var elem = this._label.draw(ctx);
+                elem.appendTo(this._elem);
+            }
+    
+            var t = this._ticks;
+            for (var i=0; i<t.length; i++) {
+                var tick = t[i];
+                if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
+                    var elem = tick.draw(ctx);
+                    elem.appendTo(this._elem);
+                }
+            }
+        }
+        return this._elem;
+    };
+    
+    // called with scope of an axis
+    $.jqplot.LinearAxisRenderer.prototype.reset = function() {
+        this.min = this._min;
+        this.max = this._max;
+        this.tickInterval = this._tickInterval;
+        this.numberTicks = this._numberTicks;
+        // this._ticks = this.__ticks;
+    };
+    
+    // called with scope of axis
+    $.jqplot.LinearAxisRenderer.prototype.set = function() { 
+        var dim = 0;
+        var temp;
+        var w = 0;
+        var h = 0;
+        var lshow = (this._label == null) ? false : this._label.show;
+        if (this.show) {
+            var t = this._ticks;
+            for (var i=0; i<t.length; i++) {
+                var tick = t[i];
+                if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
+                    if (this.name == 'xaxis' || this.name == 'x2axis') {
+                        temp = tick._elem.outerHeight(true);
+                    }
+                    else {
+                        temp = tick._elem.outerWidth(true);
+                    }
+                    if (temp > dim) {
+                        dim = temp;
+                    }
+                }
+            }
+            
+            if (lshow) {
+                w = this._label._elem.outerWidth(true);
+                h = this._label._elem.outerHeight(true); 
+            }
+            if (this.name == 'xaxis') {
+                dim = dim + h;
+                this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'});
+            }
+            else if (this.name == 'x2axis') {
+                dim = dim + h;
+                this._elem.css({'height':dim+'px', left:'0px', top:'0px'});
+            }
+            else if (this.name == 'yaxis') {
+                dim = dim + w;
+                this._elem.css({'width':dim+'px', left:'0px', top:'0px'});
+                if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
+                    this._label._elem.css('width', w+'px');
+                }
+            }
+            else {
+                dim = dim + w;
+                this._elem.css({'width':dim+'px', right:'0px', top:'0px'});
+                if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
+                    this._label._elem.css('width', w+'px');
+                }
+            }
+        }  
+    };    
+    
+    // called with scope of axis
+    $.jqplot.LinearAxisRenderer.prototype.createTicks = function() {
+        // we're are operating on an axis here
+        var ticks = this._ticks;
+        var userTicks = this.ticks;
+        var name = this.name;
+        // databounds were set on axis initialization.
+        var db = this._dataBounds;
+        var dim, interval;
+        var min, max;
+        var pos1, pos2;
+        var tt, i;
+        // get a copy of user's settings for min/max.
+        var userMin = this.min;
+        var userMax = this.max;
+        var userNT = this.numberTicks;
+        var userTI = this.tickInterval;
+        
+        // if we already have ticks, use them.
+        // ticks must be in order of increasing value.
+        
+        if (userTicks.length) {
+            // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed
+            for (i=0; i<userTicks.length; i++){
+                var ut = userTicks[i];
+                var t = new this.tickRenderer(this.tickOptions);
+                if (ut.constructor == Array) {
+                    t.value = ut[0];
+                    t.label = ut[1];
+                    t.setTick(ut[0], this.name);
+                    this._ticks.push(t);
+                }
+                
+                else {
+                    t.value = ut;
+                    t.setTick(ut, this.name);
+                    this._ticks.push(t);
+                }
+            }
+            this.numberTicks = userTicks.length;
+            this.min = this._ticks[0].value;
+            this.max = this._ticks[this.numberTicks-1].value;
+            this.tickInterval = (this.max - this.min) / (this.numberTicks - 1);
+        }
+        
+        // we don't have any ticks yet, let's make some!
+        else {
+            if (name == 'xaxis' || name == 'x2axis') {
+                dim = this._plotDimensions.width;
+            }
+            else {
+                dim = this._plotDimensions.height;
+            }
+            
+            // if min, max and number of ticks specified, user can't specify interval.
+            if (!this.autoscale && this.min != null && this.max != null && this.numberTicks != null) {
+                this.tickInterval = null;
+            }
+            
+            // if max, min, and interval specified and interval won't fit, ignore interval.
+            // if (this.min != null && this.max != null && this.tickInterval != null) {
+            //     if (parseInt((this.max-this.min)/this.tickInterval, 10) != (this.max-this.min)/this.tickInterval) {
+            //         this.tickInterval = null;
+            //     }
+            // }
+        
+            min = ((this.min != null) ? this.min : db.min);
+            max = ((this.max != null) ? this.max : db.max);
+            
+            // if min and max are same, space them out a bit
+            if (min == max) {
+                var adj = 0.05;
+                if (min > 0) {
+                    adj = Math.max(Math.log(min)/Math.LN10, 0.05);
+                }
+                min -= adj;
+                max += adj;
+            }
+
+            var range = max - min;
+            var rmin, rmax;
+            var temp;
+            
+            // autoscale.  Can't autoscale if min or max is supplied.
+            // Will use numberTicks and tickInterval if supplied.  Ticks
+            // across multiple axes may not line up depending on how
+            // bars are to be plotted.
+            if (this.autoscale && this.min == null && this.max == null) {
+                var rrange, ti, margin;
+                var forceMinZero = false;
+                var forceZeroLine = false;
+                var intervals = {min:null, max:null, average:null, stddev:null};
+                // if any series are bars, or if any are fill to zero, and if this
+                // is the axis to fill toward, check to see if we can start axis at zero.
+                for (var i=0; i<this._series.length; i++) {
+                    var s = this._series[i];
+                    var faname = (s.fillAxis == 'x') ? s._xaxis.name : s._yaxis.name;
+                    // check to see if this is the fill axis
+                    if (this.name == faname) {
+                        var vals = s._plotValues[s.fillAxis];
+                        var vmin = vals[0];
+                        var vmax = vals[0];
+                        for (var j=1; j<vals.length; j++) {
+                            if (vals[j] < vmin) {
+                                vmin = vals[j];
+                            }
+                            else if (vals[j] > vmax) {
+                                vmax = vals[j];
+                            }
+                        }
+                        var dp = (vmax - vmin) / vmax;
+                        // is this sries a bar?
+                        if (s.renderer.constructor == $.jqplot.BarRenderer) {
+                            // if no negative values and could also check range.
+                            if (vmin >= 0 && (s.fillToZero || dp > 0.1)) {
+                                forceMinZero = true;
+                            }
+                            else {
+                                forceMinZero = false;
+                                if (s.fill && s.fillToZero && vmin < 0 && vmax > 0) {
+                                    forceZeroLine = true;
+                                }
+                                else {
+                                    forceZeroLine = false;
+                                }
+                            }
+                        }
+                        
+                        // if not a bar and filling, use appropriate method.
+                        else if (s.fill) {
+                            if (vmin >= 0 && (s.fillToZero || dp > 0.1)) {
+                                forceMinZero = true;
+                            }
+                            else if (vmin < 0 && vmax > 0 && s.fillToZero) {
+                                forceMinZero = false;
+                                forceZeroLine = true;
+                            }
+                            else {
+                                forceMinZero = false;
+                                forceZeroLine = false;
+                            }
+                        }
+                        
+                        // if not a bar and not filling, only change existing state
+                        // if it doesn't make sense
+                        else if (vmin < 0) {
+                            forceMinZero = false;
+                        }
+                    }
+                }
+                
+                // check if we need make axis min at 0.
+                if (forceMinZero) {
+                    // compute number of ticks
+                    this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
+                    this.min = 0;
+                    userMin = 0;
+                    // what order is this range?
+                    // what tick interval does that give us?
+                    ti = max/(this.numberTicks-1);
+                    temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10)));
+                    if (ti/temp == parseInt(ti/temp, 10)) {
+                        ti += temp;
+                    }
+                    this.tickInterval = Math.ceil(ti/temp) * temp;
+                    this.max = this.tickInterval * (this.numberTicks - 1);
+                }
+                
+                // check if we need to make sure there is a tick at 0.
+                else if (forceZeroLine) {
+                    // compute number of ticks
+                    this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
+                    var ntmin = Math.ceil(Math.abs(min)/range*(this.numberTicks-1));
+                    var ntmax = this.numberTicks - 1  - ntmin;
+                    ti = Math.max(Math.abs(min/ntmin), Math.abs(max/ntmax));
+                    temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10)));
+                    this.tickInterval = Math.ceil(ti/temp) * temp;
+                    this.max = this.tickInterval * ntmax;
+                    this.min = -this.tickInterval * ntmin;                  
+                }
+                
+                // if nothing else, do autoscaling which will try to line up ticks across axes.
+                else {  
+                    if (this.numberTicks == null){
+                        if (this.tickInterval) {
+                            this.numberTicks = 3 + Math.ceil(range / this.tickInterval);
+                        }
+                        else {
+                            this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
+                        }
+                    }
+            
+                    if (this.tickInterval == null) {
+                        // get a tick interval
+                        ti = range/(this.numberTicks - 1);
+
+                        if (ti < 1) {
+                            temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10)));
+                        }
+                        else {
+                            temp = 1;
+                        }
+                        this.tickInterval = Math.ceil(ti*temp*this.pad)/temp;
+                    }
+                    else {
+                        temp = 1 / this.tickInterval;
+                    }
+                    
+                    // try to compute a nicer, more even tick interval
+                    // temp = Math.pow(10, Math.floor(Math.log(ti)/Math.LN10));
+                    // this.tickInterval = Math.ceil(ti/temp) * temp;
+                    rrange = this.tickInterval * (this.numberTicks - 1);
+                    margin = (rrange - range)/2;
+       
+                    if (this.min == null) {
+                        this.min = Math.floor(temp*(min-margin))/temp;
+                    }
+                    if (this.max == null) {
+                        this.max = this.min + rrange;
+                    }
+                }
+            }
+            
+            // Use the default algorithm which pads each axis to make the chart
+            // centered nicely on the grid.
+            else {
+                rmin = (this.min != null) ? this.min : min - range*(this.padMin - 1);
+                rmax = (this.max != null) ? this.max : max + range*(this.padMax - 1);
+                this.min = rmin;
+                this.max = rmax;
+                range = this.max - this.min;
+    
+                if (this.numberTicks == null){
+                    // if tickInterval is specified by user, we will ignore computed maximum.
+                    // max will be equal or greater to fit even # of ticks.
+                    if (this.tickInterval != null) {
+                        this.numberTicks = Math.ceil((this.max - this.min)/this.tickInterval)+1;
+                        this.max = this.min + this.tickInterval*(this.numberTicks-1);
+                    }
+                    else if (dim > 100) {
+                        this.numberTicks = parseInt(3+(dim-100)/75, 10);
+                    }
+                    else {
+                        this.numberTicks = 2;
+                    }
+                }
+            
+                if (this.tickInterval == null) {
+                    this.tickInterval = range / (this.numberTicks-1);
+                }
+            }
+            
+            if (this.renderer.constructor == $.jqplot.LinearAxisRenderer) {
+                // fix for misleading tick display with small range and low precision.
+                range = this.max - this.min;
+                // figure out precision
+                var temptick = new this.tickRenderer(this.tickOptions);
+                // use the tick formatString or, the default.
+                var fs = temptick.formatString || $.jqplot.config.defaultTickFormatString; 
+                var fs = fs.match($.jqplot.sprintf.regex)[0];
+                var precision = 0;
+                if (fs) {
+                    if (fs.search(/[fFeEgGpP]/) > -1) {
+                        var m = fs.match(/\%\.(\d{0,})?[eEfFgGpP]/);
+                        if (m) precision = parseInt(m[1], 10);
+                        else precision = 6;
+                    }
+                    else if (fs.search(/[di]/) > -1) {
+                        precision = 0;
+                    }
+                    // fact will be <= 1;
+                    var fact = Math.pow(10, -precision);
+                    if (this.tickInterval < fact) {
+                        // need to correct underrange
+                        if (userNT == null && userTI == null) {
+                            this.tickInterval = fact;
+                            if (userMax == null && userMin == null) {
+                                // this.min = Math.floor((this._dataBounds.min - this.tickInterval)/fact) * fact;
+                                this.min = Math.floor(this._dataBounds.min/fact) * fact;
+                                if (this.min == this._dataBounds.min) {
+                                    this.min = this._dataBounds.min - this.tickInterval;
+                                }
+                                // this.max = Math.ceil((this._dataBounds.max + this.tickInterval)/fact) * fact;
+                                this.max = Math.ceil(this._dataBounds.max/fact) * fact;
+                                if (this.max == this._dataBounds.max) {
+                                    this.max = this._dataBounds.max + this.tickInterval;
+                                }
+                                var n = (this.max - this.min)/this.tickInterval;
+                                n = n.toFixed(11);
+                                n = Math.ceil(n);
+                                this.numberTicks = n + 1;
+                            }
+                            else if (userMax == null) {
+                                // add one tick for top of range.
+                                var n = (this._dataBounds.max - this.min) / this.tickInterval;
+                                n = n.toFixed(11);
+                                this.numberTicks = Math.ceil(n) + 2;
+                                this.max = this.min + this.tickInterval * (this.numberTicks-1);
+                            }
+                            else if (userMin == null) {
+                                // add one tick for bottom of range.
+                                var n = (this.max - this._dataBounds.min) / this.tickInterval;
+                                n = n.toFixed(11);
+                                this.numberTicks = Math.ceil(n) + 2;
+                                this.min = this.max - this.tickInterval * (this.numberTicks-1);
+                            }
+                            else {
+                                // calculate a number of ticks so max is within axis scale
+                                this.numberTicks = Math.ceil((userMax - userMin)/this.tickInterval) + 1;
+                                // if user's min and max don't fit evenly in ticks, adjust.
+                                // This takes care of cases such as user min set to 0, max set to 3.5 but tick
+                                // format string set to %d (integer ticks)
+                                this.min =  Math.floor(userMin*Math.pow(10, precision))/Math.pow(10, precision);
+                                this.max =  Math.ceil(userMax*Math.pow(10, precision))/Math.pow(10, precision);
+                                // this.max = this.min + this.tickInterval*(this.numberTicks-1);
+                                this.numberTicks = Math.ceil((this.max - this.min)/this.tickInterval) + 1;
+                            }
+                        }
+                    }
+                }
+            }
+            
+            
+
+            for (var i=0; i<this.numberTicks; i++){
+                tt = this.min + i * this.tickInterval;
+                var t = new this.tickRenderer(this.tickOptions);
+                // var t = new $.jqplot.AxisTickRenderer(this.tickOptions);
+
+                t.setTick(tt, this.name);
+                this._ticks.push(t);
+            }
+        }
+    };
+    
+    // called with scope of axis
+    $.jqplot.LinearAxisRenderer.prototype.pack = function(pos, offsets) {
+        var ticks = this._ticks;
+        var max = this.max;
+        var min = this.min;
+        var offmax = offsets.max;
+        var offmin = offsets.min;
+        var lshow = (this._label == null) ? false : this._label.show;
+        
+        for (var p in pos) {
+            this._elem.css(p, pos[p]);
+        }
+        
+        this._offsets = offsets;
+        // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left.
+        var pixellength = offmax - offmin;
+        var unitlength = max - min;
+        
+        // point to unit and unit to point conversions references to Plot DOM element top left corner.
+        this.p2u = function(p){
+            return (p - offmin) * unitlength / pixellength + min;
+        };
+        
+        this.u2p = function(u){
+            return (u - min) * pixellength / unitlength + offmin;
+        };
+                
+        if (this.name == 'xaxis' || this.name == 'x2axis'){
+            this.series_u2p = function(u){
+                return (u - min) * pixellength / unitlength;
+            };
+            this.series_p2u = function(p){
+                return p * unitlength / pixellength + min;
+            };
+        }
+        
+        else {
+            this.series_u2p = function(u){
+                return (u - max) * pixellength / unitlength;
+            };
+            this.series_p2u = function(p){
+                return p * unitlength / pixellength + max;
+            };
+        }
+        
+        if (this.show) {
+            if (this.name == 'xaxis' || this.name == 'x2axis') {
+                for (i=0; i<ticks.length; i++) {
+                    var t = ticks[i];
+                    if (t.show && t.showLabel) {
+                        var shim;
+                        
+                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
+                            // will need to adjust auto positioning based on which axis this is.
+                            var temp = (this.name == 'xaxis') ? 1 : -1;
+                            switch (t.labelPosition) {
+                                case 'auto':
+                                    // position at end
+                                    if (temp * t.angle < 0) {
+                                        shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    }
+                                    // position at start
+                                    else {
+                                        shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'end':
+                                    shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                                case 'start':
+                                    shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    break;
+                                case 'middle':
+                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                                default:
+                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                            }
+                        }
+                        else {
+                            shim = -t.getWidth()/2;
+                        }
+                        var val = this.u2p(t.value) + shim + 'px';
+                        t._elem.css('left', val);
+                        t.pack();
+                    }
+                }
+                if (lshow) {
+                    var w = this._label._elem.outerWidth(true);
+                    this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px');
+                    if (this.name == 'xaxis') {
+                        this._label._elem.css('bottom', '0px');
+                    }
+                    else {
+                        this._label._elem.css('top', '0px');
+                    }
+                    this._label.pack();
+                }
+            }
+            else {
+                for (i=0; i<ticks.length; i++) {
+                    var t = ticks[i];
+                    if (t.show && t.showLabel) {                        
+                        var shim;
+                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
+                            // will need to adjust auto positioning based on which axis this is.
+                            var temp = (this.name == 'yaxis') ? 1 : -1;
+                            switch (t.labelPosition) {
+                                case 'auto':
+                                    // position at end
+                                case 'end':
+                                    if (temp * t.angle < 0) {
+                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
+                                    }
+                                    else {
+                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'start':
+                                    if (t.angle > 0) {
+                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
+                                    }
+                                    else {
+                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'middle':
+                                    // if (t.angle > 0) {
+                                    //     shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    // }
+                                    // else {
+                                    //     shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    // }
+                                    shim = -t.getHeight()/2;
+                                    break;
+                                default:
+                                    shim = -t.getHeight()/2;
+                                    break;
+                            }
+                        }
+                        else {
+                            shim = -t.getHeight()/2;
+                        }
+                        
+                        var val = this.u2p(t.value) + shim + 'px';
+                        t._elem.css('top', val);
+                        t.pack();
+                    }
+                }
+                if (lshow) {
+                    var h = this._label._elem.outerHeight(true);
+                    this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px');
+                    if (this.name == 'yaxis') {
+                        this._label._elem.css('left', '0px');
+                    }
+                    else {
+                        this._label._elem.css('right', '0px');
+                    }   
+                    this._label.pack();
+                }
+            }
+        }
+    };
+
+
+    // class: $.jqplot.MarkerRenderer
+    // The default jqPlot marker renderer, rendering the points on the line.
+    $.jqplot.MarkerRenderer = function(options){
+        // Group: Properties
+        
+        // prop: show
+        // wether or not to show the marker.
+        this.show = true;
+        // prop: style
+        // One of diamond, circle, square, x, plus, dash, filledDiamond, filledCircle, filledSquare
+        this.style = 'filledCircle';
+        // prop: lineWidth
+        // size of the line for non-filled markers.
+        this.lineWidth = 2;
+        // prop: size
+        // Size of the marker (diameter or circle, length of edge of square, etc.)
+        this.size = 9.0;
+        // prop: color
+        // color of marker.  Will be set to color of series by default on init.
+        this.color = '#666666';
+        // prop: shadow
+        // wether or not to draw a shadow on the line
+        this.shadow = true;
+        // prop: shadowAngle
+        // Shadow angle in degrees
+        this.shadowAngle = 45;
+        // prop: shadowOffset
+        // Shadow offset from line in pixels
+        this.shadowOffset = 1;
+        // prop: shadowDepth
+        // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
+        this.shadowDepth = 3;
+        // prop: shadowAlpha
+        // Alpha channel transparency of shadow.  0 = transparent.
+        this.shadowAlpha = '0.07';
+        // prop: shadowRenderer
+        // Renderer that will draws the shadows on the marker.
+        this.shadowRenderer = new $.jqplot.ShadowRenderer();
+        // prop: shapeRenderer
+        // Renderer that will draw the marker.
+        this.shapeRenderer = new $.jqplot.ShapeRenderer();
+        
+        $.extend(true, this, options);
+    };
+    
+    $.jqplot.MarkerRenderer.prototype.init = function(options) {
+        $.extend(true, this, options);
+        var sdopt = {angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, lineWidth:this.lineWidth, depth:this.shadowDepth, closePath:true};
+        if (this.style.indexOf('filled') != -1) {
+            sdopt.fill = true;
+        }
+        if (this.style.indexOf('ircle') != -1) {
+            sdopt.isarc = true;
+            sdopt.closePath = false;
+        }
+        this.shadowRenderer.init(sdopt);
+        
+        var shopt = {fill:false, isarc:false, strokeStyle:this.color, fillStyle:this.color, lineWidth:this.lineWidth, closePath:true};
+        if (this.style.indexOf('filled') != -1) {
+            shopt.fill = true;
+        }
+        if (this.style.indexOf('ircle') != -1) {
+            shopt.isarc = true;
+            shopt.closePath = false;
+        }
+        this.shapeRenderer.init(shopt);
+    };
+    
+    $.jqplot.MarkerRenderer.prototype.drawDiamond = function(x, y, ctx, fill, options) {
+        var stretch = 1.2;
+        var dx = this.size/2/stretch;
+        var dy = this.size/2*stretch;
+        var points = [[x-dx, y], [x, y+dy], [x+dx, y], [x, y-dy]];
+        if (this.shadow) {
+            this.shadowRenderer.draw(ctx, points);
+        }
+        this.shapeRenderer.draw(ctx, points, options);
+
+        // ctx.restore();
+    };
+    
+    $.jqplot.MarkerRenderer.prototype.drawPlus = function(x, y, ctx, fill, options) {
+        var stretch = 1.0;
+        var dx = this.size/2*stretch;
+        var dy = this.size/2*stretch;
+        var points1 = [[x, y-dy], [x, y+dy]];
+        var points2 = [[x+dx, y], [x-dx, y]];
+        var opts = $.extend(true, {}, this.options, {closePath:false});
+        if (this.shadow) {
+            this.shadowRenderer.draw(ctx, points1, {closePath:false});
+            this.shadowRenderer.draw(ctx, points2, {closePath:false});
+        }
+        this.shapeRenderer.draw(ctx, points1, opts);
+        this.shapeRenderer.draw(ctx, points2, opts);
+
+        // ctx.restore();
+    };
+    
+    $.jqplot.MarkerRenderer.prototype.drawX = function(x, y, ctx, fill, options) {
+        var stretch = 1.0;
+        var dx = this.size/2*stretch;
+        var dy = this.size/2*stretch;
+        var opts = $.extend(true, {}, this.options, {closePath:false});
+        var points1 = [[x-dx, y-dy], [x+dx, y+dy]];
+        var points2 = [[x-dx, y+dy], [x+dx, y-dy]];
+        if (this.shadow) {
+            this.shadowRenderer.draw(ctx, points1, {closePath:false});
+            this.shadowRenderer.draw(ctx, points2, {closePath:false});
+        }
+        this.shapeRenderer.draw(ctx, points1, opts);
+        this.shapeRenderer.draw(ctx, points2, opts);
+
+        // ctx.restore();
+    };
+    
+    $.jqplot.MarkerRenderer.prototype.drawDash = function(x, y, ctx, fill, options) {
+        var stretch = 1.0;
+        var dx = this.size/2*stretch;
+        var dy = this.size/2*stretch;
+        var points = [[x-dx, y], [x+dx, y]];
+        if (this.shadow) {
+            this.shadowRenderer.draw(ctx, points);
+        }
+        this.shapeRenderer.draw(ctx, points, options);
+
+        // ctx.restore();
+    };
+    
+    $.jqplot.MarkerRenderer.prototype.drawSquare = function(x, y, ctx, fill, options) {
+        var stretch = 1.0;
+        var dx = this.size/2/stretch;
+        var dy = this.size/2*stretch;
+        var points = [[x-dx, y-dy], [x-dx, y+dy], [x+dx, y+dy], [x+dx, y-dy]];
+        if (this.shadow) {
+            this.shadowRenderer.draw(ctx, points);
+        }
+        this.shapeRenderer.draw(ctx, points, options);
+
+        // ctx.restore();
+    };
+    
+    $.jqplot.MarkerRenderer.prototype.drawCircle = function(x, y, ctx, fill, options) {
+        var radius = this.size/2;
+        var end = 2*Math.PI;
+        var points = [x, y, radius, 0, end, true];
+        if (this.shadow) {
+            this.shadowRenderer.draw(ctx, points);
+        }
+        this.shapeRenderer.draw(ctx, points, options);
+        
+        // ctx.restore();
+    };
+    
+    $.jqplot.MarkerRenderer.prototype.draw = function(x, y, ctx, options) {
+        options = options || {};
+        // hack here b/c shape renderer uses canvas based color style options
+        // and marker uses css style names.
+        if (options.show == null || options.show != false) {
+            if (options.color && !options.fillStyle) {
+                options.fillStyle = options.color;
+            }
+            if (options.color && !options.strokeStyle) {
+                options.strokeStyle = options.color;
+            }
+            switch (this.style) {
+                case 'diamond':
+                    this.drawDiamond(x,y,ctx, false, options);
+                    break;
+                case 'filledDiamond':
+                    this.drawDiamond(x,y,ctx, true, options);
+                    break;
+                case 'circle':
+                    this.drawCircle(x,y,ctx, false, options);
+                    break;
+                case 'filledCircle':
+                    this.drawCircle(x,y,ctx, true, options);
+                    break;
+                case 'square':
+                    this.drawSquare(x,y,ctx, false, options);
+                    break;
+                case 'filledSquare':
+                    this.drawSquare(x,y,ctx, true, options);
+                    break;
+                case 'x':
+                    this.drawX(x,y,ctx, true, options);
+                    break;
+                case 'plus':
+                    this.drawPlus(x,y,ctx, true, options);
+                    break;
+                case 'dash':
+                    this.drawDash(x,y,ctx, true, options);
+                    break;
+                default:
+                    this.drawDiamond(x,y,ctx, false, options);
+                    break;
+            }
+        }
+    };
+    
+    // class: $.jqplot.shadowRenderer
+    // The default jqPlot shadow renderer, rendering shadows behind shapes.
+    $.jqplot.ShadowRenderer = function(options){ 
+        // Group: Properties
+        
+        // prop: angle
+        // Angle of the shadow in degrees.  Measured counter-clockwise from the x axis.
+        this.angle = 45;
+        // prop: offset
+        // Pixel offset at the given shadow angle of each shadow stroke from the last stroke.
+        this.offset = 1;
+        // prop: alpha
+        // alpha transparency of shadow stroke.
+        this.alpha = 0.07;
+        // prop: lineWidth
+        // width of the shadow line stroke.
+        this.lineWidth = 1.5;
+        // prop: lineJoin
+        // How line segments of the shadow are joined.
+        this.lineJoin = 'miter';
+        // prop: lineCap
+        // how ends of the shadow line are rendered.
+        this.lineCap = 'round';
+        // prop; closePath
+        // whether line path segment is closed upon itself.
+        this.closePath = false;
+        // prop: fill
+        // whether to fill the shape.
+        this.fill = false;
+        // prop: depth
+        // how many times the shadow is stroked.  Each stroke will be offset by offset at angle degrees.
+        this.depth = 3;
+        this.strokeStyle = 'rgba(0,0,0,0.1)';
+        // prop: isarc
+        // wether the shadow is an arc or not.
+        this.isarc = false;
+        
+        $.extend(true, this, options);
+    };
+    
+    $.jqplot.ShadowRenderer.prototype.init = function(options) {
+        $.extend(true, this, options);
+    };
+    
+    // function: draw
+    // draws an transparent black (i.e. gray) shadow.
+    //
+    // ctx - canvas drawing context
+    // points - array of points or [x, y, radius, start angle (rad), end angle (rad)]
+    $.jqplot.ShadowRenderer.prototype.draw = function(ctx, points, options) {
+        ctx.save();
+        var opts = (options != null) ? options : {};
+        var fill = (opts.fill != null) ? opts.fill : this.fill;
+        var closePath = (opts.closePath != null) ? opts.closePath : this.closePath;
+        var offset = (opts.offset != null) ? opts.offset : this.offset;
+        var alpha = (opts.alpha != null) ? opts.alpha : this.alpha;
+        var depth = (opts.depth != null) ? opts.depth : this.depth;
+        var isarc = (opts.isarc != null) ? opts.isarc : this.isarc;
+        ctx.lineWidth = (opts.lineWidth != null) ? opts.lineWidth : this.lineWidth;
+        ctx.lineJoin = (opts.lineJoin != null) ? opts.lineJoin : this.lineJoin;
+        ctx.lineCap = (opts.lineCap != null) ? opts.lineCap : this.lineCap;
+        ctx.strokeStyle = opts.strokeStyle || this.strokeStyle || 'rgba(0,0,0,'+alpha+')';
+        ctx.fillStyle = opts.fillStyle || this.fillStyle || 'rgba(0,0,0,'+alpha+')';
+        for (var j=0; j<depth; j++) {
+            ctx.translate(Math.cos(this.angle*Math.PI/180)*offset, Math.sin(this.angle*Math.PI/180)*offset);
+            ctx.beginPath();
+            if (isarc) {
+                ctx.arc(points[0], points[1], points[2], points[3], points[4], true);                
+            }
+            else {
+                var move = true;
+                for (var i=0; i<points.length; i++) {
+                    // skip to the first non-null point and move to it.
+                    if (points[i][0] != null && points[i][1] != null) {
+                        if (move) {
+                            ctx.moveTo(points[i][0], points[i][1]);
+                            move = false;
+                        }
+                        else {
+                            ctx.lineTo(points[i][0], points[i][1]);
+                        }
+                    }
+                    else {
+                        move = true;
+                    }
+                }
+                
+            }
+            if (closePath) {
+                ctx.closePath();
+            }
+            if (fill) {
+                ctx.fill();
+            }
+            else {
+                ctx.stroke();
+            }
+        }
+        ctx.restore();
+    };
+    
+    // class: $.jqplot.shapeRenderer
+    // The default jqPlot shape renderer.  Given a set of points will
+    // plot them and either stroke a line (fill = false) or fill them (fill = true).
+    // If a filled shape is desired, closePath = true must also be set to close
+    // the shape.
+    $.jqplot.ShapeRenderer = function(options){
+        
+        this.lineWidth = 1.5;
+        // prop: lineJoin
+        // How line segments of the shadow are joined.
+        this.lineJoin = 'miter';
+        // prop: lineCap
+        // how ends of the shadow line are rendered.
+        this.lineCap = 'round';
+        // prop; closePath
+        // whether line path segment is closed upon itself.
+        this.closePath = false;
+        // prop: fill
+        // whether to fill the shape.
+        this.fill = false;
+        // prop: isarc
+        // wether the shadow is an arc or not.
+        this.isarc = false;
+        // prop: fillRect
+        // true to draw shape as a filled rectangle.
+        this.fillRect = false;
+        // prop: strokeRect
+        // true to draw shape as a stroked rectangle.
+        this.strokeRect = false;
+        // prop: clearRect
+        // true to cear a rectangle.
+        this.clearRect = false;
+        // prop: strokeStyle
+        // css color spec for the stoke style
+        this.strokeStyle = '#999999';
+        // prop: fillStyle
+        // css color spec for the fill style.
+        this.fillStyle = '#999999'; 
+        
+        $.extend(true, this, options);
+    };
+    
+    $.jqplot.ShapeRenderer.prototype.init = function(options) {
+        $.extend(true, this, options);
+    };
+    
+    // function: draw
+    // draws the shape.
+    //
+    // ctx - canvas drawing context
+    // points - array of points for shapes or 
+    // [x, y, width, height] for rectangles or
+    // [x, y, radius, start angle (rad), end angle (rad)] for circles and arcs.
+    $.jqplot.ShapeRenderer.prototype.draw = function(ctx, points, options) {
+        ctx.save();
+        var opts = (options != null) ? options : {};
+        var fill = (opts.fill != null) ? opts.fill : this.fill;
+        var closePath = (opts.closePath != null) ? opts.closePath : this.closePath;
+        var fillRect = (opts.fillRect != null) ? opts.fillRect : this.fillRect;
+        var strokeRect = (opts.strokeRect != null) ? opts.strokeRect : this.strokeRect;
+        var clearRect = (opts.clearRect != null) ? opts.clearRect : this.clearRect;
+        var isarc = (opts.isarc != null) ? opts.isarc : this.isarc;
+        ctx.lineWidth = opts.lineWidth || this.lineWidth;
+        ctx.lineJoin = opts.lineJoing || this.lineJoin;
+        ctx.lineCap = opts.lineCap || this.lineCap;
+        ctx.strokeStyle = (opts.strokeStyle || opts.color) || this.strokeStyle;
+        ctx.fillStyle = opts.fillStyle || this.fillStyle;
+        ctx.beginPath();
+        if (isarc) {
+            ctx.arc(points[0], points[1], points[2], points[3], points[4], true);   
+            if (closePath) {
+                ctx.closePath();
+            }
+            if (fill) {
+                ctx.fill();
+            }
+            else {
+                ctx.stroke();
+            }
+            ctx.restore();
+            return;
+        }
+        else if (clearRect) {
+            ctx.clearRect(points[0], points[1], points[2], points[3]);
+            ctx.restore();
+            return;
+        }
+        else if (fillRect || strokeRect) {
+            if (fillRect) {
+                ctx.fillRect(points[0], points[1], points[2], points[3]);
+            }
+            if (strokeRect) {
+                ctx.strokeRect(points[0], points[1], points[2], points[3]);
+                ctx.restore();
+                return;
+            }
+        }
+        else {
+            var move = true;
+            for (var i=0; i<points.length; i++) {
+                // skip to the first non-null point and move to it.
+                if (points[i][0] != null && points[i][1] != null) {
+                    if (move) {
+                        ctx.moveTo(points[i][0], points[i][1]);
+                        move = false;
+                    }
+                    else {
+                        ctx.lineTo(points[i][0], points[i][1]);
+                    }
+                }
+                else {
+                    move = true;
+                }
+            }
+            if (closePath) {
+                ctx.closePath();
+            }
+            if (fill) {
+                ctx.fill();
+            }
+            else {
+                ctx.stroke();
+            }
+        }
+        ctx.restore();
+    };
+    
+    // class $.jqplot.TableLegendRenderer
+    // The default legend renderer for jqPlot.
+    $.jqplot.TableLegendRenderer = function(){
+        //
+    };
+    
+    $.jqplot.TableLegendRenderer.prototype.init = function(options) {
+        $.extend(true, this, options);
+    };
+        
+    $.jqplot.TableLegendRenderer.prototype.addrow = function (label, color, pad, reverse) {
+        var rs = (pad) ? this.rowSpacing : '0';
+        if (reverse){
+            var tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem);
+        }
+        else{
+            var tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem);
+        }
+        if (this.showSwatches) {
+            $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+
+            '<div><div class="jqplot-table-legend-swatch" style="background-color:'+color+';border-color:'+color+';"></div>'+
+            '</div></td>').appendTo(tr);
+        }
+        if (this.showLabels) {
+            var elem = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
+            elem.appendTo(tr);
+            if (this.escapeHtml) {
+                elem.text(label);
+            }
+            else {
+                elem.html(label);
+            }
+        }
+    };
+    
+    // called with scope of legend
+    $.jqplot.TableLegendRenderer.prototype.draw = function() {
+        var legend = this;
+        if (this.show) {
+            var series = this._series;
+            // make a table.  one line label per row.
+            var ss = 'position:absolute;';
+            ss += (this.background) ? 'background:'+this.background+';' : '';
+            ss += (this.border) ? 'border:'+this.border+';' : '';
+            ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
+            ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : '';
+            ss += (this.textColor) ? 'color:'+this.textColor+';' : '';
+            ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : '';
+            ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : '';
+            ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : '';
+            ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : '';
+            this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>');
+        
+            var pad = false, 
+                reverse = false;
+            for (var i = 0; i< series.length; i++) {
+                s = series[i];
+                if (s._stack || s.renderer.constructor == $.jqplot.BezierCurveRenderer){
+                    reverse = true;
+                }
+                if (s.show && s.showLabel) {
+                    var lt = this.labels[i] || s.label.toString();
+                    if (lt) {
+                        var color = s.color;
+                        if (reverse && i < series.length - 1){
+                            pad = true;
+                        }
+                        else if (reverse && i == series.length - 1){
+                            pad = false;
+                        }
+                        this.renderer.addrow.call(this, lt, color, pad, reverse);
+                        pad = true;
+                    }
+                    // let plugins add more rows to legend.  Used by trend line plugin.
+                    for (var j=0; j<$.jqplot.addLegendRowHooks.length; j++) {
+                        var item = $.jqplot.addLegendRowHooks[j].call(this, s);
+                        if (item) {
+                            this.renderer.addrow.call(this, item.label, item.color, pad);
+                            pad = true;
+                        } 
+                    }
+                }
+            }
+        }
+        return this._elem;
+    };
+    
+    $.jqplot.TableLegendRenderer.prototype.pack = function(offsets) {
+        if (this.show) {       
+            if (this.placement == 'insideGrid') {
+                switch (this.location) {
+                    case 'nw':
+                        var a = offsets.left;
+                        var b = offsets.top;
+                        this._elem.css('left', a);
+                        this._elem.css('top', b);
+                        break;
+                    case 'n':
+                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+                        var b = offsets.top;
+                        this._elem.css('left', a);
+                        this._elem.css('top', b);
+                        break;
+                    case 'ne':
+                        var a = offsets.right;
+                        var b = offsets.top;
+                        this._elem.css({right:a, top:b});
+                        break;
+                    case 'e':
+                        var a = offsets.right;
+                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+                        this._elem.css({right:a, top:b});
+                        break;
+                    case 'se':
+                        var a = offsets.right;
+                        var b = offsets.bottom;
+                        this._elem.css({right:a, bottom:b});
+                        break;
+                    case 's':
+                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+                        var b = offsets.bottom;
+                        this._elem.css({left:a, bottom:b});
+                        break;
+                    case 'sw':
+                        var a = offsets.left;
+                        var b = offsets.bottom;
+                        this._elem.css({left:a, bottom:b});
+                        break;
+                    case 'w':
+                        var a = offsets.left;
+                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+                        this._elem.css({left:a, top:b});
+                        break;
+                    default:  // same as 'se'
+                        var a = offsets.right;
+                        var b = offsets.bottom;
+                        this._elem.css({right:a, bottom:b});
+                        break;
+                }
+                
+            }
+            else if (this.placement == 'outside'){
+                switch (this.location) {
+                    case 'nw':
+                        var a = this._plotDimensions.width - offsets.left;
+                        var b = offsets.top;
+                        this._elem.css('right', a);
+                        this._elem.css('top', b);
+                        break;
+                    case 'n':
+                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+                        var b = this._plotDimensions.height - offsets.top;
+                        this._elem.css('left', a);
+                        this._elem.css('bottom', b);
+                        break;
+                    case 'ne':
+                        var a = this._plotDimensions.width - offsets.right;
+                        var b = offsets.top;
+                        this._elem.css({left:a, top:b});
+                        break;
+                    case 'e':
+                        var a = this._plotDimensions.width - offsets.right;
+                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+                        this._elem.css({left:a, top:b});
+                        break;
+                    case 'se':
+                        var a = this._plotDimensions.width - offsets.right;
+                        var b = offsets.bottom;
+                        this._elem.css({left:a, bottom:b});
+                        break;
+                    case 's':
+                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+                        var b = this._plotDimensions.height - offsets.bottom;
+                        this._elem.css({left:a, top:b});
+                        break;
+                    case 'sw':
+                        var a = this._plotDimensions.width - offsets.left;
+                        var b = offsets.bottom;
+                        this._elem.css({right:a, bottom:b});
+                        break;
+                    case 'w':
+                        var a = this._plotDimensions.width - offsets.left;
+                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+                        this._elem.css({right:a, top:b});
+                        break;
+                    default:  // same as 'se'
+                        var a = offsets.right;
+                        var b = offsets.bottom;
+                        this._elem.css({right:a, bottom:b});
+                        break;
+                }
+            }
+            else {
+                switch (this.location) {
+                    case 'nw':
+                        this._elem.css({left:0, top:offsets.top});
+                        break;
+                    case 'n':
+                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+                        this._elem.css({left: a, top:offsets.top});
+                        break;
+                    case 'ne':
+                        this._elem.css({right:0, top:offsets.top});
+                        break;
+                    case 'e':
+                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+                        this._elem.css({right:offsets.right, top:b});
+                        break;
+                    case 'se':
+                        this._elem.css({right:offsets.right, bottom:offsets.bottom});
+                        break;
+                    case 's':
+                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+                        this._elem.css({left: a, bottom:offsets.bottom});
+                        break;
+                    case 'sw':
+                        this._elem.css({left:offsets.left, bottom:offsets.bottom});
+                        break;
+                    case 'w':
+                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+                        this._elem.css({left:offsets.left, top:b});
+                        break;
+                    default:  // same as 'se'
+                        this._elem.css({right:offsets.right, bottom:offsets.bottom});
+                        break;
+                }
+            }
+        } 
+    };
+
+    /**
+     * Class: $.jqplot.ThemeEngine
+     * Theme Engine provides a programatic way to change some of the  more
+     * common jqplot styling options such as fonts, colors and grid options.
+     * A theme engine instance is created with each plot.  The theme engine
+     * manages a collection of themes which can be modified, added to, or 
+     * applied to the plot.
+     * 
+     * The themeEngine class is not instantiated directly.
+     * When a plot is initialized, the current plot options are scanned
+     * an a default theme named "Default" is created.  This theme is
+     * used as the basis for other themes added to the theme engine and
+     * is always available.
+     * 
+     * A theme is a simple javascript object with styling parameters for
+     * various entities of the plot.  A theme has the form:
+     * 
+     * 
+     * > {
+     * >     _name:f "Default",
+     * >     target: {
+     * >         backgroundColor: "transparent"
+     * >     },
+     * >     legend: {
+     * >         textColor: null,
+     * >         fontFamily: null,
+     * >         fontSize: null,
+     * >         border: null,
+     * >         background: null
+     * >     },
+     * >     title: {
+     * >         textColor: "rgb(102, 102, 102)",
+     * >         fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif",
+     * >         fontSize: "19.2px",
+     * >         textAlign: "center"
+     * >     },
+     * >     seriesStyles: {},
+     * >     series: [{
+     * >         color: "#4bb2c5",
+     * >         lineWidth: 2.5,
+     * >         shadow: true,
+     * >         fillColor: "#4bb2c5",
+     * >         showMarker: true,
+     * >         markerOptions: {
+     * >             color: "#4bb2c5",
+     * >             show: true,
+     * >             style: 'filledCircle',
+     * >             lineWidth: 1.5,
+     * >             size: 4,
+     * >             shadow: true
+     * >         }
+     * >     }],
+     * >     grid: {
+     * >         drawGridlines: true,
+     * >         gridLineColor: "#cccccc",
+     * >         gridLineWidth: 1,
+     * >         backgroundColor: "#fffdf6",
+     * >         borderColor: "#999999",
+     * >         borderWidth: 2,
+     * >         shadow: true
+     * >     },
+     * >     axesStyles: {
+     * >         label: {},
+     * >         ticks: {}
+     * >     },
+     * >     axes: {
+     * >         xaxis: {
+     * >             borderColor: "#999999",
+     * >             borderWidth: 2,
+     * >             ticks: {
+     * >                 show: true,
+     * >                 showGridline: true,
+     * >                 showLabel: true,
+     * >                 showMark: true,
+     * >                 size: 4,
+     * >                 textColor: "",
+     * >                 whiteSpace: "nowrap",
+     * >                 fontSize: "12px",
+     * >                 fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif"
+     * >             },
+     * >             label: {
+     * >                 textColor: "rgb(102, 102, 102)",
+     * >                 whiteSpace: "normal",
+     * >                 fontSize: "14.6667px",
+     * >                 fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif",
+     * >                 fontWeight: "400"
+     * >             }
+     * >         },
+     * >         yaxis: {
+     * >             borderColor: "#999999",
+     * >             borderWidth: 2,
+     * >             ticks: {
+     * >                 show: true,
+     * >                 showGridline: true,
+     * >                 showLabel: true,
+     * >                 showMark: true,
+     * >                 size: 4,
+     * >                 textColor: "",
+     * >                 whiteSpace: "nowrap",
+     * >                 fontSize: "12px",
+     * >                 fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif"
+     * >             },
+     * >             label: {
+     * >                 textColor: null,
+     * >                 whiteSpace: null,
+     * >                 fontSize: null,
+     * >                 fontFamily: null,
+     * >                 fontWeight: null
+     * >             }
+     * >         },
+     * >         x2axis: {...
+     * >         },
+     * >         ...
+     * >         y9axis: {...
+     * >         }
+     * >     }
+     * > }
+     * 
+     * "seriesStyles" is a style object that will be applied to all series in the plot.
+     * It will forcibly override any styles applied on the individual series.  "axesStyles" is
+     * a style object that will be applied to all axes in the plot.  It will also forcibly
+     * override any styles on the individual axes.
+     * 
+     * The example shown above has series options for a line series.  Options for other
+     * series types are shown below:
+     * 
+     * Bar Series:
+     * 
+     * > {
+     * >     color: "#4bb2c5",
+     * >     seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
+     * >     lineWidth: 2.5,
+     * >     shadow: true,
+     * >     barPadding: 2,
+     * >     barMargin: 10,
+     * >     barWidth: 15.09375,
+     * >     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)"]
+     * > }
+     * 
+     * Pie Series:
+     * 
+     * > {
+     * >     seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
+     * >     padding: 20,
+     * >     sliceMargin: 0,
+     * >     fill: true,
+     * >     shadow: true,
+     * >     startAngle: 0,
+     * >     lineWidth: 2.5,
+     * >     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)"]
+     * > }
+     * 
+     * Funnel Series:
+     * 
+     * > {
+     * >     color: "#4bb2c5",
+     * >     lineWidth: 2,
+     * >     shadow: true,
+     * >     padding: {
+     * >         top: 20,
+     * >         right: 20,
+     * >         bottom: 20,
+     * >         left: 20
+     * >     },
+     * >     sectionMargin: 6,
+     * >     seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
+     * >     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)"]
+     * > }
+     * 
+     */
+    $.jqplot.ThemeEngine = function(){
+        // Group: Properties
+        //
+        // prop: themes
+        // hash of themes managed by the theme engine.  
+        // Indexed by theme name.
+        this.themes = {};
+        // prop: activeTheme
+        // Pointer to currently active theme
+        this.activeTheme=null;
+        
+    };
+    
+    // called with scope of plot
+    $.jqplot.ThemeEngine.prototype.init = function() {
+        // get the Default theme from the current plot settings.
+        var th = new $.jqplot.Theme({_name:'Default'});
+        var n, i;
+        
+        for (n in th.target) {
+            if (n == "textColor") {
+                th.target[n] = this.target.css('color');
+            }
+            else {
+                th.target[n] = this.target.css(n);
+            }
+        }
+        
+        if (this.title.show && this.title._elem) {
+            for (n in th.title) {
+                if (n == "textColor") {
+                    th.title[n] = this.title._elem.css('color');
+                }
+                else {
+                    th.title[n] = this.title._elem.css(n);
+                }
+            }
+        }
+        
+        for (n in th.grid) {
+            th.grid[n] = this.grid[n];
+        }
+        if (th.grid.backgroundColor == null && this.grid.background != null) {
+            th.grid.backgroundColor = this.grid.background;
+        }
+        if (this.legend.show && this.legend._elem) {
+            for (n in th.legend) {
+                if (n == 'textColor') {
+                    th.legend[n] = this.legend._elem.css('color');
+                }
+                else {
+                    th.legend[n] = this.legend._elem.css(n);
+                }
+            }
+        }
+        var s;
+        
+        for (i=0; i<this.series.length; i++) {
+            s = this.series[i];
+            if (s.renderer.constructor == $.jqplot.LineRenderer) {
+                th.series.push(new LineSeriesProperties());
+            }
+            else if (s.renderer.constructor == $.jqplot.BarRenderer) {
+                th.series.push(new BarSeriesProperties());
+            }
+            else if (s.renderer.constructor == $.jqplot.PieRenderer) {
+                th.series.push(new PieSeriesProperties());
+            }
+            else if (s.renderer.constructor == $.jqplot.DonutRenderer) {
+                th.series.push(new DonutSeriesProperties());
+            }
+            else if (s.renderer.constructor == $.jqplot.FunnelRenderer) {
+                th.series.push(new FunnelSeriesProperties());
+            }
+            else if (s.renderer.constructor == $.jqplot.MeterGaugeRenderer) {
+                th.series.push(new MeterSeriesProperties());
+            }
+            else {
+                th.series.push({});
+            }
+            for (n in th.series[i]) {
+                th.series[i][n] = s[n];
+            }
+        }
+        var a, ax;
+        for (n in this.axes) {
+            ax = this.axes[n];
+            a = th.axes[n] = new AxisProperties();
+            a.borderColor = ax.borderColor;
+            a.borderWidth = ax.borderWidth;
+            if (ax._ticks && ax._ticks[0]) {
+                for (nn in a.ticks) {
+                    if (ax._ticks[0].hasOwnProperty(nn)) {
+                        a.ticks[nn] = ax._ticks[0][nn];
+                    }
+                    else if (ax._ticks[0]._elem){
+                        a.ticks[nn] = ax._ticks[0]._elem.css(nn);
+                    }
+                }
+            }
+            if (ax._label && ax._label.show) {
+                for (nn in a.label) {
+                    // a.label[nn] = ax._label._elem.css(nn);
+                    if (ax._label[nn]) {
+                        a.label[nn] = ax._label[nn];
+                    }
+                    else if (ax._label._elem){
+                        if (nn == 'textColor') {
+                            a.label[nn] = ax._label._elem.css('color');
+                        }
+                        else {
+                            a.label[nn] = ax._label._elem.css(nn);
+                        }
+                    }
+                }
+            }
+        }
+        this.themeEngine._add(th);
+        this.themeEngine.activeTheme  = this.themeEngine.themes[th._name];
+    };
+    /**
+     * Group: methods
+     * 
+     * method: get
+     * 
+     * Get and return the named theme or the active theme if no name given.
+     * 
+     * parameter:
+     * 
+     * name - name of theme to get.
+     * 
+     * returns:
+     * 
+     * Theme instance of given name.
+     */   
+    $.jqplot.ThemeEngine.prototype.get = function(name) {
+        if (!name) {
+            // return the active theme
+            return this.activeTheme;
+        }
+        else {
+            return this.themes[name];
+        }
+    };
+    
+    function numericalOrder(a,b) { return a-b; }
+    
+    /**
+     * method: getThemeNames
+     * 
+     * Return the list of theme names in this manager in alpha-numerical order.
+     * 
+     * parameter:
+     * 
+     * None
+     * 
+     * returns:
+     * 
+     * A the list of theme names in this manager in alpha-numerical order.
+     */       
+    $.jqplot.ThemeEngine.prototype.getThemeNames = function() {
+        var tn = [];
+        for (var n in this.themes) {
+            tn.push(n);
+        }
+        return tn.sort(numericalOrder);
+    };
+
+    /**
+     * method: getThemes
+     * 
+     * Return a list of themes in alpha-numerical order by name.
+     * 
+     * parameter:
+     * 
+     * None
+     * 
+     * returns:
+     * 
+     * A list of themes in alpha-numerical order by name.
+     */ 
+    $.jqplot.ThemeEngine.prototype.getThemes = function() {
+        var tn = [];
+        var themes = [];
+        for (var n in this.themes) {
+            tn.push(n);
+        }
+        tn.sort(numericalOrder);
+        for (var i=0; i<tn.length; i++) {
+            themes.push(this.themes[tn[i]]);
+        }
+        return themes;
+    };
+    
+    $.jqplot.ThemeEngine.prototype.activate = function(plot, name) {
+        // sometimes need to redraw whole plot.
+        var redrawPlot = false;
+        if (!name && this.activeTheme && this.activeTheme._name) {
+            name = this.activeTheme._name;
+        }
+        if (!this.themes.hasOwnProperty(name)) {
+            throw new Error("No theme of that name");
+        }
+        else {
+            var th = this.themes[name];
+            this.activeTheme = th;
+            var val, checkBorderColor = false, checkBorderWidth = false;
+            var arr = ['xaxis', 'x2axis', 'yaxis', 'y2axis'];
+            
+            for (i=0; i<arr.length; i++) {
+                var ax = arr[i];
+                if (th.axesStyles.borderColor != null) {
+                    plot.axes[ax].borderColor = th.axesStyles.borderColor;
+                }
+                if (th.axesStyles.borderWidth != null) {
+                    plot.axes[ax].borderWidth = th.axesStyles.borderWidth;
+                }
+            }
+            
+            for (axname in plot.axes) {
+                var axis = plot.axes[axname];
+                if (axis.show) {
+                    var thaxis = th.axes[axname] || {};
+                    var thaxstyle = th.axesStyles;
+                    var thax = $.jqplot.extend(true, {}, thaxis, thaxstyle);
+                    val = (th.axesStyles.borderColor != null) ? th.axesStyles.borderColor : thax.borderColor;
+                    if (thax.borderColor != null) {
+                        axis.borderColor = thax.borderColor;
+                        redrawPlot = true;
+                    }
+                    val = (th.axesStyles.borderWidth != null) ? th.axesStyles.borderWidth : thax.borderWidth;
+                    if (thax.borderWidth != null) {
+                        axis.borderWidth = thax.borderWidth;
+                        redrawPlot = true;
+                    }
+                    if (axis._ticks && axis._ticks[0]) {
+                        for (nn in thax.ticks) {
+                            // val = null;
+                            // if (th.axesStyles.ticks && th.axesStyles.ticks[nn] != null) {
+                            //     val = th.axesStyles.ticks[nn];
+                            // }
+                            // else if (thax.ticks[nn] != null){
+                            //     val = thax.ticks[nn]
+                            // }
+                            val = thax.ticks[nn];
+                            if (val != null) {
+                                axis.tickOptions[nn] = val;
+                                axis._ticks = [];
+                                redrawPlot = true;
+                            }
+                        }
+                    }
+                    if (axis._label && axis._label.show) {
+                        for (nn in thax.label) {
+                            // val = null;
+                            // if (th.axesStyles.label && th.axesStyles.label[nn] != null) {
+                            //     val = th.axesStyles.label[nn];
+                            // }
+                            // else if (thax.label && thax.label[nn] != null){
+                            //     val = thax.label[nn]
+                            // }
+                            val = thax.label[nn];
+                            if (val != null) {
+                                axis.labelOptions[nn] = val;
+                                redrawPlot = true;
+                            }
+                        }
+                    }
+                    
+                }
+            }            
+            
+            for (var n in th.grid) {
+                if (th.grid[n] != null) {
+                    plot.grid[n] = th.grid[n];
+                }
+            }
+            if (!redrawPlot) {
+                plot.grid.draw();
+            }
+            
+            if (plot.legend.show) { 
+                for (n in th.legend) {
+                    if (th.legend[n] != null) {
+                        plot.legend[n] = th.legend[n];
+                    }
+                }
+            }
+            if (plot.title.show) {
+                for (n in th.title) {
+                    if (th.title[n] != null) {
+                        plot.title[n] = th.title[n];
+                    }
+                }
+            }
+            
+            var i;
+            for (i=0; i<th.series.length; i++) {
+                var opts = {};
+                var redrawSeries = false;
+                for (n in th.series[i]) {
+                    val = (th.seriesStyles[n] != null) ? th.seriesStyles[n] : th.series[i][n];
+                    if (val != null) {
+                        opts[n] = val;
+                        if (n == 'color') {
+                            plot.series[i].renderer.shapeRenderer.fillStyle = val;
+                            plot.series[i].renderer.shapeRenderer.strokeStyle = val;
+                            plot.series[i][n] = val;
+                        }
+                        else if (n == 'lineWidth') {
+                            plot.series[i].renderer.shapeRenderer.lineWidth = val;
+                            plot.series[i][n] = val;
+                        }
+                        else if (n == 'markerOptions') {
+                            merge (plot.series[i].markerOptions, val);
+                            merge (plot.series[i].markerRenderer, val);
+                        }
+                        else {
+                            plot.series[i][n] = val;
+                        }
+                        redrawPlot = true;
+                    }
+                }
+            }
+            
+            if (redrawPlot) {
+                plot.target.empty();
+                plot.draw();
+            }
+            
+            for (n in th.target) {
+                if (th.target[n] != null) {
+                    plot.target.css(n, th.target[n]);
+                }
+            }
+        }
+        
+    };
+    
+    $.jqplot.ThemeEngine.prototype._add = function(theme, name) {
+        if (name) {
+            theme._name = name;
+        }
+        if (!theme._name) {
+            theme._name = Date.parse(new Date());
+        }
+        if (!this.themes.hasOwnProperty(theme._name)) {
+            this.themes[theme._name] = theme;
+        }
+        else {
+            throw new Error("jqplot.ThemeEngine Error: Theme already in use");
+        }
+    };
+    
+    // method remove
+    // Delete the named theme, return true on success, false on failure.
+    
+
+    /**
+     * method: remove
+     * 
+     * Remove the given theme from the themeEngine.
+     * 
+     * parameters:
+     * 
+     * name - name of the theme to remove.
+     * 
+     * returns:
+     * 
+     * true on success, false on failure.
+     */
+    $.jqplot.ThemeEngine.prototype.remove = function(name) {
+        if (name == 'Default') {
+            return false;
+        }
+        return delete this.themes[name];
+    };
+
+    /**
+     * method: newTheme
+     * 
+     * Create a new theme based on the default theme, adding it the themeEngine.
+     * 
+     * parameters:
+     * 
+     * name - name of the new theme.
+     * obj - optional object of styles to be applied to this new theme.
+     * 
+     * returns:
+     * 
+     * new Theme object.
+     */
+    $.jqplot.ThemeEngine.prototype.newTheme = function(name, obj) {
+        if (typeof(name) == 'object') {
+            obj = obj || name;
+            name = null;
+        }
+        if (obj && obj._name) {
+            name = obj._name;
+        }
+        else {
+            name = name || Date.parse(new Date());
+        }
+        // var th = new $.jqplot.Theme(name);
+        var th = this.copy(this.themes['Default']._name, name);
+        $.jqplot.extend(th, obj);
+        return th;
+    };
+    
+    // function clone(obj) {
+    //     return eval(obj.toSource());
+    // }
+    
+    function clone(obj){
+        if(obj == null || typeof(obj) != 'object'){
+            return obj;
+        }
+    
+        var temp = new obj.constructor();
+        for(var key in obj){
+            temp[key] = clone(obj[key]);
+        }   
+        return temp;
+    }
+    
+    $.jqplot.clone = clone;
+    
+    function merge(obj1, obj2) {
+        if (obj2 ==  null || typeof(obj2) != 'object') {
+            return;
+        }
+        for (var key in obj2) {
+            if (key == 'highlightColors') {
+                obj1[key] = clone(obj2[key]);
+            }
+            if (obj2[key] != null && typeof(obj2[key]) == 'object') {
+                if (!obj1.hasOwnProperty(key)) {
+                    obj1[key] = {};
+                }
+                merge(obj1[key], obj2[key]);
+            }
+            else {
+                obj1[key] = obj2[key];
+            }
+        }
+    }
+    
+    $.jqplot.merge = merge;
+    
+        // Use the jQuery 1.3.2 extend function since behaviour in jQuery 1.4 seems problematic
+    $.jqplot.extend = function() {
+    	// copy reference to target object
+    	var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
+
+    	// Handle a deep copy situation
+    	if ( typeof target === "boolean" ) {
+    		deep = target;
+    		target = arguments[1] || {};
+    		// skip the boolean and the target
+    		i = 2;
+    	}
+
+    	// Handle case when target is a string or something (possible in deep copy)
+    	if ( typeof target !== "object" && !toString.call(target) === "[object Function]" ) {
+    	    target = {};
+    	}
+
+    	for ( ; i < length; i++ ){
+    		// Only deal with non-null/undefined values
+    		if ( (options = arguments[ i ]) != null ) {
+    			// Extend the base object
+    			for ( var name in options ) {
+    				var src = target[ name ], copy = options[ name ];
+
+    				// Prevent never-ending loop
+    				if ( target === copy ) {
+    					continue;
+    				}
+
+    				// Recurse if we're merging object values
+    				if ( deep && copy && typeof copy === "object" && !copy.nodeType ) {
+    					target[ name ] = $.jqplot.extend( deep, 
+    						// Never move original objects, clone them
+    						src || ( copy.length != null ? [ ] : { } )
+    					, copy );
+                    }
+    				// Don't bring in undefined values
+    				else if ( copy !== undefined ) {
+    					target[ name ] = copy;
+    				}
+    			}
+    		}
+        }
+    	// Return the modified object
+    	return target;
+    };
+
+    /**
+     * method: rename
+     * 
+     * Rename a theme.
+     * 
+     * parameters:
+     * 
+     * oldName - current name of the theme.
+     * newName - desired name of the theme.
+     * 
+     * returns:
+     * 
+     * new Theme object.
+     */
+    $.jqplot.ThemeEngine.prototype.rename = function (oldName, newName) {
+        if (oldName == 'Default' || newName == 'Default') {
+            throw new Error ("jqplot.ThemeEngine Error: Cannot rename from/to Default");
+        }
+        if (this.themes.hasOwnProperty(newName)) {
+            throw new Error ("jqplot.ThemeEngine Error: New name already in use.");
+        }
+        else if (this.themes.hasOwnProperty(oldName)) {
+            var th = this.copy (oldName, newName);
+            this.remove(oldName);
+            return th;
+        }
+        throw new Error("jqplot.ThemeEngine Error: Old name or new name invalid");
+    };
+
+    /**
+     * method: copy
+     * 
+     * Create a copy of an existing theme in the themeEngine, adding it the themeEngine.
+     * 
+     * parameters:
+     * 
+     * sourceName - name of the existing theme.
+     * targetName - name of the copy.
+     * obj - optional object of style parameter to apply to the new theme.
+     * 
+     * returns:
+     * 
+     * new Theme object.
+     */
+    $.jqplot.ThemeEngine.prototype.copy = function (sourceName, targetName, obj) {
+        if (targetName == 'Default') {
+            throw new Error ("jqplot.ThemeEngine Error: Cannot copy over Default theme");
+        }
+        if (!this.themes.hasOwnProperty(sourceName)) {
+            var s = "jqplot.ThemeEngine Error: Source name invalid";
+            throw new Error(s);
+        }
+        if (this.themes.hasOwnProperty(targetName)) {
+            var s = "jqplot.ThemeEngine Error: Target name invalid";
+            throw new Error(s);
+        }
+        else {
+            var th = clone(this.themes[sourceName]);
+            th._name = targetName;
+            $.jqplot.extend(true, th, obj);
+            this._add(th);
+            return th;
+        }
+    };
+    
+    
+    $.jqplot.Theme = function(name, obj) {
+        if (typeof(name) == 'object') {
+            obj = obj || name;
+            name = null;
+        }
+        name = name || Date.parse(new Date());
+        this._name = name;
+        this.target = {
+            backgroundColor: null
+        };
+        this.legend = {
+            textColor: null,
+            fontFamily: null,
+            fontSize: null,
+            border: null,
+            background: null
+        };
+        this.title = {
+            textColor: null,
+            fontFamily: null,
+            fontSize: null,
+            textAlign: null
+        };
+        this.seriesStyles = {};
+        this.series = [];
+        this.grid = {
+            drawGridlines: null,
+            gridLineColor: null,
+            gridLineWidth: null,
+            backgroundColor: null,
+            borderColor: null,
+            borderWidth: null,
+            shadow: null
+        };
+        this.axesStyles = {label:{}, ticks:{}};
+        this.axes = {};
+        if (typeof(obj) == 'string') {
+            this._name = obj;
+        }
+        else if(typeof(obj) == 'object') {
+            $.jqplot.extend(true, this, obj);
+        }
+    };
+    
+    var AxisProperties = function() {
+        this.borderColor = null;
+        this.borderWidth = null;
+        this.ticks = new AxisTicks();
+        this.label = new AxisLabel();
+    };
+    
+    var AxisTicks = function() {
+        this.show = null;
+        this.showGridline = null;
+        this.showLabel = null;
+        this.showMark = null;
+        this.size = null;
+        this.textColor = null;
+        this.whiteSpace = null;
+        this.fontSize = null;
+        this.fontFamily = null;
+    };
+    
+    var AxisLabel = function() {
+        this.textColor = null;
+        this.whiteSpace = null;
+        this.fontSize = null;
+        this.fontFamily = null;
+        this.fontWeight = null;
+    };
+    
+    var LineSeriesProperties = function() {
+        this.color=null;
+        this.lineWidth=null;
+        this.shadow=null;
+        this.fillColor=null;
+        this.showMarker=null;
+        this.markerOptions = new MarkerOptions();
+    };
+    
+    var MarkerOptions = function() {
+        this.show = null;
+        this.style = null;
+        this.lineWidth = null;
+        this.size = null;
+        this.color = null;
+        this.shadow = null;
+    };
+    
+    var BarSeriesProperties = function() {
+        this.color=null;
+        this.seriesColors=null;
+        this.lineWidth=null;
+        this.shadow=null;
+        this.barPadding=null;
+        this.barMargin=null;
+        this.barWidth=null;
+        this.highlightColors=null;
+    };
+    
+    var PieSeriesProperties = function() {
+        this.seriesColors=null;
+        this.padding=null;
+        this.sliceMargin=null;
+        this.fill=null;
+        this.shadow=null;
+        this.startAngle=null;
+        this.lineWidth=null;
+        this.highlightColors=null;
+    };
+    
+    var DonutSeriesProperties = function() {
+        this.seriesColors=null;
+        this.padding=null;
+        this.sliceMargin=null;
+        this.fill=null;
+        this.shadow=null;
+        this.startAngle=null;
+        this.lineWidth=null;
+        this.innerDiameter=null;
+        this.thickness=null;
+        this.ringMargin=null;
+        this.highlightColors=null;
+    };
+    
+    var FunnelSeriesProperties = function() {
+        this.color=null;
+        this.lineWidth=null;
+        this.shadow=null;
+        this.padding=null;
+        this.sectionMargin=null;
+        this.seriesColors=null;
+        this.highlightColors=null;
+    };
+    
+    var MeterSeriesProperties = function() {
+        this.padding=null;
+        this.backgroundColor=null;
+        this.ringColor=null;
+        this.tickColor=null;
+        this.ringWidth=null;
+        this.intervalColors=null;
+        this.intervalInnerRadius=null;
+        this.intervalOuterRadius=null;
+        this.hubRadius=null;
+        this.needleThickness=null;
+        this.needlePad=null;
+    };
+        
+
+      
+    /**
+     * JavaScript printf/sprintf functions.
+     * 
+     * This code has been adapted from the publicly available sprintf methods
+     * by Ash Searle. His original header follows:
+     *
+     *     This code is unrestricted: you are free to use it however you like.
+     *     
+     *     The functions should work as expected, performing left or right alignment,
+     *     truncating strings, outputting numbers with a required precision etc.
+     *
+     *     For complex cases, these functions follow the Perl implementations of
+     *     (s)printf, allowing arguments to be passed out-of-order, and to set the
+     *     precision or length of the output based on arguments instead of fixed
+     *     numbers.
+     *
+     *     See http://perldoc.perl.org/functions/sprintf.html for more information.
+     *
+     *     Implemented:
+     *     - zero and space-padding
+     *     - right and left-alignment,
+     *     - base X prefix (binary, octal and hex)
+     *     - positive number prefix
+     *     - (minimum) width
+     *     - precision / truncation / maximum width
+     *     - out of order arguments
+     *
+     *     Not implemented (yet):
+     *     - vector flag
+     *     - size (bytes, words, long-words etc.)
+     *     
+     *     Will not implement:
+     *     - %n or %p (no pass-by-reference in JavaScript)
+     *
+     *     @version 2007.04.27
+     *     @author Ash Searle 
+     * 
+     * You can see the original work and comments on his blog:
+     * http://hexmen.com/blog/2007/03/printf-sprintf/
+     * http://hexmen.com/js/sprintf.js
+     */
+     
+     /**
+      * @Modifications 2009.05.26
+      * @author Chris Leonello
+      * 
+      * Added %p %P specifier
+      * Acts like %g or %G but will not add more significant digits to the output than present in the input.
+      * Example:
+      * Format: '%.3p', Input: 0.012, Output: 0.012
+      * Format: '%.3g', Input: 0.012, Output: 0.0120
+      * Format: '%.4p', Input: 12.0, Output: 12.0
+      * Format: '%.4g', Input: 12.0, Output: 12.00
+      * Format: '%.4p', Input: 4.321e-5, Output: 4.321e-5
+      * Format: '%.4g', Input: 4.321e-5, Output: 4.3210e-5
+      */
+    $.jqplot.sprintf = function() {
+        function pad(str, len, chr, leftJustify) {
+            var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr);
+            return leftJustify ? str + padding : padding + str;
+
+        }
+
+        function justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace) {
+            var diff = minWidth - value.length;
+            if (diff > 0) {
+                var spchar = ' ';
+                if (htmlSpace) { spchar = '&nbsp;'; }
+                if (leftJustify || !zeroPad) {
+                    value = pad(value, minWidth, spchar, leftJustify);
+                } else {
+                    value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length);
+                }
+            }
+            return value;
+        }
+
+        function formatBaseX(value, base, prefix, leftJustify, minWidth, precision, zeroPad, htmlSpace) {
+            // Note: casts negative numbers to positive ones
+            var number = value >>> 0;
+            prefix = prefix && number && {'2': '0b', '8': '0', '16': '0x'}[base] || '';
+            value = prefix + pad(number.toString(base), precision || 0, '0', false);
+            return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace);
+        }
+
+        function formatString(value, leftJustify, minWidth, precision, zeroPad, htmlSpace) {
+            if (precision != null) {
+                value = value.slice(0, precision);
+            }
+            return justify(value, '', leftJustify, minWidth, zeroPad, htmlSpace);
+        }
+
+        var a = arguments, i = 0, format = a[i++];
+
+        return format.replace($.jqplot.sprintf.regex, function(substring, valueIndex, flags, minWidth, _, precision, type) {
+            if (substring == '%%') { return '%'; }
+
+            // parse flags
+            var leftJustify = false, positivePrefix = '', zeroPad = false, prefixBaseX = false, htmlSpace = false;
+                for (var j = 0; flags && j < flags.length; j++) switch (flags.charAt(j)) {
+                case ' ': positivePrefix = ' '; break;
+                case '+': positivePrefix = '+'; break;
+                case '-': leftJustify = true; break;
+                case '0': zeroPad = true; break;
+                case '#': prefixBaseX = true; break;
+                case '&': htmlSpace = true; break;
+            }
+
+            // parameters may be null, undefined, empty-string or real valued
+            // we want to ignore null, undefined and empty-string values
+
+            if (!minWidth) {
+                minWidth = 0;
+            } 
+            else if (minWidth == '*') {
+                minWidth = +a[i++];
+            } 
+            else if (minWidth.charAt(0) == '*') {
+                minWidth = +a[minWidth.slice(1, -1)];
+            } 
+            else {
+                minWidth = +minWidth;
+            }
+
+            // Note: undocumented perl feature:
+            if (minWidth < 0) {
+                minWidth = -minWidth;
+                leftJustify = true;
+            }
+
+            if (!isFinite(minWidth)) {
+                throw new Error('$.jqplot.sprintf: (minimum-)width must be finite');
+            }
+
+            if (!precision) {
+                precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : void(0);
+            } 
+            else if (precision == '*') {
+                precision = +a[i++];
+            } 
+            else if (precision.charAt(0) == '*') {
+                precision = +a[precision.slice(1, -1)];
+            } 
+            else {
+                precision = +precision;
+            }
+
+            // grab value using valueIndex if required?
+            var value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++];
+
+            switch (type) {
+            case 's': {
+                if (value == null) {
+                    return '';
+                }
+                return formatString(String(value), leftJustify, minWidth, precision, zeroPad, htmlSpace);
+            }
+            case 'c': return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad, htmlSpace);
+            case 'b': return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad,htmlSpace);
+            case 'o': return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace);
+            case 'x': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace);
+            case 'X': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace).toUpperCase();
+            case 'u': return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace);
+            case 'i': {
+              var number = parseInt(+value, 10);
+              if (isNaN(number)) {
+                return '';
+              }
+              var prefix = number < 0 ? '-' : positivePrefix;
+              value = prefix + pad(String(Math.abs(number)), precision, '0', false);
+              return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace);
+                  }
+            case 'd': {
+              var number = Math.round(+value);
+              if (isNaN(number)) {
+                return '';
+              }
+              var prefix = number < 0 ? '-' : positivePrefix;
+              value = prefix + pad(String(Math.abs(number)), precision, '0', false);
+              return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace);
+                  }
+            case 'e':
+            case 'E':
+            case 'f':
+            case 'F':
+            case 'g':
+            case 'G':
+                      {
+                      var number = +value;
+                      if (isNaN(number)) {
+                          return '';
+                      }
+                      var prefix = number < 0 ? '-' : positivePrefix;
+                      var method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())];
+                      var textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2];
+                      value = prefix + Math.abs(number)[method](precision);
+                      return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace)[textTransform]();
+                  }
+            case 'p':
+            case 'P':
+            {
+                // make sure number is a number
+                var number = +value;
+                if (isNaN(number)) {
+                    return '';
+                }
+                var prefix = number < 0 ? '-' : positivePrefix;
+
+                var parts = String(Number(Math.abs(number)).toExponential()).split(/e|E/);
+                var sd = (parts[0].indexOf('.') != -1) ? parts[0].length - 1 : parts[0].length;
+                var zeros = (parts[1] < 0) ? -parts[1] - 1 : 0;
+                
+                if (Math.abs(number) < 1) {
+                    if (sd + zeros  <= precision) {
+                        value = prefix + Math.abs(number).toPrecision(sd);
+                    }
+                    else {
+                        if (sd  <= precision - 1) {
+                            value = prefix + Math.abs(number).toExponential(sd-1);
+                        }
+                        else {
+                            value = prefix + Math.abs(number).toExponential(precision-1);
+                        }
+                    }
+                }
+                else {
+                    var prec = (sd <= precision) ? sd : precision;
+                    value = prefix + Math.abs(number).toPrecision(prec);
+                }
+                var textTransform = ['toString', 'toUpperCase']['pP'.indexOf(type) % 2];
+                return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace)[textTransform]();
+            }
+            case 'n': return '';
+            default: return substring;
+            }
+        });
+    };
+    
+    $.jqplot.sprintf.regex = /%%|%(\d+\$)?([-+#0& ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([nAscboxXuidfegpEGP])/g;
+
+})(jQuery);  
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.BezierCurveRenderer.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.BezierCurveRenderer.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.BezierCurveRenderer.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,300 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {
+    // Class: $.jqplot.BezierCurveRenderer.js
+    // Renderer which draws lines as stacked bezier curves.
+    // Data for the line will not be specified as an array of
+    // [x, y] data point values, but as a an array of [start piont, bezier curve]
+    // So, the line is specified as: [[xstart, ystart], [cp1x, cp1y, cp2x, cp2y, xend, yend]].
+    $.jqplot.BezierCurveRenderer = function(){
+        $.jqplot.LineRenderer.call(this);
+    };
+    
+    $.jqplot.BezierCurveRenderer.prototype = new $.jqplot.LineRenderer();
+    $.jqplot.BezierCurveRenderer.prototype.constructor = $.jqplot.BezierCurveRenderer;
+
+    
+    // Method: setGridData
+    // converts the user data values to grid coordinates and stores them
+    // in the gridData array.
+    // Called with scope of a series.
+    $.jqplot.BezierCurveRenderer.prototype.setGridData = function(plot) {
+        // recalculate the grid data
+        var xp = this._xaxis.series_u2p;
+        var yp = this._yaxis.series_u2p;
+        // this._plotData should be same as this.data
+        var data = this.data;
+        this.gridData = [];
+        this._prevGridData = [];
+        // if seriesIndex = 0, fill to x axis.
+        // if seriesIndex > 0, fill to previous series data.
+        var idx = this.index;
+        if (data.length == 2) {
+            if (idx == 0) {
+                this.gridData = [
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], 
+                    [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), 
+                        xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]),  
+                        xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])],
+                    [xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, this._yaxis.min)],
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)]
+                ];
+            }
+            else {
+                var psd = plot.series[idx-1].data;
+                this.gridData = [
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], 
+                    [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), 
+                        xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]),  
+                        xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])],
+                    [xp.call(this._xaxis, psd[1][4]), yp.call(this._yaxis, psd[1][5])],
+                    [xp.call(this._xaxis, psd[1][2]), yp.call(this._yaxis, psd[1][3]), 
+                        xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]),  
+                        xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])]
+                ];
+            }
+        }
+        else {
+            if (idx == 0) {
+                this.gridData = [
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], 
+                    [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), 
+                        xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]),  
+                        xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])],
+                    [xp.call(this._xaxis, data[3][1]), yp.call(this._yaxis, this._yaxis.min)],
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)]
+                ];
+            }
+            else {
+                var psd = plot.series[idx-1].data;
+                this.gridData = [
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], 
+                    [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), 
+                        xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]),  
+                        xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])],
+                    [xp.call(this._xaxis, psd[3][0]), yp.call(this._yaxis, psd[3][1])],
+                    [xp.call(this._xaxis, psd[2][0]), yp.call(this._yaxis, psd[2][1]), 
+                        xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]),  
+                        xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])]
+                ];
+            }
+        }
+    };
+    
+    // Method: makeGridData
+    // converts any arbitrary data values to grid coordinates and
+    // returns them.  This method exists so that plugins can use a series'
+    // linerenderer to generate grid data points without overwriting the
+    // grid data associated with that series.
+    // Called with scope of a series.
+    $.jqplot.BezierCurveRenderer.prototype.makeGridData = function(data, plot) {
+        // recalculate the grid data
+        var xp = this._xaxis.series_u2p;
+        var yp = this._yaxis.series_u2p;
+        var gd = [];
+        var pgd = [];
+        // if seriesIndex = 0, fill to x axis.
+        // if seriesIndex > 0, fill to previous series data.
+        var idx = this.index;
+        if (data.length == 2) {
+            if (idx == 0) {
+                gd = [
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], 
+                    [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), 
+                        xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]),  
+                        xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])],
+                    [xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, this._yaxis.min)],
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)]
+                ];
+            }
+            else {
+                var psd = plot.series[idx-1].data;
+                gd = [
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], 
+                    [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), 
+                        xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]),  
+                        xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])],
+                    [xp.call(this._xaxis, psd[1][4]), yp.call(this._yaxis, psd[1][5])],
+                    [xp.call(this._xaxis, psd[1][2]), yp.call(this._yaxis, psd[1][3]), 
+                        xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]),  
+                        xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])]
+                ];
+            }
+        }
+        else {
+            if (idx == 0) {
+                gd = [
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], 
+                    [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), 
+                        xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]),  
+                        xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])],
+                    [xp.call(this._xaxis, data[3][1]), yp.call(this._yaxis, this._yaxis.min)],
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)]
+                ];
+            }
+            else {
+                var psd = plot.series[idx-1].data;
+                gd = [
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], 
+                    [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), 
+                        xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]),  
+                        xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])],
+                    [xp.call(this._xaxis, psd[3][0]), yp.call(this._yaxis, psd[3][1])],
+                    [xp.call(this._xaxis, psd[2][0]), yp.call(this._yaxis, psd[2][1]), 
+                        xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]),  
+                        xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])]
+                ];
+            }
+        }
+        return gd;
+    };
+    
+
+    // called within scope of series.
+    $.jqplot.BezierCurveRenderer.prototype.draw = function(ctx, gd, options) {
+        var i;
+        ctx.save();
+        if (gd.length) {
+            if (this.showLine) {
+                ctx.save();
+                var opts = (options != null) ? options : {};
+                ctx.fillStyle = opts.fillStyle || this.color;
+                ctx.beginPath();
+                ctx.moveTo(gd[0][0], gd[0][1]);
+                ctx.bezierCurveTo(gd[1][0], gd[1][1], gd[1][2], gd[1][3], gd[1][4], gd[1][5]);
+                ctx.lineTo(gd[2][0], gd[2][1]);
+                if (gd[3].length == 2) {
+                    ctx.lineTo(gd[3][0], gd[3][1]);
+                }
+                else {
+                    ctx.bezierCurveTo(gd[3][0], gd[3][1], gd[3][2], gd[3][3], gd[3][4], gd[3][5]);
+                }
+                ctx.closePath();
+                ctx.fill();
+                ctx.restore();
+            }
+        }
+        
+        ctx.restore();
+    };  
+    
+    $.jqplot.BezierCurveRenderer.prototype.drawShadow = function(ctx, gd, options) {
+        // This is a no-op, shadows drawn with lines.
+    };
+    
+    $.jqplot.BezierAxisRenderer = function() {
+        $.jqplot.LinearAxisRenderer.call(this);
+    };
+    
+    $.jqplot.BezierAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
+    $.jqplot.BezierAxisRenderer.prototype.constructor = $.jqplot.BezierAxisRenderer;
+        
+    
+    // Axes on a plot with Bezier Curves
+    $.jqplot.BezierAxisRenderer.prototype.init = function(options){
+        $.extend(true, this, options);
+        var db = this._dataBounds;
+        // Go through all the series attached to this axis and find
+        // the min/max bounds for this axis.
+        for (var i=0; i<this._series.length; i++) {
+            var s = this._series[i];
+            var d = s.data;  
+            if (d.length == 4) {
+                for (var j=0; j<d.length; j++) { 
+                    if (this.name == 'xaxis' || this.name == 'x2axis') {
+                        if (d[j][0] < db.min || db.min == null) {
+                            db.min = d[j][0];
+                        }
+                        if (d[j][0] > db.max || db.max == null) {
+                            db.max = d[j][0];
+                        }
+                    }              
+                    else {
+                        if (d[j][1] < db.min || db.min == null) {
+                            db.min = d[j][1];
+                        }
+                        if (d[j][1] > db.max || db.max == null) {
+                            db.max = d[j][1];
+                        }
+                    }              
+                }
+            }          
+            else {    
+                if (this.name == 'xaxis' || this.name == 'x2axis') {
+                    if (d[0][0] < db.min || db.min == null) {
+                        db.min = d[0][0];
+                    }
+                    if (d[0][0] > db.max || db.max == null) {
+                        db.max = d[0][0];
+                    }
+                    for (var j=0; j<5; j+=2) {
+                        if (d[1][j] < db.min || db.min == null) {
+                            db.min = d[1][j];
+                        }
+                        if (d[1][j] > db.max || db.max == null) {
+                            db.max = d[1][j];
+                        }
+                    }
+                }              
+                else {
+                    if (d[0][1] < db.min || db.min == null) {
+                        db.min = d[0][1];
+                    }
+                    if (d[0][1] > db.max || db.max == null) {
+                        db.max = d[0][1];
+                    }
+                    for (var j=1; j<6; j+=2) {
+                        if (d[1][j] < db.min || db.min == null) {
+                            db.min = d[1][j];
+                        }
+                        if (d[1][j] > db.max || db.max == null) {
+                            db.max = d[1][j];
+                        }
+                    }
+                }           
+            }
+        }
+    };
+    
+    // setup default renderers for axes and legend so user doesn't have to
+    // called with scope of plot
+    function preInit(target, data, options) {
+        options = options || {};
+        options.axesDefaults = $.extend(true, {pad:0}, options.axesDefaults);
+        options.legend = $.extend(true, {placement:'outside'}, options.legend);
+        // only set these if there is a pie series
+        var setopts = false;
+        if (options.seriesDefaults.renderer == $.jqplot.BezierCurveRenderer) {
+            setopts = true;
+        }
+        else if (options.series) {
+            for (var i=0; i < options.series.length; i++) {
+                if (options.series[i].renderer == $.jqplot.BezierCurveRenderer) {
+                    setopts = true;
+                }
+            }
+        }
+        
+        if (setopts) {
+            options.axesDefaults.renderer = $.jqplot.BezierAxisRenderer;
+        }
+    }
+    
+    $.jqplot.preInitHooks.push(preInit);
+    
+})(jQuery);    
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.barRenderer.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.barRenderer.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.barRenderer.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,629 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {
+    
+    // Class: $.jqplot.BarRenderer
+    // A plugin renderer for jqPlot to draw a bar plot.
+    // Draws series as a line.
+    
+    $.jqplot.BarRenderer = function(){
+        $.jqplot.LineRenderer.call(this);
+    };
+    
+    $.jqplot.BarRenderer.prototype = new $.jqplot.LineRenderer();
+    $.jqplot.BarRenderer.prototype.constructor = $.jqplot.BarRenderer;
+    
+    // called with scope of series.
+    $.jqplot.BarRenderer.prototype.init = function(options, plot) {
+        // Group: Properties
+        //
+        // prop: barPadding
+        // Number of pixels between adjacent bars at the same axis value.
+        this.barPadding = 8;
+        // prop: barMargin
+        // Number of pixels between groups of bars at adjacent axis values.
+        this.barMargin = 10;
+        // prop: barDirection
+        // 'vertical' = up and down bars, 'horizontal' = side to side bars
+        this.barDirection = 'vertical';
+        // prop: barWidth
+        // Width of the bar in pixels (auto by devaul).  null = calculated automatically.
+        this.barWidth = null;
+        // prop: shadowOffset
+        // offset of the shadow from the slice and offset of 
+        // each succesive stroke of the shadow from the last.
+        this.shadowOffset = 2;
+        // prop: shadowDepth
+        // number of strokes to apply to the shadow, 
+        // each stroke offset shadowOffset from the last.
+        this.shadowDepth = 5;
+        // prop: shadowAlpha
+        // transparency of the shadow (0 = transparent, 1 = opaque)
+        this.shadowAlpha = 0.08;
+        // prop: waterfall
+        // true to enable waterfall plot.
+        this.waterfall = false;
+        // prop: groups
+        // group bars into this many groups
+        this.groups = 1;
+        // prop: varyBarColor
+        // true to color each bar of a series separately rather than
+        // have every bar of a given series the same color.
+        // If used for non-stacked multiple series bar plots, user should
+        // specify a separate 'seriesColors' array for each series.
+        // Otherwise, each series will set their bars to the same color array.
+        // This option has no Effect for stacked bar charts and is disabled.
+        this.varyBarColor = false;
+        // prop: highlightMouseOver
+        // True to highlight slice when moused over.
+        // This must be false to enable highlightMouseDown to highlight when clicking on a slice.
+        this.highlightMouseOver = true;
+        // prop: highlightMouseDown
+        // True to highlight when a mouse button is pressed over a slice.
+        // This will be disabled if highlightMouseOver is true.
+        this.highlightMouseDown = false;
+        // prop: highlightColors
+        // an array of colors to use when highlighting a bar.
+        this.highlightColors = [];
+        
+        // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
+        if (options.highlightMouseDown && options.highlightMouseOver == null) {
+            options.highlightMouseOver = false;
+        }
+        
+        $.extend(true, this, options);
+        // fill is still needed to properly draw the legend.
+        // bars have to be filled.
+        this.fill = true;
+        
+        if (this.waterfall) {
+            this.fillToZero = false;
+            this.disableStack = true;
+        }
+        
+        if (this.barDirection == 'vertical' ) {
+            this._primaryAxis = '_xaxis';
+            this._stackAxis = 'y';
+            this.fillAxis = 'y';
+        }
+        else {
+            this._primaryAxis = '_yaxis';
+            this._stackAxis = 'x';
+            this.fillAxis = 'x';
+        }
+        // index of the currenty highlighted point, if any
+        this._highlightedPoint = null;
+        // total number of values for all bar series, total number of bar series, and position of this series
+        this._plotSeriesInfo = null;
+        // Array of actual data colors used for each data point.
+        this._dataColors = [];
+        this._barPoints = [];
+        
+        // set the shape renderer options
+        var opts = {lineJoin:'miter', lineCap:'round', fill:true, isarc:false, strokeStyle:this.color, fillStyle:this.color, closePath:this.fill};
+        this.renderer.shapeRenderer.init(opts);
+        // set the shadow renderer options
+        var sopts = {lineJoin:'miter', lineCap:'round', fill:true, isarc:false, angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, depth:this.shadowDepth, closePath:this.fill};
+        this.renderer.shadowRenderer.init(sopts);
+        
+        plot.postInitHooks.addOnce(postInit);
+        plot.postDrawHooks.addOnce(postPlotDraw);
+        plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
+        plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown);
+        plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp);
+        plot.eventListenerHooks.addOnce('jqplotClick', handleClick);
+        plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); 
+    };
+    
+    // called with scope of series
+    function barPreInit(target, data, seriesDefaults, options) {
+        if (this.rendererOptions.barDirection == 'horizontal') {
+            this._stackAxis = 'x';
+            this._primaryAxis = '_yaxis';
+        }
+        if (this.rendererOptions.waterfall == true) {
+            this._data = $.extend(true, [], this.data);
+            var sum = 0;
+            var pos = (!this.rendererOptions.barDirection || this.rendererOptions.barDirection == 'vertical') ? 1 : 0;
+            for(var i=0; i<this.data.length; i++) {
+                sum += this.data[i][pos];
+                if (i>0) {
+                    this.data[i][pos] += this.data[i-1][pos];
+                }
+            }
+            this.data[this.data.length] = (pos == 1) ? [this.data.length+1, sum] : [sum, this.data.length+1];
+            this._data[this._data.length] = (pos == 1) ? [this._data.length+1, sum] : [sum, this._data.length+1];
+        }
+        if (this.rendererOptions.groups > 1) {
+            this.breakOnNull = true;
+            var l = this.data.length;
+            var skip = parseInt(l/this.rendererOptions.groups, 10);
+            var count = 0;
+            for (var i=skip; i<l; i+=skip) {
+                this.data.splice(i+count, 0, [null, null]);
+                count++;
+            }
+            for (i=0; i<this.data.length; i++) {
+                if (this._primaryAxis == '_xaxis') {
+                    this.data[i][0] = i+1;
+                }
+                else {
+                    this.data[i][1] = i+1;
+                }
+            }
+        }
+    }
+    
+    $.jqplot.preSeriesInitHooks.push(barPreInit);
+    
+    // needs to be called with scope of series, not renderer.
+    $.jqplot.BarRenderer.prototype.calcSeriesNumbers = function() {
+        var nvals = 0;
+        var nseries = 0;
+        var paxis = this[this._primaryAxis];
+        var s, series, pos;
+        // loop through all series on this axis
+        for (var i=0; i < paxis._series.length; i++) {
+            series = paxis._series[i];
+            if (series === this) {
+                pos = i;
+            }
+            // is the series rendered as a bar?
+            if (series.renderer.constructor == $.jqplot.BarRenderer) {
+                // gridData may not be computed yet, use data length insted
+                nvals += series.data.length;
+                nseries += 1;
+            }
+        }
+        // return total number of values for all bar series, total number of bar series, and position of this series
+        return [nvals, nseries, pos];
+    };
+
+    $.jqplot.BarRenderer.prototype.setBarWidth = function() {
+        // need to know how many data values we have on the approprate axis and figure it out.
+        var i;
+        var nvals = 0;
+        var nseries = 0;
+        var paxis = this[this._primaryAxis];
+        var s, series, pos;
+        var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this);
+        nvals = temp[0];
+        nseries = temp[1];
+        var nticks = paxis.numberTicks;
+        var nbins = (nticks-1)/2;
+        // so, now we have total number of axis values.
+        if (paxis.name == 'xaxis' || paxis.name == 'x2axis') {
+            if (this._stack) {
+                this.barWidth = (paxis._offsets.max - paxis._offsets.min) / nvals * nseries - this.barMargin;
+            }
+            else {
+                this.barWidth = ((paxis._offsets.max - paxis._offsets.min)/nbins  - this.barPadding * (nseries-1) - this.barMargin*2)/nseries;
+                // this.barWidth = (paxis._offsets.max - paxis._offsets.min) / nvals - this.barPadding - this.barMargin/nseries;
+            }
+        }
+        else {
+            if (this._stack) {
+                this.barWidth = (paxis._offsets.min - paxis._offsets.max) / nvals * nseries - this.barMargin;
+            }
+            else {
+                this.barWidth = ((paxis._offsets.min - paxis._offsets.max)/nbins  - this.barPadding * (nseries-1) - this.barMargin*2)/nseries;
+                // this.barWidth = (paxis._offsets.min - paxis._offsets.max) / nvals - this.barPadding - this.barMargin/nseries;
+            }
+        }
+        return [nvals, nseries];
+    };
+
+    function computeHighlightColors (colors) {
+        var ret = [];
+        for (var i=0; i<colors.length; i++){
+            var rgba = $.jqplot.getColorComponents(colors[i]);
+            var newrgb = [rgba[0], rgba[1], rgba[2]];
+            var sum = newrgb[0] + newrgb[1] + newrgb[2];
+            for (var j=0; j<3; j++) {
+                // when darkening, lowest color component can be is 60.
+                newrgb[j] = (sum > 570) ?  newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]);
+                newrgb[j] = parseInt(newrgb[j], 10);
+            }
+            ret.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')');
+        }
+        return ret;
+    }
+    
+    $.jqplot.BarRenderer.prototype.draw = function(ctx, gridData, options) {
+        var i;
+        var opts = (options != undefined) ? options : {};
+        var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
+        var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
+        var fill = (opts.fill != undefined) ? opts.fill : this.fill;
+        var xaxis = this.xaxis;
+        var yaxis = this.yaxis;
+        var xp = this._xaxis.series_u2p;
+        var yp = this._yaxis.series_u2p;
+        var pointx, pointy, nvals, nseries, pos;
+        // clear out data colors.
+        this._dataColors = [];
+        this._barPoints = [];
+        
+        if (this.barWidth == null) {
+            this.renderer.setBarWidth.call(this);
+        }
+        
+        var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this);
+        nvals = temp[0];
+        nseries = temp[1];
+        pos = temp[2];
+        
+        if (this._stack) {
+            this._barNudge = 0;
+        }
+        else {
+            this._barNudge = (-Math.abs(nseries/2 - 0.5) + pos) * (this.barWidth + this.barPadding);
+        }
+        if (showLine) {
+            var negativeColors = new $.jqplot.ColorGenerator(this.negativeSeriesColors);
+            var positiveColors = new $.jqplot.ColorGenerator(this.seriesColors);
+            var negativeColor = negativeColors.get(this.index);
+            if (! this.useNegativeColors) {
+                negativeColor = opts.fillStyle;
+            }
+            var positiveColor = opts.fillStyle;
+            
+            if (this.barDirection == 'vertical') {
+                for (var i=0; i<gridData.length; i++) {
+                    if (this.data[i][1] == null) {
+                        continue;
+                    }
+                    points = [];
+                    var base = gridData[i][0] + this._barNudge;
+                    var ystart;
+                    
+                    // stacked
+                    if (this._stack && this._prevGridData.length) {
+                        ystart = this._prevGridData[i][1];
+                    }
+                    // not stacked and first series in stack
+                    else {
+                        if (this.fillToZero) {
+                            ystart = this._yaxis.series_u2p(0);
+                        }
+                        else if (this.waterfall && i > 0 && i < this.gridData.length-1) {
+                            ystart = this.gridData[i-1][1];
+                        }
+                        else {
+                            ystart = ctx.canvas.height;
+                        }
+                    }
+                    if ((this.fillToZero && this._plotData[i][1] < 0) || (this.waterfall && this._data[i][1] < 0)) {
+                        if (this.varyBarColor && !this._stack) {
+                            if (this.useNegativeColors) {
+                                opts.fillStyle = negativeColors.next();
+                            }
+                            else {
+                                opts.fillStyle = positiveColors.next();
+                            }
+                        }
+                        else {
+                            opts.fillStyle = negativeColor;
+                        }
+                    }
+                    else {
+                        if (this.varyBarColor && !this._stack) {
+                            opts.fillStyle = positiveColors.next();
+                        }
+                        else {
+                            opts.fillStyle = positiveColor;
+                        }
+                    }
+                    
+                    points.push([base-this.barWidth/2, ystart]);
+                    points.push([base-this.barWidth/2, gridData[i][1]]);
+                    points.push([base+this.barWidth/2, gridData[i][1]]);
+                    points.push([base+this.barWidth/2, ystart]);
+                    this._barPoints.push(points);
+                    // now draw the shadows if not stacked.
+                    // for stacked plots, they are predrawn by drawShadow
+                    if (shadow && !this._stack) {
+                        var sopts = $.extend(true, {}, opts);
+                        // need to get rid of fillStyle on shadow.
+                        delete sopts.fillStyle;
+                        this.renderer.shadowRenderer.draw(ctx, points, sopts);
+                    }
+                    var clr = opts.fillStyle || this.color;
+                    this._dataColors.push(clr);
+                    this.renderer.shapeRenderer.draw(ctx, points, opts); 
+                }
+            }
+            
+            else if (this.barDirection == 'horizontal'){
+                for (var i=0; i<gridData.length; i++) {
+                    if (this.data[i][0] == null) {
+                        continue;
+                    }
+                    points = [];
+                    var base = gridData[i][1] - this._barNudge;
+                    var xstart;
+                    
+                    if (this._stack && this._prevGridData.length) {
+                        xstart = this._prevGridData[i][0];
+                    }
+                    // not stacked and first series in stack
+                    else {
+                        if (this.fillToZero) {
+                            xstart = this._xaxis.series_u2p(0);
+                        }
+                        else if (this.waterfall && i > 0 && i < this.gridData.length-1) {
+                            xstart = this.gridData[i-1][1];
+                        }
+                        else {
+                            xstart = 0;
+                        }
+                    }
+                    if ((this.fillToZero && this._plotData[i][1] < 0) || (this.waterfall && this._data[i][1] < 0)) {
+                        if (this.varyBarColor && !this._stack) {
+                            if (this.useNegativeColors) {
+                                opts.fillStyle = negativeColors.next();
+                            }
+                            else {
+                                opts.fillStyle = positiveColors.next();
+                            }
+                        }
+                    }
+                    else {
+                        if (this.varyBarColor && !this._stack) {
+                            opts.fillStyle = positiveColors.next();
+                        }
+                        else {
+                            opts.fillStyle = positiveColor;
+                        }                    
+                    }
+                    
+                    points.push([xstart, base+this.barWidth/2]);
+                    points.push([xstart, base-this.barWidth/2]);
+                    points.push([gridData[i][0], base-this.barWidth/2]);
+                    points.push([gridData[i][0], base+this.barWidth/2]);
+                    this._barPoints.push(points);
+                    // now draw the shadows if not stacked.
+                    // for stacked plots, they are predrawn by drawShadow
+                    if (shadow && !this._stack) {
+                        var sopts = $.extend(true, {}, opts);
+                        delete sopts.fillStyle;
+                        this.renderer.shadowRenderer.draw(ctx, points, sopts);
+                    }
+                    var clr = opts.fillStyle || this.color;
+                    this._dataColors.push(clr);
+                    this.renderer.shapeRenderer.draw(ctx, points, opts); 
+                }  
+            }
+        }                
+        
+        if (this.highlightColors.length == 0) {
+            this.highlightColors = computeHighlightColors(this._dataColors);
+        }
+        
+        else if (typeof(this.highlightColors) == 'string') {
+            var temp = this.highlightColors;
+            this.highlightColors = [];
+            for (var i=0; i<this._dataColors.length; i++) {
+                this.highlightColors.push(temp);
+            }
+        }
+        
+    };
+    
+     
+    // for stacked plots, shadows will be pre drawn by drawShadow.
+    $.jqplot.BarRenderer.prototype.drawShadow = function(ctx, gridData, options) {
+        var i;
+        var opts = (options != undefined) ? options : {};
+        var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
+        var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
+        var fill = (opts.fill != undefined) ? opts.fill : this.fill;
+        var xaxis = this.xaxis;
+        var yaxis = this.yaxis;
+        var xp = this._xaxis.series_u2p;
+        var yp = this._yaxis.series_u2p;
+        var pointx, pointy, nvals, nseries, pos;
+        
+        if (this._stack && this.shadow) {
+            if (this.barWidth == null) {
+                this.renderer.setBarWidth.call(this);
+            }
+        
+            var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this);
+            nvals = temp[0];
+            nseries = temp[1];
+            pos = temp[2];
+        
+            if (this._stack) {
+                this._barNudge = 0;
+            }
+            else {
+                this._barNudge = (-Math.abs(nseries/2 - 0.5) + pos) * (this.barWidth + this.barPadding);
+            }
+            if (showLine) {
+            
+                if (this.barDirection == 'vertical') {
+                    for (var i=0; i<gridData.length; i++) {
+                        if (this.data[i][1] == null) {
+                            continue;
+                        }
+                        points = [];
+                        var base = gridData[i][0] + this._barNudge;
+                        var ystart;
+                    
+                        if (this._stack && this._prevGridData.length) {
+                            ystart = this._prevGridData[i][1];
+                        }
+                        else {
+                            if (this.fillToZero) {
+                                ystart = this._yaxis.series_u2p(0);
+                            }
+                            else {
+                                ystart = ctx.canvas.height;
+                            }
+                        }
+                    
+                        points.push([base-this.barWidth/2, ystart]);
+                        points.push([base-this.barWidth/2, gridData[i][1]]);
+                        points.push([base+this.barWidth/2, gridData[i][1]]);
+                        points.push([base+this.barWidth/2, ystart]);
+                        this.renderer.shadowRenderer.draw(ctx, points, opts);
+                    }
+                }
+            
+                else if (this.barDirection == 'horizontal'){
+                    for (var i=0; i<gridData.length; i++) {
+                        if (this.data[i][0] == null) {
+                            continue;
+                        }
+                        points = [];
+                        var base = gridData[i][1] - this._barNudge;
+                        var xstart;
+                    
+                        if (this._stack && this._prevGridData.length) {
+                            xstart = this._prevGridData[i][0];
+                        }
+                        else {
+                            xstart = 0;
+                        }
+                    
+                        points.push([xstart, base+this.barWidth/2]);
+                        points.push([gridData[i][0], base+this.barWidth/2]);
+                        points.push([gridData[i][0], base-this.barWidth/2]);
+                        points.push([xstart, base-this.barWidth/2]);
+                        this.renderer.shadowRenderer.draw(ctx, points, opts);
+                    }  
+                }
+            }   
+            
+        }
+    };
+    
+    function postInit(target, data, options) {
+        for (i=0; i<this.series.length; i++) {
+            if (this.series[i].renderer.constructor == $.jqplot.BarRenderer) {
+                // don't allow mouseover and mousedown at same time.
+                if (this.series[i].highlightMouseOver) {
+                    this.series[i].highlightMouseDown = false;
+                }
+            }
+        }
+        this.target.bind('mouseout', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
+    }
+    
+    // called within context of plot
+    // create a canvas which we can draw on.
+    // insert it before the eventCanvas, so eventCanvas will still capture events.
+    function postPlotDraw() {
+        this.plugins.barRenderer = {highlightedSeriesIndex:null};
+        this.plugins.barRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
+        
+        this.eventCanvas._elem.before(this.plugins.barRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-barRenderer-highlight-canvas', this._plotDimensions));
+        var hctx = this.plugins.barRenderer.highlightCanvas.setContext();
+    }   
+    
+    function highlight (plot, sidx, pidx, points) {
+        var s = plot.series[sidx];
+        var canvas = plot.plugins.barRenderer.highlightCanvas;
+        canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height);
+        s._highlightedPoint = pidx;
+        plot.plugins.barRenderer.highlightedSeriesIndex = sidx;
+        var opts = {fillStyle: s.highlightColors[pidx]};
+        s.renderer.shapeRenderer.draw(canvas._ctx, points, opts);
+    }
+    
+    function unhighlight (plot) {
+        var canvas = plot.plugins.barRenderer.highlightCanvas;
+        canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
+        for (var i=0; i<plot.series.length; i++) {
+            plot.series[i]._highlightedPoint = null;
+        }
+        plot.plugins.barRenderer.highlightedSeriesIndex = null;
+        plot.target.trigger('jqplotDataUnhighlight');
+    }
+    
+    
+    function handleMove(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var evt1 = jQuery.Event('jqplotDataMouseOver');
+            evt1.pageX = ev.pageX;
+            evt1.pageY = ev.pageY;
+            plot.target.trigger(evt1, ins);
+            if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.barRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
+                var evt = jQuery.Event('jqplotDataHighlight');
+                evt.pageX = ev.pageX;
+                evt.pageY = ev.pageY;
+                plot.target.trigger(evt, ins);
+                highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    }
+    
+    function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.barRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
+                var evt = jQuery.Event('jqplotDataHighlight');
+                evt.pageX = ev.pageX;
+                evt.pageY = ev.pageY;
+                plot.target.trigger(evt, ins);
+                highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    }
+    
+    function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
+        var idx = plot.plugins.barRenderer.highlightedSeriesIndex;
+        if (idx != null && plot.series[idx].highlightMouseDown) {
+            unhighlight(plot);
+        }
+    }
+    
+    function handleClick(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var evt = jQuery.Event('jqplotDataClick');
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            plot.target.trigger(evt, ins);
+        }
+    }
+    
+    function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var idx = plot.plugins.barRenderer.highlightedSeriesIndex;
+            if (idx != null && plot.series[idx].highlightMouseDown) {
+                unhighlight(plot);
+            }
+            var evt = jQuery.Event('jqplotDataRightClick');
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            plot.target.trigger(evt, ins);
+        }
+    }
+    
+    
+})(jQuery);    
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.blockRenderer.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.blockRenderer.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.blockRenderer.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,221 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {
+    /**
+     * Class: $.jqplot.BlockRenderer
+     * Plugin renderer to draw a x-y block chart.  A Block chart has data points displayed as
+     * colored squares with a text label inside.  Data must be supplied in the form:
+     * 
+     * > [[x1, y1, "label 1", {css}], [x2, y2, "label 2", {css}], ...]
+     * 
+     * The label and css object are optional.  If the label is ommitted, the
+     * box will collapse unless a css height and/or width is specified.
+     * 
+     * The css object is an object specifying css properties 
+     * such as:
+     * 
+     * > {background:'#4f98a5', border:'3px solid gray', padding:'1px'}
+     * 
+     * Note that css properties specified with the data point override defaults
+     * specified with the series.
+     * 
+     */
+    $.jqplot.BlockRenderer = function(){
+        $.jqplot.LineRenderer.call(this);
+    };
+    
+    $.jqplot.BlockRenderer.prototype = new $.jqplot.LineRenderer();
+    $.jqplot.BlockRenderer.prototype.constructor = $.jqplot.BlockRenderer;
+    
+    // called with scope of a series
+    $.jqplot.BlockRenderer.prototype.init = function(options) {
+        // Group: Properties
+        //
+        // prop: css
+        // default css styles that will be applied to all data blocks.
+        // these values will be overridden by css styles supplied with the
+        // individulal data points.
+        this.css = {padding:'2px', border:'1px solid #999', textAlign:'center'};
+        // prop: escapeHtml
+        // true to escape html in the box label.
+        this.escapeHtml = false;
+        // prop: insertBreaks
+        // true to turn spaces in data block label into html breaks <br />.
+        this.insertBreaks = true;
+        // prop: varyBlockColors
+        // true to vary the color of each block in this series according to
+        // the seriesColors array.  False to set each block to the color
+        // specified on this series.  This has no effect if a css background color
+        // option is specified in the renderer css options.
+        this.varyBlockColors = false;
+        $.extend(true, this, options);
+        if (this.css.backgroundColor) {
+            this.color = this.css.backgroundColor;
+        }
+        else if (this.css.background) {
+            this.color = this.css.background;
+        }
+        else if (!this.varyBlockColors) {
+            this.css.background = this.color;
+        }
+        this.canvas = new $.jqplot.BlockCanvas();
+        this.shadowCanvas =  new $.jqplot.BlockCanvas();
+        this.canvas._plotDimensions = this._plotDimensions;
+        this.shadowCanvas._plotDimensions = this._plotDimensions;
+        
+        // group: Methods 
+        //
+        // Method: moveBlock
+        // Moves an individual block.  More efficient than redrawing
+        // the whole series by calling plot.drawSeries().
+        // Properties:
+        // idx - the 0 based index of the block or point in this series.
+        // x - the x coordinate in data units (value on x axis) to move the block to.
+        // y - the y coordinate in data units (value on the y axis) to move the block to.
+        // duration - optional parameter to create an animated movement.  Can be a
+        // number (higher is slower animation) or 'fast', 'normal' or 'slow'.  If not
+        // provided, the element is moved without any animation.
+        this.moveBlock = function (idx, x, y, duration) {
+            // update plotData, stackData, data and gridData
+            // x and y are in data coordinates.
+            var el = this.canvas._elem.children(':eq('+idx+')');
+            this.data[idx][0] = x;
+            this.data[idx][1] = y;
+            this._plotData[idx][0] = x;
+            this._plotData[idx][1] = y;
+            this._stackData[idx][0] = x;
+            this._stackData[idx][1] = y;
+            this.gridData[idx][0] = this._xaxis.series_u2p(x);
+            this.gridData[idx][1] = this._yaxis.series_u2p(y);
+            var w = el.outerWidth();
+            var h = el.outerHeight();
+            var left = this.gridData[idx][0] - w/2 + 'px';
+            var top = this.gridData[idx][1] - h/2 + 'px';
+            if (duration) {
+                if (parseInt(duration, 10)) {
+                    duration = parseInt(duration, 10);
+                }
+                el.animate({left:left, top:top}, duration);
+            }
+            else {
+                el.css({left:left, top:top});
+            }
+            el = null;
+        };
+    };
+    
+    // called with scope of series
+    $.jqplot.BlockRenderer.prototype.draw = function (ctx, gd, options) {
+        if (this.plugins.pointLabels) {
+            this.plugins.pointLabels.show = false;
+        }
+        var i, el, d, gd, t, css, w, h, left, top;
+        var opts = (options != undefined) ? options : {};
+        var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors);
+        this.canvas._elem.empty();
+        for (i=0; i<this.gridData.length; i++) {
+            d = this.data[i];
+            gd = this.gridData[i];
+            t = '';
+            css = {};
+            if (typeof d[2] == 'string') {
+                t = d[2];
+            }
+            else if (typeof d[2] == 'object') {
+                css = d[2];
+            }
+            if (typeof d[3] ==  'object') {
+                css = d[3];
+            }
+            if (this.insertBreaks){ 
+                t = t.replace(/ /g, '<br />');
+            }
+            css = $.extend(true, {}, this.css, css);
+            // create a div
+            el = $('<div style="position:absolute;margin-left:auto;margin-right:auto;"></div>');
+            this.canvas._elem.append(el);
+            // set text
+            this.escapeHtml ? el.text(t) : el.html(t);
+            // style it
+            // remove styles we don't want overridden.
+            delete css.position;
+            delete css.marginRight;
+            delete css.marginLeft;
+            if (!css.background && !css.backgroundColor && !css.backgroundImage){ 
+                css.background = colorGenerator.next();
+            }
+            el.css(css);
+            w = el.outerWidth();
+            h = el.outerHeight();
+            left = gd[0] - w/2 + 'px';
+            top = gd[1] - h/2 + 'px';
+            el.css({left:left, top:top});
+            el = null;
+        }
+    };
+    
+    $.jqplot.BlockCanvas = function() {
+        $.jqplot.ElemContainer.call(this);
+        this._ctx;  
+    };
+    
+    $.jqplot.BlockCanvas.prototype = new $.jqplot.ElemContainer();
+    $.jqplot.BlockCanvas.prototype.constructor = $.jqplot.BlockCanvas;
+    
+    $.jqplot.BlockCanvas.prototype.createElement = function(offsets, clss, plotDimensions) {
+        this._offsets = offsets;
+        var klass = 'jqplot-blockCanvas';
+        if (clss != undefined) {
+            klass = clss;
+        }
+        var elem;
+        // if this canvas already has a dom element, don't make a new one.
+        if (this._elem) {
+            elem = this._elem.get(0);
+        }
+        else {
+            elem = document.createElement('div');
+        }
+        // if new plotDimensions supplied, use them.
+        if (plotDimensions != undefined) {
+            this._plotDimensions = plotDimensions;
+        }
+        
+        var w = this._plotDimensions.width - this._offsets.left - this._offsets.right + 'px';
+        var h = this._plotDimensions.height - this._offsets.top - this._offsets.bottom + 'px';
+        this._elem = $(elem);
+        this._elem.css({ position: 'absolute', width:w, height:h, left: this._offsets.left, top: this._offsets.top });
+        
+        this._elem.addClass(klass);
+        return this._elem;
+    };
+    
+    $.jqplot.BlockCanvas.prototype.setContext = function() {
+        this._ctx = {
+            canvas:{
+                width:0,
+                height:0
+            },
+            clearRect:function(){return null;}
+        };
+        return this._ctx;
+    };
+    
+})(jQuery);
+    
+    
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.bubbleRenderer.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.bubbleRenderer.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.bubbleRenderer.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,724 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {
+    var arrayMax = function( array ){
+        return Math.max.apply( Math, array );
+    };
+    var arrayMin = function( array ){
+        return Math.min.apply( Math, array );
+    };
+
+    /**
+     * Class: $.jqplot.BubbleRenderer
+     * Plugin renderer to draw a bubble chart.  A Bubble chart has data points displayed as
+     * colored circles with an optional text label inside.  To use
+     * the bubble renderer, you must include the bubble renderer like:
+     * 
+     * > <script language="javascript" type="text/javascript" src="../src/plugins/jqplot.bubbleRenderer.js"></script>
+     * 
+     * Data must be supplied in 
+     * the form:
+     * 
+     * > [[x1, y1, r1, <label or {label:'text', color:color}>], ...]
+     * 
+     * where the label or options 
+     * object is optional.  
+     * 
+     * Note that all bubble colors will be the same
+     * unless the "varyBubbleColors" option is set to true.  Colors can be specified in the data array
+     * or in the seriesColors array option on the series.  If no colors are defined, the default jqPlot
+     * series of 16 colors are used.  Colors are automatically cycled around again if there are more
+     * bubbles than colors.
+     * 
+     * Bubbles are autoscaled by default to fit within the chart area while maintaining 
+     * relative sizes.  If the "autoscaleBubbles" option is set to false, the r(adius) values
+     * in the data array a treated as literal pixel values for the radii of the bubbles.
+     * 
+     * Properties are passed into the bubble renderer in the rendererOptions object of
+     * the series options like:
+     * 
+     * > seriesDefaults: {
+     * >     renderer: $.jqplot.BubbleRenderer,
+     * >     rendererOptions: {
+     * >         bubbleAlpha: 0.7,
+     * >         varyBubbleColors: false
+     * >     }
+     * > }
+     * 
+     */
+    $.jqplot.BubbleRenderer = function(){
+        $.jqplot.LineRenderer.call(this);
+    };
+    
+    $.jqplot.BubbleRenderer.prototype = new $.jqplot.LineRenderer();
+    $.jqplot.BubbleRenderer.prototype.constructor = $.jqplot.BubbleRenderer;
+    
+    // called with scope of a series
+    $.jqplot.BubbleRenderer.prototype.init = function(options, plot) {
+        // Group: Properties
+        //
+        // prop: varyBubbleColors
+        // True to vary the color of each bubble in this series according to
+        // the seriesColors array.  False to set each bubble to the color
+        // specified on this series.  This has no effect if a css background color
+        // option is specified in the renderer css options.
+        this.varyBubbleColors = true;
+        // prop: autoscaleBubbles
+        // True to scale the bubble radius based on plot size.
+        // False will use the radius value as provided as a raw pixel value for
+        // bubble radius.
+        this.autoscaleBubbles = true;
+        // prop: autoscaleMultiplier
+        // Multiplier the bubble size if autoscaleBubbles is true.
+        this.autoscaleMultiplier = 1.0;
+        // prop: autoscalePointsFactor
+        // Factor which decreases bubble size based on how many bubbles on on the chart.
+        // 0 means no adjustment for number of bubbles.  Negative values will decrease
+        // size of bubbles as more bubbles are added.  Values between 0 and -0.2
+        // should work well.
+        this.autoscalePointsFactor = -0.07;
+        // prop: escapeHtml
+        // True to escape html in bubble label text.
+        this.escapeHtml = true;
+        // prop: highlightMouseOver
+        // True to highlight bubbles when moused over.
+        // This must be false to enable highlightMouseDown to highlight when clicking on a slice.
+        this.highlightMouseOver = true;
+        // prop: highlightMouseDown
+        // True to highlight when a mouse button is pressed over a bubble.
+        // This will be disabled if highlightMouseOver is true.
+        this.highlightMouseDown = false;
+        // prop: highlightColors
+        // An array of colors to use when highlighting a slice.  Calculated automatically
+        // if not supplied.
+        this.highlightColors = [];
+        // prop: bubbleAlpha
+        // Alpha transparency to apply to all bubbles in this series.
+        this.bubbleAlpha = 1.0;
+        // prop: highlightAlpha
+        // Alpha transparency to apply when highlighting bubble.
+        // Set to value of bubbleAlpha by default.
+        this.highlightAlpha = null;
+        // prop: bubbleGradients
+        // True to color the bubbles with gradient fills instead of flat colors.
+        // NOT AVAILABLE IN IE due to lack of excanvas support for radial gradient fills.
+        // will be ignored in IE.
+        this.bubbleGradients = false;
+        // prop: showLabels
+        // True to show labels on bubbles (if any), false to not show.
+        this.showLabels = true;
+        // array of [point index, radius] which will be sorted in descending order to plot 
+        // largest points below smaller points.
+        this.radii = [];
+        this.maxRadius = 0;
+        // index of the currenty highlighted point, if any
+        this._highlightedPoint = null;
+        // array of jQuery labels.
+        this.labels = [];
+        this.bubbleCanvases = [];
+        
+        // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
+        if (options.highlightMouseDown && options.highlightMouseOver == null) {
+            options.highlightMouseOver = false;
+        }
+        
+        $.extend(true, this, options);
+        
+        if (this.highlightAlpha == null) {
+            this.highlightAlpha = this.bubbleAlpha;
+            if (this.bubbleGradients) {
+                this.highlightAlpha = 0.35;
+            }
+        }
+        
+        this.autoscaleMultiplier = this.autoscaleMultiplier * Math.pow(this.data.length, this.autoscalePointsFactor);
+        
+        // index of the currenty highlighted point, if any
+        this._highlightedPoint = null;
+        
+        // adjust the series colors for options colors passed in with data or for alpha.
+        // note, this can leave undefined holes in the seriesColors array.
+        for (var i=0; i<this.data.length; i++) {
+            var color = null;
+            var d = this.data[i];
+            this.maxRadius = Math.max(this.maxRadius, d[2]);
+            if (d[3]) {
+                if (typeof(d[3]) == 'object') {
+                    color = d[3]['color'];
+                }
+            }
+            
+            if (color == null) {
+                if (this.seriesColors[i] != null) {
+                    color = this.seriesColors[i];
+                }
+            }
+            
+            if (color && this.bubbleAlpha < 1.0) {
+                comps = $.jqplot.getColorComponents(color);
+                color = 'rgba('+comps[0]+', '+comps[1]+', '+comps[2]+', '+this.bubbleAlpha+')';
+            }
+            
+            if (color) {
+                this.seriesColors[i] = color;
+            }
+        }
+        
+        if (!this.varyBubbleColors) {
+            this.seriesColors = [this.color];
+        }
+        
+        this.colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors);
+        
+        // set highlight colors if none provided
+        if (this.highlightColors.length == 0) {
+            for (var i=0; i<this.seriesColors.length; i++){
+                var rgba = $.jqplot.getColorComponents(this.seriesColors[i]);
+                var newrgb = [rgba[0], rgba[1], rgba[2]];
+                var sum = newrgb[0] + newrgb[1] + newrgb[2];
+                for (var j=0; j<3; j++) {
+                    // when darkening, lowest color component can be is 60.
+                    newrgb[j] = (sum > 570) ?  newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]);
+                    newrgb[j] = parseInt(newrgb[j], 10);
+                }
+                this.highlightColors.push('rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+', '+this.highlightAlpha+')');
+            }
+        }
+        
+        this.highlightColorGenerator = new $.jqplot.ColorGenerator(this.highlightColors);
+        
+        var sopts = {fill:true, isarc:true, angle:this.shadowAngle, alpha:this.shadowAlpha, closePath:true};
+        
+        this.renderer.shadowRenderer.init(sopts);
+        
+        this.canvas = new $.jqplot.DivCanvas();
+        this.canvas._plotDimensions = this._plotDimensions;
+        
+        plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
+        plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown);
+        plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp);
+        plot.eventListenerHooks.addOnce('jqplotClick', handleClick);
+        plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick);
+        plot.postDrawHooks.addOnce(postPlotDraw);
+        
+    };
+    
+
+    // converts the user data values to grid coordinates and stores them
+    // in the gridData array.
+    // Called with scope of a series.
+    $.jqplot.BubbleRenderer.prototype.setGridData = function(plot) {
+        // recalculate the grid data
+        var xp = this._xaxis.series_u2p;
+        var yp = this._yaxis.series_u2p;
+        var data = this._plotData;
+        this.gridData = [];
+        var radii = [];
+        this.radii = [];
+        var dim = Math.min(plot._height, plot._width);
+        for (var i=0; i<this.data.length; i++) {
+            if (data[i] != null) {
+                this.gridData.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1]), data[i][2]]);
+                this.radii.push([i, data[i][2]]);
+                radii.push(data[i][2]);
+            }
+        }
+        var r, val, maxr = this.maxRadius = arrayMax(radii);
+        var l = this.gridData.length;
+        if (this.autoscaleBubbles) {
+            for (var i=0; i<l; i++) {
+                val = radii[i]/maxr;
+                r = this.autoscaleMultiplier * dim / 6;
+                this.gridData[i][2] = r * val;
+            }
+        }
+        
+        this.radii.sort(function(a, b) { return b[1] - a[1]; });
+    };
+    
+    // converts any arbitrary data values to grid coordinates and
+    // returns them.  This method exists so that plugins can use a series'
+    // linerenderer to generate grid data points without overwriting the
+    // grid data associated with that series.
+    // Called with scope of a series.
+    $.jqplot.BubbleRenderer.prototype.makeGridData = function(data, plot) {
+        // recalculate the grid data
+        var xp = this._xaxis.series_u2p;
+        var yp = this._yaxis.series_u2p;
+        var gd = [];
+        var radii = [];
+        this.radii = [];
+        var dim = Math.min(plot._height, plot._width);
+        for (var i=0; i<data.length; i++) {
+            if (data[i] != null) {
+                gd.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1]), data[i][2]]);
+                radii.push(data[i][2]);
+                this.radii.push([i, data[i][2]]);
+            }
+        }
+        var r, val, maxr = this.maxRadius = arrayMax(radii);
+        var l = this.gridData.length;
+        if (this.autoscaleBubbles) {
+            for (var i=0; i<l; i++) {
+                val = radii[i]/maxr;
+                r = this.autoscaleMultiplier * dim / 6;
+                gd[i][2] = r * val;
+            }
+        }
+        this.radii.sort(function(a, b) { return b[1] - a[1]; });
+        return gd;
+    };
+    
+    // called with scope of series
+    $.jqplot.BubbleRenderer.prototype.draw = function (ctx, gd, options) {
+        if (this.plugins.pointLabels) {
+            this.plugins.pointLabels.show = false;
+        }
+        var opts = (options != undefined) ? options : {};
+        var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
+        this.canvas._elem.empty();
+        for (var i=0; i<this.radii.length; i++) {
+            var idx = this.radii[i][0];
+            var t=null;
+            var color = null;
+            var el = tel = null;
+            var d = this.data[idx];
+            var gd = this.gridData[idx];
+            if (d[3]) {
+                if (typeof(d[3]) == 'object') {
+                    t = d[3]['label'];
+                }
+                else if (typeof(d[3]) == 'string') {
+                    t = d[3];
+                }
+            }
+            
+            // color = (this.varyBubbleColors) ? this.colorGenerator.get(idx) : this.color;
+            color = this.colorGenerator.get(idx);
+            
+            // If we're drawing a shadow, expand the canvas dimensions to accomodate.
+            var canvasRadius = gd[2];
+            var offset, depth;
+            if (this.shadow) {
+                offset = (0.7 + gd[2]/40).toFixed(1);
+                depth = 1 + Math.ceil(gd[2]/15);
+                canvasRadius += offset*depth;
+            }
+            this.bubbleCanvases[idx] = new $.jqplot.BubbleCanvas();
+            this.canvas._elem.append(this.bubbleCanvases[idx].createElement(gd[0], gd[1], canvasRadius));
+            this.bubbleCanvases[idx].setContext();
+            var ctx = this.bubbleCanvases[idx]._ctx;
+            var x = ctx.canvas.width/2;
+            var y = ctx.canvas.height/2;
+            if (this.shadow) {
+                this.renderer.shadowRenderer.draw(ctx, [x, y, gd[2], 0, 2*Math.PI], {offset: offset, depth: depth});
+            }
+            this.bubbleCanvases[idx].draw(gd[2], color, this.bubbleGradients, this.shadowAngle/180*Math.PI);
+            
+            // now draw label.
+            if (t && this.showLabels) {
+                tel = $('<div style="position:absolute;" class="jqplot-bubble-label"></div>');
+                if (this.escapeHtml) {
+                    tel.text(t);
+                }
+                else {
+                    tel.html(t);
+                }
+                this.canvas._elem.append(tel);
+                var h = $(tel).outerHeight();
+                var w = $(tel).outerWidth();
+                var top = gd[1] - 0.5*h;
+                var left = gd[0] - 0.5*w;
+                tel.css({top: top, left: left});
+                this.labels[idx] = $(tel);
+            }
+        }
+    };
+
+    
+    $.jqplot.DivCanvas = function() {
+        $.jqplot.ElemContainer.call(this);
+        this._ctx;  
+    };
+    
+    $.jqplot.DivCanvas.prototype = new $.jqplot.ElemContainer();
+    $.jqplot.DivCanvas.prototype.constructor = $.jqplot.DivCanvas;
+    
+    $.jqplot.DivCanvas.prototype.createElement = function(offsets, clss, plotDimensions) {
+        this._offsets = offsets;
+        var klass = 'jqplot-DivCanvas';
+        if (clss != undefined) {
+            klass = clss;
+        }
+        var elem;
+        // if this canvas already has a dom element, don't make a new one.
+        if (this._elem) {
+            elem = this._elem.get(0);
+        }
+        else {
+            elem = document.createElement('div');
+        }
+        // if new plotDimensions supplied, use them.
+        if (plotDimensions != undefined) {
+            this._plotDimensions = plotDimensions;
+        }
+        
+        var w = this._plotDimensions.width - this._offsets.left - this._offsets.right + 'px';
+        var h = this._plotDimensions.height - this._offsets.top - this._offsets.bottom + 'px';
+        this._elem = $(elem);
+        this._elem.css({ position: 'absolute', width:w, height:h, left: this._offsets.left, top: this._offsets.top });
+        
+        this._elem.addClass(klass);
+        return this._elem;
+    };
+    
+    $.jqplot.DivCanvas.prototype.setContext = function() {
+        this._ctx = {
+            canvas:{
+                width:0,
+                height:0
+            },
+            clearRect:function(){return null;}
+        };
+        return this._ctx;
+    };
+    
+    $.jqplot.BubbleCanvas = function() {
+        $.jqplot.ElemContainer.call(this);
+        this._ctx;
+    };
+    
+    $.jqplot.BubbleCanvas.prototype = new $.jqplot.ElemContainer();
+    $.jqplot.BubbleCanvas.prototype.constructor = $.jqplot.BubbleCanvas;
+    
+    // initialize with the x,y pont of bubble center and the bubble radius.
+    $.jqplot.BubbleCanvas.prototype.createElement = function(x, y, r) {     
+        var klass = 'jqplot-bubble-point';
+
+        var elem;
+        // if this canvas already has a dom element, don't make a new one.
+        if (this._elem) {
+            elem = this._elem.get(0);
+        }
+        else {
+            elem = document.createElement('canvas');
+        }
+        
+        elem.width = (r != null) ? 2*r : elem.width;
+        elem.height = (r != null) ? 2*r : elem.height;
+        this._elem = $(elem);
+        var l = (x != null && r != null) ? x - r : this._elem.css('left');
+        var t = (y != null && r != null) ? y - r : this._elem.css('top');
+        this._elem.css({ position: 'absolute', left: l, top: t });
+        
+        this._elem.addClass(klass);
+        if ($.jqplot.use_excanvas) {
+            window.G_vmlCanvasManager.init_(document);
+            elem = window.G_vmlCanvasManager.initElement(elem);
+        }
+        
+        return this._elem;
+    };
+    
+    $.jqplot.BubbleCanvas.prototype.draw = function(r, color, gradients, angle) {
+        var ctx = this._ctx;
+        // r = Math.floor(r*1.04);
+        // var x = Math.round(ctx.canvas.width/2);
+        // var y = Math.round(ctx.canvas.height/2);
+        var x = ctx.canvas.width/2;
+        var y = ctx.canvas.height/2;
+        ctx.save();
+        if (gradients && !$.browser.msie) {
+            r = r*1.04;
+            var comps = $.jqplot.getColorComponents(color);
+            var colorinner = 'rgba('+Math.round(comps[0]+0.8*(255-comps[0]))+', '+Math.round(comps[1]+0.8*(255-comps[1]))+', '+Math.round(comps[2]+0.8*(255-comps[2]))+', '+comps[3]+')';
+            var colorend = 'rgba('+comps[0]+', '+comps[1]+', '+comps[2]+', 0)';
+            // var rinner = Math.round(0.35 * r);
+            // var xinner = Math.round(x - Math.cos(angle) * 0.33 * r);
+            // var yinner = Math.round(y - Math.sin(angle) * 0.33 * r);
+            var rinner = 0.35 * r;
+            var xinner = x - Math.cos(angle) * 0.33 * r;
+            var yinner = y - Math.sin(angle) * 0.33 * r;
+            var radgrad = ctx.createRadialGradient(xinner, yinner, rinner, x, y, r);
+            radgrad.addColorStop(0, colorinner);
+            radgrad.addColorStop(0.93, color);
+            radgrad.addColorStop(0.96, colorend);
+            radgrad.addColorStop(1, colorend);
+            // radgrad.addColorStop(.98, colorend);
+            ctx.fillStyle = radgrad;
+            ctx.fillRect(0,0, ctx.canvas.width, ctx.canvas.height);
+        }
+        else {
+            ctx.fillStyle = color;
+            ctx.strokeStyle = color;
+            ctx.lineWidth = 1;
+            ctx.beginPath();
+            var ang = 2*Math.PI;
+            ctx.arc(x, y, r, 0, ang, 0);
+            ctx.closePath();
+            ctx.fill();
+        }
+        ctx.restore();
+    };
+    
+    $.jqplot.BubbleCanvas.prototype.setContext = function() {
+        this._ctx = this._elem.get(0).getContext("2d");
+        return this._ctx;
+    };
+    
+    $.jqplot.BubbleAxisRenderer = function() {
+        $.jqplot.LinearAxisRenderer.call(this);
+    };
+    
+    $.jqplot.BubbleAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
+    $.jqplot.BubbleAxisRenderer.prototype.constructor = $.jqplot.BubbleAxisRenderer;
+        
+    // called with scope of axis object.
+    $.jqplot.BubbleAxisRenderer.prototype.init = function(options){
+        $.extend(true, this, options);
+        var db = this._dataBounds;
+        var minsidx=minpidx=maxsids=maxpidx=maxr=minr=minMaxRadius=maxMaxRadius=maxMult=minMult=0;
+        // Go through all the series attached to this axis and find
+        // the min/max bounds for this axis.
+        for (var i=0; i<this._series.length; i++) {
+            var s = this._series[i];
+            var d = s._plotData;
+            
+            for (var j=0; j<d.length; j++) { 
+                if (this.name == 'xaxis' || this.name == 'x2axis') {
+                    if (d[j][0] < db.min || db.min == null) {
+                        db.min = d[j][0];
+                        minsidx=i;
+                        minpidx=j;
+                        minr = d[j][2];
+                        minMaxRadius = s.maxRadius;
+                        minMult = s.autoscaleMultiplier;
+                    }
+                    if (d[j][0] > db.max || db.max == null) {
+                        db.max = d[j][0];
+                        maxsidx=i;
+                        maxpidx=j;
+                        maxr = d[j][2];
+                        maxMaxRadius = s.maxRadius;
+                        maxMult = s.autoscaleMultiplier;
+                    }
+                }              
+                else {
+                    if (d[j][1] < db.min || db.min == null) {
+                        db.min = d[j][1];
+                        minsidx=i;
+                        minpidx=j;
+                        minr = d[j][2];
+                        minMaxRadius = s.maxRadius;
+                        minMult = s.autoscaleMultiplier;
+                    }
+                    if (d[j][1] > db.max || db.max == null) {
+                        db.max = d[j][1];
+                        maxsidx=i;
+                        maxpidx=j;
+                        maxr = d[j][2];
+                        maxMaxRadius = s.maxRadius;
+                        maxMult = s.autoscaleMultiplier;
+                    }
+                }              
+            }
+        }
+        
+        var minRatio = minr/minMaxRadius;
+        var maxRatio = maxr/maxMaxRadius;
+        
+        // need to estimate the effect of the radius on total axis span and adjust axis accordingly.
+        var span = db.max - db.min;
+        // var dim = (this.name == 'xaxis' || this.name == 'x2axis') ? this._plotDimensions.width : this._plotDimensions.height;
+        var dim = Math.min(this._plotDimensions.width, this._plotDimensions.height);
+        
+        var minfact = minRatio * minMult/3 * span;
+        var maxfact = maxRatio * maxMult/3 * span;
+        db.max += maxfact;
+        db.min -= minfact;
+    };
+    
+    function highlight (plot, sidx, pidx) {
+        plot.plugins.bubbleRenderer.highlightLabelCanvas.empty();
+        var s = plot.series[sidx];
+        var canvas = plot.plugins.bubbleRenderer.highlightCanvas;
+        var ctx = canvas._ctx;
+        ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
+        s._highlightedPoint = pidx;
+        plot.plugins.bubbleRenderer.highlightedSeriesIndex = sidx;
+        
+        var color = s.highlightColorGenerator.get(pidx);
+        var x = s.gridData[pidx][0],
+            y = s.gridData[pidx][1],
+            r = s.gridData[pidx][2];
+        ctx.save();
+        ctx.fillStyle = color;
+        ctx.strokeStyle = color;
+        ctx.lineWidth = 1;
+        ctx.beginPath();
+        ctx.arc(x, y, r, 0, 2*Math.PI, 0);
+        ctx.closePath();
+        ctx.fill();
+        ctx.restore();        
+        // bring label to front
+        if (s.labels[pidx]) {
+            plot.plugins.bubbleRenderer.highlightLabel = s.labels[pidx].clone();
+            plot.plugins.bubbleRenderer.highlightLabel.appendTo(plot.plugins.bubbleRenderer.highlightLabelCanvas);
+            plot.plugins.bubbleRenderer.highlightLabel.addClass('jqplot-bubble-label-highlight');
+        }
+    }
+    
+    function unhighlight (plot) {
+        var canvas = plot.plugins.bubbleRenderer.highlightCanvas;
+        var sidx = plot.plugins.bubbleRenderer.highlightedSeriesIndex;
+        plot.plugins.bubbleRenderer.highlightLabelCanvas.empty();
+        canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
+        for (var i=0; i<plot.series.length; i++) {
+            plot.series[i]._highlightedPoint = null;
+        }
+        plot.plugins.bubbleRenderer.highlightedSeriesIndex = null;
+        plot.target.trigger('jqplotDataUnhighlight');
+    }
+    
+ 
+    function handleMove(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var si = neighbor.seriesIndex;
+            var pi = neighbor.pointIndex;
+            var ins = [si, pi, neighbor.data, plot.series[si].gridData[pi][2]];
+            var evt1 = jQuery.Event('jqplotDataMouseOver');
+            evt1.pageX = ev.pageX;
+            evt1.pageY = ev.pageY;
+            plot.target.trigger(evt1, ins);
+            if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.bubbleRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
+                var evt = jQuery.Event('jqplotDataHighlight');
+                evt.pageX = ev.pageX;
+                evt.pageY = ev.pageY;
+                plot.target.trigger(evt, ins);
+                highlight (plot, ins[0], ins[1]);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    } 
+    
+    function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var si = neighbor.seriesIndex;
+            var pi = neighbor.pointIndex;
+            var ins = [si, pi, neighbor.data, plot.series[si].gridData[pi][2]];
+            if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.bubbleRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
+                var evt = jQuery.Event('jqplotDataHighlight');
+                evt.pageX = ev.pageX;
+                evt.pageY = ev.pageY;
+                plot.target.trigger(evt, ins);
+                highlight (plot, ins[0], ins[1]);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    }
+    
+    function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
+        var idx = plot.plugins.bubbleRenderer.highlightedSeriesIndex;
+        if (idx != null && plot.series[idx].highlightMouseDown) {
+            unhighlight(plot);
+        }
+    }
+    
+    function handleClick(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var si = neighbor.seriesIndex;
+            var pi = neighbor.pointIndex;
+            var ins = [si, pi, neighbor.data, plot.series[si].gridData[pi][2]];
+            var evt = jQuery.Event('jqplotDataClick');
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            plot.target.trigger(evt, ins);
+        }
+    }
+    
+    function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var si = neighbor.seriesIndex;
+            var pi = neighbor.pointIndex;
+            var ins = [si, pi, neighbor.data, plot.series[si].gridData[pi][2]];
+            var idx = plot.plugins.bubbleRenderer.highlightedSeriesIndex;
+            if (idx != null && plot.series[idx].highlightMouseDown) {
+                unhighlight(plot);
+            }
+            var evt = jQuery.Event('jqplotDataRightClick');
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            plot.target.trigger(evt, ins);
+        }
+    }
+    
+    // called within context of plot
+    // create a canvas which we can draw on.
+    // insert it before the eventCanvas, so eventCanvas will still capture events.
+    function postPlotDraw() {
+        this.plugins.bubbleRenderer = {highlightedSeriesIndex:null};
+        this.plugins.bubbleRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
+        this.plugins.bubbleRenderer.highlightLabel = null;
+        this.plugins.bubbleRenderer.highlightLabelCanvas = $('<div style="position:absolute;"></div>');
+        var top = this._gridPadding.top;
+        var left = this._gridPadding.left;
+        var width = this._plotDimensions.width - this._gridPadding.left - this._gridPadding.right;
+        var height = this._plotDimensions.height - this._gridPadding.top - this._gridPadding.bottom;
+        this.plugins.bubbleRenderer.highlightLabelCanvas.css({top:top, left:left, width:width+'px', height:height+'px'});
+
+        this.eventCanvas._elem.before(this.plugins.bubbleRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-bubbleRenderer-highlight-canvas', this._plotDimensions));
+        this.eventCanvas._elem.before(this.plugins.bubbleRenderer.highlightLabelCanvas);
+        
+        var hctx = this.plugins.bubbleRenderer.highlightCanvas.setContext();
+    }
+
+    
+    // setup default renderers for axes and legend so user doesn't have to
+    // called with scope of plot
+    function preInit(target, data, options) {
+        options = options || {};
+        options.axesDefaults = options.axesDefaults || {};
+        options.seriesDefaults = options.seriesDefaults || {};
+        // only set these if there is a Bubble series
+        var setopts = false;
+        if (options.seriesDefaults.renderer == $.jqplot.BubbleRenderer) {
+            setopts = true;
+        }
+        else if (options.series) {
+            for (var i=0; i < options.series.length; i++) {
+                if (options.series[i].renderer == $.jqplot.BubbleRenderer) {
+                    setopts = true;
+                }
+            }
+        }
+        
+        if (setopts) {
+            options.axesDefaults.renderer = $.jqplot.BubbleAxisRenderer;
+            options.sortData = false;
+        }
+    }
+    
+    $.jqplot.preInitHooks.push(preInit);
+    
+})(jQuery);
+    
+    
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.canvasAxisLabelRenderer.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.canvasAxisLabelRenderer.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.canvasAxisLabelRenderer.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,187 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {
+    /**
+    * Class: $.jqplot.CanvasAxisLabelRenderer
+    * Renderer to draw axis labels with a canvas element to support advanced
+    * featrues such as rotated text.  This renderer uses a separate rendering engine
+    * to draw the text on the canvas.  Two modes of rendering the text are available.
+    * If the browser has native font support for canvas fonts (currently Mozila 3.5
+    * and Safari 4), you can enable text rendering with the canvas fillText method.
+    * You do so by setting the "enableFontSupport" option to true. 
+    * 
+    * Browsers lacking native font support will have the text drawn on the canvas
+    * using the Hershey font metrics.  Even if the "enableFontSupport" option is true
+    * non-supporting browsers will still render with the Hershey font.
+    * 
+    */
+    $.jqplot.CanvasAxisLabelRenderer = function(options) {
+        // Group: Properties
+        
+        // prop: angle
+        // angle of text, measured clockwise from x axis.
+        this.angle = 0;
+        // name of the axis associated with this tick
+        this.axis;
+        // prop: show
+        // wether or not to show the tick (mark and label).
+        this.show = true;
+        // prop: showLabel
+        // wether or not to show the label.
+        this.showLabel = true;
+        // prop: label
+        // label for the axis.
+        this.label = '';
+        // prop: fontFamily
+        // CSS spec for the font-family css attribute.
+        // Applies only to browsers supporting native font rendering in the
+        // canvas tag.  Currently Mozilla 3.5 and Safari 4.
+        this.fontFamily = '"Trebuchet MS", Arial, Helvetica, sans-serif';
+        // prop: fontSize
+        // CSS spec for font size.
+        this.fontSize = '11pt';
+        // prop: fontWeight
+        // CSS spec for fontWeight:  normal, bold, bolder, lighter or a number 100 - 900
+        this.fontWeight = 'normal';
+        // prop: fontStretch
+        // Multiplier to condense or expand font width.  
+        // Applies only to browsers which don't support canvas native font rendering.
+        this.fontStretch = 1.0;
+        // prop: textColor
+        // css spec for the color attribute.
+        this.textColor = '#666666';
+        // prop: enableFontSupport
+        // true to turn on native canvas font support in Mozilla 3.5+ and Safari 4+.
+        // If true, label will be drawn with canvas tag native support for fonts.
+        // If false, label will be drawn with Hershey font metrics.
+        this.enableFontSupport = true;
+        // prop: pt2px
+        // Point to pixel scaling factor, used for computing height of bounding box
+        // around a label.  The labels text renderer has a default setting of 1.4, which 
+        // should be suitable for most fonts.  Leave as null to use default.  If tops of
+        // letters appear clipped, increase this.  If bounding box seems too big, decrease.
+        // This is an issue only with the native font renderering capabilities of Mozilla
+        // 3.5 and Safari 4 since they do not provide a method to determine the font height.
+        this.pt2px = null;
+        
+        this._elem;
+        this._ctx;
+        this._plotWidth;
+        this._plotHeight;
+        this._plotDimensions = {height:null, width:null};
+        
+        $.extend(true, this, options);
+        
+        if (options.angle == null && this.axis != 'xaxis' && this.axis != 'x2axis') {
+            this.angle = -90;
+        }
+        
+        var ropts = {fontSize:this.fontSize, fontWeight:this.fontWeight, fontStretch:this.fontStretch, fillStyle:this.textColor, angle:this.getAngleRad(), fontFamily:this.fontFamily};
+        if (this.pt2px) {
+            ropts.pt2px = this.pt2px;
+        }
+        
+        if (this.enableFontSupport) {
+            
+            function support_canvas_text() {
+                return !!(document.createElement('canvas').getContext && typeof document.createElement('canvas').getContext('2d').fillText == 'function');
+            }
+            
+            if (support_canvas_text()) {
+                this._textRenderer = new $.jqplot.CanvasFontRenderer(ropts);
+            }
+            
+            else {
+                this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); 
+            }
+        }
+        else {
+            this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); 
+        }
+    };
+    
+    $.jqplot.CanvasAxisLabelRenderer.prototype.init = function(options) {
+        $.extend(true, this, options);
+        this._textRenderer.init({fontSize:this.fontSize, fontWeight:this.fontWeight, fontStretch:this.fontStretch, fillStyle:this.textColor, angle:this.getAngleRad(), fontFamily:this.fontFamily});
+    };
+    
+    // return width along the x axis
+    // will check first to see if an element exists.
+    // if not, will return the computed text box width.
+    $.jqplot.CanvasAxisLabelRenderer.prototype.getWidth = function(ctx) {
+        if (this._elem) {
+         return this._elem.outerWidth(true);
+        }
+        else {
+            var tr = this._textRenderer;
+            var l = tr.getWidth(ctx);
+            var h = tr.getHeight(ctx);
+            var w = Math.abs(Math.sin(tr.angle)*h) + Math.abs(Math.cos(tr.angle)*l);
+            return w;
+        }
+    };
+    
+    // return height along the y axis.
+    $.jqplot.CanvasAxisLabelRenderer.prototype.getHeight = function(ctx) {
+        if (this._elem) {
+         return this._elem.outerHeight(true);
+        }
+        else {
+            var tr = this._textRenderer;
+            var l = tr.getWidth(ctx);
+            var h = tr.getHeight(ctx);
+            var w = Math.abs(Math.cos(tr.angle)*h) + Math.abs(Math.sin(tr.angle)*l);
+            return w;
+        }
+    };
+    
+    $.jqplot.CanvasAxisLabelRenderer.prototype.getAngleRad = function() {
+        var a = this.angle * Math.PI/180;
+        return a;
+    };
+    
+    $.jqplot.CanvasAxisLabelRenderer.prototype.draw = function(ctx) {
+        // create a canvas here, but can't draw on it untill it is appended
+        // to dom for IE compatability.
+        var domelem = document.createElement('canvas');
+        this._textRenderer.setText(this.label, ctx);
+        var w = this.getWidth(ctx);
+        var h = this.getHeight(ctx);
+        domelem.width = w;
+        domelem.height = h;
+        domelem.style.width = w;
+        domelem.style.height = h;
+        // domelem.style.textAlign = 'center';
+        domelem.style.position = 'absolute';
+        this._domelem = domelem;
+        this._elem = $(domelem);
+        this._elem.addClass('jqplot-'+this.axis+'-label');
+        
+        return this._elem;
+    };
+    
+    $.jqplot.CanvasAxisLabelRenderer.prototype.pack = function() {
+        if ($.jqplot.use_excanvas) {
+            window.G_vmlCanvasManager.init_(document);
+            this._domelem = window.G_vmlCanvasManager.initElement(this._domelem);
+        }
+        var ctx = this._elem.get(0).getContext("2d");
+        this._textRenderer.draw(ctx, this.label);
+    };
+    
+})(jQuery);
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.canvasAxisTickRenderer.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.canvasAxisTickRenderer.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.canvasAxisTickRenderer.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,226 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {
+    /**
+    *  Class: $.jqplot.CanvasAxisTickRenderer
+    * Renderer to draw axis ticks with a canvas element to support advanced
+    * featrues such as rotated text.  This renderer uses a separate rendering engine
+    * to draw the text on the canvas.  Two modes of rendering the text are available.
+    * If the browser has native font support for canvas fonts (currently Mozila 3.5
+    * and Safari 4), you can enable text rendering with the canvas fillText method.
+    * You do so by setting the "enableFontSupport" option to true. 
+    * 
+    * Browsers lacking native font support will have the text drawn on the canvas
+    * using the Hershey font metrics.  Even if the "enableFontSupport" option is true
+    * non-supporting browsers will still render with the Hershey font.
+    */
+    $.jqplot.CanvasAxisTickRenderer = function(options) {
+        // Group: Properties
+        
+        // prop: mark
+        // tick mark on the axis.  One of 'inside', 'outside', 'cross', '' or null.
+        this.mark = 'outside';
+        // prop: showMark
+        // wether or not to show the mark on the axis.
+        this.showMark = true;
+        // prop: showGridline
+        // wether or not to draw the gridline on the grid at this tick.
+        this.showGridline = true;
+        // prop: isMinorTick
+        // if this is a minor tick.
+        this.isMinorTick = false;
+        // prop: angle
+        // angle of text, measured clockwise from x axis.
+        this.angle = 0;
+        // prop:  markSize
+        // Length of the tick marks in pixels.  For 'cross' style, length
+        // will be stoked above and below axis, so total length will be twice this.
+        this.markSize = 4;
+        // prop: show
+        // wether or not to show the tick (mark and label).
+        this.show = true;
+        // prop: showLabel
+        // wether or not to show the label.
+        this.showLabel = true;
+        // prop: labelPosition
+        // 'auto', 'start', 'middle' or 'end'.
+        // Whether tick label should be positioned so the start, middle, or end
+        // of the tick mark.
+        this.labelPosition = 'auto';
+        this.label = '';
+        this.value = null;
+        this._styles = {};
+        // prop: formatter
+        // A class of a formatter for the tick text.
+        // The default $.jqplot.DefaultTickFormatter uses sprintf.
+        this.formatter = $.jqplot.DefaultTickFormatter;
+        // prop: formatString
+        // string passed to the formatter.
+        this.formatString = '';
+        // prop: prefix
+        // string appended to the tick label if no formatString is specified.
+        this.prefix = '';
+        // prop: fontFamily
+        // css spec for the font-family css attribute.
+        this.fontFamily = '"Trebuchet MS", Arial, Helvetica, sans-serif';
+        // prop: fontSize
+        // CSS spec for font size.
+        this.fontSize = '10pt';
+        // prop: fontWeight
+        // CSS spec for fontWeight
+        this.fontWeight = 'normal';
+        // prop: fontStretch
+        // Multiplier to condense or expand font width.  
+        // Applies only to browsers which don't support canvas native font rendering.
+        this.fontStretch = 1.0;
+        // prop: textColor
+        // css spec for the color attribute.
+        this.textColor = '#666666';
+        // prop: enableFontSupport
+        // true to turn on native canvas font support in Mozilla 3.5+ and Safari 4+.
+        // If true, tick label will be drawn with canvas tag native support for fonts.
+        // If false, tick label will be drawn with Hershey font metrics.
+        this.enableFontSupport = true;
+        // prop: pt2px
+        // Point to pixel scaling factor, used for computing height of bounding box
+        // around a label.  The labels text renderer has a default setting of 1.4, which 
+        // should be suitable for most fonts.  Leave as null to use default.  If tops of
+        // letters appear clipped, increase this.  If bounding box seems too big, decrease.
+        // This is an issue only with the native font renderering capabilities of Mozilla
+        // 3.5 and Safari 4 since they do not provide a method to determine the font height.
+        this.pt2px = null;
+        
+        this._elem;
+        this._ctx;
+        this._plotWidth;
+        this._plotHeight;
+        this._plotDimensions = {height:null, width:null};
+        
+        $.extend(true, this, options);
+        
+        var ropts = {fontSize:this.fontSize, fontWeight:this.fontWeight, fontStretch:this.fontStretch, fillStyle:this.textColor, angle:this.getAngleRad(), fontFamily:this.fontFamily};
+        if (this.pt2px) {
+            ropts.pt2px = this.pt2px;
+        }
+        
+        if (this.enableFontSupport) {
+            
+            function support_canvas_text() {
+                return !!(document.createElement('canvas').getContext && typeof document.createElement('canvas').getContext('2d').fillText == 'function');
+            }
+            
+            if (support_canvas_text()) {
+                this._textRenderer = new $.jqplot.CanvasFontRenderer(ropts);
+            }
+            
+            else {
+                this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); 
+            }
+        }
+        else {
+            this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); 
+        }
+    };
+    
+    $.jqplot.CanvasAxisTickRenderer.prototype.init = function(options) {
+        $.extend(true, this, options);
+        this._textRenderer.init({fontSize:this.fontSize, fontWeight:this.fontWeight, fontStretch:this.fontStretch, fillStyle:this.textColor, angle:this.getAngleRad(), fontFamily:this.fontFamily});
+    };
+    
+    // return width along the x axis
+    // will check first to see if an element exists.
+    // if not, will return the computed text box width.
+    $.jqplot.CanvasAxisTickRenderer.prototype.getWidth = function(ctx) {
+        if (this._elem) {
+         return this._elem.outerWidth(true);
+        }
+        else {
+            var tr = this._textRenderer;
+            var l = tr.getWidth(ctx);
+            var h = tr.getHeight(ctx);
+            var w = Math.abs(Math.sin(tr.angle)*h) + Math.abs(Math.cos(tr.angle)*l);
+            return w;
+        }
+    };
+    
+    // return height along the y axis.
+    $.jqplot.CanvasAxisTickRenderer.prototype.getHeight = function(ctx) {
+        if (this._elem) {
+         return this._elem.outerHeight(true);
+        }
+        else {
+            var tr = this._textRenderer;
+            var l = tr.getWidth(ctx);
+            var h = tr.getHeight(ctx);
+            var w = Math.abs(Math.cos(tr.angle)*h) + Math.abs(Math.sin(tr.angle)*l);
+            return w;
+        }
+    };
+    
+    $.jqplot.CanvasAxisTickRenderer.prototype.getAngleRad = function() {
+        var a = this.angle * Math.PI/180;
+        return a;
+    };
+    
+    
+    $.jqplot.CanvasAxisTickRenderer.prototype.setTick = function(value, axisName, isMinor) {
+        this.value = value;
+        if (isMinor) {
+            this.isMinorTick = true;
+        }
+        return this;
+    };
+    
+    $.jqplot.CanvasAxisTickRenderer.prototype.draw = function(ctx) {
+        if (!this.label) {
+            this.label = this.formatter(this.formatString, this.value);
+        }
+        // add prefix if needed
+        if (this.prefix && !this.formatString) {
+            this.label = this.prefix + this.label;
+        }
+        // create a canvas here, but can't draw on it untill it is appended
+        // to dom for IE compatability.
+        var domelem = document.createElement('canvas');
+        this._textRenderer.setText(this.label, ctx);
+        var w = this.getWidth(ctx);
+        var h = this.getHeight(ctx);
+        domelem.width = w;
+        domelem.height = h;
+        domelem.style.width = w;
+        domelem.style.height = h;
+        domelem.style.textAlign = 'left';
+        domelem.style.position = 'absolute';
+        this._domelem = domelem;
+        this._elem = $(domelem);
+        this._elem.css(this._styles);
+        this._elem.addClass('jqplot-'+this.axis+'-tick');
+        
+        return this._elem;
+    };
+    
+    $.jqplot.CanvasAxisTickRenderer.prototype.pack = function() {
+        if ($.jqplot.use_excanvas) {
+            window.G_vmlCanvasManager.init_(document);
+            this._domelem = window.G_vmlCanvasManager.initElement(this._domelem);
+        }
+        var ctx = this._elem.get(0).getContext("2d");
+        this._textRenderer.draw(ctx, this.label);
+    };
+    
+})(jQuery);
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.canvasTextRenderer.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.canvasTextRenderer.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.canvasTextRenderer.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,408 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {    
+    // This code is a modified version of the canvastext.js code, copyright below:
+    //
+    // This code is released to the public domain by Jim Studt, 2007.
+    // He may keep some sort of up to date copy at http://www.federated.com/~jim/canvastext/
+    //
+    $.jqplot.CanvasTextRenderer = function(options){
+        this.fontStyle = 'normal';  // normal, italic, oblique [not implemented]
+        this.fontVariant = 'normal';    // normal, small caps [not implemented]
+        this.fontWeight = 'normal'; // normal, bold, bolder, lighter, 100 - 900
+        this.fontSize = '10px'; 
+        this.fontFamily = 'sans-serif';
+        this.fontStretch = 1.0;
+        this.fillStyle = '#666666';
+        this.angle = 0;
+        this.textAlign = 'start';
+        this.textBaseline = 'alphabetic';
+        this.text;
+        this.width;
+        this.height;
+        this.pt2px = 1.28;
+
+        $.extend(true, this, options);
+        this.normalizedFontSize = this.normalizeFontSize(this.fontSize);
+        this.setHeight();
+    };
+    
+    $.jqplot.CanvasTextRenderer.prototype.init = function(options) {
+        $.extend(true, this, options);
+        this.normalizedFontSize = this.normalizeFontSize(this.fontSize);
+        this.setHeight();
+    };
+    
+    // convert css spec into point size
+    // returns float
+    $.jqplot.CanvasTextRenderer.prototype.normalizeFontSize = function(sz) {
+        sz = String(sz);
+        n = parseFloat(sz);
+        if (sz.indexOf('px') > -1) {
+            return n/this.pt2px;
+        }
+        else if (sz.indexOf('pt') > -1) {
+            return n;
+        }
+        else if (sz.indexOf('em') > -1) {
+            return n*12;
+        }
+        else if (sz.indexOf('%') > -1) {
+            return n*12/100;
+        }
+        // default to pixels;
+        else {
+            return n/this.pt2px;
+        }
+    };
+    
+    
+    $.jqplot.CanvasTextRenderer.prototype.fontWeight2Float = function(w) {
+        // w = normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
+        // return values adjusted for Hershey font.
+        if (Number(w)) {
+            return w/400;
+        }
+        else {
+            switch (w) {
+                case 'normal':
+                    return 1;
+                    break;
+                case 'bold':
+                    return 1.75;
+                    break;
+                case 'bolder':
+                    return 2.25;
+                    break;
+                case 'lighter':
+                    return 0.75;
+                    break;
+                default:
+                    return 1;
+                    break;
+             }   
+        }
+    };
+    
+    $.jqplot.CanvasTextRenderer.prototype.getText = function() {
+        return this.text;
+    };
+    
+    $.jqplot.CanvasTextRenderer.prototype.setText = function(t, ctx) {
+        this.text = t;
+        this.setWidth(ctx);
+        return this;
+    };
+    
+    $.jqplot.CanvasTextRenderer.prototype.getWidth = function(ctx) {
+        return this.width;
+    };
+    
+    $.jqplot.CanvasTextRenderer.prototype.setWidth = function(ctx, w) {
+        if (!w) {
+            this.width = this.measure(ctx, this.text);
+        }
+        else {
+            this.width = w;   
+        }
+        return this;
+    };
+    
+    // return height in pixels.
+    $.jqplot.CanvasTextRenderer.prototype.getHeight = function(ctx) {
+        return this.height;
+    };
+    
+    // w - height in pt
+    // set heigh in px
+    $.jqplot.CanvasTextRenderer.prototype.setHeight = function(w) {
+        if (!w) {
+            //height = this.fontSize /0.75;
+            this.height = this.normalizedFontSize * this.pt2px;
+        }
+        else {
+            this.height = w;   
+        }
+        return this;
+    };
+
+    $.jqplot.CanvasTextRenderer.prototype.letter = function (ch)
+    {
+        return this.letters[ch];
+    };
+
+    $.jqplot.CanvasTextRenderer.prototype.ascent = function()
+    {
+        return this.normalizedFontSize;
+    };
+
+    $.jqplot.CanvasTextRenderer.prototype.descent = function()
+    {
+        return 7.0*this.normalizedFontSize/25.0;
+    };
+
+    $.jqplot.CanvasTextRenderer.prototype.measure = function(ctx, str)
+    {
+        var total = 0;
+        var len = str.length;
+ 
+        for ( i = 0; i < len; i++) {
+            var c = this.letter(str.charAt(i));
+            if (c) {
+                total += c.width * this.normalizedFontSize / 25.0 * this.fontStretch;
+            }
+        }
+        return total;
+    };
+
+    $.jqplot.CanvasTextRenderer.prototype.draw = function(ctx,str)
+    {
+        var x = 0;
+        // leave room at bottom for descenders.
+        var y = this.height*0.72;
+         var total = 0;
+         var len = str.length;
+         var mag = this.normalizedFontSize / 25.0;
+
+         ctx.save();
+         var tx, ty;
+         
+         // 1st quadrant
+         if ((-Math.PI/2 <= this.angle && this.angle <= 0) || (Math.PI*3/2 <= this.angle && this.angle <= Math.PI*2)) {
+             tx = 0;
+             ty = -Math.sin(this.angle) * this.width;
+         }
+         // 4th quadrant
+         else if ((0 < this.angle && this.angle <= Math.PI/2) || (-Math.PI*2 <= this.angle && this.angle <= -Math.PI*3/2)) {
+             tx = Math.sin(this.angle) * this.height;
+             ty = 0;
+         }
+         // 2nd quadrant
+         else if ((-Math.PI < this.angle && this.angle < -Math.PI/2) || (Math.PI <= this.angle && this.angle <= Math.PI*3/2)) {
+             tx = -Math.cos(this.angle) * this.width;
+             ty = -Math.sin(this.angle) * this.width - Math.cos(this.angle) * this.height;
+         }
+         // 3rd quadrant
+         else if ((-Math.PI*3/2 < this.angle && this.angle < Math.PI) || (Math.PI/2 < this.angle && this.angle < Math.PI)) {
+             tx = Math.sin(this.angle) * this.height - Math.cos(this.angle)*this.width;
+             ty = -Math.cos(this.angle) * this.height;
+         }
+         
+         ctx.strokeStyle = this.fillStyle;
+         ctx.fillStyle = this.fillStyle;
+         ctx.translate(tx, ty);
+         ctx.rotate(this.angle);
+         ctx.lineCap = "round";
+         // multiplier was 2.0
+         var fact = (this.normalizedFontSize > 30) ? 2.0 : 2 + (30 - this.normalizedFontSize)/20;
+         ctx.lineWidth = fact * mag * this.fontWeight2Float(this.fontWeight);
+         
+         for ( var i = 0; i < len; i++) {
+            var c = this.letter( str.charAt(i));
+            if ( !c) {
+                continue;
+            }
+
+            ctx.beginPath();
+
+            var penUp = 1;
+            var needStroke = 0;
+            for ( var j = 0; j < c.points.length; j++) {
+              var a = c.points[j];
+              if ( a[0] == -1 && a[1] == -1) {
+                  penUp = 1;
+                  continue;
+              }
+              if ( penUp) {
+                  ctx.moveTo( x + a[0]*mag*this.fontStretch, y - a[1]*mag);
+                  penUp = false;
+              } else {
+                  ctx.lineTo( x + a[0]*mag*this.fontStretch, y - a[1]*mag);
+              }
+            }
+            ctx.stroke();
+            x += c.width*mag*this.fontStretch;
+         }
+         ctx.restore();
+         return total;
+    };
+
+    $.jqplot.CanvasTextRenderer.prototype.letters = {
+         ' ': { width: 16, points: [] },
+         '!': { width: 10, points: [[5,21],[5,7],[-1,-1],[5,2],[4,1],[5,0],[6,1],[5,2]] },
+         '"': { width: 16, points: [[4,21],[4,14],[-1,-1],[12,21],[12,14]] },
+         '#': { width: 21, points: [[11,25],[4,-7],[-1,-1],[17,25],[10,-7],[-1,-1],[4,12],[18,12],[-1,-1],[3,6],[17,6]] },
+         '$': { width: 20, points: [[8,25],[8,-4],[-1,-1],[12,25],[12,-4],[-1,-1],[17,18],[15,20],[12,21],[8,21],[5,20],[3,18],[3,16],[4,14],[5,13],[7,12],[13,10],[15,9],[16,8],[17,6],[17,3],[15,1],[12,0],[8,0],[5,1],[3,3]] },
+         '%': { width: 24, points: [[21,21],[3,0],[-1,-1],[8,21],[10,19],[10,17],[9,15],[7,14],[5,14],[3,16],[3,18],[4,20],[6,21],[8,21],[10,20],[13,19],[16,19],[19,20],[21,21],[-1,-1],[17,7],[15,6],[14,4],[14,2],[16,0],[18,0],[20,1],[21,3],[21,5],[19,7],[17,7]] },
+         '&': { width: 26, points: [[23,12],[23,13],[22,14],[21,14],[20,13],[19,11],[17,6],[15,3],[13,1],[11,0],[7,0],[5,1],[4,2],[3,4],[3,6],[4,8],[5,9],[12,13],[13,14],[14,16],[14,18],[13,20],[11,21],[9,20],[8,18],[8,16],[9,13],[11,10],[16,3],[18,1],[20,0],[22,0],[23,1],[23,2]] },
+         '\'': { width: 10, points: [[5,19],[4,20],[5,21],[6,20],[6,18],[5,16],[4,15]] },
+         '(': { width: 14, points: [[11,25],[9,23],[7,20],[5,16],[4,11],[4,7],[5,2],[7,-2],[9,-5],[11,-7]] },
+         ')': { width: 14, points: [[3,25],[5,23],[7,20],[9,16],[10,11],[10,7],[9,2],[7,-2],[5,-5],[3,-7]] },
+         '*': { width: 16, points: [[8,21],[8,9],[-1,-1],[3,18],[13,12],[-1,-1],[13,18],[3,12]] },
+         '+': { width: 26, points: [[13,18],[13,0],[-1,-1],[4,9],[22,9]] },
+         ',': { width: 10, points: [[6,1],[5,0],[4,1],[5,2],[6,1],[6,-1],[5,-3],[4,-4]] },
+         '-': { width: 18, points: [[6,9],[12,9]] },
+         '.': { width: 10, points: [[5,2],[4,1],[5,0],[6,1],[5,2]] },
+         '/': { width: 22, points: [[20,25],[2,-7]] },
+         '0': { width: 20, points: [[9,21],[6,20],[4,17],[3,12],[3,9],[4,4],[6,1],[9,0],[11,0],[14,1],[16,4],[17,9],[17,12],[16,17],[14,20],[11,21],[9,21]] },
+         '1': { width: 20, points: [[6,17],[8,18],[11,21],[11,0]] },
+         '2': { width: 20, points: [[4,16],[4,17],[5,19],[6,20],[8,21],[12,21],[14,20],[15,19],[16,17],[16,15],[15,13],[13,10],[3,0],[17,0]] },
+         '3': { width: 20, points: [[5,21],[16,21],[10,13],[13,13],[15,12],[16,11],[17,8],[17,6],[16,3],[14,1],[11,0],[8,0],[5,1],[4,2],[3,4]] },
+         '4': { width: 20, points: [[13,21],[3,7],[18,7],[-1,-1],[13,21],[13,0]] },
+         '5': { width: 20, points: [[15,21],[5,21],[4,12],[5,13],[8,14],[11,14],[14,13],[16,11],[17,8],[17,6],[16,3],[14,1],[11,0],[8,0],[5,1],[4,2],[3,4]] },
+         '6': { width: 20, points: [[16,18],[15,20],[12,21],[10,21],[7,20],[5,17],[4,12],[4,7],[5,3],[7,1],[10,0],[11,0],[14,1],[16,3],[17,6],[17,7],[16,10],[14,12],[11,13],[10,13],[7,12],[5,10],[4,7]] },
+         '7': { width: 20, points: [[17,21],[7,0],[-1,-1],[3,21],[17,21]] },
+         '8': { width: 20, points: [[8,21],[5,20],[4,18],[4,16],[5,14],[7,13],[11,12],[14,11],[16,9],[17,7],[17,4],[16,2],[15,1],[12,0],[8,0],[5,1],[4,2],[3,4],[3,7],[4,9],[6,11],[9,12],[13,13],[15,14],[16,16],[16,18],[15,20],[12,21],[8,21]] },
+         '9': { width: 20, points: [[16,14],[15,11],[13,9],[10,8],[9,8],[6,9],[4,11],[3,14],[3,15],[4,18],[6,20],[9,21],[10,21],[13,20],[15,18],[16,14],[16,9],[15,4],[13,1],[10,0],[8,0],[5,1],[4,3]] },
+         ':': { width: 10, points: [[5,14],[4,13],[5,12],[6,13],[5,14],[-1,-1],[5,2],[4,1],[5,0],[6,1],[5,2]] },
+         ';': { width: 10, points: [[5,14],[4,13],[5,12],[6,13],[5,14],[-1,-1],[6,1],[5,0],[4,1],[5,2],[6,1],[6,-1],[5,-3],[4,-4]] },
+         '<': { width: 24, points: [[20,18],[4,9],[20,0]] },
+         '=': { width: 26, points: [[4,12],[22,12],[-1,-1],[4,6],[22,6]] },
+         '>': { width: 24, points: [[4,18],[20,9],[4,0]] },
+         '?': { width: 18, points: [[3,16],[3,17],[4,19],[5,20],[7,21],[11,21],[13,20],[14,19],[15,17],[15,15],[14,13],[13,12],[9,10],[9,7],[-1,-1],[9,2],[8,1],[9,0],[10,1],[9,2]] },
+         '@': { width: 27, points: [[18,13],[17,15],[15,16],[12,16],[10,15],[9,14],[8,11],[8,8],[9,6],[11,5],[14,5],[16,6],[17,8],[-1,-1],[12,16],[10,14],[9,11],[9,8],[10,6],[11,5],[-1,-1],[18,16],[17,8],[17,6],[19,5],[21,5],[23,7],[24,10],[24,12],[23,15],[22,17],[20,19],[18,20],[15,21],[12,21],[9,20],[7,19],[5,17],[4,15],[3,12],[3,9],[4,6],[5,4],[7,2],[9,1],[12,0],[15,0],[18,1],[20,2],[21,3],[-1,-1],[19,16],[18,8],[18,6],[19,5]] },
+         'A': { width: 18, points: [[9,21],[1,0],[-1,-1],[9,21],[17,0],[-1,-1],[4,7],[14,7]] },
+         'B': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,15],[17,13],[16,12],[13,11],[-1,-1],[4,11],[13,11],[16,10],[17,9],[18,7],[18,4],[17,2],[16,1],[13,0],[4,0]] },
+         'C': { width: 21, points: [[18,16],[17,18],[15,20],[13,21],[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5]] },
+         'D': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[11,21],[14,20],[16,18],[17,16],[18,13],[18,8],[17,5],[16,3],[14,1],[11,0],[4,0]] },
+         'E': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,21],[17,21],[-1,-1],[4,11],[12,11],[-1,-1],[4,0],[17,0]] },
+         'F': { width: 18, points: [[4,21],[4,0],[-1,-1],[4,21],[17,21],[-1,-1],[4,11],[12,11]] },
+         'G': { width: 21, points: [[18,16],[17,18],[15,20],[13,21],[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[18,8],[-1,-1],[13,8],[18,8]] },
+         'H': { width: 22, points: [[4,21],[4,0],[-1,-1],[18,21],[18,0],[-1,-1],[4,11],[18,11]] },
+         'I': { width: 8, points: [[4,21],[4,0]] },
+         'J': { width: 16, points: [[12,21],[12,5],[11,2],[10,1],[8,0],[6,0],[4,1],[3,2],[2,5],[2,7]] },
+         'K': { width: 21, points: [[4,21],[4,0],[-1,-1],[18,21],[4,7],[-1,-1],[9,12],[18,0]] },
+         'L': { width: 17, points: [[4,21],[4,0],[-1,-1],[4,0],[16,0]] },
+         'M': { width: 24, points: [[4,21],[4,0],[-1,-1],[4,21],[12,0],[-1,-1],[20,21],[12,0],[-1,-1],[20,21],[20,0]] },
+         'N': { width: 22, points: [[4,21],[4,0],[-1,-1],[4,21],[18,0],[-1,-1],[18,21],[18,0]] },
+         'O': { width: 22, points: [[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[19,8],[19,13],[18,16],[17,18],[15,20],[13,21],[9,21]] },
+         'P': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,14],[17,12],[16,11],[13,10],[4,10]] },
+         'Q': { width: 22, points: [[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[19,8],[19,13],[18,16],[17,18],[15,20],[13,21],[9,21],[-1,-1],[12,4],[18,-2]] },
+         'R': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,15],[17,13],[16,12],[13,11],[4,11],[-1,-1],[11,11],[18,0]] },
+         'S': { width: 20, points: [[17,18],[15,20],[12,21],[8,21],[5,20],[3,18],[3,16],[4,14],[5,13],[7,12],[13,10],[15,9],[16,8],[17,6],[17,3],[15,1],[12,0],[8,0],[5,1],[3,3]] },
+         'T': { width: 16, points: [[8,21],[8,0],[-1,-1],[1,21],[15,21]] },
+         'U': { width: 22, points: [[4,21],[4,6],[5,3],[7,1],[10,0],[12,0],[15,1],[17,3],[18,6],[18,21]] },
+         'V': { width: 18, points: [[1,21],[9,0],[-1,-1],[17,21],[9,0]] },
+         'W': { width: 24, points: [[2,21],[7,0],[-1,-1],[12,21],[7,0],[-1,-1],[12,21],[17,0],[-1,-1],[22,21],[17,0]] },
+         'X': { width: 20, points: [[3,21],[17,0],[-1,-1],[17,21],[3,0]] },
+         'Y': { width: 18, points: [[1,21],[9,11],[9,0],[-1,-1],[17,21],[9,11]] },
+         'Z': { width: 20, points: [[17,21],[3,0],[-1,-1],[3,21],[17,21],[-1,-1],[3,0],[17,0]] },
+         '[': { width: 14, points: [[4,25],[4,-7],[-1,-1],[5,25],[5,-7],[-1,-1],[4,25],[11,25],[-1,-1],[4,-7],[11,-7]] },
+         '\\': { width: 14, points: [[0,21],[14,-3]] },
+         ']': { width: 14, points: [[9,25],[9,-7],[-1,-1],[10,25],[10,-7],[-1,-1],[3,25],[10,25],[-1,-1],[3,-7],[10,-7]] },
+         '^': { width: 16, points: [[6,15],[8,18],[10,15],[-1,-1],[3,12],[8,17],[13,12],[-1,-1],[8,17],[8,0]] },
+         '_': { width: 16, points: [[0,-2],[16,-2]] },
+         '`': { width: 10, points: [[6,21],[5,20],[4,18],[4,16],[5,15],[6,16],[5,17]] },
+         'a': { width: 19, points: [[15,14],[15,0],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] },
+         'b': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,11],[6,13],[8,14],[11,14],[13,13],[15,11],[16,8],[16,6],[15,3],[13,1],[11,0],[8,0],[6,1],[4,3]] },
+         'c': { width: 18, points: [[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] },
+         'd': { width: 19, points: [[15,21],[15,0],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] },
+         'e': { width: 18, points: [[3,8],[15,8],[15,10],[14,12],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] },
+         'f': { width: 12, points: [[10,21],[8,21],[6,20],[5,17],[5,0],[-1,-1],[2,14],[9,14]] },
+         'g': { width: 19, points: [[15,14],[15,-2],[14,-5],[13,-6],[11,-7],[8,-7],[6,-6],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] },
+         'h': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0]] },
+         'i': { width: 8, points: [[3,21],[4,20],[5,21],[4,22],[3,21],[-1,-1],[4,14],[4,0]] },
+         'j': { width: 10, points: [[5,21],[6,20],[7,21],[6,22],[5,21],[-1,-1],[6,14],[6,-3],[5,-6],[3,-7],[1,-7]] },
+         'k': { width: 17, points: [[4,21],[4,0],[-1,-1],[14,14],[4,4],[-1,-1],[8,8],[15,0]] },
+         'l': { width: 8, points: [[4,21],[4,0]] },
+         'm': { width: 30, points: [[4,14],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0],[-1,-1],[15,10],[18,13],[20,14],[23,14],[25,13],[26,10],[26,0]] },
+         'n': { width: 19, points: [[4,14],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0]] },
+         'o': { width: 19, points: [[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3],[16,6],[16,8],[15,11],[13,13],[11,14],[8,14]] },
+         'p': { width: 19, points: [[4,14],[4,-7],[-1,-1],[4,11],[6,13],[8,14],[11,14],[13,13],[15,11],[16,8],[16,6],[15,3],[13,1],[11,0],[8,0],[6,1],[4,3]] },
+         'q': { width: 19, points: [[15,14],[15,-7],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] },
+         'r': { width: 13, points: [[4,14],[4,0],[-1,-1],[4,8],[5,11],[7,13],[9,14],[12,14]] },
+         's': { width: 17, points: [[14,11],[13,13],[10,14],[7,14],[4,13],[3,11],[4,9],[6,8],[11,7],[13,6],[14,4],[14,3],[13,1],[10,0],[7,0],[4,1],[3,3]] },
+         't': { width: 12, points: [[5,21],[5,4],[6,1],[8,0],[10,0],[-1,-1],[2,14],[9,14]] },
+         'u': { width: 19, points: [[4,14],[4,4],[5,1],[7,0],[10,0],[12,1],[15,4],[-1,-1],[15,14],[15,0]] },
+         'v': { width: 16, points: [[2,14],[8,0],[-1,-1],[14,14],[8,0]] },
+         'w': { width: 22, points: [[3,14],[7,0],[-1,-1],[11,14],[7,0],[-1,-1],[11,14],[15,0],[-1,-1],[19,14],[15,0]] },
+         'x': { width: 17, points: [[3,14],[14,0],[-1,-1],[14,14],[3,0]] },
+         'y': { width: 16, points: [[2,14],[8,0],[-1,-1],[14,14],[8,0],[6,-4],[4,-6],[2,-7],[1,-7]] },
+         'z': { width: 17, points: [[14,14],[3,0],[-1,-1],[3,14],[14,14],[-1,-1],[3,0],[14,0]] },
+         '{': { width: 14, points: [[9,25],[7,24],[6,23],[5,21],[5,19],[6,17],[7,16],[8,14],[8,12],[6,10],[-1,-1],[7,24],[6,22],[6,20],[7,18],[8,17],[9,15],[9,13],[8,11],[4,9],[8,7],[9,5],[9,3],[8,1],[7,0],[6,-2],[6,-4],[7,-6],[-1,-1],[6,8],[8,6],[8,4],[7,2],[6,1],[5,-1],[5,-3],[6,-5],[7,-6],[9,-7]] },
+         '|': { width: 8, points: [[4,25],[4,-7]] },
+         '}': { width: 14, points: [[5,25],[7,24],[8,23],[9,21],[9,19],[8,17],[7,16],[6,14],[6,12],[8,10],[-1,-1],[7,24],[8,22],[8,20],[7,18],[6,17],[5,15],[5,13],[6,11],[10,9],[6,7],[5,5],[5,3],[6,1],[7,0],[8,-2],[8,-4],[7,-6],[-1,-1],[8,8],[6,6],[6,4],[7,2],[8,1],[9,-1],[9,-3],[8,-5],[7,-6],[5,-7]] },
+         '~': { width: 24, points: [[3,6],[3,8],[4,11],[6,12],[8,12],[10,11],[14,8],[16,7],[18,7],[20,8],[21,10],[-1,-1],[3,8],[4,10],[6,11],[8,11],[10,10],[14,7],[16,6],[18,6],[20,7],[21,10],[21,12]] }
+     };
+     
+    $.jqplot.CanvasFontRenderer = function(options) {
+        options = options || {};
+        if (!options.pt2px) {
+            options.pt2px = 1.5;
+        }
+        $.jqplot.CanvasTextRenderer.call(this, options);
+    };
+    
+    $.jqplot.CanvasFontRenderer.prototype = new $.jqplot.CanvasTextRenderer({});
+    $.jqplot.CanvasFontRenderer.prototype.constructor = $.jqplot.CanvasFontRenderer;
+
+    $.jqplot.CanvasFontRenderer.prototype.measure = function(ctx, str)
+    {
+        // var fstyle = this.fontStyle+' '+this.fontVariant+' '+this.fontWeight+' '+this.fontSize+' '+this.fontFamily;
+        var fstyle = this.fontSize+' '+this.fontFamily;
+        ctx.save();
+        ctx.font = fstyle;
+        var w = ctx.measureText(str).width;
+        ctx.restore();
+        return w;
+    };
+
+    $.jqplot.CanvasFontRenderer.prototype.draw = function(ctx, str)
+    {
+        var x = 0;
+        // leave room at bottom for descenders.
+        var y = this.height*0.72;
+        //var y = 12;
+
+         ctx.save();
+         var tx, ty;
+         
+         // 1st quadrant
+         if ((-Math.PI/2 <= this.angle && this.angle <= 0) || (Math.PI*3/2 <= this.angle && this.angle <= Math.PI*2)) {
+             tx = 0;
+             ty = -Math.sin(this.angle) * this.width;
+         }
+         // 4th quadrant
+         else if ((0 < this.angle && this.angle <= Math.PI/2) || (-Math.PI*2 <= this.angle && this.angle <= -Math.PI*3/2)) {
+             tx = Math.sin(this.angle) * this.height;
+             ty = 0;
+         }
+         // 2nd quadrant
+         else if ((-Math.PI < this.angle && this.angle < -Math.PI/2) || (Math.PI <= this.angle && this.angle <= Math.PI*3/2)) {
+             tx = -Math.cos(this.angle) * this.width;
+             ty = -Math.sin(this.angle) * this.width - Math.cos(this.angle) * this.height;
+         }
+         // 3rd quadrant
+         else if ((-Math.PI*3/2 < this.angle && this.angle < Math.PI) || (Math.PI/2 < this.angle && this.angle < Math.PI)) {
+             tx = Math.sin(this.angle) * this.height - Math.cos(this.angle)*this.width;
+             ty = -Math.cos(this.angle) * this.height;
+         }
+         ctx.strokeStyle = this.fillStyle;
+         ctx.fillStyle = this.fillStyle;
+        // var fstyle = this.fontStyle+' '+this.fontVariant+' '+this.fontWeight+' '+this.fontSize+' '+this.fontFamily;
+        var fstyle = this.fontSize+' '+this.fontFamily;
+         ctx.font = fstyle;
+         ctx.translate(tx, ty);
+         ctx.rotate(this.angle);
+         ctx.fillText(str, x, y);
+         // ctx.strokeText(str, x, y);
+
+         ctx.restore();
+    };
+    
+})(jQuery);
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.categoryAxisRenderer.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.categoryAxisRenderer.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.categoryAxisRenderer.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,621 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {   
+    /**
+    *  class: $.jqplot.CategoryAxisRenderer
+    *  A plugin for jqPlot to render a category style axis, with equal pixel spacing between y data values of a series.
+    *  
+    *  To use this renderer, include the plugin in your source
+    *  > <script type="text/javascript" language="javascript" src="plugins/jqplot.categoryAxisRenderer.js"></script>
+    *  
+    *  and supply the appropriate options to your plot
+    *  
+    *  > {axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer}}}
+    **/
+    $.jqplot.CategoryAxisRenderer = function(options) {
+        $.jqplot.LinearAxisRenderer.call(this);
+        // prop: sortMergedLabels
+        // True to sort tick labels when labels are created by merging
+        // x axis values from multiple series.  That is, say you have
+        // two series like:
+        // > line1 = [[2006, 4],            [2008, 9], [2009, 16]];
+        // > line2 = [[2006, 3], [2007, 7], [2008, 6]];
+        // If no label array is specified, tick labels will be collected
+        // from the x values of the series.  With sortMergedLabels
+        // set to true, tick labels will be:
+        // > [2006, 2007, 2008, 2009]
+        // With sortMergedLabels set to false, tick labels will be:
+        // > [2006, 2008, 2009, 2007]
+        //
+        // Note, this property is specified on the renderOptions for the 
+        // axes when creating a plot:
+        // > axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer, rendererOptions:{sortMergedLabels:true}}}
+        this.sortMergedLabels = false;
+    };
+    
+    $.jqplot.CategoryAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
+    $.jqplot.CategoryAxisRenderer.prototype.constructor = $.jqplot.CategoryAxisRenderer;
+    
+    $.jqplot.CategoryAxisRenderer.prototype.init = function(options){
+        this.groups = 1;
+        this.groupLabels = [];
+        this._groupLabels = [];
+        this._grouped = false;
+        this._barsPerGroup = null;
+        // prop: tickRenderer
+        // A class of a rendering engine for creating the ticks labels displayed on the plot, 
+        // See <$.jqplot.AxisTickRenderer>.
+        // this.tickRenderer = $.jqplot.AxisTickRenderer;
+        // this.labelRenderer = $.jqplot.AxisLabelRenderer;
+        $.extend(true, this, {tickOptions:{formatString:'%d'}}, options);
+        var db = this._dataBounds;
+        // Go through all the series attached to this axis and find
+        // the min/max bounds for this axis.
+        for (var i=0; i<this._series.length; i++) {
+            var s = this._series[i];
+            if (s.groups) {
+                this.groups = s.groups;
+            }
+            var d = s.data;
+            
+            for (var j=0; j<d.length; j++) { 
+                if (this.name == 'xaxis' || this.name == 'x2axis') {
+                    if (d[j][0] < db.min || db.min == null) {
+                        db.min = d[j][0];
+                    }
+                    if (d[j][0] > db.max || db.max == null) {
+                        db.max = d[j][0];
+                    }
+                }              
+                else {
+                    if (d[j][1] < db.min || db.min == null) {
+                        db.min = d[j][1];
+                    }
+                    if (d[j][1] > db.max || db.max == null) {
+                        db.max = d[j][1];
+                    }
+                }              
+            }
+        }
+        
+        if (this.groupLabels.length) {
+            this.groups = this.groupLabels.length;
+        }
+    };
+ 
+
+    $.jqplot.CategoryAxisRenderer.prototype.createTicks = function() {
+        // we're are operating on an axis here
+        var ticks = this._ticks;
+        var userTicks = this.ticks;
+        var name = this.name;
+        // databounds were set on axis initialization.
+        var db = this._dataBounds;
+        var dim, interval;
+        var min, max;
+        var pos1, pos2;
+        var tt, i;
+
+        // if we already have ticks, use them.
+        if (userTicks.length) {
+            // adjust with blanks if we have groups
+            if (this.groups > 1 && !this._grouped) {
+                var l = userTicks.length;
+                var skip = parseInt(l/this.groups, 10);
+                var count = 0;
+                for (var i=skip; i<l; i+=skip) {
+                    userTicks.splice(i+count, 0, ' ');
+                    count++;
+                }
+                this._grouped = true;
+            }
+            this.min = 0.5;
+            this.max = userTicks.length + 0.5;
+            var range = this.max - this.min;
+            this.numberTicks = 2*userTicks.length + 1;
+            for (i=0; i<userTicks.length; i++){
+                tt = this.min + 2 * i * range / (this.numberTicks-1);
+                // need a marker before and after the tick
+                var t = new this.tickRenderer(this.tickOptions);
+                t.showLabel = false;
+                // t.showMark = true;
+                t.setTick(tt, this.name);
+                this._ticks.push(t);
+                var t = new this.tickRenderer(this.tickOptions);
+                t.label = userTicks[i];
+                // t.showLabel = true;
+                t.showMark = false;
+                t.showGridline = false;
+                t.setTick(tt+0.5, this.name);
+                this._ticks.push(t);
+            }
+            // now add the last tick at the end
+            var t = new this.tickRenderer(this.tickOptions);
+            t.showLabel = false;
+            // t.showMark = true;
+            t.setTick(tt+1, this.name);
+            this._ticks.push(t);
+        }
+
+        // we don't have any ticks yet, let's make some!
+        else {
+            if (name == 'xaxis' || name == 'x2axis') {
+                dim = this._plotDimensions.width;
+            }
+            else {
+                dim = this._plotDimensions.height;
+            }
+            
+            // if min, max and number of ticks specified, user can't specify interval.
+            if (this.min != null && this.max != null && this.numberTicks != null) {
+                this.tickInterval = null;
+            }
+            
+            // if max, min, and interval specified and interval won't fit, ignore interval.
+            if (this.min != null && this.max != null && this.tickInterval != null) {
+                if (parseInt((this.max-this.min)/this.tickInterval, 10) != (this.max-this.min)/this.tickInterval) {
+                    this.tickInterval = null;
+                }
+            }
+        
+            // find out how many categories are in the lines and collect labels
+            var labels = [];
+            var numcats = 0;
+            var min = 0.5;
+            var max, val;
+            var isMerged = false;
+            for (var i=0; i<this._series.length; i++) {
+                var s = this._series[i];
+                for (var j=0; j<s.data.length; j++) {
+                    if (this.name == 'xaxis' || this.name == 'x2axis') {
+                        val = s.data[j][0];
+                    }
+                    else {
+                        val = s.data[j][1];
+                    }
+                    if ($.inArray(val, labels) == -1) {
+                        isMerged = true;
+                        numcats += 1;      
+                        labels.push(val);
+                    }
+                }
+            }
+            
+            if (isMerged && this.sortMergedLabels) {
+                labels.sort(function(a,b) { return a - b; });
+            }
+            
+            // keep a reference to these tick labels to use for redrawing plot (see bug #57)
+            this.ticks = labels;
+            
+            // now bin the data values to the right lables.
+            for (var i=0; i<this._series.length; i++) {
+                var s = this._series[i];
+                for (var j=0; j<s.data.length; j++) {
+                    if (this.name == 'xaxis' || this.name == 'x2axis') {
+                        val = s.data[j][0];
+                    }
+                    else {
+                        val = s.data[j][1];
+                    }
+                    // for category axis, force the values into category bins.
+                    // we should have the value in the label array now.
+                    var idx = $.inArray(val, labels)+1;
+                    if (this.name == 'xaxis' || this.name == 'x2axis') {
+                        s.data[j][0] = idx;
+                    }
+                    else {
+                        s.data[j][1] = idx;
+                    }
+                }
+            }
+            
+            // adjust with blanks if we have groups
+            if (this.groups > 1 && !this._grouped) {
+                var l = labels.length;
+                var skip = parseInt(l/this.groups, 10);
+                var count = 0;
+                for (var i=skip; i<l; i+=skip+1) {
+                    labels[i] = ' ';
+                }
+                this._grouped = true;
+            }
+        
+            max = numcats + 0.5;
+            if (this.numberTicks == null) {
+                this.numberTicks = 2*numcats + 1;
+            }
+
+            var range = max - min;
+            this.min = min;
+            this.max = max;
+            var track = 0;
+            
+            // todo: adjust this so more ticks displayed.
+            var maxVisibleTicks = parseInt(3+dim/20, 10);
+            var skip = parseInt(numcats/maxVisibleTicks, 10);
+
+            if (this.tickInterval == null) {
+
+                this.tickInterval = range / (this.numberTicks-1);
+
+            }
+            // if tickInterval is specified, we will ignore any computed maximum.
+            for (var i=0; i<this.numberTicks; i++){
+                tt = this.min + i * this.tickInterval;
+                var t = new this.tickRenderer(this.tickOptions);
+                // if even tick, it isn't a category, it's a divider
+                if (i/2 == parseInt(i/2, 10)) {
+                    t.showLabel = false;
+                    t.showMark = true;
+                }
+                else {
+                    if (skip>0 && track<skip) {
+                        t.showLabel = false;
+                        track += 1;
+                    }
+                    else {
+                        t.showLabel = true;
+                        track = 0;
+                    } 
+                    t.label = t.formatter(t.formatString, labels[(i-1)/2]);
+                    t.showMark = false;
+                    t.showGridline = false;
+                }
+                t.setTick(tt, this.name);
+                this._ticks.push(t);
+            }
+        }
+        
+    };
+    
+    // called with scope of axis
+    $.jqplot.CategoryAxisRenderer.prototype.draw = function(ctx) {
+        if (this.show) {
+            // populate the axis label and value properties.
+            // createTicks is a method on the renderer, but
+            // call it within the scope of the axis.
+            this.renderer.createTicks.call(this);
+            // fill a div with axes labels in the right direction.
+            // Need to pregenerate each axis to get it's bounds and
+            // position it and the labels correctly on the plot.
+            var dim=0;
+            var temp;
+            // Added for theming.
+            if (this._elem) {
+                this._elem.empty();
+            }
+            
+            this._elem = this._elem || $('<div class="jqplot-axis jqplot-'+this.name+'" style="position:absolute;"></div>');
+            
+            if (this.name == 'xaxis' || this.name == 'x2axis') {
+                this._elem.width(this._plotDimensions.width);
+            }
+            else {
+                this._elem.height(this._plotDimensions.height);
+            }
+            
+            // create a _label object.
+            this.labelOptions.axis = this.name;
+            this._label = new this.labelRenderer(this.labelOptions);
+            if (this._label.show) {
+                var elem = this._label.draw(ctx);
+                elem.appendTo(this._elem);
+            }
+    
+            var t = this._ticks;
+            for (var i=0; i<t.length; i++) {
+                var tick = t[i];
+                if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
+                    var elem = tick.draw(ctx);
+                    elem.appendTo(this._elem);
+                }
+            }
+        
+            this._groupLabels = [];
+            // now make group labels
+            for (var i=0; i<this.groupLabels.length; i++)
+            {
+                var elem = $('<div style="position:absolute;" class="jqplot-'+this.name+'-groupLabel"></div>');
+                elem.html(this.groupLabels[i]);
+                this._groupLabels.push(elem);
+                elem.appendTo(this._elem);
+            }
+        }
+        return this._elem;
+    };
+    
+    // called with scope of axis
+    $.jqplot.CategoryAxisRenderer.prototype.set = function() { 
+        var dim = 0;
+        var temp;
+        var w = 0;
+        var h = 0;
+        var lshow = (this._label == null) ? false : this._label.show;
+        if (this.show) {
+            var t = this._ticks;
+            for (var i=0; i<t.length; i++) {
+                var tick = t[i];
+                if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
+                    if (this.name == 'xaxis' || this.name == 'x2axis') {
+                        temp = tick._elem.outerHeight(true);
+                    }
+                    else {
+                        temp = tick._elem.outerWidth(true);
+                    }
+                    if (temp > dim) {
+                        dim = temp;
+                    }
+                }
+            }
+            
+            var dim2 = 0;
+            for (var i=0; i<this._groupLabels.length; i++) {
+                var l = this._groupLabels[i];
+                if (this.name == 'xaxis' || this.name == 'x2axis') {
+                    temp = l.outerHeight(true);
+                }
+                else {
+                    temp = l.outerWidth(true);
+                }
+                if (temp > dim2) {
+                    dim2 = temp;
+                }
+            }
+            
+            if (lshow) {
+                w = this._label._elem.outerWidth(true);
+                h = this._label._elem.outerHeight(true); 
+            }
+            if (this.name == 'xaxis') {
+                dim += dim2 + h;
+                this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'});
+            }
+            else if (this.name == 'x2axis') {
+                dim += dim2 + h;
+                this._elem.css({'height':dim+'px', left:'0px', top:'0px'});
+            }
+            else if (this.name == 'yaxis') {
+                dim += dim2 + w;
+                this._elem.css({'width':dim+'px', left:'0px', top:'0px'});
+                if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
+                    this._label._elem.css('width', w+'px');
+                }
+            }
+            else {
+                dim += dim2 + w;
+                this._elem.css({'width':dim+'px', right:'0px', top:'0px'});
+                if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
+                    this._label._elem.css('width', w+'px');
+                }
+            }
+        }  
+    };
+    
+    // called with scope of axis
+    $.jqplot.CategoryAxisRenderer.prototype.pack = function(pos, offsets) {
+        var ticks = this._ticks;
+        var max = this.max;
+        var min = this.min;
+        var offmax = offsets.max;
+        var offmin = offsets.min;
+        var lshow = (this._label == null) ? false : this._label.show;
+        
+        for (var p in pos) {
+            this._elem.css(p, pos[p]);
+        }
+        
+        this._offsets = offsets;
+        // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left.
+        var pixellength = offmax - offmin;
+        var unitlength = max - min;
+        
+        // point to unit and unit to point conversions references to Plot DOM element top left corner.
+        this.p2u = function(p){
+            return (p - offmin) * unitlength / pixellength + min;
+        };
+        
+        this.u2p = function(u){
+            return (u - min) * pixellength / unitlength + offmin;
+        };
+                
+        if (this.name == 'xaxis' || this.name == 'x2axis'){
+            this.series_u2p = function(u){
+                return (u - min) * pixellength / unitlength;
+            };
+            this.series_p2u = function(p){
+                return p * unitlength / pixellength + min;
+            };
+        }
+        
+        else {
+            this.series_u2p = function(u){
+                return (u - max) * pixellength / unitlength;
+            };
+            this.series_p2u = function(p){
+                return p * unitlength / pixellength + max;
+            };
+        }
+        
+        if (this.show) {
+            if (this.name == 'xaxis' || this.name == 'x2axis') {
+                for (i=0; i<ticks.length; i++) {
+                    var t = ticks[i];
+                    if (t.show && t.showLabel) {
+                        var shim;
+                        
+                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
+                            // will need to adjust auto positioning based on which axis this is.
+                            var temp = (this.name == 'xaxis') ? 1 : -1;
+                            switch (t.labelPosition) {
+                                case 'auto':
+                                    // position at end
+                                    if (temp * t.angle < 0) {
+                                        shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    }
+                                    // position at start
+                                    else {
+                                        shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'end':
+                                    shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                                case 'start':
+                                    shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    break;
+                                case 'middle':
+                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                                default:
+                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                            }
+                        }
+                        else {
+                            shim = -t.getWidth()/2;
+                        }
+                        var val = this.u2p(t.value) + shim + 'px';
+                        t._elem.css('left', val);
+                        t.pack();
+                    }
+                }
+                
+                var labeledge=['bottom', 0];
+                if (lshow) {
+                    var w = this._label._elem.outerWidth(true);
+                    this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px');
+                    if (this.name == 'xaxis') {
+                        this._label._elem.css('bottom', '0px');
+                        labeledge = ['bottom', this._label._elem.outerHeight(true)];
+                    }
+                    else {
+                        this._label._elem.css('top', '0px');
+                        labeledge = ['top', this._label._elem.outerHeight(true)];
+                    }
+                    this._label.pack();
+                }
+                
+                // draw the group labels
+                var step = parseInt(this._ticks.length/this.groups, 10);
+                for (i=0; i<this._groupLabels.length; i++) {
+                    var mid = 0;
+                    var count = 0;
+                    for (var j=i*step; j<=(i+1)*step; j++) {
+                        if (this._ticks[j]._elem && this._ticks[j].label != " ") {
+                            var t = this._ticks[j]._elem;
+                            var p = t.position();
+                            mid += p.left + t.outerWidth(true)/2;
+                            count++;
+                        }
+                    }
+                    mid = mid/count;
+                    this._groupLabels[i].css({'left':(mid - this._groupLabels[i].outerWidth(true)/2)});
+                    this._groupLabels[i].css(labeledge[0], labeledge[1]);
+                }
+            }
+            else {
+                for (i=0; i<ticks.length; i++) {
+                    var t = ticks[i];
+                    if (t.show && t.showLabel) {                        
+                        var shim;
+                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
+                            // will need to adjust auto positioning based on which axis this is.
+                            var temp = (this.name == 'yaxis') ? 1 : -1;
+                            switch (t.labelPosition) {
+                                case 'auto':
+                                    // position at end
+                                case 'end':
+                                    if (temp * t.angle < 0) {
+                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
+                                    }
+                                    else {
+                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'start':
+                                    if (t.angle > 0) {
+                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
+                                    }
+                                    else {
+                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'middle':
+                                    // if (t.angle > 0) {
+                                    //     shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    // }
+                                    // else {
+                                    //     shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    // }
+                                    shim = -t.getHeight()/2;
+                                    break;
+                                default:
+                                    shim = -t.getHeight()/2;
+                                    break;
+                            }
+                        }
+                        else {
+                            shim = -t.getHeight()/2;
+                        }
+                        
+                        var val = this.u2p(t.value) + shim + 'px';
+                        t._elem.css('top', val);
+                        t.pack();
+                    }
+                }
+                
+                var labeledge=['left', 0];
+                if (lshow) {
+                    var h = this._label._elem.outerHeight(true);
+                    this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px');
+                    if (this.name == 'yaxis') {
+                        this._label._elem.css('left', '0px');
+                        labeledge = ['left', this._label._elem.outerWidth(true)];
+                    }
+                    else {
+                        this._label._elem.css('right', '0px');
+                        labeledge = ['right', this._label._elem.outerWidth(true)];
+                    }   
+                    this._label.pack();
+                }
+                
+                // draw the group labels, position top here, do left after label position.
+                var step = parseInt(this._ticks.length/this.groups, 10);
+                for (i=0; i<this._groupLabels.length; i++) {
+                    var mid = 0;
+                    var count = 0;
+                    for (var j=i*step; j<=(i+1)*step; j++) {
+                        if (this._ticks[j]._elem && this._ticks[j].label != " ") {
+                            var t = this._ticks[j]._elem;
+                            var p = t.position();
+                            mid += p.top + t.outerHeight()/2;
+                            count++;
+                        }
+                    }
+                    mid = mid/count;
+                    this._groupLabels[i].css({'top':mid - this._groupLabels[i].outerHeight()/2});
+                    this._groupLabels[i].css(labeledge[0], labeledge[1]);
+                    
+                }
+            }
+        }
+    };    
+    
+    
+})(jQuery);
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.ciParser.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.ciParser.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.ciParser.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,90 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {
+    /**
+     * Class: $.jqplot.ciParser
+     * Data Renderer which converts a custom JSON data object into jqPlot data format.
+     * 
+     * Parameters:
+     * data - JSON encoded string or object.
+     * plot - reference to jqPlot Plot object.
+     * 
+     * Returns:
+     * data array in jqPlot format.
+     * 
+     */
+    $.jqplot.ciParser = function (data, plot) {
+        var ret = [],
+            line,
+            i, j, k, kk;
+    
+         if (typeof(data) == "string") {
+             data =  $.jqplot.JSON.parse(data, handleStrings);
+         }
+ 
+         else if (typeof(data) == "object") {
+             for (k in data) {
+                 for (i=0; i<data[k].length; i++) {
+                     for (kk in data[k][i]) {
+                         data[k][i][kk] = handleStrings(kk, data[k][i][kk]);
+                     }
+                 }
+             }
+         }
+ 
+         else return null;
+ 
+         // function handleStrings
+         // Checks any JSON encoded strings to see if they are
+         // encoded dates.  If so, pull out the timestamp.
+         // Expects dates to be represented by js timestamps.
+ 
+         function handleStrings(key, value) {
+            var a;
+            if (value != null) {
+                if (value.toString().indexOf('Date') >= 0) {
+                    //here we will try to extract the ticks from the Date string in the "value" fields of JSON returned data
+                    a = /^\/Date\((-?[0-9]+)\)\/$/.exec(value);
+                    if (a) {
+                        return parseInt(a[1], 10);
+                    }
+                }
+                return value;
+            }
+         }
+ 
+        for (var prop in data) {
+            line = [];
+            temp = data[prop];
+            switch (prop) {
+                case "PriceTicks":
+                    for (i=0; i<temp.length; i++) {
+                        line.push([temp[i]['TickDate'], temp[i]['Price']]);
+                    }
+                    break;
+                case "PriceBars":
+                    for (i=0; i<temp.length; i++) {
+                        line.push([temp[i]['BarDate'], temp[i]['Open'], temp[i]['High'], temp[i]['Low'], temp[i]['Close']]);
+                    }
+                    break;
+            }
+            ret.push(line);
+        }
+        return ret;
+    };
+})(jQuery);
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.cursor.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.cursor.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.cursor.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,952 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {
+    
+    /**
+     * Class: $.jqplot.Cursor
+     * Plugin class representing the cursor as displayed on the plot.
+     */
+    $.jqplot.Cursor = function(options) {
+        // Group: Properties
+        //
+        // prop: style
+        // CSS spec for cursor style
+        this.style = 'crosshair';
+        this.previousCursor = 'auto';
+        // prop: show
+        // wether to show the cursor or not.
+        this.show = $.jqplot.config.enablePlugins;
+        // prop: showTooltip
+        // show a cursor position tooltip.  Location of the tooltip
+        // will be controlled by followMouse and tooltipLocation.
+        this.showTooltip = true;
+        // prop: followMouse
+        // Tooltip follows the mouse, it is not at a fixed location.
+        // Tooltip will show on the grid at the location given by
+        // tooltipLocation, offset from the grid edge by tooltipOffset.
+        this.followMouse = false;
+        // prop: tooltipLocation
+        // Where to position tooltip.  If followMouse is true, this is
+        // relative to the cursor, otherwise, it is relative to the grid.
+        // One of 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'
+        this.tooltipLocation = 'se';
+        // prop: tooltipOffset
+        // Pixel offset of tooltip from the grid boudaries or cursor center.
+        this.tooltipOffset = 6;
+        // prop: showTooltipGridPosition
+        // show the grid pixel coordinates of the mouse.
+        this.showTooltipGridPosition = false;
+        // prop: showTooltipUnitPosition
+        // show the unit (data) coordinates of the mouse.
+        this.showTooltipUnitPosition = true;
+        // prop: showTooltipDataPosition
+        // Used with showVerticalLine to show intersecting data points in the tooltip.
+        this.showTooltipDataPosition = false;
+        // prop: tooltipFormatString
+        // sprintf format string for the tooltip.
+        // Uses Ash Searle's javascript sprintf implementation
+        // found here: http://hexmen.com/blog/2007/03/printf-sprintf/
+        // See http://perldoc.perl.org/functions/sprintf.html for reference
+        // Note, if showTooltipDataPosition is true, the default tooltipFormatString
+        // will be set to the cursorLegendFormatString, not the default given here.
+        this.tooltipFormatString = '%.4P, %.4P';
+        // prop: useAxesFormatters
+        // Use the x and y axes formatters to format the text in the tooltip.
+        this.useAxesFormatters = true;
+        // prop: tooltipAxisGroups
+        // Show position for the specified axes.
+        // This is an array like [['xaxis', 'yaxis'], ['xaxis', 'y2axis']]
+        // Default is to compute automatically for all visible axes.
+        this.tooltipAxisGroups = [];
+        // prop: zoom
+        // Enable plot zooming.
+        this.zoom = false;
+        // zoomProxy and zoomTarget properties are not directly set by user.  
+        // They Will be set through call to zoomProxy method.
+        this.zoomProxy = false;
+        this.zoomTarget = false;
+        // prop: clickReset
+        // Will reset plot zoom if single click on plot without drag.
+        this.clickReset = false;
+        // prop: dblClickReset
+        // Will reset plot zoom if double click on plot without drag.
+        this.dblClickReset = true;
+        // prop: showVerticalLine
+        // draw a vertical line across the plot which follows the cursor.
+        // When the line is near a data point, a special legend and/or tooltip can
+        // be updated with the data values.
+        this.showVerticalLine = false;
+        // prop: showHorizontalLine
+        // draw a horizontal line across the plot which follows the cursor.
+        this.showHorizontalLine = false;
+        // prop: constrainZoomTo
+        // 'none', 'x' or 'y'
+        this.constrainZoomTo = 'none';
+        // // prop: autoscaleConstraint
+        // // when a constrained axis is specified, true will
+        // // auatoscale the adjacent axis.
+        // this.autoscaleConstraint = true;
+        this.shapeRenderer = new $.jqplot.ShapeRenderer();
+        this._zoom = {start:[], end:[], started: false, zooming:false, isZoomed:false, axes:{start:{}, end:{}}, gridpos:{}, datapos:{}};
+        this._tooltipElem;
+        this.zoomCanvas;
+        this.cursorCanvas;
+        // prop: intersectionThreshold
+        // pixel distance from data point or marker to consider cursor lines intersecting with point.
+        // If data point markers are not shown, this should be >= 1 or will often miss point intersections.
+        this.intersectionThreshold = 2;
+        // prop: showCursorLegend
+        // Replace the plot legend with an enhanced legend displaying intersection information.
+        this.showCursorLegend = false;
+        // prop: cursorLegendFormatString
+        // Format string used in the cursor legend.  If showTooltipDataPosition is true,
+        // this will also be the default format string used by tooltipFormatString.
+        this.cursorLegendFormatString = $.jqplot.Cursor.cursorLegendFormatString;
+        // whether the cursor is over the grid or not.
+        this._oldHandlers = {onselectstart: null, ondrag: null, onmousedown: null};
+        // prop: constrainOutsideZoom
+        // True to limit actual zoom area to edges of grid, even when zooming
+        // outside of plot area.  That is, can't zoom out by mousing outside plot.
+        this.constrainOutsideZoom = true;
+        // prop: showTooltipOutsideZoom
+        // True will keep updating the tooltip when zooming of the grid.
+        this.showTooltipOutsideZoom = false;
+        // true if mouse is over grid, false if not.
+        this.onGrid = false;
+        $.extend(true, this, options);
+    };
+    
+    $.jqplot.Cursor.cursorLegendFormatString = '%s x:%s, y:%s';
+    
+    // called with scope of plot
+    $.jqplot.Cursor.init = function (target, data, opts){
+        // add a cursor attribute to the plot
+        var options = opts || {};
+        this.plugins.cursor = new $.jqplot.Cursor(options.cursor);
+        var c = this.plugins.cursor;
+
+        if (c.show) {
+            $.jqplot.eventListenerHooks.push(['jqplotMouseEnter', handleMouseEnter]);
+            $.jqplot.eventListenerHooks.push(['jqplotMouseLeave', handleMouseLeave]);
+            $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMouseMove]);
+            
+            if (c.showCursorLegend) {              
+                opts.legend = opts.legend || {};
+                opts.legend.renderer =  $.jqplot.CursorLegendRenderer;
+                opts.legend.formatString = this.plugins.cursor.cursorLegendFormatString;
+                opts.legend.show = true;
+            }
+            
+            if (c.zoom) {
+                $.jqplot.eventListenerHooks.push(['jqplotMouseDown', handleMouseDown]);
+                
+                if (c.clickReset) {
+                    $.jqplot.eventListenerHooks.push(['jqplotClick', handleClick]);
+                }
+                
+                if (c.dblClickReset) {
+                    $.jqplot.eventListenerHooks.push(['jqplotDblClick', handleDblClick]);
+                }             
+            }
+    
+            this.resetZoom = function() {
+                var axes = this.axes;
+                if (!c.zoomProxy) {
+                    for (var ax in axes) {
+                        axes[ax].reset();
+                    }
+                    this.redraw();
+                }
+                else {
+                    var ctx = this.plugins.cursor.zoomCanvas._ctx;
+                    ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
+                }
+                this.plugins.cursor._zoom.isZoomed = false;
+                this.target.trigger('jqplotResetZoom', [this, this.plugins.cursor]);
+            };
+            
+
+            if (c.showTooltipDataPosition) {
+                c.showTooltipUnitPosition = false;
+                c.showTooltipGridPosition = false;
+                if (options.cursor.tooltipFormatString == undefined) {
+                    c.tooltipFormatString = $.jqplot.Cursor.cursorLegendFormatString;
+                }
+            }
+        }
+    };
+    
+    // called with context of plot
+    $.jqplot.Cursor.postDraw = function() {
+        var c = this.plugins.cursor;
+        // if (c.zoom) {
+        c.zoomCanvas = new $.jqplot.GenericCanvas();
+        this.eventCanvas._elem.before(c.zoomCanvas.createElement(this._gridPadding, 'jqplot-zoom-canvas', this._plotDimensions));
+        var zctx = c.zoomCanvas.setContext();
+        // }
+        c._tooltipElem = $('<div class="jqplot-cursor-tooltip" style="position:absolute;display:none"></div>');
+        c.zoomCanvas._elem.before(c._tooltipElem);
+        if (c.showVerticalLine || c.showHorizontalLine) {
+            c.cursorCanvas = new $.jqplot.GenericCanvas();
+            this.eventCanvas._elem.before(c.cursorCanvas.createElement(this._gridPadding, 'jqplot-cursor-canvas', this._plotDimensions));
+            var zctx = c.cursorCanvas.setContext();
+        }
+
+        // if we are showing the positions in unit coordinates, and no axes groups
+        // were specified, create a default set.
+        if (c.showTooltipUnitPosition){
+            if (c.tooltipAxisGroups.length === 0) {
+                var series = this.series;
+                var s;
+                var temp = [];
+                for (var i=0; i<series.length; i++) {
+                    s = series[i];
+                    var ax = s.xaxis+','+s.yaxis;
+                    if ($.inArray(ax, temp) == -1) {
+                        temp.push(ax);
+                    }
+                }
+                for (var i=0; i<temp.length; i++) {
+                    c.tooltipAxisGroups.push(temp[i].split(','));
+                }
+            }
+        }
+    };
+    
+    // Group: methods
+    //
+    // method: $.jqplot.Cursor.zoomProxy
+    // links targetPlot to controllerPlot so that plot zooming of
+    // targetPlot will be controlled by zooming on the controllerPlot.
+    // controllerPlot will not actually zoom, but acts as an
+    // overview plot.  Note, the zoom options must be set to true for
+    // zoomProxy to work.
+    $.jqplot.Cursor.zoomProxy = function(targetPlot, controllerPlot) {
+        var tc = targetPlot.plugins.cursor;
+        var cc = controllerPlot.plugins.cursor;
+        tc.zoomTarget = true;
+        tc.zoom = true;
+        tc.style = 'auto';
+        tc.dblClickReset = false;
+        cc.zoom = true;
+        cc.zoomProxy = true;
+              
+        controllerPlot.target.bind('jqplotZoom', plotZoom);
+        controllerPlot.target.bind('jqplotResetZoom', plotReset);
+
+        function plotZoom(ev, gridpos, datapos, plot, cursor) {
+            tc.doZoom(gridpos, datapos, targetPlot, cursor);
+        } 
+
+        function plotReset(ev, plot, cursor) {
+            targetPlot.resetZoom();
+        }
+    };
+    
+    $.jqplot.Cursor.prototype.resetZoom = function(plot, cursor) {
+        var axes = plot.axes;
+        var cax = cursor._zoom.axes;
+        if (!plot.plugins.cursor.zoomProxy && cursor._zoom.isZoomed) {
+            for (var ax in axes) {
+                axes[ax]._ticks = [];
+                axes[ax].min = cax[ax].min;
+                axes[ax].max = cax[ax].max;
+                axes[ax].numberTicks = cax[ax].numberTicks; 
+                axes[ax].tickInterval = cax[ax].tickInterval;
+                // for date axes
+                axes[ax].daTickInterval = cax[ax].daTickInterval;
+            }
+            plot.redraw();
+            cursor._zoom.isZoomed = false;
+        }
+        else {
+            var ctx = cursor.zoomCanvas._ctx;
+            ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
+        }
+        plot.target.trigger('jqplotResetZoom', [plot, cursor]);
+    };
+    
+    $.jqplot.Cursor.resetZoom = function(plot) {
+        plot.resetZoom();
+    };
+    
+    $.jqplot.Cursor.prototype.doZoom = function (gridpos, datapos, plot, cursor) {
+        var c = cursor;
+        var axes = plot.axes;
+        var zaxes = c._zoom.axes;
+        var start = zaxes.start;
+        var end = zaxes.end;
+        var min, max;
+        var ctx = plot.plugins.cursor.zoomCanvas._ctx;
+        // don't zoom is zoom area is too small (in pixels)
+        if ((c.constrainZoomTo == 'none' && Math.abs(gridpos.x - c._zoom.start[0]) > 6 && Math.abs(gridpos.y - c._zoom.start[1]) > 6) || (c.constrainZoomTo == 'x' && Math.abs(gridpos.x - c._zoom.start[0]) > 6) ||  (c.constrainZoomTo == 'y' && Math.abs(gridpos.y - c._zoom.start[1]) > 6)) {
+            if (!plot.plugins.cursor.zoomProxy) {
+                for (var ax in datapos) {
+                    // make a copy of the original axes to revert back.
+                    if (c._zoom.axes[ax] == undefined) {
+                        c._zoom.axes[ax] = {};
+                        c._zoom.axes[ax].numberTicks = axes[ax].numberTicks;
+                        c._zoom.axes[ax].tickInterval = axes[ax].tickInterval;
+                        // for date axes...
+                        c._zoom.axes[ax].daTickInterval = axes[ax].daTickInterval;
+                        c._zoom.axes[ax].min = axes[ax].min;
+                        c._zoom.axes[ax].max = axes[ax].max;
+                    }
+                    if ((c.constrainZoomTo == 'none') || (c.constrainZoomTo == 'x' && ax.charAt(0) == 'x') || (c.constrainZoomTo == 'y' && ax.charAt(0) == 'y')) {   
+                        dp = datapos[ax];
+                        if (dp != null) {           
+                            if (dp > start[ax]) { 
+                                axes[ax].min = start[ax];
+                                axes[ax].max = dp;
+                            }
+                            else {
+                                span = start[ax] - dp;
+                                axes[ax].max = start[ax];
+                                axes[ax].min = dp;
+                            }
+                            axes[ax].tickInterval = null;
+                            // for date axes...
+                            axes[ax].daTickInterval = null;
+                            axes[ax]._ticks = [];
+                        }
+                    }
+                            
+                    // if ((c.constrainZoomTo == 'x' && ax.charAt(0) == 'y' && c.autoscaleConstraint) || (c.constrainZoomTo == 'y' && ax.charAt(0) == 'x' && c.autoscaleConstraint)) {
+                    //     dp = datapos[ax];
+                    //     if (dp != null) {
+                    //         axes[ax].max == null;
+                    //         axes[ax].min = null;
+                    //     }
+                    // }
+                }
+                ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
+                plot.redraw();
+                c._zoom.isZoomed = true;
+            }
+            plot.target.trigger('jqplotZoom', [gridpos, datapos, plot, cursor]);
+        }
+    };
+    
+    $.jqplot.preInitHooks.push($.jqplot.Cursor.init);
+    $.jqplot.postDrawHooks.push($.jqplot.Cursor.postDraw);
+    
+    function updateTooltip(gridpos, datapos, plot) {
+        var c = plot.plugins.cursor;
+        var s = '';
+        var addbr = false;
+        if (c.showTooltipGridPosition) {
+            s = gridpos.x+', '+gridpos.y;
+            addbr = true;
+        }
+        if (c.showTooltipUnitPosition) {
+            var g;
+            for (var i=0; i<c.tooltipAxisGroups.length; i++) {
+                g = c.tooltipAxisGroups[i];
+                if (addbr) {
+                    s += '<br />';
+                }
+                if (c.useAxesFormatters) {
+                    var xf = plot.axes[g[0]]._ticks[0].formatter;
+                    var yf = plot.axes[g[1]]._ticks[0].formatter;
+                    var xfstr = plot.axes[g[0]]._ticks[0].formatString;
+                    var yfstr = plot.axes[g[1]]._ticks[0].formatString;
+                    s += xf(xfstr, datapos[g[0]]) + ', '+ yf(yfstr, datapos[g[1]]);
+                }
+                else {
+                    s += $.jqplot.sprintf(c.tooltipFormatString, datapos[g[0]], datapos[g[1]]);
+                }
+                addbr = true;
+            }
+        }
+        
+        if (c.showTooltipDataPosition) {
+            var series = plot.series; 
+            var ret = getIntersectingPoints(plot, gridpos.x, gridpos.y);
+            var addbr = false;
+        
+            for (var i = 0; i< series.length; i++) {
+                if (series[i].show) {
+                    var idx = series[i].index;
+                    var label = series[i].label.toString();
+                    var cellid = $.inArray(idx, ret.indices);
+                    var sx = undefined;
+                    var sy = undefined;
+                    if (cellid != -1) {
+                        var data = ret.data[cellid].data;
+                        if (c.useAxesFormatters) {
+                            var xf = series[i]._xaxis._ticks[0].formatter;
+                            var yf = series[i]._yaxis._ticks[0].formatter;
+                            var xfstr = series[i]._xaxis._ticks[0].formatString;
+                            var yfstr = series[i]._yaxis._ticks[0].formatString;
+                            sx = xf(xfstr, data[0]);
+                            sy = yf(yfstr, data[1]);
+                        }
+                        else {
+                            sx = data[0];
+                            sy = data[1];
+                        }
+                        if (addbr) {
+                            s += '<br />';
+                        }
+                        s += $.jqplot.sprintf(c.tooltipFormatString, label, sx, sy);
+                        addbr = true;
+                    }
+                }
+            }
+            
+        }
+        c._tooltipElem.html(s);
+    }
+    
+    function moveLine(gridpos, plot) {
+        var c = plot.plugins.cursor;
+        var ctx = c.cursorCanvas._ctx;
+        ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
+        if (c.showVerticalLine) {
+            c.shapeRenderer.draw(ctx, [[gridpos.x, 0], [gridpos.x, ctx.canvas.height]]);
+        }
+        if (c.showHorizontalLine) {
+            c.shapeRenderer.draw(ctx, [[0, gridpos.y], [ctx.canvas.width, gridpos.y]]);
+        }
+        var ret = getIntersectingPoints(plot, gridpos.x, gridpos.y);
+        if (c.showCursorLegend) {
+            var cells = $(plot.targetId + ' td.jqplot-cursor-legend-label');
+            for (var i=0; i<cells.length; i++) {
+                var idx = $(cells[i]).data('seriesIndex');
+                var series = plot.series[idx];
+                var label = series.label.toString();
+                var cellid = $.inArray(idx, ret.indices);
+                var sx = undefined;
+                var sy = undefined;
+                if (cellid != -1) {
+                    var data = ret.data[cellid].data;
+                    if (c.useAxesFormatters) {
+                        var xf = series._xaxis._ticks[0].formatter;
+                        var yf = series._yaxis._ticks[0].formatter;
+                        var xfstr = series._xaxis._ticks[0].formatString;
+                        var yfstr = series._yaxis._ticks[0].formatString;
+                        sx = xf(xfstr, data[0]);
+                        sy = yf(yfstr, data[1]);
+                    }
+                    else {
+                        sx = data[0];
+                        sy = data[1];
+                    }
+                }
+                if (plot.legend.escapeHtml) {
+                    $(cells[i]).text($.jqplot.sprintf(c.cursorLegendFormatString, label, sx, sy));
+                }
+                else {
+                    $(cells[i]).html($.jqplot.sprintf(c.cursorLegendFormatString, label, sx, sy));
+                }
+            }        
+        }
+    }
+        
+    function getIntersectingPoints(plot, x, y) {
+        var ret = {indices:[], data:[]};
+        var s, i, d0, d, j, r;
+        var threshold;
+        var c = plot.plugins.cursor;
+        for (var i=0; i<plot.series.length; i++) {
+            s = plot.series[i];
+            r = s.renderer;
+            if (s.show) {
+                threshold = c.intersectionThreshold;
+                if (s.showMarker) {
+                    threshold += s.markerRenderer.size/2;
+                }
+                for (var j=0; j<s.gridData.length; j++) {
+                    p = s.gridData[j];
+                    // check vertical line
+                    if (c.showVerticalLine) {
+                        if (Math.abs(x-p[0]) <= threshold) {
+                            ret.indices.push(i);
+                            ret.data.push({seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]});
+                        }
+                    }
+                } 
+            }
+        }
+        return ret;
+    }
+    
+    function moveTooltip(gridpos, plot) {
+        var c = plot.plugins.cursor;  
+        var elem = c._tooltipElem;
+        switch (c.tooltipLocation) {
+            case 'nw':
+                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - c.tooltipOffset;
+                var y = gridpos.y + plot._gridPadding.top - c.tooltipOffset - elem.outerHeight(true);
+                break;
+            case 'n':
+                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2;
+                var y = gridpos.y + plot._gridPadding.top - c.tooltipOffset - elem.outerHeight(true);
+                break;
+            case 'ne':
+                var x = gridpos.x + plot._gridPadding.left + c.tooltipOffset;
+                var y = gridpos.y + plot._gridPadding.top - c.tooltipOffset - elem.outerHeight(true);
+                break;
+            case 'e':
+                var x = gridpos.x + plot._gridPadding.left + c.tooltipOffset;
+                var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2;
+                break;
+            case 'se':
+                var x = gridpos.x + plot._gridPadding.left + c.tooltipOffset;
+                var y = gridpos.y + plot._gridPadding.top + c.tooltipOffset;
+                break;
+            case 's':
+                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2;
+                var y = gridpos.y + plot._gridPadding.top + c.tooltipOffset;
+                break;
+            case 'sw':
+                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - c.tooltipOffset;
+                var y = gridpos.y + plot._gridPadding.top + c.tooltipOffset;
+                break;
+            case 'w':
+                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - c.tooltipOffset;
+                var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2;
+                break;
+            default:
+                var x = gridpos.x + plot._gridPadding.left + c.tooltipOffset;
+                var y = gridpos.y + plot._gridPadding.top + c.tooltipOffset;
+                break;
+        }
+            
+        c._tooltipElem.css('left', x);
+        c._tooltipElem.css('top', y);
+    }
+    
+    function positionTooltip(plot) { 
+        // fake a grid for positioning
+        var grid = plot._gridPadding; 
+        var c = plot.plugins.cursor;
+        var elem = c._tooltipElem;  
+        switch (c.tooltipLocation) {
+            case 'nw':
+                var a = grid.left + c.tooltipOffset;
+                var b = grid.top + c.tooltipOffset;
+                elem.css('left', a);
+                elem.css('top', b);
+                break;
+            case 'n':
+                var a = (grid.left + (plot._plotDimensions.width - grid.right))/2 - elem.outerWidth(true)/2;
+                var b = grid.top + c.tooltipOffset;
+                elem.css('left', a);
+                elem.css('top', b);
+                break;
+            case 'ne':
+                var a = grid.right + c.tooltipOffset;
+                var b = grid.top + c.tooltipOffset;
+                elem.css({right:a, top:b});
+                break;
+            case 'e':
+                var a = grid.right + c.tooltipOffset;
+                var b = (grid.top + (plot._plotDimensions.height - grid.bottom))/2 - elem.outerHeight(true)/2;
+                elem.css({right:a, top:b});
+                break;
+            case 'se':
+                var a = grid.right + c.tooltipOffset;
+                var b = grid.bottom + c.tooltipOffset;
+                elem.css({right:a, bottom:b});
+                break;
+            case 's':
+                var a = (grid.left + (plot._plotDimensions.width - grid.right))/2 - elem.outerWidth(true)/2;
+                var b = grid.bottom + c.tooltipOffset;
+                elem.css({left:a, bottom:b});
+                break;
+            case 'sw':
+                var a = grid.left + c.tooltipOffset;
+                var b = grid.bottom + c.tooltipOffset;
+                elem.css({left:a, bottom:b});
+                break;
+            case 'w':
+                var a = grid.left + c.tooltipOffset;
+                var b = (grid.top + (plot._plotDimensions.height - grid.bottom))/2 - elem.outerHeight(true)/2;
+                elem.css({left:a, top:b});
+                break;
+            default:  // same as 'se'
+                var a = grid.right - c.tooltipOffset;
+                var b = grid.bottom + c.tooltipOffset;
+                elem.css({right:a, bottom:b});
+                break;
+        }
+    }
+    
+    function handleClick (ev, gridpos, datapos, neighbor, plot) {
+        ev.preventDefault();
+        ev.stopImmediatePropagation();
+        var c = plot.plugins.cursor;
+        if (c.clickReset) {
+            c.resetZoom(plot, c);
+        }
+        var sel = window.getSelection;
+        if (document.selection && document.selection.empty)
+        {
+            document.selection.empty();
+        }
+        else if (sel && !sel().isCollapsed) {
+            sel().collapse();
+        }
+        return false;
+    }
+    
+    function handleDblClick (ev, gridpos, datapos, neighbor, plot) {
+        ev.preventDefault();
+        ev.stopImmediatePropagation();
+        var c = plot.plugins.cursor;
+        if (c.dblClickReset) {
+            c.resetZoom(plot, c);
+        }
+        var sel = window.getSelection;
+        if (document.selection && document.selection.empty)
+        {
+            document.selection.empty();
+        }
+        else if (sel && !sel().isCollapsed) {
+            sel().collapse();
+        }
+        return false;
+    }
+    
+    function handleMouseLeave(ev, gridpos, datapos, neighbor, plot) {
+        var c = plot.plugins.cursor;
+        c.onGrid = false;
+        if (c.show) {
+            $(ev.target).css('cursor', c.previousCursor);
+            if (c.showTooltip && !(c._zoom.zooming && c.showTooltipOutsideZoom && !c.constrainOutsideZoom)) {
+                c._tooltipElem.hide();
+            }
+            if (c.zoom) {
+                c._zoom.gridpos = gridpos;
+                c._zoom.datapos = datapos;
+            }
+            if (c.showVerticalLine || c.showHorizontalLine) {
+                var ctx = c.cursorCanvas._ctx;
+                ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
+            }
+            if (c.showCursorLegend) {
+                var cells = $(plot.targetId + ' td.jqplot-cursor-legend-label');
+                for (var i=0; i<cells.length; i++) {
+                    var idx = $(cells[i]).data('seriesIndex');
+                    var series = plot.series[idx];
+                    var label = series.label.toString();
+                    if (plot.legend.escapeHtml) {
+                        $(cells[i]).text($.jqplot.sprintf(c.cursorLegendFormatString, label, undefined, undefined));
+                    }
+                    else {
+                        $(cells[i]).html($.jqplot.sprintf(c.cursorLegendFormatString, label, undefined, undefined));
+                    }
+                
+                }        
+            }
+        }
+    }
+    
+    function handleMouseEnter(ev, gridpos, datapos, neighbor, plot) {
+        var c = plot.plugins.cursor;
+        c.onGrid = true;
+        if (c.show) {
+            c.previousCursor = ev.target.style.cursor;
+            ev.target.style.cursor = c.style;
+            if (c.showTooltip) {
+                updateTooltip(gridpos, datapos, plot);
+                if (c.followMouse) {
+                    moveTooltip(gridpos, plot);
+                }
+                else {
+                    positionTooltip(plot);
+                }
+                c._tooltipElem.show();
+            }
+            if (c.showVerticalLine || c.showHorizontalLine) {
+                moveLine(gridpos, plot);
+            }
+        }
+
+    }    
+    
+    function handleMouseMove(ev, gridpos, datapos, neighbor, plot) {
+        var c = plot.plugins.cursor;
+        var ctx = c.zoomCanvas._ctx;
+        if (c.show) {
+            if (c.showTooltip) {
+                updateTooltip(gridpos, datapos, plot);
+                if (c.followMouse) {
+                    moveTooltip(gridpos, plot);
+                }
+            }
+            if (c.showVerticalLine || c.showHorizontalLine) {
+                moveLine(gridpos, plot);
+            }
+        }
+    }
+            
+    function getEventPosition(ev) {
+        var plot = ev.data.plot;
+        var go = plot.eventCanvas._elem.offset();
+        var gridPos = {x:ev.pageX - go.left, y:ev.pageY - go.top};
+        var dataPos = {xaxis:null, yaxis:null, x2axis:null, y2axis:null, y3axis:null, y4axis:null, y5axis:null, y6axis:null, y7axis:null, y8axis:null, y9axis:null};
+        var an = ['xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis'];
+        var ax = plot.axes;
+        var n, axis;
+        for (n=11; n>0; n--) {
+            axis = an[n-1];
+            if (ax[axis].show) {
+                dataPos[axis] = ax[axis].series_p2u(gridPos[axis.charAt(0)]);
+            }
+        }
+
+        return {offsets:go, gridPos:gridPos, dataPos:dataPos};
+    }    
+    
+    function handleZoomMove(ev) {
+        var plot = ev.data.plot;
+        var c = plot.plugins.cursor;
+        // don't do anything if not on grid.
+        if (c.show && c.zoom && c._zoom.started && !c.zoomTarget) {
+            var ctx = c.zoomCanvas._ctx;
+            var positions = getEventPosition(ev);
+            var gridpos = positions.gridPos;
+            var datapos = positions.dataPos;
+            c._zoom.gridpos = gridpos;
+            c._zoom.datapos = datapos;
+            c._zoom.zooming = true;
+            var xpos = gridpos.x;
+            var ypos = gridpos.y;
+            var height = ctx.canvas.height;
+            var width = ctx.canvas.width;
+            if (c.showTooltip && !c.onGrid && c.showTooltipOutsideZoom) {
+                updateTooltip(gridpos, datapos, plot);
+                if (c.followMouse) {
+                    moveTooltip(gridpos, plot);
+                }
+            }
+            if (c.constrainZoomTo == 'x') {
+                c._zoom.end = [xpos, height];
+            }
+            else if (c.constrainZoomTo == 'y') {
+                c._zoom.end = [width, ypos];
+            }
+            else {
+                c._zoom.end = [xpos, ypos];
+            }
+            var sel = window.getSelection;
+            if (document.selection && document.selection.empty)
+            {
+                document.selection.empty();
+            }
+            else if (sel && !sel().isCollapsed) {
+                sel().collapse();
+            }
+            drawZoomBox.call(c);
+        }
+    }
+    
+    function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
+        var c = plot.plugins.cursor;
+        $(document).one('mouseup.jqplot_cursor', {plot:plot}, handleMouseUp);
+        var axes = plot.axes;
+        if (document.onselectstart != undefined) {
+            c._oldHandlers.onselectstart = document.onselectstart;
+            document.onselectstart = function () { return false; };
+        }
+        if (document.ondrag != undefined) {
+            c._oldHandlers.ondrag = document.ondrag;
+            document.ondrag = function () { return false; };
+        }
+        if (document.onmousedown != undefined) {
+            c._oldHandlers.onmousedown = document.onmousedown;
+            document.onmousedown = function () { return false; };
+        }
+        if (c.zoom) {
+            if (!c.zoomProxy) {
+                var ctx = c.zoomCanvas._ctx;
+                ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
+            }
+            if (c.constrainZoomTo == 'x') {
+                c._zoom.start = [gridpos.x, 0];
+            }
+            else if (c.constrainZoomTo == 'y') {
+                c._zoom.start = [0, gridpos.y];
+            }
+            else {
+                c._zoom.start = [gridpos.x, gridpos.y];
+            }
+            c._zoom.started = true;
+            for (var ax in datapos) {
+                // get zoom starting position.
+                c._zoom.axes.start[ax] = datapos[ax];
+            }  
+            $(document).bind('mousemove.jqplotCursor', {plot:plot}, handleZoomMove);              
+        }
+    }
+    
+    function handleMouseUp(ev) {
+        var plot = ev.data.plot;
+        var c = plot.plugins.cursor;
+        if (c.zoom && c._zoom.zooming && !c.zoomTarget) {
+            var xpos = c._zoom.gridpos.x;
+            var ypos = c._zoom.gridpos.y;
+            var datapos = c._zoom.datapos;
+            var height = c.zoomCanvas._ctx.canvas.height;
+            var width = c.zoomCanvas._ctx.canvas.width;
+            var axes = plot.axes;
+            
+            if (c.constrainOutsideZoom && !c.onGrid) {
+                if (xpos < 0) { xpos = 0; }
+                else if (xpos > width) { xpos = width; }
+                if (ypos < 0) { ypos = 0; }
+                else if (ypos > height) { ypos = height; }
+                
+                for (var axis in datapos) {
+                    if (datapos[axis]) {
+                        if (axis.charAt(0) == 'x') {
+                            datapos[axis] = axes[axis].series_p2u(xpos);
+                        }
+                        else {
+                            datapos[axis] = axes[axis].series_p2u(ypos);
+                        }
+                    }
+                }
+            }
+            
+            if (c.constrainZoomTo == 'x') {
+                ypos = height;
+            }
+            else if (c.constrainZoomTo == 'y') {
+                xpos = width;
+            }
+            c._zoom.end = [xpos, ypos];
+            c._zoom.gridpos = {x:xpos, y:ypos};
+            
+            c.doZoom(c._zoom.gridpos, datapos, plot, c);
+        }
+        c._zoom.started = false;
+        c._zoom.zooming = false;
+        
+        $(document).unbind('mousemove.jqplotCursor', handleZoomMove);
+        
+        if (document.onselectstart != undefined && c._oldHandlers.onselectstart != null){
+            document.onselectstart = c._oldHandlers.onselectstart;
+            c._oldHandlers.onselectstart = null;
+        }
+        if (document.ondrag != undefined && c._oldHandlers.ondrag != null){
+            document.ondrag = c._oldHandlers.ondrag;
+            c._oldHandlers.ondrag = null;
+        }
+        if (document.onmousedown != undefined && c._oldHandlers.onmousedown != null){
+            document.onmousedown = c._oldHandlers.onmousedown;
+            c._oldHandlers.onmousedown = null;
+        }
+
+    }
+    
+    function drawZoomBox() {
+        var start = this._zoom.start;
+        var end = this._zoom.end;
+        var ctx = this.zoomCanvas._ctx;
+        var l, t, h, w;
+        if (end[0] > start[0]) {
+            l = start[0];
+            w = end[0] - start[0];
+        }
+        else {
+            l = end[0];
+            w = start[0] - end[0];
+        }
+        if (end[1] > start[1]) {
+            t = start[1];
+            h = end[1] - start[1];
+        }
+        else {
+            t = end[1];
+            h = start[1] - end[1];
+        }
+        ctx.fillStyle = 'rgba(0,0,0,0.2)';
+        ctx.strokeStyle = '#999999';
+        ctx.lineWidth = 1.0;
+        ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
+        ctx.fillRect(0,0,ctx.canvas.width, ctx.canvas.height);
+        ctx.clearRect(l, t, w, h);
+        // IE won't show transparent fill rect, so stroke a rect also.
+        ctx.strokeRect(l,t,w,h);
+    }
+    
+    $.jqplot.CursorLegendRenderer = function(options) {
+        $.jqplot.TableLegendRenderer.call(this, options);
+        this.formatString = '%s';
+    };
+    
+    $.jqplot.CursorLegendRenderer.prototype = new $.jqplot.TableLegendRenderer();
+    $.jqplot.CursorLegendRenderer.prototype.constructor = $.jqplot.CursorLegendRenderer;
+    
+    // called in context of a Legend
+    $.jqplot.CursorLegendRenderer.prototype.draw = function() {
+        if (this.show) {
+            var series = this._series;
+            // make a table.  one line label per row.
+            this._elem = $('<table class="jqplot-legend jqplot-cursor-legend" style="position:absolute"></table>');
+        
+            var pad = false;
+            for (var i = 0; i< series.length; i++) {
+                s = series[i];
+                if (s.show) {
+                    var lt = $.jqplot.sprintf(this.formatString, s.label.toString());
+                    if (lt) {
+                        var color = s.color;
+                        if (s._stack && !s.fill) {
+                            color = '';
+                        }
+                        addrow.call(this, lt, color, pad, i);
+                        pad = true;
+                    }
+                    // let plugins add more rows to legend.  Used by trend line plugin.
+                    for (var j=0; j<$.jqplot.addLegendRowHooks.length; j++) {
+                        var item = $.jqplot.addLegendRowHooks[j].call(this, s);
+                        if (item) {
+                            addrow.call(this, item.label, item.color, pad);
+                            pad = true;
+                        } 
+                    }
+                }
+            }
+        }
+        
+        function addrow(label, color, pad, idx) {
+            var rs = (pad) ? this.rowSpacing : '0';
+            var tr = $('<tr class="jqplot-legend jqplot-cursor-legend"></tr>').appendTo(this._elem);
+            tr.data('seriesIndex', idx);
+            $('<td class="jqplot-legend jqplot-cursor-legend-swatch" style="padding-top:'+rs+';">'+
+                '<div style="border:1px solid #cccccc;padding:0.2em;">'+
+                '<div class="jqplot-cursor-legend-swatch" style="background-color:'+color+';"></div>'+
+                '</div></td>').appendTo(tr);
+            var td = $('<td class="jqplot-legend jqplot-cursor-legend-label" style="vertical-align:middle;padding-top:'+rs+';"></td>');
+            td.appendTo(tr);
+            td.data('seriesIndex', idx);
+            if (this.escapeHtml) {
+                td.text(label);
+            }
+            else {
+                td.html(label);
+            }
+        }
+        return this._elem;
+    };
+    
+})(jQuery);
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.dateAxisRenderer.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.dateAxisRenderer.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.dateAxisRenderer.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,313 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {  
+    /**
+     * Class: $.jqplot.DateAxisRenderer
+     * A plugin for a jqPlot to render an axis as a series of date values.
+     * This renderer has no options beyond those supplied by the <Axis> class.
+     * It supplies it's own tick formatter, so the tickOptions.formatter option
+     * should not be overridden.
+     * 
+     * Thanks to Ken Synder for his enhanced Date instance methods which are
+     * included with this code <http://kendsnyder.com/sandbox/date/>.
+     * 
+     * To use this renderer, include the plugin in your source
+     * > <script type="text/javascript" language="javascript" src="plugins/jqplot.dateAxisRenderer.js"></script>
+     * 
+     * and supply the appropriate options to your plot
+     * 
+     * > {axes:{xaxis:{renderer:$.jqplot.DateAxisRenderer}}}
+     * 
+     * Dates can be passed into the axis in almost any recognizable value and 
+     * will be parsed.  They will be rendered on the axis in the format
+     * specified by tickOptions.formatString.  e.g. tickOptions.formatString = '%Y-%m-%d'.
+     * 
+     * Accecptable format codes 
+     * are:
+     * 
+     * > Code    Result                  Description
+     * >             == Years ==
+     * > %Y      2008                Four-digit year
+     * > %y      08                  Two-digit year
+     * >             == Months ==
+     * > %m      09                  Two-digit month
+     * > %#m     9                   One or two-digit month
+     * > %B      September           Full month name
+     * > %b      Sep                 Abbreviated month name
+     * >             == Days ==
+     * > %d      05                  Two-digit day of month
+     * > %#d     5                   One or two-digit day of month
+     * > %e      5                   One or two-digit day of month
+     * > %A      Sunday              Full name of the day of the week
+     * > %a      Sun                 Abbreviated name of the day of the week
+     * > %w      0                   Number of the day of the week (0 = Sunday, 6 = Saturday)
+     * > %o      th                  The ordinal suffix string following the day of the month
+     * >             == Hours ==
+     * > %H      23                  Hours in 24-hour format (two digits)
+     * > %#H     3                   Hours in 24-hour integer format (one or two digits)
+     * > %I      11                  Hours in 12-hour format (two digits)
+     * > %#I     3                   Hours in 12-hour integer format (one or two digits)
+     * > %p      PM                  AM or PM
+     * >             == Minutes ==
+     * > %M      09                  Minutes (two digits)
+     * > %#M     9                   Minutes (one or two digits)
+     * >             == Seconds ==
+     * > %S      02                  Seconds (two digits)
+     * > %#S     2                   Seconds (one or two digits)
+     * > %s      1206567625723       Unix timestamp (Seconds past 1970-01-01 00:00:00)
+     * >             == Milliseconds ==
+     * > %N      008                 Milliseconds (three digits)
+     * > %#N     8                   Milliseconds (one to three digits)
+     * >             == Timezone ==
+     * > %O      360                 difference in minutes between local time and GMT
+     * > %Z      Mountain Standard Time  Name of timezone as reported by browser
+     * > %G      -06:00              Hours and minutes between GMT
+     * >             == Shortcuts ==
+     * > %F      2008-03-26          %Y-%m-%d
+     * > %T      05:06:30            %H:%M:%S
+     * > %X      05:06:30            %H:%M:%S
+     * > %x      03/26/08            %m/%d/%y
+     * > %D      03/26/08            %m/%d/%y
+     * > %#c     Wed Mar 26 15:31:00 2008  %a %b %e %H:%M:%S %Y
+     * > %v      3-Sep-2008          %e-%b-%Y
+     * > %R      15:31               %H:%M
+     * > %r      3:31:00 PM          %I:%M:%S %p
+     * >             == Characters ==
+     * > %n      \n                  Newline
+     * > %t      \t                  Tab
+     * > %%      %                   Percent Symbol 
+     */
+    $.jqplot.DateAxisRenderer = function() {
+        $.jqplot.LinearAxisRenderer.call(this);
+    };
+    
+    $.jqplot.DateAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
+    $.jqplot.DateAxisRenderer.prototype.constructor = $.jqplot.DateAxisRenderer;
+    
+    $.jqplot.DateTickFormatter = function(format, val) {
+        if (!format) {
+            format = '%Y/%m/%d';
+        }
+        return Date.create(val).strftime(format);
+    };
+    
+    $.jqplot.DateAxisRenderer.prototype.init = function(options){
+        // prop: tickRenderer
+        // A class of a rendering engine for creating the ticks labels displayed on the plot, 
+        // See <$.jqplot.AxisTickRenderer>.
+        // this.tickRenderer = $.jqplot.AxisTickRenderer;
+        // this.labelRenderer = $.jqplot.AxisLabelRenderer;
+        this.tickOptions.formatter = $.jqplot.DateTickFormatter;
+        this.daTickInterval = null;
+        this._daTickInterval = null;
+        $.extend(true, this, options);
+        var db = this._dataBounds;
+        // Go through all the series attached to this axis and find
+        // the min/max bounds for this axis.
+        for (var i=0; i<this._series.length; i++) {
+            var s = this._series[i];
+            var d = s.data;
+            var pd = s._plotData;
+            var sd = s._stackData;
+            
+            for (var j=0; j<d.length; j++) { 
+                if (this.name == 'xaxis' || this.name == 'x2axis') {
+                    d[j][0] = Date.create(d[j][0]).getTime();
+                    pd[j][0] = Date.create(d[j][0]).getTime();
+                    sd[j][0] = Date.create(d[j][0]).getTime();
+                    if (d[j][0] < db.min || db.min == null) {
+                        db.min = d[j][0];
+                    }
+                    if (d[j][0] > db.max || db.max == null) {
+                        db.max = d[j][0];
+                    }
+                }              
+                else {
+                    d[j][1] = Date.create(d[j][1]).getTime();
+                    pd[j][1] = Date.create(d[j][1]).getTime();
+                    sd[j][1] = Date.create(d[j][1]).getTime();
+                    if (d[j][1] < db.min || db.min == null) {
+                        db.min = d[j][1];
+                    }
+                    if (d[j][1] > db.max || db.max == null) {
+                        db.max = d[j][1];
+                    }
+                }              
+            }
+        }
+    };
+    
+    // called with scope of an axis
+    $.jqplot.DateAxisRenderer.prototype.reset = function() {
+        this.min = this._min;
+        this.max = this._max;
+        this.tickInterval = this._tickInterval;
+        this.numberTicks = this._numberTicks;
+        this.daTickInterval = this._daTickInterval;
+        // this._ticks = this.__ticks;
+    };
+    
+    $.jqplot.DateAxisRenderer.prototype.createTicks = function() {
+        // we're are operating on an axis here
+        var ticks = this._ticks;
+        var userTicks = this.ticks;
+        var name = this.name;
+        // databounds were set on axis initialization.
+        var db = this._dataBounds;
+        var dim, interval;
+        var min, max;
+        var pos1, pos2;
+        var tt, i;
+        
+        // if we already have ticks, use them.
+        // ticks must be in order of increasing value.
+        
+        if (userTicks.length) {
+            // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed
+            for (i=0; i<userTicks.length; i++){
+                var ut = userTicks[i];
+                var t = new this.tickRenderer(this.tickOptions);
+                if (ut.constructor == Array) {
+                    t.value = Date.create(ut[0]).getTime();
+                    t.label = ut[1];
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    t.setTick(t.value, this.name);
+                    this._ticks.push(t);
+                }
+                
+                else {
+                    t.value = Date.create(ut).getTime();
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    t.setTick(t.value, this.name);
+                    this._ticks.push(t);
+                }
+            }
+            this.numberTicks = userTicks.length;
+            this.min = this._ticks[0].value;
+            this.max = this._ticks[this.numberTicks-1].value;
+            this.daTickInterval = [(this.max - this.min) / (this.numberTicks - 1)/1000, 'seconds'];
+        }
+        
+        // we don't have any ticks yet, let's make some!
+        else {
+            if (name == 'xaxis' || name == 'x2axis') {
+                dim = this._plotDimensions.width;
+            }
+            else {
+                dim = this._plotDimensions.height;
+            }
+            
+            // if min, max and number of ticks specified, user can't specify interval.
+            if (this.min != null && this.max != null && this.numberTicks != null) {
+                this.tickInterval = null;
+            }
+            
+            // if user specified a tick interval, convert to usable.
+            if (this.tickInterval != null)
+            {
+                // if interval is a number or can be converted to one, use it.
+                // Assume it is in SECONDS!!!
+                if (Number(this.tickInterval)) {
+                    this.daTickInterval = [Number(this.tickInterval), 'seconds'];
+                }
+                // else, parse out something we can build from.
+                else if (typeof this.tickInterval == "string") {
+                    var parts = this.tickInterval.split(' ');
+                    if (parts.length == 1) {
+                        this.daTickInterval = [1, parts[0]];
+                    }
+                    else if (parts.length == 2) {
+                        this.daTickInterval = [parts[0], parts[1]];
+                    }
+                }
+            }
+        
+            min = ((this.min != null) ? Date.create(this.min).getTime() : db.min);
+            max = ((this.max != null) ? Date.create(this.max).getTime() : db.max);
+            
+            // if min and max are same, space them out a bit
+            if (min == max) {
+                var adj = 24*60*60*500;  // 1/2 day
+                min -= adj;
+                max += adj;
+            }
+
+            var range = max - min;
+            var rmin, rmax;
+        
+            rmin = (this.min != null) ? Date.create(this.min).getTime() : min - range/2*(this.padMin - 1);
+            rmax = (this.max != null) ? Date.create(this.max).getTime() : max + range/2*(this.padMax - 1);
+            this.min = rmin;
+            this.max = rmax;
+            range = this.max - this.min;
+    
+            if (this.numberTicks == null){
+                // if tickInterval is specified by user, we will ignore computed maximum.
+                // max will be equal or greater to fit even # of ticks.
+                if (this.daTickInterval != null) {
+                    var nc = Date.create(this.max).diff(this.min, this.daTickInterval[1], true);
+                    this.numberTicks = Math.ceil(nc/this.daTickInterval[0]) +1;
+                    // this.max = Date.create(this.min).add(this.numberTicks-1, this.daTickInterval[1]).getTime();
+                    this.max = Date.create(this.min).add((this.numberTicks-1) * this.daTickInterval[0], this.daTickInterval[1]).getTime();
+                }
+                else if (dim > 200) {
+                    this.numberTicks = parseInt(3+(dim-200)/100, 10);
+                }
+                else {
+                    this.numberTicks = 2;
+                }
+            }
+    
+            if (this.daTickInterval == null) {
+                this.daTickInterval = [range / (this.numberTicks-1)/1000, 'seconds'];
+            }
+            for (var i=0; i<this.numberTicks; i++){
+                var min = Date.create(this.min);
+                tt = min.add(i*this.daTickInterval[0], this.daTickInterval[1]).getTime();
+                var t = new this.tickRenderer(this.tickOptions);
+                // var t = new $.jqplot.AxisTickRenderer(this.tickOptions);
+                if (!this.showTicks) {
+                    t.showLabel = false;
+                    t.showMark = false;
+                }
+                else if (!this.showTickMarks) {
+                    t.showMark = false;
+                }
+                t.setTick(tt, this.name);
+                this._ticks.push(t);
+            }
+        }
+        if (this._daTickInterval == null) {
+            this._daTickInterval = this.daTickInterval;    
+        }
+    };
+   
+})(jQuery);
+

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.donutRenderer.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.donutRenderer.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.donutRenderer.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,891 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {
+    /**
+     * Class: $.jqplot.DonutRenderer
+     * Plugin renderer to draw a donut chart.
+     * x values, if present, will be used as slice labels.
+     * y values give slice size.
+     * 
+     * To use this renderer, you need to include the 
+     * donut renderer plugin, for example:
+     * 
+     * > <script type="text/javascript" src="plugins/jqplot.donutRenderer.js"></script>
+     * 
+     * Properties described here are passed into the $.jqplot function
+     * as options on the series renderer.  For example:
+     * 
+     * > plot2 = $.jqplot('chart2', [s1, s2], {
+     * >     seriesDefaults: {
+     * >         renderer:$.jqplot.DonutRenderer,
+     * >         rendererOptions:{
+     * >              sliceMargin: 2,
+     * >              innerDiameter: 110,
+     * >              startAngle: -90
+     * >          }
+     * >      }
+     * > });
+     * 
+     * A donut plot will trigger events on the plot target
+     * according to user interaction.  All events return the event object,
+     * the series index, the point (slice) index, and the point data for 
+     * the appropriate slice.
+     * 
+     * 'jqplotDataMouseOver' - triggered when user mouseing over a slice.
+     * 'jqplotDataHighlight' - triggered the first time user mouses over a slice,
+     * if highlighting is enabled.
+     * 'jqplotDataUnhighlight' - triggered when a user moves the mouse out of
+     * a highlighted slice.
+     * 'jqplotDataClick' - triggered when the user clicks on a slice.
+     * 'jqplotDataRightClick' - tiggered when the user right clicks on a slice if
+     * the "captureRightClick" option is set to true on the plot.
+     */
+    $.jqplot.DonutRenderer = function(){
+        $.jqplot.LineRenderer.call(this);
+    };
+    
+    $.jqplot.DonutRenderer.prototype = new $.jqplot.LineRenderer();
+    $.jqplot.DonutRenderer.prototype.constructor = $.jqplot.DonutRenderer;
+    
+    // called with scope of a series
+    $.jqplot.DonutRenderer.prototype.init = function(options, plot) {
+        // Group: Properties
+        //
+        // prop: diameter
+        // Outer diameter of the donut, auto computed by default
+        this.diameter = null;
+        // prop: innerDiameter
+        // Inner diameter of teh donut, auto calculated by default.
+        // If specified will override thickness value.
+        this.innerDiameter = null;
+        // prop: thickness
+        // thickness of the donut, auto computed by default
+        // Overridden by if innerDiameter is specified.
+        this.thickness = null;
+        // prop: padding
+        // padding between the donut and plot edges, legend, etc.
+        this.padding = 20;
+        // prop: sliceMargin
+        // angular spacing between donut slices in degrees.
+        this.sliceMargin = 0;
+        // prop: ringMargin
+        // pixel distance between rings, or multiple series in a donut plot.
+        // null will compute ringMargin based on sliceMargin.
+        this.ringMargin = null;
+        // prop: fill
+        // true or false, wether to fil the slices.
+        this.fill = true;
+        // prop: shadowOffset
+        // offset of the shadow from the slice and offset of 
+        // each succesive stroke of the shadow from the last.
+        this.shadowOffset = 2;
+        // prop: shadowAlpha
+        // transparency of the shadow (0 = transparent, 1 = opaque)
+        this.shadowAlpha = 0.07;
+        // prop: shadowDepth
+        // number of strokes to apply to the shadow, 
+        // each stroke offset shadowOffset from the last.
+        this.shadowDepth = 5;
+        // prop: highlightMouseOver
+        // True to highlight slice when moused over.
+        // This must be false to enable highlightMouseDown to highlight when clicking on a slice.
+        this.highlightMouseOver = true;
+        // prop: highlightMouseDown
+        // True to highlight when a mouse button is pressed over a slice.
+        // This will be disabled if highlightMouseOver is true.
+        this.highlightMouseDown = false;
+        // prop: highlightColors
+        // an array of colors to use when highlighting a slice.
+        this.highlightColors = [];
+        // prop: dataLabels
+        // Either 'label', 'value', 'percent' or an array of labels to place on the pie slices.
+        // Defaults to percentage of each pie slice.
+        this.dataLabels = 'percent';
+        // prop: showDataLabels
+        // true to show data labels on slices.
+        this.showDataLabels = false;
+        // prop: dataLabelFormatString
+        // Format string for data labels.  If none, '%s' is used for "label" and for arrays, '%d' for value and '%d%%' for percentage.
+        this.dataLabelFormatString = null;
+        // prop: dataLabelThreshold
+        // Threshhold in percentage (0 - 100) of pie area, below which no label will be displayed.
+        // This applies to all label types, not just to percentage labels.
+        this.dataLabelThreshold = 3;
+        // prop: dataLabelPositionFactor
+        // A Multiplier (0-1) of the pie radius which controls position of label on slice.
+        // Increasing will slide label toward edge of pie, decreasing will slide label toward center of pie.
+        this.dataLabelPositionFactor = 0.5;
+        // prop: dataLabelNudge
+        // Number of pixels to slide the label away from (+) or toward (-) the center of the pie.
+        this.dataLabelNudge = 0;
+        // prop: startAngle
+        // Angle to start drawing donut in degrees.  
+        // According to orientation of canvas coordinate system:
+        // 0 = on the positive x axis
+        // -90 = on the positive y axis.
+        // 90 = on the negaive y axis.
+        // 180 or - 180 = on the negative x axis.
+        this.startAngle = 0;
+        this.tickRenderer = $.jqplot.DonutTickRenderer;
+        // Used as check for conditions where donut shouldn't be drawn.
+        this._drawData = true;
+        
+        // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
+        if (options.highlightMouseDown && options.highlightMouseOver == null) {
+            options.highlightMouseOver = false;
+        }
+        
+        $.extend(true, this, options);
+        if (this.diameter != null) {
+            this.diameter = this.diameter - this.sliceMargin;
+        }
+        this._diameter = null;
+        this._innerDiameter = null;
+        this._radius = null;
+        this._innerRadius = null;
+        this._thickness = null;
+        // references to the previous series in the plot to properly calculate diameters
+        // and thicknesses of nested rings.
+        this._previousSeries = [];
+        this._numberSeries = 1;
+        // array of [start,end] angles arrays, one for each slice.  In radians.
+        this._sliceAngles = [];
+        // index of the currenty highlighted point, if any
+        this._highlightedPoint = null;
+        
+        // set highlight colors if none provided
+        if (this.highlightColors.length == 0) {
+            for (var i=0; i<this.seriesColors.length; i++){
+                var rgba = $.jqplot.getColorComponents(this.seriesColors[i]);
+                var newrgb = [rgba[0], rgba[1], rgba[2]];
+                var sum = newrgb[0] + newrgb[1] + newrgb[2];
+                for (var j=0; j<3; j++) {
+                    // when darkening, lowest color component can be is 60.
+                    newrgb[j] = (sum > 570) ?  newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]);
+                    newrgb[j] = parseInt(newrgb[j], 10);
+                }
+                this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')');
+            }
+        }
+        
+        plot.postParseOptionsHooks.addOnce(postParseOptions);
+        plot.postInitHooks.addOnce(postInit);
+        plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
+        plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown);
+        plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp);
+        plot.eventListenerHooks.addOnce('jqplotClick', handleClick);
+        plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick);
+        plot.postDrawHooks.addOnce(postPlotDraw);
+        
+        
+    };
+    
+    $.jqplot.DonutRenderer.prototype.setGridData = function(plot) {
+        // set gridData property.  This will hold angle in radians of each data point.
+        var stack = [];
+        var td = [];
+        var sa = this.startAngle/180*Math.PI;
+        var tot = 0;
+        // don't know if we have any valid data yet, so set plot to not draw.
+        this._drawData = false;
+        for (var i=0; i<this.data.length; i++){
+            if (this.data[i][1] != 0) {
+                // we have data, O.K. to draw.
+                this._drawData = true;
+            }
+            stack.push(this.data[i][1]);
+            td.push([this.data[i][0]]);
+            if (i>0) {
+                stack[i] += stack[i-1];
+            }
+            tot += this.data[i][1];
+        }
+        var fact = Math.PI*2/stack[stack.length - 1];
+        
+        for (var i=0; i<stack.length; i++) {
+            td[i][1] = stack[i] * fact;
+            td[i][2] = this.data[i][1]/tot;
+        }
+        this.gridData = td;
+    };
+    
+    $.jqplot.DonutRenderer.prototype.makeGridData = function(data, plot) {
+        var stack = [];
+        var td = [];
+        var tot = 0;
+        var sa = this.startAngle/180*Math.PI;
+        // don't know if we have any valid data yet, so set plot to not draw.
+        this._drawData = false;
+        for (var i=0; i<data.length; i++){
+            if (this.data[i][1] != 0) {
+                // we have data, O.K. to draw.
+                this._drawData = true;
+            }
+            stack.push(data[i][1]);
+            td.push([data[i][0]]);
+            if (i>0) {
+                stack[i] += stack[i-1];
+            }
+            tot += data[i][1];
+        }
+        var fact = Math.PI*2/stack[stack.length - 1];
+        
+        for (var i=0; i<stack.length; i++) {
+            td[i][1] = stack[i] * fact;
+            td[i][2] = data[i][1]/tot;
+        }
+        return td;
+    };
+    
+    $.jqplot.DonutRenderer.prototype.drawSlice = function (ctx, ang1, ang2, color, isShadow) {
+        var r = this._diameter / 2;
+        var ri = r - this._thickness;
+        var fill = this.fill;
+        // var lineWidth = this.lineWidth;
+        ctx.save();
+        ctx.translate(this._center[0], this._center[1]);
+        // ctx.translate(this.sliceMargin*Math.cos((ang1+ang2)/2), this.sliceMargin*Math.sin((ang1+ang2)/2));
+        
+        if (isShadow) {
+            for (var i=0; i<this.shadowDepth; i++) {
+                ctx.save();
+                ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));
+                doDraw();
+            }
+        }
+        
+        else {
+            doDraw();
+        }
+        
+        function doDraw () {
+            // Fix for IE and Chrome that can't seem to draw circles correctly.
+            // ang2 should always be <= 2 pi since that is the way the data is converted.
+             if (ang2 > 6.282 + this.startAngle) {
+                ang2 = 6.282 + this.startAngle;
+                if (ang1 > ang2) {
+                    ang1 = 6.281 + this.startAngle;
+                }
+            }
+            // Fix for IE, where it can't seem to handle 0 degree angles.  Also avoids
+            // ugly line on unfilled donuts.
+            if (ang1 >= ang2) {
+                return;
+            }
+            ctx.beginPath();  
+            ctx.fillStyle = color;
+            ctx.strokeStyle = color;
+            // ctx.lineWidth = lineWidth;
+            ctx.arc(0, 0, r, ang1, ang2, false);
+            ctx.lineTo(ri*Math.cos(ang2), ri*Math.sin(ang2));
+            ctx.arc(0,0, ri, ang2, ang1, true);
+            ctx.closePath();
+            if (fill) {
+                ctx.fill();
+            }
+            else {
+                ctx.stroke();
+            }
+        }
+        
+        if (isShadow) {
+            for (var i=0; i<this.shadowDepth; i++) {
+                ctx.restore();
+            }
+        }
+        
+        ctx.restore();
+    };
+    
+    // called with scope of series
+    $.jqplot.DonutRenderer.prototype.draw = function (ctx, gd, options, plot) {
+        var i;
+        var opts = (options != undefined) ? options : {};
+        // offset and direction of offset due to legend placement
+        var offx = 0;
+        var offy = 0;
+        var trans = 1;
+        // var colorGenerator = new this.colorGenerator(this.seriesColors);
+        if (options.legendInfo && options.legendInfo.placement == 'insideGrid') {
+            var li = options.legendInfo;
+            switch (li.location) {
+                case 'nw':
+                    offx = li.width + li.xoffset;
+                    break;
+                case 'w':
+                    offx = li.width + li.xoffset;
+                    break;
+                case 'sw':
+                    offx = li.width + li.xoffset;
+                    break;
+                case 'ne':
+                    offx = li.width + li.xoffset;
+                    trans = -1;
+                    break;
+                case 'e':
+                    offx = li.width + li.xoffset;
+                    trans = -1;
+                    break;
+                case 'se':
+                    offx = li.width + li.xoffset;
+                    trans = -1;
+                    break;
+                case 'n':
+                    offy = li.height + li.yoffset;
+                    break;
+                case 's':
+                    offy = li.height + li.yoffset;
+                    trans = -1;
+                    break;
+                default:
+                    break;
+            }
+        }
+        
+        var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
+        var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
+        var fill = (opts.fill != undefined) ? opts.fill : this.fill;
+        var cw = ctx.canvas.width;
+        var ch = ctx.canvas.height;
+        var w = cw - offx - 2 * this.padding;
+        var h = ch - offy - 2 * this.padding;
+        var mindim = Math.min(w,h);
+        var d = mindim;
+        var ringmargin =  (this.ringMargin == null) ? this.sliceMargin * 2.0 : this.ringMargin;
+        
+        for (var i=0; i<this._previousSeries.length; i++) {
+            d -= 2.0 * this._previousSeries[i]._thickness + 2.0 * ringmargin;
+        }
+        this._diameter = this.diameter || d;
+        if (this.innerDiameter != null) {
+            var od = (this._numberSeries > 1 && this.index > 0) ? this._previousSeries[0]._diameter : this._diameter;
+            this._thickness = this.thickness || (od - this.innerDiameter - 2.0*ringmargin*this._numberSeries) / this._numberSeries/2.0;
+        }
+        else {
+            this._thickness = this.thickness || mindim / 2 / (this._numberSeries + 1) * 0.85;
+        }
+
+        var r = this._radius = this._diameter/2;
+        this._innerRadius = this._radius - this._thickness;
+        var sa = this.startAngle / 180 * Math.PI;
+        this._center = [(cw - trans * offx)/2 + trans * offx, (ch - trans*offy)/2 + trans * offy];
+        
+        if (this.shadow) {
+            var shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')';
+            for (var i=0; i<gd.length; i++) {
+                var ang1 = (i == 0) ? sa : gd[i-1][1] + sa;
+                // Adjust ang1 and ang2 for sliceMargin
+                ang1 += this.sliceMargin/180*Math.PI;
+                this.renderer.drawSlice.call (this, ctx, ang1, gd[i][1]+sa, shadowColor, true);
+            }
+            
+        }
+        for (var i=0; i<gd.length; i++) {
+            var ang1 = (i == 0) ? sa : gd[i-1][1] + sa;
+            // Adjust ang1 and ang2 for sliceMargin
+            ang1 += this.sliceMargin/180*Math.PI;
+            var ang2 = gd[i][1] + sa;
+            this._sliceAngles.push([ang1, ang2]);
+            this.renderer.drawSlice.call (this, ctx, ang1, ang2, this.seriesColors[i], false);
+            
+            if (this.showDataLabels && gd[i][2]*100 >= this.dataLabelThreshold) {
+                var fstr, avgang = (ang1+ang2)/2, label;
+                
+                if (this.dataLabels == 'label') {
+                    fstr = this.dataLabelFormatString || '%s';
+                    label = $.jqplot.sprintf(fstr, gd[i][0]);
+                }
+                else if (this.dataLabels == 'value') {
+                    fstr = this.dataLabelFormatString || '%d';
+                    label = $.jqplot.sprintf(fstr, gd[i][1]);
+                }
+                else if (this.dataLabels == 'percent') {
+                    fstr = this.dataLabelFormatString || '%d%%';
+                    label = $.jqplot.sprintf(fstr, gd[i][2]*100);
+                }
+                else if (this.dataLabels.constructor == Array) {
+                    fstr = this.dataLabelFormatString || '%s';
+                    label = $.jqplot.sprintf(fstr, this.dataLabels[i]);
+                }
+                
+                var fact = this._innerRadius + this._thickness * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge;
+                
+                var x = this._center[0] + Math.cos(avgang) * fact + this.canvas._offsets.left;
+                var y = this._center[1] + Math.sin(avgang) * fact + this.canvas._offsets.top;
+                
+                var labelelem = $('<span class="jqplot-donut-series jqplot-data-label" style="position:absolute;">' + label + '</span>').insertBefore(plot.eventCanvas._elem);
+                x -= labelelem.width()/2;
+                y -= labelelem.height()/2;
+                x = Math.round(x);
+                y = Math.round(y);
+                labelelem.css({left: x, top: y});
+            }
+        }
+               
+    };
+    
+    $.jqplot.DonutAxisRenderer = function() {
+        $.jqplot.LinearAxisRenderer.call(this);
+    };
+    
+    $.jqplot.DonutAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
+    $.jqplot.DonutAxisRenderer.prototype.constructor = $.jqplot.DonutAxisRenderer;
+        
+    
+    // There are no traditional axes on a donut chart.  We just need to provide
+    // dummy objects with properties so the plot will render.
+    // called with scope of axis object.
+    $.jqplot.DonutAxisRenderer.prototype.init = function(options){
+        //
+        this.tickRenderer = $.jqplot.DonutTickRenderer;
+        $.extend(true, this, options);
+        // I don't think I'm going to need _dataBounds here.
+        // have to go Axis scaling in a way to fit chart onto plot area
+        // and provide u2p and p2u functionality for mouse cursor, etc.
+        // for convienence set _dataBounds to 0 and 100 and
+        // set min/max to 0 and 100.
+        this._dataBounds = {min:0, max:100};
+        this.min = 0;
+        this.max = 100;
+        this.showTicks = false;
+        this.ticks = [];
+        this.showMark = false;
+        this.show = false; 
+    };
+    
+    
+    
+    
+    $.jqplot.DonutLegendRenderer = function(){
+        $.jqplot.TableLegendRenderer.call(this);
+    };
+    
+    $.jqplot.DonutLegendRenderer.prototype = new $.jqplot.TableLegendRenderer();
+    $.jqplot.DonutLegendRenderer.prototype.constructor = $.jqplot.DonutLegendRenderer;
+    
+    /**
+     * Class: $.jqplot.DonutLegendRenderer
+     * Legend Renderer specific to donut plots.  Set by default
+     * when user creates a donut plot.
+     */
+    $.jqplot.DonutLegendRenderer.prototype.init = function(options) {
+        // Group: Properties
+        //
+        // prop: numberRows
+        // Maximum number of rows in the legend.  0 or null for unlimited.
+        this.numberRows = null;
+        // prop: numberColumns
+        // Maximum number of columns in the legend.  0 or null for unlimited.
+        this.numberColumns = null;
+        $.extend(true, this, options);
+    };
+    
+    // called with context of legend
+    $.jqplot.DonutLegendRenderer.prototype.draw = function() {
+        var legend = this;
+        if (this.show) {
+            var series = this._series;
+            var ss = 'position:absolute;';
+            ss += (this.background) ? 'background:'+this.background+';' : '';
+            ss += (this.border) ? 'border:'+this.border+';' : '';
+            ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
+            ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : '';
+            ss += (this.textColor) ? 'color:'+this.textColor+';' : '';
+            ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : '';
+            ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : '';
+            ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : '';
+            ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : '';
+            this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>');
+            // Donut charts legends don't go by number of series, but by number of data points
+            // in the series.  Refactor things here for that.
+            
+            var pad = false, 
+                reverse = false,
+                nr, nc;
+            var s = series[0];
+            var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors);
+            
+            if (s.show) {
+                var pd = s.data;
+                if (this.numberRows) {
+                    nr = this.numberRows;
+                    if (!this.numberColumns){
+                        nc = Math.ceil(pd.length/nr);
+                    }
+                    else{
+                        nc = this.numberColumns;
+                    }
+                }
+                else if (this.numberColumns) {
+                    nc = this.numberColumns;
+                    nr = Math.ceil(pd.length/this.numberColumns);
+                }
+                else {
+                    nr = pd.length;
+                    nc = 1;
+                }
+                
+                var i, j, tr, td1, td2, lt, rs, color;
+                var idx = 0;    
+                
+                for (i=0; i<nr; i++) {
+                    if (reverse){
+                        tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem);
+                    }
+                    else{
+                        tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem);
+                    }
+                    for (j=0; j<nc; j++) {
+                        if (idx < pd.length){
+                            lt = this.labels[idx] || pd[idx][0].toString();
+                            color = colorGenerator.next();
+                            if (!reverse){
+                                if (i>0){
+                                    pad = true;
+                                }
+                                else{
+                                    pad = false;
+                                }
+                            }
+                            else{
+                                if (i == nr -1){
+                                    pad = false;
+                                }
+                                else{
+                                    pad = true;
+                                }
+                            }
+                            rs = (pad) ? this.rowSpacing : '0';
+                
+                            td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+
+                                '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+
+                                '</div></td>');
+                            td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
+                            if (this.escapeHtml){
+                                td2.text(lt);
+                            }
+                            else {
+                                td2.html(lt);
+                            }
+                            if (reverse) {
+                                td2.prependTo(tr);
+                                td1.prependTo(tr);
+                            }
+                            else {
+                                td1.appendTo(tr);
+                                td2.appendTo(tr);
+                            }
+                            pad = true;
+                        }
+                        idx++;
+                    }   
+                }
+            }
+        }
+        return this._elem;                
+    };
+    
+    // $.jqplot.DonutLegendRenderer.prototype.pack = function(offsets) {
+    //     if (this.show) {
+    //         // fake a grid for positioning
+    //         var grid = {_top:offsets.top, _left:offsets.left, _right:offsets.right, _bottom:this._plotDimensions.height - offsets.bottom};        
+    //         if (this.placement == 'insideGrid') {
+    //             switch (this.location) {
+    //                 case 'nw':
+    //                     var a = grid._left + this.xoffset;
+    //                     var b = grid._top + this.yoffset;
+    //                     this._elem.css('left', a);
+    //                     this._elem.css('top', b);
+    //                     break;
+    //                 case 'n':
+    //                     var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+    //                     var b = grid._top + this.yoffset;
+    //                     this._elem.css('left', a);
+    //                     this._elem.css('top', b);
+    //                     break;
+    //                 case 'ne':
+    //                     var a = offsets.right + this.xoffset;
+    //                     var b = grid._top + this.yoffset;
+    //                     this._elem.css({right:a, top:b});
+    //                     break;
+    //                 case 'e':
+    //                     var a = offsets.right + this.xoffset;
+    //                     var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+    //                     this._elem.css({right:a, top:b});
+    //                     break;
+    //                 case 'se':
+    //                     var a = offsets.right + this.xoffset;
+    //                     var b = offsets.bottom + this.yoffset;
+    //                     this._elem.css({right:a, bottom:b});
+    //                     break;
+    //                 case 's':
+    //                     var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+    //                     var b = offsets.bottom + this.yoffset;
+    //                     this._elem.css({left:a, bottom:b});
+    //                     break;
+    //                 case 'sw':
+    //                     var a = grid._left + this.xoffset;
+    //                     var b = offsets.bottom + this.yoffset;
+    //                     this._elem.css({left:a, bottom:b});
+    //                     break;
+    //                 case 'w':
+    //                     var a = grid._left + this.xoffset;
+    //                     var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+    //                     this._elem.css({left:a, top:b});
+    //                     break;
+    //                 default:  // same as 'se'
+    //                     var a = grid._right - this.xoffset;
+    //                     var b = grid._bottom + this.yoffset;
+    //                     this._elem.css({right:a, bottom:b});
+    //                     break;
+    //             }
+    //             
+    //         }
+    //         else {
+    //             switch (this.location) {
+    //                 case 'nw':
+    //                     var a = this._plotDimensions.width - grid._left + this.xoffset;
+    //                     var b = grid._top + this.yoffset;
+    //                     this._elem.css('right', a);
+    //                     this._elem.css('top', b);
+    //                     break;
+    //                 case 'n':
+    //                     var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+    //                     var b = this._plotDimensions.height - grid._top + this.yoffset;
+    //                     this._elem.css('left', a);
+    //                     this._elem.css('bottom', b);
+    //                     break;
+    //                 case 'ne':
+    //                     var a = this._plotDimensions.width - offsets.right + this.xoffset;
+    //                     var b = grid._top + this.yoffset;
+    //                     this._elem.css({left:a, top:b});
+    //                     break;
+    //                 case 'e':
+    //                     var a = this._plotDimensions.width - offsets.right + this.xoffset;
+    //                     var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+    //                     this._elem.css({left:a, top:b});
+    //                     break;
+    //                 case 'se':
+    //                     var a = this._plotDimensions.width - offsets.right + this.xoffset;
+    //                     var b = offsets.bottom + this.yoffset;
+    //                     this._elem.css({left:a, bottom:b});
+    //                     break;
+    //                 case 's':
+    //                     var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+    //                     var b = this._plotDimensions.height - offsets.bottom + this.yoffset;
+    //                     this._elem.css({left:a, top:b});
+    //                     break;
+    //                 case 'sw':
+    //                     var a = this._plotDimensions.width - grid._left + this.xoffset;
+    //                     var b = offsets.bottom + this.yoffset;
+    //                     this._elem.css({right:a, bottom:b});
+    //                     break;
+    //                 case 'w':
+    //                     var a = this._plotDimensions.width - grid._left + this.xoffset;
+    //                     var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+    //                     this._elem.css({right:a, top:b});
+    //                     break;
+    //                 default:  // same as 'se'
+    //                     var a = grid._right - this.xoffset;
+    //                     var b = grid._bottom + this.yoffset;
+    //                     this._elem.css({right:a, bottom:b});
+    //                     break;
+    //             }
+    //         }
+    //     } 
+    // };
+    
+    // setup default renderers for axes and legend so user doesn't have to
+    // called with scope of plot
+    function preInit(target, data, options) {
+        options = options || {};
+        options.axesDefaults = options.axesDefaults || {};
+        options.legend = options.legend || {};
+        options.seriesDefaults = options.seriesDefaults || {};
+        // only set these if there is a donut series
+        var setopts = false;
+        if (options.seriesDefaults.renderer == $.jqplot.DonutRenderer) {
+            setopts = true;
+        }
+        else if (options.series) {
+            for (var i=0; i < options.series.length; i++) {
+                if (options.series[i].renderer == $.jqplot.DonutRenderer) {
+                    setopts = true;
+                }
+            }
+        }
+        
+        if (setopts) {
+            options.axesDefaults.renderer = $.jqplot.DonutAxisRenderer;
+            options.legend.renderer = $.jqplot.DonutLegendRenderer;
+            options.legend.preDraw = true;
+            options.seriesDefaults.pointLabels = {show: false};
+        }
+    }
+    
+    // called with scope of plot.
+    function postInit(target, data, options) {
+        // if multiple series, add a reference to the previous one so that
+        // donut rings can nest.
+        for (var i=1; i<this.series.length; i++) {
+            if (!this.series[i]._previousSeries.length){
+                for (var j=0; j<i; j++) {
+                    if (this.series[i].renderer.constructor == $.jqplot.DonutRenderer && this.series[j].renderer.constructor == $.jqplot.DonutRenderer) {
+                        this.series[i]._previousSeries.push(this.series[j]);
+                    }
+                }
+            }
+        }
+        for (i=0; i<this.series.length; i++) {
+            if (this.series[i].renderer.constructor == $.jqplot.DonutRenderer) {
+                this.series[i]._numberSeries = this.series.length;
+                // don't allow mouseover and mousedown at same time.
+                if (this.series[i].highlightMouseOver) {
+                    this.series[i].highlightMouseDown = false;
+                }
+            }
+        }
+        this.target.bind('mouseout', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
+    }
+    
+    var postParseOptionsRun = false;
+    // called with scope of plot
+    function postParseOptions(options) {
+        for (var i=0; i<this.series.length; i++) {
+            this.series[i].seriesColors = this.seriesColors;
+            this.series[i].colorGenerator = this.colorGenerator;
+        }
+    }
+    
+    function highlight (plot, sidx, pidx) {
+        var s = plot.series[sidx];
+        var canvas = plot.plugins.donutRenderer.highlightCanvas;
+        canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height);
+        s._highlightedPoint = pidx;
+        plot.plugins.donutRenderer.highlightedSeriesIndex = sidx;
+        s.renderer.drawSlice.call(s, canvas._ctx, s._sliceAngles[pidx][0], s._sliceAngles[pidx][1], s.highlightColors[pidx], false);
+    }
+    
+    function unhighlight (plot) {
+        var canvas = plot.plugins.donutRenderer.highlightCanvas;
+        canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
+        for (var i=0; i<plot.series.length; i++) {
+            plot.series[i]._highlightedPoint = null;
+        }
+        plot.plugins.donutRenderer.highlightedSeriesIndex = null;
+        plot.target.trigger('jqplotDataUnhighlight');
+    }
+ 
+    function handleMove(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var evt1 = jQuery.Event('jqplotDataMouseOver');
+            evt1.pageX = ev.pageX;
+            evt1.pageY = ev.pageY;
+            plot.target.trigger(evt1, ins);
+            if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.donutRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
+                var evt = jQuery.Event('jqplotDataHighlight');
+                evt.pageX = ev.pageX;
+                evt.pageY = ev.pageY;
+                plot.target.trigger(evt, ins);
+                highlight (plot, ins[0], ins[1]);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    } 
+    
+    function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.donutRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
+                var evt = jQuery.Event('jqplotDataHighlight');
+                evt.pageX = ev.pageX;
+                evt.pageY = ev.pageY;
+                plot.target.trigger(evt, ins);
+                highlight (plot, ins[0], ins[1]);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    }
+    
+    function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
+        var idx = plot.plugins.donutRenderer.highlightedSeriesIndex;
+        if (idx != null && plot.series[idx].highlightMouseDown) {
+            unhighlight(plot);
+        }
+    }
+    
+    function handleClick(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var evt = jQuery.Event('jqplotDataClick');
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            plot.target.trigger(evt, ins);
+        }
+    }
+    
+    function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var idx = plot.plugins.donutRenderer.highlightedSeriesIndex;
+            if (idx != null && plot.series[idx].highlightMouseDown) {
+                unhighlight(plot);
+            }
+            var evt = jQuery.Event('jqplotDataRightClick');
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            plot.target.trigger(evt, ins);
+        }
+    }    
+    
+    // called within context of plot
+    // create a canvas which we can draw on.
+    // insert it before the eventCanvas, so eventCanvas will still capture events.
+    function postPlotDraw() {
+        this.plugins.donutRenderer = {highlightedSeriesIndex:null};
+        this.plugins.donutRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
+        // do we have any data labels?  if so, put highlight canvas before those
+        // Fix for broken jquery :first selector with canvas (VML) elements.
+        var labels = $(this.targetId+' .jqplot-data-label');
+        if (labels.length) {
+            $(labels[0]).before(this.plugins.donutRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-donutRenderer-highlight-canvas', this._plotDimensions));
+        }
+        // else put highlight canvas before event canvas.
+        else {
+            this.eventCanvas._elem.before(this.plugins.donutRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-donutRenderer-highlight-canvas', this._plotDimensions));
+        }
+        var hctx = this.plugins.donutRenderer.highlightCanvas.setContext();
+    }
+    
+    $.jqplot.preInitHooks.push(preInit);
+    
+    $.jqplot.DonutTickRenderer = function() {
+        $.jqplot.AxisTickRenderer.call(this);
+    };
+    
+    $.jqplot.DonutTickRenderer.prototype = new $.jqplot.AxisTickRenderer();
+    $.jqplot.DonutTickRenderer.prototype.constructor = $.jqplot.DonutTickRenderer;
+    
+})(jQuery);
+    
+    
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.dragable.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.dragable.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.dragable.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,206 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {
+    
+    /**
+     * Class: $.jqplot.Dragable
+     * Plugin to make plotted points dragable by the user.
+     */
+    $.jqplot.Dragable = function(options) {
+        // Group: Properties
+        this.markerRenderer = new $.jqplot.MarkerRenderer({shadow:false});
+        this.shapeRenderer = new $.jqplot.ShapeRenderer();
+        this.isDragging = false;
+        this.isOver = false;
+        this._ctx;
+        this._elem;
+        this._point;
+        this._gridData;
+        // prop: color
+        // CSS color spec for the dragged point (and adjacent line segment or bar).
+        this.color;
+        // prop: constrainTo
+        // Constrain dragging motion to an axis or to none.
+        // Allowable values are 'none', 'x', 'y'
+        this.constrainTo = 'none';  // 'x', 'y', or 'none';
+        $.extend(true, this, options);
+    };
+    
+    function DragCanvas() {
+        $.jqplot.GenericCanvas.call(this);
+        this.isDragging = false;
+        this.isOver = false;
+        this._neighbor;
+        this._cursors = [];
+    }
+    
+    DragCanvas.prototype = new $.jqplot.GenericCanvas();
+    DragCanvas.prototype.constructor = DragCanvas;
+    
+    
+    // called within scope of series
+    $.jqplot.Dragable.parseOptions = function (defaults, opts) {
+        var options = opts || {};
+        this.plugins.dragable = new $.jqplot.Dragable(options.dragable);
+        // since this function is called before series options are parsed,
+        // we can set this here and it will be overridden if needed.
+        this.isDragable = $.jqplot.config.enablePlugins;
+    };
+    
+    // called within context of plot
+    // create a canvas which we can draw on.
+    // insert it before the eventCanvas, so eventCanvas will still capture events.
+    // add a new DragCanvas object to the plot plugins to handle drawing on this new canvas.
+    $.jqplot.Dragable.postPlotDraw = function() {
+        this.plugins.dragable = {previousCursor:'auto', isOver:false};
+        this.plugins.dragable.dragCanvas = new DragCanvas();
+        
+        this.eventCanvas._elem.before(this.plugins.dragable.dragCanvas.createElement(this._gridPadding, 'jqplot-dragable-canvas', this._plotDimensions));
+        var dctx = this.plugins.dragable.dragCanvas.setContext();
+    };
+    
+    //$.jqplot.preInitHooks.push($.jqplot.Dragable.init);
+    $.jqplot.preParseSeriesOptionsHooks.push($.jqplot.Dragable.parseOptions);
+    $.jqplot.postDrawHooks.push($.jqplot.Dragable.postPlotDraw);
+    $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMove]);
+    $.jqplot.eventListenerHooks.push(['jqplotMouseDown', handleDown]);
+    $.jqplot.eventListenerHooks.push(['jqplotMouseUp', handleUp]);
+
+    
+    function initDragPoint(plot, neighbor) {
+        var s = plot.series[neighbor.seriesIndex];
+        var drag = s.plugins.dragable;
+        
+        // first, init the mark renderer for the dragged point
+        var smr = s.markerRenderer;
+        var mr = drag.markerRenderer;
+        mr.style = smr.style;
+        mr.lineWidth = smr.lineWidth + 2.5;
+        mr.size = smr.size + 5;
+        if (!drag.color) {
+            var rgba = $.jqplot.getColorComponents(smr.color);
+            var newrgb = [rgba[0], rgba[1], rgba[2]];
+            var alpha = (rgba[3] >= 0.6) ? rgba[3]*0.6 : rgba[3]*(2-rgba[3]);
+            drag.color = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+alpha+')';
+        }
+        mr.color = drag.color;
+        mr.init();
+
+        var start = (neighbor.pointIndex > 0) ? neighbor.pointIndex - 1 : 0;
+        var end = neighbor.pointIndex+2;
+        drag._gridData = s.gridData.slice(start, end);
+    }
+    
+    function handleMove(ev, gridpos, datapos, neighbor, plot) {
+        if (plot.plugins.dragable.dragCanvas.isDragging) {
+            var dc = plot.plugins.dragable.dragCanvas;
+            var dp = dc._neighbor;
+            var s = plot.series[dp.seriesIndex];
+            var drag = s.plugins.dragable;
+            var gd = s.gridData;
+            
+            // compute the new grid position with any constraints.
+            var x = (drag.constrainTo == 'y') ? dp.gridData[0] : gridpos.x;
+            var y = (drag.constrainTo == 'x') ? dp.gridData[1] : gridpos.y;
+            
+            // compute data values for any listeners.
+            var xu = s._xaxis.series_p2u(x);
+            var yu = s._yaxis.series_p2u(y);
+            
+            // clear the canvas then redraw effect at new position.
+            var ctx = dc._ctx;
+            ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+            
+            // adjust our gridData for the new mouse position
+            if (dp.pointIndex > 0) {
+                drag._gridData[1] = [x, y];
+            }
+            else {
+                drag._gridData[0] = [x, y];
+            }
+            plot.series[dp.seriesIndex].draw(dc._ctx, {gridData:drag._gridData, shadow:false, preventJqPlotSeriesDrawTrigger:true, color:drag.color, markerOptions:{color:drag.color, shadow:false}, trendline:{show:false}});
+            plot.target.trigger('jqplotSeriesPointChange', [dp.seriesIndex, dp.pointIndex, [xu,yu], [x,y]]);
+        }
+        else if (neighbor != null) {
+            var series = plot.series[neighbor.seriesIndex];
+            if (series.isDragable) {
+                var dc = plot.plugins.dragable.dragCanvas;
+                if (!dc.isOver) {
+                    dc._cursors.push(ev.target.style.cursor);
+                    ev.target.style.cursor = "pointer";
+                }
+                dc.isOver = true;
+            }
+        }
+        else if (neighbor == null) {
+            var dc = plot.plugins.dragable.dragCanvas;
+            if (dc.isOver) {
+                ev.target.style.cursor = dc._cursors.pop();
+                dc.isOver = false;
+            }
+        }
+    }
+    
+    function handleDown(ev, gridpos, datapos, neighbor, plot) {
+        var dc = plot.plugins.dragable.dragCanvas;
+        dc._cursors.push(ev.target.style.cursor);
+        if (neighbor != null) {
+            var s = plot.series[neighbor.seriesIndex];
+            var drag = s.plugins.dragable;
+            if (s.isDragable && !dc.isDragging) {
+                dc._neighbor = neighbor;
+                dc.isDragging = true;
+                initDragPoint(plot, neighbor);
+                drag.markerRenderer.draw(s.gridData[neighbor.pointIndex][0], s.gridData[neighbor.pointIndex][1], dc._ctx);
+                ev.target.style.cursor = "move";
+                plot.target.trigger('jqlotDragStart', [neighbor.seriesIndex, neighbor.pointIndex, gridpos, datapos]);
+            }
+        }
+        // Just in case of a hickup, we'll clear the drag canvas and reset.
+        else {
+           var ctx = dc._ctx;
+           ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+           dc.isDragging = false;
+        }
+    }
+    
+    function handleUp(ev, gridpos, datapos, neighbor, plot) {
+        if (plot.plugins.dragable.dragCanvas.isDragging) {
+            var dc = plot.plugins.dragable.dragCanvas;
+            // clear the canvas
+            var ctx = dc._ctx;
+            ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+            dc.isDragging = false;
+            // redraw the series canvas at the new point.
+            var dp = dc._neighbor;
+            var s = plot.series[dp.seriesIndex];
+            var drag = s.plugins.dragable;
+            // compute the new grid position with any constraints.
+            var x = (drag.constrainTo == 'y') ? dp.data[0] : datapos[s.xaxis];
+            var y = (drag.constrainTo == 'x') ? dp.data[1] : datapos[s.yaxis];
+            // var x = datapos[s.xaxis];
+            // var y = datapos[s.yaxis];
+            s.data[dp.pointIndex][0] = x;
+            s.data[dp.pointIndex][1] = y;
+            plot.drawSeries({preventJqPlotSeriesDrawTrigger:true}, dp.seriesIndex);
+            dc._neighbor = null;
+            ev.target.style.cursor = dc._cursors.pop();
+            plot.target.trigger('jqlotDragStop', [gridpos, datapos]);
+        }
+    }
+})(jQuery);
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.enhancedLegendRenderer.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.enhancedLegendRenderer.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.enhancedLegendRenderer.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,186 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {
+    // class $.jqplot.EnhancedLegendRenderer
+    // Legend renderer which can specify the number of rows and/or columns in the legend.
+    $.jqplot.EnhancedLegendRenderer = function(){
+        $.jqplot.TableLegendRenderer.call(this);
+    };
+    
+    $.jqplot.EnhancedLegendRenderer.prototype = new $.jqplot.TableLegendRenderer();
+    $.jqplot.EnhancedLegendRenderer.prototype.constructor = $.jqplot.EnhancedLegendRenderer;
+    
+    // called with scope of legend.
+    $.jqplot.EnhancedLegendRenderer.prototype.init = function(options) {
+        // prop: numberRows
+        // Maximum number of rows in the legend.  0 or null for unlimited.
+        this.numberRows = null;
+        // prop: numberColumns
+        // Maximum number of columns in the legend.  0 or null for unlimited.
+        this.numberColumns = null;
+        // prop: seriesToggle
+        // false to not enable series on/off toggling on the legend.
+        // true or a fadein/fadeout speed (number of milliseconds or 'fast', 'normal', 'slow') 
+        // to enable show/hide of series on click of legend item.
+        this.seriesToggle = 'normal';
+        // prop: disableIEFading
+        // true to toggle series with a show/hide method only and not allow fading in/out.  
+        // This is to overcome poor performance of fade in some versions of IE.
+        this.disableIEFading = true;
+        $.extend(true, this, options);
+        
+        if (this.seriesToggle) {
+            $.jqplot.postDrawHooks.push(postDraw);
+        }
+    };
+    
+    // called with scope of legend
+    $.jqplot.EnhancedLegendRenderer.prototype.draw = function() {
+        var legend = this;
+        if (this.show) {
+            var series = this._series;
+            var ss = 'position:absolute;';
+            ss += (this.background) ? 'background:'+this.background+';' : '';
+            ss += (this.border) ? 'border:'+this.border+';' : '';
+            ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
+            ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : '';
+            ss += (this.textColor) ? 'color:'+this.textColor+';' : '';
+            ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : '';
+            ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : '';
+            ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : '';
+            ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : '';
+            this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>');
+            if (this.seriesToggle) {
+                this._elem.css('z-index', '3');
+            }
+        
+            var pad = false, 
+                reverse = false,
+                nr, nc;
+            if (this.numberRows) {
+                nr = this.numberRows;
+                if (!this.numberColumns){
+                    nc = Math.ceil(series.length/nr);
+                }
+                else{
+                    nc = this.numberColumns;
+                }
+            }
+            else if (this.numberColumns) {
+                nc = this.numberColumns;
+                nr = Math.ceil(series.length/this.numberColumns);
+            }
+            else {
+                nr = series.length;
+                nc = 1;
+            }
+                
+            var i, j, tr, td1, td2, lt, rs;
+            var idx = 0;
+            // check to see if we need to reverse
+            for (i=series.length-1; i>=0; i--) {
+                if (series[i]._stack || series[i].renderer.constructor == $.jqplot.BezierCurveRenderer){
+                    reverse = true;
+                }
+            }    
+                
+            for (i=0; i<nr; i++) {
+                if (reverse){
+                    tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem);
+                }
+                else{
+                    tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem);
+                }
+                for (j=0; j<nc; j++) {
+                    if (idx < series.length && series[idx].show && series[idx].showLabel){
+                        s = series[idx];
+                        lt = this.labels[idx] || s.label.toString();
+                        if (lt) {
+                            var color = s.color;
+                            if (!reverse){
+                                if (i>0){
+                                    pad = true;
+                                }
+                                else{
+                                    pad = false;
+                                }
+                            }
+                            else{
+                                if (i == nr -1){
+                                    pad = false;
+                                }
+                                else{
+                                    pad = true;
+                                }
+                            }
+                            rs = (pad) ? this.rowSpacing : '0';
+                    
+                            td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+
+                                '<div><div class="jqplot-table-legend-swatch" style="background-color:'+color+';border-color:'+color+';"></div>'+
+                                '</div></td>');
+                            td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
+                            if (this.escapeHtml){
+                                td2.text(lt);
+                            }
+                            else {
+                                td2.html(lt);
+                            }
+                            if (reverse) {
+                                if (this.showLabels) {td2.prependTo(tr);}
+                                if (this.showSwatches) {td1.prependTo(tr);}
+                            }
+                            else {
+                                if (this.showSwatches) {td1.appendTo(tr);}
+                                if (this.showLabels) {td2.appendTo(tr);}
+                            }
+                            
+                            if (this.seriesToggle) {
+                                var speed;
+                                if (typeof(this.seriesToggle) == 'string' || typeof(this.seriesToggle) == 'number') {
+                                    if (!$.browser.msie || !this.disableIEFading) {
+                                        speed = this.seriesToggle;
+                                    }
+                                } 
+                                if (this.showSwatches) {
+                                    td1.bind('click', {series:s, speed:speed}, s.toggleDisplay);
+                                    td1.addClass('jqplot-seriesToggle');
+                                }
+                                if (this.showLabels)  {
+                                    td2.bind('click', {series:s, speed:speed}, s.toggleDisplay);
+                                    td2.addClass('jqplot-seriesToggle');
+                                }
+                            }
+                            
+                            pad = true;
+                        }
+                    }
+                    idx++;
+                }   
+            }
+        }
+        return this._elem;
+    };
+    
+    // called with scope of plot.
+    postDraw = function () {
+        if (this.legend.renderer.constructor == $.jqplot.EnhancedLegendRenderer && this.legend.seriesToggle){
+            var e = this.legend._elem.detach();
+            this.eventCanvas._elem.after(e);
+        }
+    };
+})(jQuery);
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.funnelRenderer.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.funnelRenderer.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.funnelRenderer.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,919 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {
+    /**
+     * Class: $.jqplot.FunnelRenderer
+     * Plugin renderer to draw a funnel chart.
+     * x values, if present, will be used as labels.
+     * y values give area size.
+     * 
+     * Funnel charts will draw a single series
+     * only.
+     * 
+     * To use this renderer, you need to include the 
+     * funnel renderer plugin, for example:
+     * 
+     * > <script type="text/javascript" src="plugins/jqplot.funnelRenderer.js"></script>
+     * 
+     * Properties described here are passed into the $.jqplot function
+     * as options on the series renderer.  For example:
+     * 
+     * > plot2 = $.jqplot('chart2', [s1, s2], {
+     * >     seriesDefaults: {
+     * >         renderer:$.jqplot.FunnelRenderer,
+     * >         rendererOptions:{
+     * >              sectionMargin: 12,
+     * >              widthRatio: 0.3
+     * >          }
+     * >      }
+     * > });
+     * 
+     * IMPORTANT
+     * 
+     * *The funnel renderer will reorder data in descending order* so the largest value in
+     * the data set is first and displayed on top of the funnel.  Data will then
+     * be displayed in descending order down the funnel.  The area of each funnel
+     * section will correspond to the value of each data point relative to the sum
+     * of all values.  That is section area is proportional to section value divided by 
+     * sum of all section values.
+     * 
+     * If your data is not in descending order when passed into the plot, *it will be
+     * reordered* when stored in the series.data property.  A copy of the unordered
+     * data is kept in the series._unorderedData property.
+     * 
+     * A funnel plot will trigger events on the plot target
+     * according to user interaction.  All events return the event object,
+     * the series index, the point (section) index, and the point data for 
+     * the appropriate section. *Note* the point index will referr to the ordered
+     * data, not the original unordered data.
+     * 
+     * 'jqplotDataMouseOver' - triggered when mousing over a section.
+     * 'jqplotDataHighlight' - triggered the first time user mouses over a section,
+     * if highlighting is enabled.
+     * 'jqplotDataUnhighlight' - triggered when a user moves the mouse out of
+     * a highlighted section.
+     * 'jqplotDataClick' - triggered when the user clicks on a section.
+     * 'jqplotDataRightClick' - tiggered when the user right clicks on a section if
+     * the "captureRightClick" option is set to true on the plot.
+     */
+    $.jqplot.FunnelRenderer = function(){
+        $.jqplot.LineRenderer.call(this);
+    };
+    
+    $.jqplot.FunnelRenderer.prototype = new $.jqplot.LineRenderer();
+    $.jqplot.FunnelRenderer.prototype.constructor = $.jqplot.FunnelRenderer;
+    
+    // called with scope of a series
+    $.jqplot.FunnelRenderer.prototype.init = function(options, plot) {
+        // Group: Properties
+        //
+        // prop: padding
+        // padding between the funnel and plot edges, legend, etc.
+        this.padding = {top: 20, right: 20, bottom: 20, left: 20};
+        // prop: sectionMargin
+        // spacing between funnel sections in pixels.
+        this.sectionMargin = 6;
+        // prop: fill
+        // true or false, wether to fill the areas.
+        this.fill = true;
+        // prop: shadowOffset
+        // offset of the shadow from the area and offset of 
+        // each succesive stroke of the shadow from the last.
+        this.shadowOffset = 2;
+        // prop: shadowAlpha
+        // transparency of the shadow (0 = transparent, 1 = opaque)
+        this.shadowAlpha = 0.07;
+        // prop: shadowDepth
+        // number of strokes to apply to the shadow, 
+        // each stroke offset shadowOffset from the last.
+        this.shadowDepth = 5;
+        // prop: highlightMouseOver
+        // True to highlight area when moused over.
+        // This must be false to enable highlightMouseDown to highlight when clicking on a area.
+        this.highlightMouseOver = true;
+        // prop: highlightMouseDown
+        // True to highlight when a mouse button is pressed over a area.
+        // This will be disabled if highlightMouseOver is true.
+        this.highlightMouseDown = false;
+        // prop: highlightColors
+        // array of colors to use when highlighting an area.
+        this.highlightColors = [];
+        // prop: widthRatio
+        // The ratio of the width of the top of the funnel to the bottom.
+        // a ratio of 0 will make an upside down pyramid. 
+        this.widthRatio = 0.2;
+        // prop: lineWidth
+        // width of line if areas are stroked and not filled.
+        this.lineWidth = 2;
+        // prop: dataLabels
+        // Either 'label', 'value', 'percent' or an array of labels to place on the pie slices.
+        // Defaults to percentage of each pie slice.
+        this.dataLabels = 'percent';
+        // prop: showDataLabels
+        // true to show data labels on slices.
+        this.showDataLabels = false;
+        // prop: dataLabelFormatString
+        // Format string for data labels.  If none, '%s' is used for "label" and for arrays, '%d' for value and '%d%%' for percentage.
+        this.dataLabelFormatString = null;
+        // prop: dataLabelThreshold
+        // Threshhold in percentage (0 - 100) of pie area, below which no label will be displayed.
+        // This applies to all label types, not just to percentage labels.
+        this.dataLabelThreshold = 3;
+        
+        this.tickRenderer = $.jqplot.FunnelTickRenderer;
+        
+        // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
+        if (options.highlightMouseDown && options.highlightMouseOver == null) {
+            options.highlightMouseOver = false;
+        }
+        
+        $.extend(true, this, options);
+        
+        // index of the currenty highlighted point, if any
+        this._highlightedPoint = null;
+        
+        // lengths of bases, or horizontal sides of areas of trapezoid.
+        this._bases = [];
+        // total area
+        this._atot;
+        // areas of segments.
+        this._areas = [];
+        // vertical lengths of segments.
+        this._lengths = [];
+        // angle of the funnel to vertical.
+        this._angle;
+        this._dataIndices = [];
+        
+        // sort data
+        this._unorderedData = $.extend(true, [], this.data);
+        var idxs = $.extend(true, [], this.data);
+        for (var i=0; i<idxs.length; i++) {
+            idxs[i].push(i);
+        }
+        this.data.sort( function (a, b) { return b[1] - a[1]; } );
+        idxs.sort( function (a, b) { return b[1] - a[1]; });
+        for (var i=0; i<idxs.length; i++) {
+            this._dataIndices.push(idxs[i][2]);
+        }
+        
+        // set highlight colors if none provided
+        if (this.highlightColors.length == 0) {
+            for (var i=0; i<this.seriesColors.length; i++){
+                var rgba = $.jqplot.getColorComponents(this.seriesColors[i]);
+                var newrgb = [rgba[0], rgba[1], rgba[2]];
+                var sum = newrgb[0] + newrgb[1] + newrgb[2];
+                for (var j=0; j<3; j++) {
+                    // when darkening, lowest color component can be is 60.
+                    newrgb[j] = (sum > 570) ?  newrgb[j] * 0.8 : newrgb[j] + 0.4 * (255 - newrgb[j]);
+                    newrgb[j] = parseInt(newrgb[j], 10);
+                }
+                this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')');
+            }
+        }
+
+        plot.postParseOptionsHooks.addOnce(postParseOptions);
+        plot.postInitHooks.addOnce(postInit);
+        plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
+        plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown);
+        plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp);
+        plot.eventListenerHooks.addOnce('jqplotClick', handleClick);
+        plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick);
+        plot.postDrawHooks.addOnce(postPlotDraw);        
+        
+    };
+    
+    // gridData will be of form [label, percentage of total]
+    $.jqplot.FunnelRenderer.prototype.setGridData = function(plot) {
+        // set gridData property.  This will hold angle in radians of each data point.
+        var sum = 0;
+        var td = [];
+        for (var i=0; i<this.data.length; i++){
+            sum += this.data[i][1];
+            td.push([this.data[i][0], this.data[i][1]]);
+        }
+        
+        // normalize y values, so areas are proportional.
+        for (var i=0; i<td.length; i++) {
+            td[i][1] = td[i][1]/sum;
+        }
+        
+        this._bases = new Array(td.length + 1);
+        this._lengths = new Array(td.length);
+        
+        this.gridData = td;
+    };
+    
+    $.jqplot.FunnelRenderer.prototype.makeGridData = function(data, plot) {
+        // set gridData property.  This will hold angle in radians of each data point.
+        var sum = 0;
+        var td = [];
+        for (var i=0; i<this.data.length; i++){
+            sum += this.data[i][1];
+            td.push([this.data[i][0], this.data[i][1]]);
+        }
+        
+        // normalize y values, so areas are proportional.
+        for (var i=0; i<td.length; i++) {
+            td[i][1] = td[i][1]/sum;
+        }
+        
+        this._bases = new Array(td.length + 1);
+        this._lengths = new Array(td.length);
+        
+        return td;
+    };
+    
+    $.jqplot.FunnelRenderer.prototype.drawSection = function (ctx, vertices, color, isShadow) {
+        var fill = this.fill;
+        var lineWidth = this.lineWidth;
+        ctx.save();
+        
+        if (isShadow) {
+            for (var i=0; i<this.shadowDepth; i++) {
+                ctx.save();
+                ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));
+                doDraw();
+            }
+        }
+        
+        else {
+            doDraw();
+        }
+        
+        function doDraw () {
+            ctx.beginPath();  
+            ctx.fillStyle = color;
+            ctx.strokeStyle = color;
+            ctx.lineWidth = lineWidth;
+            ctx.moveTo(vertices[0][0], vertices[0][1]);
+            for (var i=1; i<4; i++) {
+                ctx.lineTo(vertices[i][0], vertices[i][1]);
+            }
+            ctx.closePath();
+            if (fill) {
+                ctx.fill();
+            }
+            else {
+                ctx.stroke();
+            }
+        }
+        
+        if (isShadow) {
+            for (var i=0; i<this.shadowDepth; i++) {
+                ctx.restore();
+            }
+        }
+        
+        ctx.restore();
+    };
+    
+    // called with scope of series
+    $.jqplot.FunnelRenderer.prototype.draw = function (ctx, gd, options, plot) {
+        var i;
+        var opts = (options != undefined) ? options : {};
+        // offset and direction of offset due to legend placement
+        var offx = 0;
+        var offy = 0;
+        var trans = 1;
+        this._areas = [];
+        // var colorGenerator = new this.colorGenerator(this.seriesColors);
+        if (options.legendInfo && options.legendInfo.placement == 'insideGrid') {
+            var li = options.legendInfo;
+            switch (li.location) {
+                case 'nw':
+                    offx = li.width + li.xoffset;
+                    break;
+                case 'w':
+                    offx = li.width + li.xoffset;
+                    break;
+                case 'sw':
+                    offx = li.width + li.xoffset;
+                    break;
+                case 'ne':
+                    offx = li.width + li.xoffset;
+                    trans = -1;
+                    break;
+                case 'e':
+                    offx = li.width + li.xoffset;
+                    trans = -1;
+                    break;
+                case 'se':
+                    offx = li.width + li.xoffset;
+                    trans = -1;
+                    break;
+                case 'n':
+                    offy = li.height + li.yoffset;
+                    break;
+                case 's':
+                    offy = li.height + li.yoffset;
+                    trans = -1;
+                    break;
+                default:
+                    break;
+            }
+        }
+        
+        var loff = (trans==1) ? this.padding.left + offx : this.padding.left;
+        var toff = (trans==1) ? this.padding.top + offy : this.padding.top;
+        var roff = (trans==-1) ? this.padding.right + offx : this.padding.right;
+        var boff = (trans==-1) ? this.padding.bottom + offy : this.padding.bottom;
+        
+        var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
+        var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
+        var fill = (opts.fill != undefined) ? opts.fill : this.fill;
+        var cw = ctx.canvas.width;
+        var ch = ctx.canvas.height;
+        this._bases[0] = cw - loff - roff;
+        var ltot = this._length = ch - toff - boff;
+
+        var hend = this._bases[0]*this.widthRatio;
+        this._atot = ltot/2 * (this._bases[0] + this._bases[0]*this.widthRatio);
+
+        this._angle = Math.atan((this._bases[0] - hend)/2/ltot);
+
+        for (i=0; i<gd.length; i++) {
+            this._areas.push(gd[i][1] * this._atot);
+        }
+
+        
+        var guess, err, count, lsum=0;
+        var tolerance = 0.0001;
+
+        for (i=0; i<this._areas.length; i++) {
+            guess = this._areas[i]/this._bases[i];
+            err = 999999;
+            this._lengths[i] = guess;
+            count = 0;
+            while (err > this._lengths[i]*tolerance && count < 100) {
+                this._lengths[i] = this._areas[i]/(this._bases[i] - this._lengths[i] * Math.tan(this._angle));
+                err = Math.abs(this._lengths[i] - guess);
+                this._bases[i+1] = this._bases[i] - (2*this._lengths[i]*Math.tan(this._angle));
+                guess = this._lengths[i];
+                count++;
+            }
+            lsum += this._lengths[i];
+        }
+        
+        // figure out vertices of each section
+        this._vertices = new Array(gd.length);
+        
+        // these are 4 coners of entire trapezoid
+        var p0 = [loff, toff],
+            p1 = [loff+this._bases[0], toff],
+            p2 = [loff + (this._bases[0] - this._bases[this._bases.length-1])/2, toff + this._length],
+            p3 = [p2[0] + this._bases[this._bases.length-1], p2[1]];
+            
+        // equations of right and left sides, returns x, y values given height of section (y value)
+        function findleft (l) {
+            var m = (p0[1] - p2[1])/(p0[0] - p2[0]);
+            var b = p0[1] - m*p0[0];
+            var y = l + p0[1];
+            
+            return [(y - b)/m, y];
+        }
+        
+        function findright (l) {
+            var m = (p1[1] - p3[1])/(p1[0] - p3[0]);
+            var b = p1[1] - m*p1[0];
+            var y = l + p1[1];
+            
+            return [(y - b)/m, y];
+        }
+        
+        var x = offx, y = offy;
+        var h=0, adj=0;
+        
+        for (i=0; i<gd.length; i++) {
+            this._vertices[i] = new Array();
+            var v = this._vertices[i];
+            var sm = this.sectionMargin;
+            if (i == 0) {
+                adj = 0;
+            }
+            if (i == 1) {
+                adj = sm/3;
+            }
+            else if (i > 0 && i < gd.length-1) {
+                adj = sm/2;
+            }
+            else if (i == gd.length -1) {
+                adj = 2*sm/3;
+            }
+            v.push(findleft(h+adj));
+            v.push(findright(h+adj));
+            h += this._lengths[i];
+            if (i == 0) {
+                adj = -2*sm/3;
+            }
+            else if (i > 0 && i < gd.length-1) {
+                adj = -sm/2;
+            }
+            else if (i == gd.length - 1) {
+                adj = 0;
+            }
+            v.push(findright(h+adj));
+            v.push(findleft(h+adj));
+            
+        }
+
+        if (this.shadow) {
+            var shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')';
+            for (var i=0; i<gd.length; i++) {
+                this.renderer.drawSection.call (this, ctx, this._vertices[i], shadowColor, true);
+            }
+            
+        }
+        for (var i=0; i<gd.length; i++) {
+            var v = this._vertices[i];
+            this.renderer.drawSection.call (this, ctx, v, this.seriesColors[i]);
+            
+            if (this.showDataLabels && gd[i][1]*100 >= this.dataLabelThreshold) {
+                var fstr, label;
+                
+                if (this.dataLabels == 'label') {
+                    fstr = this.dataLabelFormatString || '%s';
+                    label = $.jqplot.sprintf(fstr, gd[i][0]);
+                }
+                else if (this.dataLabels == 'value') {
+                    fstr = this.dataLabelFormatString || '%d';
+                    label = $.jqplot.sprintf(fstr, this.data[i][1]);
+                }
+                else if (this.dataLabels == 'percent') {
+                    fstr = this.dataLabelFormatString || '%d%%';
+                    label = $.jqplot.sprintf(fstr, gd[i][1]*100);
+                }
+                else if (this.dataLabels.constructor == Array) {
+                    fstr = this.dataLabelFormatString || '%s';
+                    label = $.jqplot.sprintf(fstr, this.dataLabels[this._dataIndices[i]]);
+                }
+                
+                var fact = (this._radius ) * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge;
+                
+                var x = (v[0][0] + v[1][0])/2 + this.canvas._offsets.left;
+                var y = (v[1][1] + v[2][1])/2 + this.canvas._offsets.top;
+                
+                var labelelem = $('<span class="jqplot-funnel-series jqplot-data-label" style="position:absolute;">' + label + '</span>').insertBefore(plot.eventCanvas._elem);
+                x -= labelelem.width()/2;
+                y -= labelelem.height()/2;
+                x = Math.round(x);
+                y = Math.round(y);
+                labelelem.css({left: x, top: y});
+            }
+            
+        }
+               
+    };
+    
+    $.jqplot.FunnelAxisRenderer = function() {
+        $.jqplot.LinearAxisRenderer.call(this);
+    };
+    
+    $.jqplot.FunnelAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
+    $.jqplot.FunnelAxisRenderer.prototype.constructor = $.jqplot.FunnelAxisRenderer;
+        
+    
+    // There are no traditional axes on a funnel chart.  We just need to provide
+    // dummy objects with properties so the plot will render.
+    // called with scope of axis object.
+    $.jqplot.FunnelAxisRenderer.prototype.init = function(options){
+        //
+        this.tickRenderer = $.jqplot.FunnelTickRenderer;
+        $.extend(true, this, options);
+        // I don't think I'm going to need _dataBounds here.
+        // have to go Axis scaling in a way to fit chart onto plot area
+        // and provide u2p and p2u functionality for mouse cursor, etc.
+        // for convienence set _dataBounds to 0 and 100 and
+        // set min/max to 0 and 100.
+        this._dataBounds = {min:0, max:100};
+        this.min = 0;
+        this.max = 100;
+        this.showTicks = false;
+        this.ticks = [];
+        this.showMark = false;
+        this.show = false; 
+    };
+    
+    
+    
+    /**
+     * Class: $.jqplot.FunnelLegendRenderer
+     * Legend Renderer specific to funnel plots.  Set by default
+     * when the user creates a funnel plot.
+     */
+    $.jqplot.FunnelLegendRenderer = function(){
+        $.jqplot.TableLegendRenderer.call(this);
+    };
+    
+    $.jqplot.FunnelLegendRenderer.prototype = new $.jqplot.TableLegendRenderer();
+    $.jqplot.FunnelLegendRenderer.prototype.constructor = $.jqplot.FunnelLegendRenderer;
+    
+    $.jqplot.FunnelLegendRenderer.prototype.init = function(options) {
+        // Group: Properties
+        //
+        // prop: numberRows
+        // Maximum number of rows in the legend.  0 or null for unlimited.
+        this.numberRows = null;
+        // prop: numberColumns
+        // Maximum number of columns in the legend.  0 or null for unlimited.
+        this.numberColumns = null;
+        $.extend(true, this, options);
+    };
+    
+    // called with context of legend
+    $.jqplot.FunnelLegendRenderer.prototype.draw = function() {
+        var legend = this;
+        if (this.show) {
+            var series = this._series;
+            var ss = 'position:absolute;';
+            ss += (this.background) ? 'background:'+this.background+';' : '';
+            ss += (this.border) ? 'border:'+this.border+';' : '';
+            ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
+            ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : '';
+            ss += (this.textColor) ? 'color:'+this.textColor+';' : '';
+            ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : '';
+            ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : '';
+            ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : '';
+            ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : '';
+            this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>');
+            // Funnel charts legends don't go by number of series, but by number of data points
+            // in the series.  Refactor things here for that.
+            
+            var pad = false, 
+                reverse = false,
+                nr, nc;
+            var s = series[0];
+            var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors);
+            
+            if (s.show) {
+                var pd = s.data;
+                if (this.numberRows) {
+                    nr = this.numberRows;
+                    if (!this.numberColumns){
+                        nc = Math.ceil(pd.length/nr);
+                    }
+                    else{
+                        nc = this.numberColumns;
+                    }
+                }
+                else if (this.numberColumns) {
+                    nc = this.numberColumns;
+                    nr = Math.ceil(pd.length/this.numberColumns);
+                }
+                else {
+                    nr = pd.length;
+                    nc = 1;
+                }
+                
+                var i, j, tr, td1, td2, lt, rs, color;
+                var idx = 0;    
+                
+                for (i=0; i<nr; i++) {
+                    if (reverse){
+                        tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem);
+                    }
+                    else{
+                        tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem);
+                    }
+                    for (j=0; j<nc; j++) {
+                        if (idx < pd.length){
+                            lt = this.labels[idx] || pd[idx][0].toString();
+                            color = colorGenerator.next();
+                            if (!reverse){
+                                if (i>0){
+                                    pad = true;
+                                }
+                                else{
+                                    pad = false;
+                                }
+                            }
+                            else{
+                                if (i == nr -1){
+                                    pad = false;
+                                }
+                                else{
+                                    pad = true;
+                                }
+                            }
+                            rs = (pad) ? this.rowSpacing : '0';
+                
+                            td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+
+                                '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+
+                                '</div></td>');
+                            td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
+                            if (this.escapeHtml){
+                                td2.text(lt);
+                            }
+                            else {
+                                td2.html(lt);
+                            }
+                            if (reverse) {
+                                td2.prependTo(tr);
+                                td1.prependTo(tr);
+                            }
+                            else {
+                                td1.appendTo(tr);
+                                td2.appendTo(tr);
+                            }
+                            pad = true;
+                        }
+                        idx++;
+                    }   
+                }
+            }
+        }
+        return this._elem;                
+    };
+    
+    // $.jqplot.FunnelLegendRenderer.prototype.pack = function(offsets) {
+    //     if (this.show) {
+    //         // fake a grid for positioning
+    //         var grid = {_top:offsets.top, _left:offsets.left, _right:offsets.right, _bottom:this._plotDimensions.height - offsets.bottom};        
+    //         if (this.placement == 'insideGrid') {
+    //             switch (this.location) {
+    //                 case 'nw':
+    //                     var a = grid._left + this.xoffset;
+    //                     var b = grid._top + this.yoffset;
+    //                     this._elem.css('left', a);
+    //                     this._elem.css('top', b);
+    //                     break;
+    //                 case 'n':
+    //                     var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+    //                     var b = grid._top + this.yoffset;
+    //                     this._elem.css('left', a);
+    //                     this._elem.css('top', b);
+    //                     break;
+    //                 case 'ne':
+    //                     var a = offsets.right + this.xoffset;
+    //                     var b = grid._top + this.yoffset;
+    //                     this._elem.css({right:a, top:b});
+    //                     break;
+    //                 case 'e':
+    //                     var a = offsets.right + this.xoffset;
+    //                     var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+    //                     this._elem.css({right:a, top:b});
+    //                     break;
+    //                 case 'se':
+    //                     var a = offsets.right + this.xoffset;
+    //                     var b = offsets.bottom + this.yoffset;
+    //                     this._elem.css({right:a, bottom:b});
+    //                     break;
+    //                 case 's':
+    //                     var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+    //                     var b = offsets.bottom + this.yoffset;
+    //                     this._elem.css({left:a, bottom:b});
+    //                     break;
+    //                 case 'sw':
+    //                     var a = grid._left + this.xoffset;
+    //                     var b = offsets.bottom + this.yoffset;
+    //                     this._elem.css({left:a, bottom:b});
+    //                     break;
+    //                 case 'w':
+    //                     var a = grid._left + this.xoffset;
+    //                     var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+    //                     this._elem.css({left:a, top:b});
+    //                     break;
+    //                 default:  // same as 'se'
+    //                     var a = grid._right - this.xoffset;
+    //                     var b = grid._bottom + this.yoffset;
+    //                     this._elem.css({right:a, bottom:b});
+    //                     break;
+    //             }
+    //             
+    //         }
+    //         else {
+    //             switch (this.location) {
+    //                 case 'nw':
+    //                     var a = this._plotDimensions.width - grid._left + this.xoffset;
+    //                     var b = grid._top + this.yoffset;
+    //                     this._elem.css('right', a);
+    //                     this._elem.css('top', b);
+    //                     break;
+    //                 case 'n':
+    //                     var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+    //                     var b = this._plotDimensions.height - grid._top + this.yoffset;
+    //                     this._elem.css('left', a);
+    //                     this._elem.css('bottom', b);
+    //                     break;
+    //                 case 'ne':
+    //                     var a = this._plotDimensions.width - offsets.right + this.xoffset;
+    //                     var b = grid._top + this.yoffset;
+    //                     this._elem.css({left:a, top:b});
+    //                     break;
+    //                 case 'e':
+    //                     var a = this._plotDimensions.width - offsets.right + this.xoffset;
+    //                     var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+    //                     this._elem.css({left:a, top:b});
+    //                     break;
+    //                 case 'se':
+    //                     var a = this._plotDimensions.width - offsets.right + this.xoffset;
+    //                     var b = offsets.bottom + this.yoffset;
+    //                     this._elem.css({left:a, bottom:b});
+    //                     break;
+    //                 case 's':
+    //                     var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+    //                     var b = this._plotDimensions.height - offsets.bottom + this.yoffset;
+    //                     this._elem.css({left:a, top:b});
+    //                     break;
+    //                 case 'sw':
+    //                     var a = this._plotDimensions.width - grid._left + this.xoffset;
+    //                     var b = offsets.bottom + this.yoffset;
+    //                     this._elem.css({right:a, bottom:b});
+    //                     break;
+    //                 case 'w':
+    //                     var a = this._plotDimensions.width - grid._left + this.xoffset;
+    //                     var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+    //                     this._elem.css({right:a, top:b});
+    //                     break;
+    //                 default:  // same as 'se'
+    //                     var a = grid._right - this.xoffset;
+    //                     var b = grid._bottom + this.yoffset;
+    //                     this._elem.css({right:a, bottom:b});
+    //                     break;
+    //             }
+    //         }
+    //     } 
+    // };
+    
+    // setup default renderers for axes and legend so user doesn't have to
+    // called with scope of plot
+    function preInit(target, data, options) {
+        options = options || {};
+        options.axesDefaults = options.axesDefaults || {};
+        options.legend = options.legend || {};
+        options.seriesDefaults = options.seriesDefaults || {};
+        // only set these if there is a funnel series
+        var setopts = false;
+        if (options.seriesDefaults.renderer == $.jqplot.FunnelRenderer) {
+            setopts = true;
+        }
+        else if (options.series) {
+            for (var i=0; i < options.series.length; i++) {
+                if (options.series[i].renderer == $.jqplot.FunnelRenderer) {
+                    setopts = true;
+                }
+            }
+        }
+        
+        if (setopts) {
+            options.axesDefaults.renderer = $.jqplot.FunnelAxisRenderer;
+            options.legend.renderer = $.jqplot.FunnelLegendRenderer;
+            options.legend.preDraw = true;
+            options.sortData = false;
+            options.seriesDefaults.pointLabels = {show: false};
+        }
+    }
+    
+    function postInit(target, data, options) {
+        // if multiple series, add a reference to the previous one so that
+        // funnel rings can nest.
+        for (i=0; i<this.series.length; i++) {
+            if (this.series[i].renderer.constructor == $.jqplot.FunnelRenderer) {
+                // don't allow mouseover and mousedown at same time.
+                if (this.series[i].highlightMouseOver) {
+                    this.series[i].highlightMouseDown = false;
+                }
+            }
+        }
+        this.target.bind('mouseout', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
+    }
+    
+    // called with scope of plot
+    function postParseOptions(options) {
+        for (var i=0; i<this.series.length; i++) {
+            this.series[i].seriesColors = this.seriesColors;
+            this.series[i].colorGenerator = this.colorGenerator;
+        }
+    }
+    
+    function highlight (plot, sidx, pidx) {
+        var s = plot.series[sidx];
+        var canvas = plot.plugins.funnelRenderer.highlightCanvas;
+        canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height);
+        s._highlightedPoint = pidx;
+        plot.plugins.funnelRenderer.highlightedSeriesIndex = sidx;
+        s.renderer.drawSection.call(s, canvas._ctx, s._vertices[pidx], s.highlightColors[pidx], false);
+    }
+    
+    function unhighlight (plot) {
+        var canvas = plot.plugins.funnelRenderer.highlightCanvas;
+        canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
+        for (var i=0; i<plot.series.length; i++) {
+            plot.series[i]._highlightedPoint = null;
+        }
+        plot.plugins.funnelRenderer.highlightedSeriesIndex = null;
+        plot.target.trigger('jqplotDataUnhighlight');
+    }
+    
+    function handleMove(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var evt1 = jQuery.Event('jqplotDataMouseOver');
+            evt1.pageX = ev.pageX;
+            evt1.pageY = ev.pageY;
+            plot.target.trigger(evt1, ins);
+            if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.funnelRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
+                var evt = jQuery.Event('jqplotDataHighlight');
+                evt.pageX = ev.pageX;
+                evt.pageY = ev.pageY;
+                plot.target.trigger(evt, ins);
+                highlight (plot, ins[0], ins[1]);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    }
+    
+    function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.funnelRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
+                var evt = jQuery.Event('jqplotDataHighlight');
+                evt.pageX = ev.pageX;
+                evt.pageY = ev.pageY;
+                plot.target.trigger(evt, ins);
+                highlight (plot, ins[0], ins[1]);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    }
+    
+    function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
+        var idx = plot.plugins.funnelRenderer.highlightedSeriesIndex;
+        if (idx != null && plot.series[idx].highlightMouseDown) {
+            unhighlight(plot);
+        }
+    }
+    
+    function handleClick(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var evt = jQuery.Event('jqplotDataClick');
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            plot.target.trigger(evt, ins);
+        }
+    }
+    
+    function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var idx = plot.plugins.funnelRenderer.highlightedSeriesIndex;
+            if (idx != null && plot.series[idx].highlightMouseDown) {
+                unhighlight(plot);
+            }
+            var evt = jQuery.Event('jqplotDataRightClick');
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            plot.target.trigger(evt, ins);
+        }
+    }
+    
+    // called within context of plot
+    // create a canvas which we can draw on.
+    // insert it before the eventCanvas, so eventCanvas will still capture events.
+    function postPlotDraw() {
+        this.plugins.funnelRenderer = {};
+        this.plugins.funnelRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
+        
+        // do we have any data labels?  if so, put highlight canvas before those
+        var labels = $(this.targetId+' .jqplot-data-label');
+        if (labels.length) {
+            $(labels[0]).before(this.plugins.funnelRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-funnelRenderer-highlight-canvas', this._plotDimensions));
+        }
+        // else put highlight canvas before event canvas.
+        else {
+            this.eventCanvas._elem.before(this.plugins.funnelRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-funnelRenderer-highlight-canvas', this._plotDimensions));
+        }
+        var hctx = this.plugins.funnelRenderer.highlightCanvas.setContext();
+    }
+    
+    $.jqplot.preInitHooks.push(preInit);
+    
+    $.jqplot.FunnelTickRenderer = function() {
+        $.jqplot.AxisTickRenderer.call(this);
+    };
+    
+    $.jqplot.FunnelTickRenderer.prototype = new $.jqplot.AxisTickRenderer();
+    $.jqplot.FunnelTickRenderer.prototype.constructor = $.jqplot.FunnelTickRenderer;
+    
+})(jQuery);
+    
+    
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.highlighter.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.highlighter.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.highlighter.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,374 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {
+    $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMove]);
+    
+    /**
+     * Class: $.jqplot.Highlighter
+     * Plugin which will highlight data points when they are moused over.
+     * 
+     * To use this plugin, include the js
+     * file in your source:
+     * 
+     * > <script type="text/javascript" src="plugins/jqplot.highlighter.js"></script>
+     * 
+     * A tooltip providing information about the data point is enabled by default.
+     * To disable the tooltip, set "showTooltip" to false.
+     * 
+     * You can control what data is displayed in the tooltip with various
+     * options.  The "tooltipAxes" option controls wether the x, y or both
+     * data values are displayed.
+     * 
+     * Some chart types (e.g. hi-low-close) have more than one y value per
+     * data point. To display the additional values in the tooltip, set the
+     * "yvalues" option to the desired number of y values present (3 for a hlc chart).
+     * 
+     * By default, data values will be formatted with the same formatting
+     * specifiers as used to format the axis ticks.  A custom format code
+     * can be supplied with the tooltipFormatString option.  This will apply 
+     * to all values in the tooltip.  
+     * 
+     * For more complete control, the "formatString" option can be set.  This
+     * Allows conplete control over tooltip formatting.  Values are passed to
+     * the format string in an order determined by the "tooltipAxes" and "yvalues"
+     * options.  So, if you have a hi-low-close chart and you just want to display 
+     * the hi-low-close values in the tooltip, you could set a formatString like:
+     * 
+     * > highlighter: {
+     * >     tooltipAxes: 'y',
+     * >     yvalues: 3,
+     * >     formatString:'<table class="jqplot-highlighter">
+     * >         <tr><td>hi:</td><td>%s</td></tr>
+     * >         <tr><td>low:</td><td>%s</td></tr>
+     * >         <tr><td>close:</td><td>%s</td></tr></table>'
+     * > }
+     * 
+     */
+    $.jqplot.Highlighter = function(options) {
+        // Group: Properties
+        //
+        //prop: show
+        // true to show the highlight.
+        this.show = $.jqplot.config.enablePlugins;
+        // prop: markerRenderer
+        // Renderer used to draw the marker of the highlighted point.
+        // Renderer will assimilate attributes from the data point being highlighted,
+        // so no attributes need set on the renderer directly.
+        // Default is to turn off shadow drawing on the highlighted point.
+        this.markerRenderer = new $.jqplot.MarkerRenderer({shadow:false});
+        // prop: showMarker
+        // true to show the marker
+        this.showMarker  = true;
+        // prop: lineWidthAdjust
+        // Pixels to add to the lineWidth of the highlight.
+        this.lineWidthAdjust = 2.5;
+        // prop: sizeAdjust
+        // Pixels to add to the overall size of the highlight.
+        this.sizeAdjust = 5;
+        // prop: showTooltip
+        // Show a tooltip with data point values.
+        this.showTooltip = true;
+        // prop: tooltipLocation
+        // Where to position tooltip, 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'
+        this.tooltipLocation = 'nw';
+        // prop: fadeTooltip
+        // true = fade in/out tooltip, flase = show/hide tooltip
+        this.fadeTooltip = true;
+        // prop: tooltipFadeSpeed
+        // 'slow', 'def', 'fast', or number of milliseconds.
+        this.tooltipFadeSpeed = "fast";
+        // prop: tooltipOffset
+        // Pixel offset of tooltip from the highlight.
+        this.tooltipOffset = 2;
+        // prop: tooltipAxes
+        // Which axes to display in tooltip, 'x', 'y' or 'both', 'xy' or 'yx'
+        // 'both' and 'xy' are equivalent, 'yx' reverses order of labels.
+        this.tooltipAxes = 'both';
+        // prop; tooltipSeparator
+        // String to use to separate x and y axes in tooltip.
+        this.tooltipSeparator = ', ';
+        // prop: useAxesFormatters
+        // Use the x and y axes formatters to format the text in the tooltip.
+        this.useAxesFormatters = true;
+        // prop: tooltipFormatString
+        // sprintf format string for the tooltip.
+        // Uses Ash Searle's javascript sprintf implementation
+        // found here: http://hexmen.com/blog/2007/03/printf-sprintf/
+        // See http://perldoc.perl.org/functions/sprintf.html for reference.
+        // Additional "p" and "P" format specifiers added by Chris Leonello.
+        this.tooltipFormatString = '%.5P';
+        // prop: formatString
+        // alternative to tooltipFormatString
+        // will format the whole tooltip text, populating with x, y values as
+        // indicated by tooltipAxes option.  So, you could have a tooltip like:
+        // 'Date: %s, number of cats: %d' to format the whole tooltip at one go.
+        // If useAxesFormatters is true, values will be formatted according to
+        // Axes formatters and you can populate your tooltip string with 
+        // %s placeholders.
+        this.formatString = null;
+        // prop: yvalues
+        // Number of y values to expect in the data point array.
+        // Typically this is 1.  Certain plots, like OHLC, will
+        // have more y values in each data point array.
+        this.yvalues = 1;
+        // prop: bringSeriesToFront
+        // This option requires jQuery 1.4+
+        // True to bring the series of the highlighted point to the front
+        // of other series.
+        this.bringSeriesToFront = false;
+        this._tooltipElem;
+        this.isHighlighting = false;
+
+        $.extend(true, this, options);
+    };
+    
+    // axis.renderer.tickrenderer.formatter
+    
+    // called with scope of plot
+    $.jqplot.Highlighter.init = function (target, data, opts){
+        var options = opts || {};
+        // add a highlighter attribute to the plot
+        this.plugins.highlighter = new $.jqplot.Highlighter(options.highlighter);
+    };
+    
+    // called within scope of series
+    $.jqplot.Highlighter.parseOptions = function (defaults, options) {
+        // Add a showHighlight option to the series 
+        // and set it to true by default.
+        this.showHighlight = true;
+    };
+    
+    // called within context of plot
+    // create a canvas which we can draw on.
+    // insert it before the eventCanvas, so eventCanvas will still capture events.
+    $.jqplot.Highlighter.postPlotDraw = function() {
+        this.plugins.highlighter.highlightCanvas = new $.jqplot.GenericCanvas();
+        
+        this.eventCanvas._elem.before(this.plugins.highlighter.highlightCanvas.createElement(this._gridPadding, 'jqplot-highlight-canvas', this._plotDimensions));
+        var hctx = this.plugins.highlighter.highlightCanvas.setContext();
+        
+        var p = this.plugins.highlighter;
+        p._tooltipElem = $('<div class="jqplot-highlighter-tooltip" style="position:absolute;display:none"></div>');
+        this.eventCanvas._elem.before(p._tooltipElem);
+    };
+    
+    $.jqplot.preInitHooks.push($.jqplot.Highlighter.init);
+    $.jqplot.preParseSeriesOptionsHooks.push($.jqplot.Highlighter.parseOptions);
+    $.jqplot.postDrawHooks.push($.jqplot.Highlighter.postPlotDraw);
+    
+    function draw(plot, neighbor) {
+        var hl = plot.plugins.highlighter;
+        var s = plot.series[neighbor.seriesIndex];
+        var smr = s.markerRenderer;
+        var mr = hl.markerRenderer;
+        mr.style = smr.style;
+        mr.lineWidth = smr.lineWidth + hl.lineWidthAdjust;
+        mr.size = smr.size + hl.sizeAdjust;
+        var rgba = $.jqplot.getColorComponents(smr.color);
+        var newrgb = [rgba[0], rgba[1], rgba[2]];
+        var alpha = (rgba[3] >= 0.6) ? rgba[3]*0.6 : rgba[3]*(2-rgba[3]);
+        mr.color = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+alpha+')';
+        mr.init();
+        mr.draw(s.gridData[neighbor.pointIndex][0], s.gridData[neighbor.pointIndex][1], hl.highlightCanvas._ctx);
+    }
+    
+    function showTooltip(plot, series, neighbor) {
+        // neighbor looks like: {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}
+        // gridData should be x,y pixel coords on the grid.
+        // add the plot._gridPadding to that to get x,y in the target.
+        var hl = plot.plugins.highlighter;
+        var elem = hl._tooltipElem;
+        if (hl.useAxesFormatters) {
+            var xf = series._xaxis._ticks[0].formatter;
+            var yf = series._yaxis._ticks[0].formatter;
+            var xfstr = series._xaxis._ticks[0].formatString;
+            var yfstr = series._yaxis._ticks[0].formatString;
+            var str;
+            var xstr = xf(xfstr, neighbor.data[0]);
+            var ystrs = [];
+            for (var i=1; i<hl.yvalues+1; i++) {
+                ystrs.push(yf(yfstr, neighbor.data[i]));
+            }
+            if (hl.formatString) {
+                switch (hl.tooltipAxes) {
+                    case 'both':
+                    case 'xy':
+                        ystrs.unshift(xstr);
+                        ystrs.unshift(hl.formatString);
+                        str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
+                        break;
+                    case 'yx':
+                        ystrs.push(xstr);
+                        ystrs.unshift(hl.formatString);
+                        str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
+                        break;
+                    case 'x':
+                        str = $.jqplot.sprintf.apply($.jqplot.sprintf, [hl.formatString, xstr]);
+                        break;
+                    case 'y':
+                        ystrs.unshift(hl.formatString);
+                        str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
+                        break;
+                    default: // same as xy
+                        ystrs.unshift(xstr);
+                        ystrs.unshift(hl.formatString);
+                        str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
+                        break;
+                } 
+            }
+            else {
+                switch (hl.tooltipAxes) {
+                    case 'both':
+                    case 'xy':
+                        str = xstr;
+                        for (var i=0; i<ystrs.length; i++) {
+                            str += hl.tooltipSeparator + ystrs[i];
+                        }
+                        break;
+                    case 'yx':
+                        str = '';
+                        for (var i=0; i<ystrs.length; i++) {
+                            str += ystrs[i] + hl.tooltipSeparator;
+                        }
+                        str += xstr;
+                        break;
+                    case 'x':
+                        str = xstr;
+                        break;
+                    case 'y':
+                        str = '';
+                        for (var i=0; i<ystrs.length; i++) {
+                            str += ystrs[i] + hl.tooltipSeparator;
+                        }
+                        break;
+                    default: // same as 'xy'
+                        str = xstr;
+                        for (var i=0; i<ystrs.length; i++) {
+                            str += hl.tooltipSeparator + ystrs[i];
+                        }
+                        break;
+                    
+                }                
+            }
+        }
+        else {
+            var str;
+            if (hl.tooltipAxes == 'both' || hl.tooltipAxes == 'xy') {
+                str = $.jqplot.sprintf(hl.tooltipFormatString, neighbor.data[0]) + hl.tooltipSeparator + $.jqplot.sprintf(hl.tooltipFormatString, neighbor.data[1]);
+            }
+            else if (hl.tooltipAxes == 'yx') {
+                str = $.jqplot.sprintf(hl.tooltipFormatString, neighbor.data[1]) + hl.tooltipSeparator + $.jqplot.sprintf(hl.tooltipFormatString, neighbor.data[0]);
+            }
+            else if (hl.tooltipAxes == 'x') {
+                str = $.jqplot.sprintf(hl.tooltipFormatString, neighbor.data[0]);
+            }
+            else if (hl.tooltipAxes == 'y') {
+                str = $.jqplot.sprintf(hl.tooltipFormatString, neighbor.data[1]);
+            } 
+        }
+        elem.html(str);
+        var gridpos = {x:neighbor.gridData[0], y:neighbor.gridData[1]};
+        var ms = 0;
+        var fact = 0.707;
+        if (series.markerRenderer.show == true) { 
+            ms = (series.markerRenderer.size + hl.sizeAdjust)/2;
+        }
+        switch (hl.tooltipLocation) {
+            case 'nw':
+                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - hl.tooltipOffset - fact * ms;
+                var y = gridpos.y + plot._gridPadding.top - hl.tooltipOffset - elem.outerHeight(true) - fact * ms;
+                break;
+            case 'n':
+                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2;
+                var y = gridpos.y + plot._gridPadding.top - hl.tooltipOffset - elem.outerHeight(true) - ms;
+                break;
+            case 'ne':
+                var x = gridpos.x + plot._gridPadding.left + hl.tooltipOffset + fact * ms;
+                var y = gridpos.y + plot._gridPadding.top - hl.tooltipOffset - elem.outerHeight(true) - fact * ms;
+                break;
+            case 'e':
+                var x = gridpos.x + plot._gridPadding.left + hl.tooltipOffset + ms;
+                var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2;
+                break;
+            case 'se':
+                var x = gridpos.x + plot._gridPadding.left + hl.tooltipOffset + fact * ms;
+                var y = gridpos.y + plot._gridPadding.top + hl.tooltipOffset + fact * ms;
+                break;
+            case 's':
+                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2;
+                var y = gridpos.y + plot._gridPadding.top + hl.tooltipOffset + ms;
+                break;
+            case 'sw':
+                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - hl.tooltipOffset - fact * ms;
+                var y = gridpos.y + plot._gridPadding.top + hl.tooltipOffset + fact * ms;
+                break;
+            case 'w':
+                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - hl.tooltipOffset - ms;
+                var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2;
+                break;
+            default: // same as 'nw'
+                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - hl.tooltipOffset - fact * ms;
+                var y = gridpos.y + plot._gridPadding.top - hl.tooltipOffset - elem.outerHeight(true) - fact * ms;
+                break;
+        }
+        elem.css('left', x);
+        elem.css('top', y);
+        if (hl.fadeTooltip) {
+            // Fix for stacked up animations.  Thnanks Trevor!
+            elem.stop(true,true).fadeIn(hl.tooltipFadeSpeed);
+        }
+        else {
+            elem.show();
+        }
+        
+    }
+    
+    function handleMove(ev, gridpos, datapos, neighbor, plot) {
+        var hl = plot.plugins.highlighter;
+        var c = plot.plugins.cursor;
+        if (hl.show) {
+            if (neighbor == null && hl.isHighlighting) {
+               var ctx = hl.highlightCanvas._ctx;
+               ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+                if (hl.fadeTooltip) {
+                    hl._tooltipElem.fadeOut(hl.tooltipFadeSpeed);
+                }
+                else {
+                    hl._tooltipElem.hide();
+                }
+                if (hl.bringSeriesToFront) {
+                    plot.restorePreviousSeriesOrder();
+                }
+               hl.isHighlighting = false;
+            
+            }
+            if (neighbor != null && plot.series[neighbor.seriesIndex].showHighlight && !hl.isHighlighting) {
+                hl.isHighlighting = true;
+                if (hl.showMarker) {
+                    draw(plot, neighbor);
+                }
+                if (hl.showTooltip && (!c || !c._zoom.started)) {
+                    showTooltip(plot, plot.series[neighbor.seriesIndex], neighbor);
+                }
+                if (hl.bringSeriesToFront) {
+                    plot.moveSeriesToFront(neighbor.seriesIndex);
+                }
+            }
+        }
+    }
+})(jQuery);
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.json2.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.json2.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.json2.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,475 @@
+/*
+    2010-11-01 Chris Leonello
+    
+    Slightly modified version of the original json2.js to put JSON
+    functions under the $.jqplot namespace.
+    
+    licensing and orignal comments follow:
+    
+    http://www.JSON.org/json2.js
+    2010-08-25
+    
+    Public Domain.
+
+    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+
+    See http://www.JSON.org/js.html
+
+
+    This code should be minified before deployment.
+    See http://javascript.crockford.com/jsmin.html
+
+    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+    NOT CONTROL.
+
+
+    This file creates a global JSON object containing two methods: stringify
+    and parse.
+
+        $.jqplot.JSON.stringify(value, replacer, space)
+            value       any JavaScript value, usually an object or array.
+
+            replacer    an optional parameter that determines how object
+                        values are stringified for objects. It can be a
+                        function or an array of strings.
+
+            space       an optional parameter that specifies the indentation
+                        of nested structures. If it is omitted, the text will
+                        be packed without extra whitespace. If it is a number,
+                        it will specify the number of spaces to indent at each
+                        level. If it is a string (such as '\t' or '&nbsp;'),
+                        it contains the characters used to indent at each level.
+
+            This method produces a JSON text from a JavaScript value.
+
+            When an object value is found, if the object contains a toJSON
+            method, its toJSON method will be called and the result will be
+            stringified. A toJSON method does not serialize: it returns the
+            value represented by the name/value pair that should be serialized,
+            or undefined if nothing should be serialized. The toJSON method
+            will be passed the key associated with the value, and this will be
+            bound to the value
+
+            For example, this would serialize Dates as ISO strings.
+
+                Date.prototype.toJSON = function (key) {
+                    function f(n) {
+                        // Format integers to have at least two digits.
+                        return n < 10 ? '0' + n : n;
+                    }
+
+                    return this.getUTCFullYear()   + '-' +
+                         f(this.getUTCMonth() + 1) + '-' +
+                         f(this.getUTCDate())      + 'T' +
+                         f(this.getUTCHours())     + ':' +
+                         f(this.getUTCMinutes())   + ':' +
+                         f(this.getUTCSeconds())   + 'Z';
+                };
+
+            You can provide an optional replacer method. It will be passed the
+            key and value of each member, with this bound to the containing
+            object. The value that is returned from your method will be
+            serialized. If your method returns undefined, then the member will
+            be excluded from the serialization.
+
+            If the replacer parameter is an array of strings, then it will be
+            used to select the members to be serialized. It filters the results
+            such that only members with keys listed in the replacer array are
+            stringified.
+
+            Values that do not have JSON representations, such as undefined or
+            functions, will not be serialized. Such values in objects will be
+            dropped; in arrays they will be replaced with null. You can use
+            a replacer function to replace those with JSON values.
+            $.jqplot.JSON.stringify(undefined) returns undefined.
+
+            The optional space parameter produces a stringification of the
+            value that is filled with line breaks and indentation to make it
+            easier to read.
+
+            If the space parameter is a non-empty string, then that string will
+            be used for indentation. If the space parameter is a number, then
+            the indentation will be that many spaces.
+
+            Example:
+
+            text = $.jqplot.JSON.stringify(['e', {pluribus: 'unum'}]);
+            // text is '["e",{"pluribus":"unum"}]'
+
+
+            text = $.jqplot.JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
+            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
+
+            text = $.jqplot.JSON.stringify([new Date()], function (key, value) {
+                return this[key] instanceof Date ?
+                    'Date(' + this[key] + ')' : value;
+            });
+            // text is '["Date(---current time---)"]'
+
+
+        $.jqplot.JSON.parse(text, reviver)
+            This method parses a JSON text to produce an object or array.
+            It can throw a SyntaxError exception.
+
+            The optional reviver parameter is a function that can filter and
+            transform the results. It receives each of the keys and values,
+            and its return value is used instead of the original value.
+            If it returns what it received, then the structure is not modified.
+            If it returns undefined then the member is deleted.
+
+            Example:
+
+            // Parse the text. Values that look like ISO date strings will
+            // be converted to Date objects.
+
+            myData = $.jqplot.JSON.parse(text, function (key, value) {
+                var a;
+                if (typeof value === 'string') {
+                    a =
+/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
+                    if (a) {
+                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+                            +a[5], +a[6]));
+                    }
+                }
+                return value;
+            });
+
+            myData = $.jqplot.JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+                var d;
+                if (typeof value === 'string' &&
+                        value.slice(0, 5) === 'Date(' &&
+                        value.slice(-1) === ')') {
+                    d = new Date(value.slice(5, -1));
+                    if (d) {
+                        return d;
+                    }
+                }
+                return value;
+            });
+
+
+    This is a reference implementation. You are free to copy, modify, or
+    redistribute.
+*/
+
+(function($) {
+
+    $.jqplot.JSON = window.JSON;
+
+    if (!window.JSON) {
+        $.jqplot.JSON = {};
+    }
+    
+    function f(n) {
+        // Format integers to have at least two digits.
+        return n < 10 ? '0' + n : n;
+    }
+
+    if (typeof Date.prototype.toJSON !== 'function') {
+
+        Date.prototype.toJSON = function (key) {
+
+            return isFinite(this.valueOf()) ?
+                   this.getUTCFullYear()   + '-' +
+                 f(this.getUTCMonth() + 1) + '-' +
+                 f(this.getUTCDate())      + 'T' +
+                 f(this.getUTCHours())     + ':' +
+                 f(this.getUTCMinutes())   + ':' +
+                 f(this.getUTCSeconds())   + 'Z' : null;
+        };
+
+        String.prototype.toJSON =
+        Number.prototype.toJSON =
+        Boolean.prototype.toJSON = function (key) {
+            return this.valueOf();
+        };
+    }
+
+    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        gap,
+        indent,
+        meta = {    // table of character substitutions
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        },
+        rep;
+
+
+    function quote(string) {
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe escape
+// sequences.
+
+        escapable.lastIndex = 0;
+        return escapable.test(string) ?
+            '"' + string.replace(escapable, function (a) {
+                var c = meta[a];
+                return typeof c === 'string' ? c :
+                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+            }) + '"' :
+            '"' + string + '"';
+    }
+
+
+    function str(key, holder) {
+
+// Produce a string from holder[key].
+
+        var i,          // The loop counter.
+            k,          // The member key.
+            v,          // The member value.
+            length,
+            mind = gap,
+            partial,
+            value = holder[key];
+
+// If the value has a toJSON method, call it to obtain a replacement value.
+
+        if (value && typeof value === 'object' &&
+                typeof value.toJSON === 'function') {
+            value = value.toJSON(key);
+        }
+
+// If we were called with a replacer function, then call the replacer to
+// obtain a replacement value.
+
+        if (typeof rep === 'function') {
+            value = rep.call(holder, key, value);
+        }
+
+// What happens next depends on the value's type.
+
+        switch (typeof value) {
+        case 'string':
+            return quote(value);
+
+        case 'number':
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+            return isFinite(value) ? String(value) : 'null';
+
+        case 'boolean':
+        case 'null':
+
+// If the value is a boolean or null, convert it to a string. Note:
+// typeof null does not produce 'null'. The case is included here in
+// the remote chance that this gets fixed someday.
+
+            return String(value);
+
+// If the type is 'object', we might be dealing with an object or an array or
+// null.
+
+        case 'object':
+
+// Due to a specification blunder in ECMAScript, typeof null is 'object',
+// so watch out for that case.
+
+            if (!value) {
+                return 'null';
+            }
+
+// Make an array to hold the partial results of stringifying this object value.
+
+            gap += indent;
+            partial = [];
+
+// Is the value an array?
+
+            if (Object.prototype.toString.apply(value) === '[object Array]') {
+
+// The value is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+                length = value.length;
+                for (i = 0; i < length; i += 1) {
+                    partial[i] = str(i, value) || 'null';
+                }
+
+// Join all of the elements together, separated with commas, and wrap them in
+// brackets.
+
+                v = partial.length === 0 ? '[]' :
+                    gap ? '[\n' + gap +
+                            partial.join(',\n' + gap) + '\n' +
+                                mind + ']' :
+                          '[' + partial.join(',') + ']';
+                gap = mind;
+                return v;
+            }
+
+// If the replacer is an array, use it to select the members to be stringified.
+
+            if (rep && typeof rep === 'object') {
+                length = rep.length;
+                for (i = 0; i < length; i += 1) {
+                    k = rep[i];
+                    if (typeof k === 'string') {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                        }
+                    }
+                }
+            } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+                for (k in value) {
+                    if (Object.hasOwnProperty.call(value, k)) {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                        }
+                    }
+                }
+            }
+
+// Join all of the member texts together, separated with commas,
+// and wrap them in braces.
+
+            v = partial.length === 0 ? '{}' :
+                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
+                        mind + '}' : '{' + partial.join(',') + '}';
+            gap = mind;
+            return v;
+        }
+    }
+
+// If the JSON object does not yet have a stringify method, give it one.
+
+    if (typeof $.jqplot.JSON.stringify !== 'function') {
+        $.jqplot.JSON.stringify = function (value, replacer, space) {
+
+// The stringify method takes a value and an optional replacer, and an optional
+// space parameter, and returns a JSON text. The replacer can be a function
+// that can replace values, or an array of strings that will select the keys.
+// A default replacer method can be provided. Use of the space parameter can
+// produce text that is more easily readable.
+
+            var i;
+            gap = '';
+            indent = '';
+
+// If the space parameter is a number, make an indent string containing that
+// many spaces.
+
+            if (typeof space === 'number') {
+                for (i = 0; i < space; i += 1) {
+                    indent += ' ';
+                }
+
+// If the space parameter is a string, it will be used as the indent string.
+
+            } else if (typeof space === 'string') {
+                indent = space;
+            }
+
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
+
+            rep = replacer;
+            if (replacer && typeof replacer !== 'function' &&
+                    (typeof replacer !== 'object' ||
+                     typeof replacer.length !== 'number')) {
+                throw new Error('$.jqplot.JSON.stringify');
+            }
+
+// Make a fake root object containing our value under the key of ''.
+// Return the result of stringifying the value.
+
+            return str('', {'': value});
+        };
+    }
+
+
+// If the JSON object does not yet have a parse method, give it one.
+
+    if (typeof $.jqplot.JSON.parse !== 'function') {
+        $.jqplot.JSON.parse = function (text, reviver) {
+
+// The parse method takes a text and an optional reviver function, and returns
+// a JavaScript value if the text is a valid JSON text.
+
+            var j;
+
+            function walk(holder, key) {
+
+// The walk method is used to recursively walk the resulting structure so
+// that modifications can be made.
+
+                var k, v, value = holder[key];
+                if (value && typeof value === 'object') {
+                    for (k in value) {
+                        if (Object.hasOwnProperty.call(value, k)) {
+                            v = walk(value, k);
+                            if (v !== undefined) {
+                                value[k] = v;
+                            } else {
+                                delete value[k];
+                            }
+                        }
+                    }
+                }
+                return reviver.call(holder, key, value);
+            }
+
+
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
+
+            text = String(text);
+            cx.lastIndex = 0;
+            if (cx.test(text)) {
+                text = text.replace(cx, function (a) {
+                    return '\\u' +
+                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+                });
+            }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
+
+// We split the second stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
+// replace all simple value tokens with ']' characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or ']' or
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+            if (/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+// In the third stage we use the eval function to compile the text into a
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+                j = eval('(' + text + ')');
+
+// In the optional fourth stage, we recursively walk the new structure, passing
+// each name/value pair to a reviver function for possible transformation.
+
+                return typeof reviver === 'function' ?
+                    walk({'': j}, '') : j;
+            }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+            throw new SyntaxError('$.jqplot.JSON.parse');
+        };
+    }
+})(jQuery);

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.logAxisRenderer.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.logAxisRenderer.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.logAxisRenderer.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,434 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */ 
+(function($) {
+    /**
+    *  class: $.jqplot.LogAxisRenderer
+    *  A plugin for a jqPlot to render a logarithmic axis.
+    * 
+    *  To use this renderer, include the plugin in your source
+    *  > <script type="text/javascript" language="javascript" src="plugins/jqplot.logAxisRenderer.js"></script>
+    *  
+    *  and supply the appropriate options to your plot
+    *  
+    *  > {axes:{xaxis:{renderer:$.jqplot.LogAxisRenderer}}}
+    **/ 
+    $.jqplot.LogAxisRenderer = function() {
+        $.jqplot.LinearAxisRenderer.call(this);
+        // prop: axisDefaults
+        // Default properties which will be applied directly to the series.
+        //
+        // Group: Properties
+        //
+        // Properties
+        //
+        /// base - the logarithmic base, commonly 2, 10 or Math.E
+        // tickDistribution - 'even' or 'power'.  'even' gives equal pixel
+        // spacing of the ticks on the plot.  'power' gives ticks in powers
+        // of 10.
+        this.axisDefaults = {
+            base : 10,
+            tickDistribution :'even'
+        };
+    };
+    
+    $.jqplot.LogAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
+    $.jqplot.LogAxisRenderer.prototype.constructor = $.jqplot.LogAxisRenderer;
+    
+    $.jqplot.LogAxisRenderer.prototype.init = function(options) {
+        // prop: tickRenderer
+        // A class of a rendering engine for creating the ticks labels displayed on the plot, 
+        // See <$.jqplot.AxisTickRenderer>.
+        // this.tickRenderer = $.jqplot.AxisTickRenderer;
+        // this.labelRenderer = $.jqplot.AxisLabelRenderer;
+        $.extend(true, this.renderer, options);
+        for (var d in this.renderer.axisDefaults) {
+            if (this[d] == null) {
+                this[d] = this.renderer.axisDefaults[d];
+            }
+        }
+        var db = this._dataBounds;
+        // Go through all the series attached to this axis and find
+        // the min/max bounds for this axis.
+        for (var i=0; i<this._series.length; i++) {
+            var s = this._series[i];
+            var d = s.data;
+            
+            for (var j=0; j<d.length; j++) { 
+                if (this.name == 'xaxis' || this.name == 'x2axis') {
+                    if (d[j][0] > db.max || db.max == null) {
+                        db.max = d[j][0];
+                    }
+                    if (d[j][0] > db.max || db.max == null) {
+                        db.max = d[j][0];
+                    }
+                }              
+                else {
+                    if (d[j][1] < db.min || db.min == null) {
+                        db.min = d[j][1];
+                    }
+                    if (d[j][1] > db.max || db.max == null) {
+                        db.max = d[j][1];
+                    }
+                }              
+            }
+        }
+    };
+    
+    $.jqplot.LogAxisRenderer.prototype.createTicks = function() {
+        // we're are operating on an axis here
+        var ticks = this._ticks;
+        var userTicks = this.ticks;
+        var name = this.name;
+        var db = this._dataBounds;
+        var dim, interval;
+        var min, max;
+        var pos1, pos2;
+        var tt, i;
+
+        // if we already have ticks, use them.
+        // ticks must be in order of increasing value.
+        if (userTicks.length) {
+            // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed
+            for (i=0; i<userTicks.length; i++){
+                var ut = userTicks[i];
+                var t = new this.tickRenderer(this.tickOptions);
+                if (ut.constructor == Array) {
+                    t.value = ut[0];
+                    t.label = ut[1];
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    t.setTick(ut[0], this.name);
+                    this._ticks.push(t);
+                }
+                
+                else {
+                    t.value = ut;
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    t.setTick(ut, this.name);
+                    this._ticks.push(t);
+                }
+            }
+            this.numberTicks = userTicks.length;
+            this.min = this._ticks[0].value;
+            this.max = this._ticks[this.numberTicks-1].value;
+        }
+        
+        // we don't have any ticks yet, let's make some!
+        else {
+            if (name == 'xaxis' || name == 'x2axis') {
+                dim = this._plotDimensions.width;
+            }
+            else {
+                dim = this._plotDimensions.height;
+            }
+        
+            min = ((this.min != null) ? this.min : db.min);
+            max = ((this.max != null) ? this.max : db.max);
+            
+            // if min and max are same, space them out a bit
+            if (min == max) {
+                var adj = 0.05;
+                min = min*(1-adj);
+                max = max*(1+adj);
+            }
+            
+            // perform some checks
+            if (this.min != null && this.min <= 0) {
+                throw('log axis minimum must be greater than 0');
+            }
+            if (this.max != null && this.max <= 0) {
+                throw('log axis maximum must be greater than 0');
+            }
+            // if (this.pad >1.99) this.pad = 1.99;
+            var range = max - min;
+            var rmin, rmax;
+
+            if (this.tickDistribution == 'even') {                    
+                rmin = (this.min != null) ? this.min : min - min*((this.padMin-1)/2);
+                rmax = (this.max != null) ? this.max : max + max*((this.padMax-1)/2);
+                this.min = rmin;
+                this.max = rmax;
+                range = this.max - this.min;            
+        
+                if (this.numberTicks == null){
+                    if (dim > 100) {
+                        this.numberTicks = parseInt(3+(dim-100)/75, 10);
+                    }
+                    else {
+                        this.numberTicks = 2;
+                    }
+                }
+    
+                var u = Math.pow(this.base, (1/(this.numberTicks-1)*Math.log(this.max/this.min)/Math.log(this.base)));
+                for (var i=0; i<this.numberTicks; i++){
+                    tt = this.min * Math.pow(u, i);
+                    var t = new this.tickRenderer(this.tickOptions);
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    t.setTick(tt, this.name);
+                    this._ticks.push(t);
+                }
+                
+            }
+            
+            else if (this.tickDistribution == 'power'){
+                // for power distribution, open up range to get a nice power of axis.renderer.base.
+                // power distribution won't respect the user's min/max settings.
+                rmin = Math.pow(this.base, Math.ceil(Math.log(min*(2-this.padMin))/Math.log(this.base))-1);
+                rmax = Math.pow(this.base, Math.floor(Math.log(max*this.padMax)/Math.log(this.base))+1);
+                this.min = rmin;
+                this.max = rmax;
+                range = this.max - this.min;            
+        
+                var fittedTicks = 0;
+                var minorTicks = 0;
+                if (this.numberTicks == null){
+                    if (dim > 100) {
+                        this.numberTicks = Math.round(Math.log(this.max/this.min)/Math.log(this.base) + 1);
+                        if (this.numberTicks < 2) {
+                            this.numberTicks = 2;
+                        }
+                        fittedTicks = parseInt(3+(dim-100)/75, 10);
+                    }
+                    else {
+                        this.numberTicks = 2;
+                        fittedTicks = 2;
+                    }
+                    // if we don't have enough ticks, add some intermediate ticks
+                    // how many to have between major ticks.
+                    if (this.numberTicks < fittedTicks-1) {
+                        minorTicks = Math.floor(fittedTicks/this.numberTicks);
+                    }
+                }
+
+                for (var i=0; i<this.numberTicks; i++){
+                    tt = Math.pow(this.base, i - this.numberTicks + 1) * this.max;
+                    var t = new this.tickRenderer(this.tickOptions);
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    t.setTick(tt, this.name);
+                    this._ticks.push(t);
+            
+                    if (minorTicks && i<this.numberTicks-1) {
+                        var tt1 = Math.pow(this.base, i - this.numberTicks + 2) * this.max;
+                        var spread = tt1 - tt;
+                        var interval = tt1 / (minorTicks+1);
+                        for (var j=minorTicks-1; j>=0; j--) {
+                            var val = tt1-interval*(j+1);
+                            var t = new this.tickRenderer(this.tickOptions);
+                            if (!this.showTicks) {
+                                t.showLabel = false;
+                                t.showMark = false;
+                            }
+                            else if (!this.showTickMarks) {
+                                t.showMark = false;
+                            }
+                            t.setTick(val, this.name);
+                            this._ticks.push(t);
+                        }
+                    }       
+                }                    
+            }       
+        }
+    };
+    
+    $.jqplot.LogAxisRenderer.prototype.pack = function(pos, offsets) {
+        var lb = parseInt(this.base, 10);
+        var ticks = this._ticks;
+        var trans = function (v) { return Math.log(v)/Math.log(lb); };
+        var invtrans = function (v) { return Math.pow(Math.E, (Math.log(lb)*v)); };
+        max = trans(this.max);
+        min = trans(this.min);
+        var offmax = offsets.max;
+        var offmin = offsets.min;
+        var lshow = (this._label == null) ? false : this._label.show;
+        
+        for (var p in pos) {
+            this._elem.css(p, pos[p]);
+        }
+        
+        this._offsets = offsets;
+        // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left.
+        var pixellength = offmax - offmin;
+        var unitlength = max - min;
+        
+        // point to unit and unit to point conversions references to Plot DOM element top left corner.
+        this.p2u = function(p){
+            return invtrans((p - offmin) * unitlength / pixellength + min);
+        };
+        
+        this.u2p = function(u){
+            return (trans(u) - min) * pixellength / unitlength + offmin;
+        };
+        
+        if (this.name == 'xaxis' || this.name == 'x2axis'){
+            this.series_u2p = function(u){
+                return (trans(u) - min) * pixellength / unitlength;
+            };
+            this.series_p2u = function(p){
+                return invtrans(p * unitlength / pixellength + min);
+            };
+        }
+        // yaxis is max at top of canvas.
+        else {
+            this.series_u2p = function(u){
+                return (trans(u) - max) * pixellength / unitlength;
+            };
+            this.series_p2u = function(p){
+                return invtrans(p * unitlength / pixellength + max);
+            };
+        }
+        
+        if (this.show) {
+            if (this.name == 'xaxis' || this.name == 'x2axis') {
+                for (i=0; i<ticks.length; i++) {
+                    var t = ticks[i];
+                    if (t.show && t.showLabel) {
+                        var shim;
+                        
+                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
+                            switch (t.labelPosition) {
+                                case 'auto':
+                                    // position at end
+                                    if (t.angle < 0) {
+                                        shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    }
+                                    // position at start
+                                    else {
+                                        shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'end':
+                                    shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                                case 'start':
+                                    shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    break;
+                                case 'middle':
+                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                                default:
+                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                            }
+                        }
+                        else {
+                            shim = -t.getWidth()/2;
+                        }
+                        // var shim = t.getWidth()/2;
+                        var val = this.u2p(t.value) + shim + 'px';
+                        t._elem.css('left', val);
+                        t.pack();
+                    }
+                }
+                if (lshow) {
+                    var w = this._label._elem.outerWidth(true);
+                    this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px');
+                    if (this.name == 'xaxis') {
+                        this._label._elem.css('bottom', '0px');
+                    }
+                    else {
+                        this._label._elem.css('top', '0px');
+                    }
+                    this._label.pack();
+                }
+            }
+            else {
+                for (i=0; i<ticks.length; i++) {
+                    var t = ticks[i];
+                    if (t.show && t.showLabel) {                        
+                        var shim;
+                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
+                            switch (t.labelPosition) {
+                                case 'auto':
+                                    // position at end
+                                case 'end':
+                                    if (t.angle < 0) {
+                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
+                                    }
+                                    else {
+                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'start':
+                                    if (t.angle > 0) {
+                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
+                                    }
+                                    else {
+                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'middle':
+                                    // if (t.angle > 0) {
+                                    //     shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    // }
+                                    // else {
+                                    //     shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    // }
+                                    shim = -t.getHeight()/2;
+                                    break;
+                                default:
+                                    shim = -t.getHeight()/2;
+                                    break;
+                            }
+                        }
+                        else {
+                            shim = -t.getHeight()/2;
+                        }
+                        
+                        var val = this.u2p(t.value) + shim + 'px';
+                        t._elem.css('top', val);
+                        t.pack();
+                    }
+                }
+                if (lshow) {
+                    var h = this._label._elem.outerHeight(true);
+                    this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px');
+                    if (this.name == 'yaxis') {
+                        this._label._elem.css('left', '0px');
+                    }
+                    else {
+                        this._label._elem.css('right', '0px');
+                    }   
+                    this._label.pack();
+                }
+            }
+        }        
+    };
+})(jQuery);
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.mekkoAxisRenderer.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.mekkoAxisRenderer.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.mekkoAxisRenderer.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,595 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {
+    // class: $.jqplot.MekkoAxisRenderer
+    // An axis renderer for a Mekko chart.
+    // Should be used with a Mekko chart where the mekkoRenderer is used on the series.
+    // Displays the Y axis as a range from 0 to 1 (0 to 100%) and the x axis with a tick
+    // for each series scaled to the sum of all the y values.
+    $.jqplot.MekkoAxisRenderer = function() {
+    };
+    
+    // called with scope of axis object.
+    $.jqplot.MekkoAxisRenderer.prototype.init = function(options){
+        // prop: tickMode
+        // How to space the ticks on the axis.
+        // 'bar' will place a tick at the width of each bar.  
+        // This is the default for the x axis.
+        // 'even' will place ticks at even intervals.  This is
+        // the default for x2 axis and y axis.  y axis cannot be changed.
+        this.tickMode;
+        // prop: barLabelRenderer
+        // renderer to use to draw labels under each bar.
+        this.barLabelRenderer = $.jqplot.AxisLabelRenderer;
+        // prop: barLabels
+        // array of labels to put under each bar.
+        this.barLabels = this.barLabels || [];
+        // prop: barLabelOptions
+        // options object to pass to the bar label renderer.
+        this.barLabelOptions = {};
+        this.tickOptions = $.extend(true, {showGridline:false}, this.tickOptions);
+        this._barLabels = [];
+        $.extend(true, this, options);
+        if (this.name == 'yaxis') {
+            this.tickOptions.formatString = this.tickOptions.formatString || "%d\%";
+        }
+        var db = this._dataBounds;
+        db.min = 0;
+        // for y axes, scale always go from 0 to 1 (0 to 100%)
+        if (this.name == 'yaxis' || this.name == 'y2axis') {
+            db.max = 100;
+            this.tickMode = 'even';
+        }
+        // For x axes, scale goes from 0 to sum of all y values.
+        else if (this.name == 'xaxis'){
+            this.tickMode = (this.tickMode == null) ? 'bar' : this.tickMode;
+            for (var i=0; i<this._series.length; i++) {
+                db.max += this._series[i]._sumy;
+            }
+        }
+        else if (this.name == 'x2axis'){
+            this.tickMode = (this.tickMode == null) ? 'even' : this.tickMode;
+            for (var i=0; i<this._series.length; i++) {
+                db.max += this._series[i]._sumy;
+            }
+        }
+    };
+    
+    // called with scope of axis
+    $.jqplot.MekkoAxisRenderer.prototype.draw = function(ctx) {
+        if (this.show) {
+            // populate the axis label and value properties.
+            // createTicks is a method on the renderer, but
+            // call it within the scope of the axis.
+            this.renderer.createTicks.call(this);
+            // fill a div with axes labels in the right direction.
+            // Need to pregenerate each axis to get it's bounds and
+            // position it and the labels correctly on the plot.
+            var dim=0;
+            var temp;
+            
+            this._elem = $('<div class="jqplot-axis jqplot-'+this.name+'" style="position:absolute;"></div>');
+            
+            if (this.name == 'xaxis' || this.name == 'x2axis') {
+                this._elem.width(this._plotDimensions.width);
+            }
+            else {
+                this._elem.height(this._plotDimensions.height);
+            }
+            
+            // draw the axis label
+            // create a _label object.
+            this.labelOptions.axis = this.name;
+            this._label = new this.labelRenderer(this.labelOptions);
+            if (this._label.show) {
+                var elem = this._label.draw(ctx);
+                elem.appendTo(this._elem);
+            }
+            
+            var t, tick, elem;
+            if (this.showTicks) {
+                t = this._ticks;
+                for (var i=0; i<t.length; i++) {
+                    tick = t[i];
+                    if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
+                        elem = tick.draw(ctx);
+                        elem.appendTo(this._elem);
+                    }
+                }
+            }
+            
+            // draw the series labels
+            for (i=0; i<this.barLabels.length; i++) {
+                this.barLabelOptions.axis = this.name;
+                this.barLabelOptions.label = this.barLabels[i];
+                this._barLabels.push(new this.barLabelRenderer(this.barLabelOptions));
+                if (this.tickMode != 'bar') {
+                    this._barLabels[i].show = false;
+                }
+                if (this._barLabels[i].show) {
+                    var elem = this._barLabels[i].draw(ctx);
+                    elem.removeClass('jqplot-'+this.name+'-label');
+                    elem.addClass('jqplot-'+this.name+'-tick');
+                    elem.addClass('jqplot-mekko-barLabel');
+                    elem.appendTo(this._elem);
+                }   
+            }
+            
+        }
+        return this._elem;
+    };
+    
+    // called with scope of an axis
+    $.jqplot.MekkoAxisRenderer.prototype.reset = function() {
+        this.min = this._min;
+        this.max = this._max;
+        this.tickInterval = this._tickInterval;
+        this.numberTicks = this._numberTicks;
+        // this._ticks = this.__ticks;
+    };
+    
+    // called with scope of axis
+    $.jqplot.MekkoAxisRenderer.prototype.set = function() { 
+        var dim = 0;
+        var temp;
+        var w = 0;
+        var h = 0;
+        var lshow = (this._label == null) ? false : this._label.show;
+        if (this.show && this.showTicks) {
+            var t = this._ticks;
+            for (var i=0; i<t.length; i++) {
+                var tick = t[i];
+                if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
+                    if (this.name == 'xaxis' || this.name == 'x2axis') {
+                        temp = tick._elem.outerHeight(true);
+                    }
+                    else {
+                        temp = tick._elem.outerWidth(true);
+                    }
+                    if (temp > dim) {
+                        dim = temp;
+                    }
+                }
+            }
+            
+            if (lshow) {
+                w = this._label._elem.outerWidth(true);
+                h = this._label._elem.outerHeight(true); 
+            }
+            if (this.name == 'xaxis') {
+                dim = dim + h;
+                this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'});
+            }
+            else if (this.name == 'x2axis') {
+                dim = dim + h;
+                this._elem.css({'height':dim+'px', left:'0px', top:'0px'});
+            }
+            else if (this.name == 'yaxis') {
+                dim = dim + w;
+                this._elem.css({'width':dim+'px', left:'0px', top:'0px'});
+                if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
+                    this._label._elem.css('width', w+'px');
+                }
+            }
+            else {
+                dim = dim + w;
+                this._elem.css({'width':dim+'px', right:'0px', top:'0px'});
+                if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
+                    this._label._elem.css('width', w+'px');
+                }
+            }
+        }  
+    };    
+    
+    // called with scope of axis
+    $.jqplot.MekkoAxisRenderer.prototype.createTicks = function() {
+        // we're are operating on an axis here
+        var ticks = this._ticks;
+        var userTicks = this.ticks;
+        var name = this.name;
+        // databounds were set on axis initialization.
+        var db = this._dataBounds;
+        var dim, interval;
+        var min, max;
+        var pos1, pos2;
+        var t, tt, i, j;
+        
+        // if we already have ticks, use them.
+        // ticks must be in order of increasing value.
+        
+        if (userTicks.length) {
+            // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed
+            for (i=0; i<userTicks.length; i++){
+                var ut = userTicks[i];
+                var t = new this.tickRenderer(this.tickOptions);
+                if (ut.constructor == Array) {
+                    t.value = ut[0];
+                    t.label = ut[1];
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    t.setTick(ut[0], this.name);
+                    this._ticks.push(t);
+                }
+                
+                else {
+                    t.value = ut;
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    t.setTick(ut, this.name);
+                    this._ticks.push(t);
+                }
+            }
+            this.numberTicks = userTicks.length;
+            this.min = this._ticks[0].value;
+            this.max = this._ticks[this.numberTicks-1].value;
+            this.tickInterval = (this.max - this.min) / (this.numberTicks - 1);
+        }
+        
+        // we don't have any ticks yet, let's make some!
+        else {
+            if (name == 'xaxis' || name == 'x2axis') {
+                dim = this._plotDimensions.width;
+            }
+            else {
+                dim = this._plotDimensions.height;
+            }
+            
+            // if min, max and number of ticks specified, user can't specify interval.
+            if (this.min != null && this.max != null && this.numberTicks != null) {
+                this.tickInterval = null;
+            }
+        
+            min = (this.min != null) ? this.min : db.min;
+            max = (this.max != null) ? this.max : db.max;
+            
+            // if min and max are same, space them out a bit.+
+            if (min == max) {
+                var adj = 0.05;
+                if (min > 0) {
+                    adj = Math.max(Math.log(min)/Math.LN10, 0.05);
+                }
+                min -= adj;
+                max += adj;
+            }
+
+            var range = max - min;
+            var rmin, rmax;
+            var temp, prev, curr;
+            var ynumticks = [3,5,6,11,21];
+            
+            // yaxis divide ticks in nice intervals from 0 to 1.
+            if (this.name == 'yaxis' || this.name == 'y2axis') { 
+                this.min = 0;
+                this.max = 100; 
+                // user didn't specify number of ticks.
+                if (!this.numberTicks){
+                    if (this.tickInterval) {
+                        this.numberTicks = 3 + Math.ceil(range / this.tickInterval);
+                    }
+                    else {
+                        temp = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
+                        for (i=0; i<ynumticks.length; i++) {
+                            curr = temp/ynumticks[i];
+                            if (curr == 1) {
+                                this.numberTicks = ynumticks[i];
+                                break;
+                            }
+                            else if (curr > 1) {
+                                prev = curr;
+                                continue;
+                            }
+                            else if (curr < 1) {
+                                // was prev or is curr closer to one?
+                                if (Math.abs(prev - 1) < Math.abs(curr - 1)) {
+                                    this.numberTicks = ynumticks[i-1];
+                                    break;
+                                }
+                                else {
+                                    this.numberTicks = ynumticks[i];
+                                    break;
+                                }
+                            }
+                            else if (i == ynumticks.length -1) {
+                                this.numberTicks = ynumticks[i];
+                            }
+                        }
+                        this.tickInterval = range / (this.numberTicks - 1);
+                    }
+                }
+                
+                // user did specify number of ticks.
+                else {
+                    this.tickInterval = range / (this.numberTicks - 1);
+                }
+
+                for (var i=0; i<this.numberTicks; i++){
+                    tt = this.min + i * this.tickInterval;
+                    t = new this.tickRenderer(this.tickOptions);
+                    // var t = new $.jqplot.AxisTickRenderer(this.tickOptions);
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    t.setTick(tt, this.name);
+                    this._ticks.push(t);
+                }
+            }
+            
+            // for x axes, have number ot ticks equal to number of series and ticks placed
+            // at sum of y values for each series.
+            else if (this.tickMode == 'bar') {
+                this.min = 0;
+                this.numberTicks = this._series.length + 1;
+                t = new this.tickRenderer(this.tickOptions);
+                if (!this.showTicks) {
+                    t.showLabel = false;
+                    t.showMark = false;
+                }
+                else if (!this.showTickMarks) {
+                    t.showMark = false;
+                }
+                t.setTick(0, this.name);
+                this._ticks.push(t);
+                
+                temp = 0;
+
+                for (i=1; i<this.numberTicks; i++){
+                    temp += this._series[i-1]._sumy;
+                    t = new this.tickRenderer(this.tickOptions);
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    t.setTick(temp, this.name);
+                    this._ticks.push(t);
+                }
+                this.max = this.max || temp;
+                
+                // if user specified a max and it is greater than sum, add a tick
+                if (this.max > temp) {
+                     t = new this.tickRenderer(this.tickOptions);
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    t.setTick(this.max, this.name);
+                    this._ticks.push(t);
+                    
+                }
+            }
+            
+            else if (this.tickMode == 'even') {
+                this.min = 0;
+                this.max = this.max || db.max;
+                // get a desired number of ticks
+                var nt = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
+                range = this.max - this.min;
+                this.numberTicks = nt;
+                this.tickInterval = range / (this.numberTicks - 1);
+
+                for (i=0; i<this.numberTicks; i++){
+                    tt = this.min + i * this.tickInterval;
+                    t = new this.tickRenderer(this.tickOptions);
+                    // var t = new $.jqplot.AxisTickRenderer(this.tickOptions);
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    t.setTick(tt, this.name);
+                    this._ticks.push(t);
+                }
+                
+            }
+        }
+    };
+    
+    // called with scope of axis
+    $.jqplot.MekkoAxisRenderer.prototype.pack = function(pos, offsets) {
+        var ticks = this._ticks;
+        var max = this.max;
+        var min = this.min;
+        var offmax = offsets.max;
+        var offmin = offsets.min;
+        var lshow = (this._label == null) ? false : this._label.show;
+        
+        for (var p in pos) {
+            this._elem.css(p, pos[p]);
+        }
+        
+        this._offsets = offsets;
+        // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left.
+        var pixellength = offmax - offmin;
+        var unitlength = max - min;
+        
+        // point to unit and unit to point conversions references to Plot DOM element top left corner.
+        this.p2u = function(p){
+            return (p - offmin) * unitlength / pixellength + min;
+        };
+        
+        this.u2p = function(u){
+            return (u - min) * pixellength / unitlength + offmin;
+        };
+                
+        if (this.name == 'xaxis' || this.name == 'x2axis'){
+            this.series_u2p = function(u){
+                return (u - min) * pixellength / unitlength;
+            };
+            this.series_p2u = function(p){
+                return p * unitlength / pixellength + min;
+            };
+        }
+        
+        else {
+            this.series_u2p = function(u){
+                return (u - max) * pixellength / unitlength;
+            };
+            this.series_p2u = function(p){
+                return p * unitlength / pixellength + max;
+            };
+        }
+        
+        if (this.show) {
+            if (this.name == 'xaxis' || this.name == 'x2axis') {
+                for (i=0; i<ticks.length; i++) {
+                    var t = ticks[i];
+                    if (t.show && t.showLabel) {
+                        var shim;
+                        
+                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
+                            // will need to adjust auto positioning based on which axis this is.
+                            var temp = (this.name == 'xaxis') ? 1 : -1;
+                            switch (t.labelPosition) {
+                                case 'auto':
+                                    // position at end
+                                    if (temp * t.angle < 0) {
+                                        shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    }
+                                    // position at start
+                                    else {
+                                        shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'end':
+                                    shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                                case 'start':
+                                    shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    break;
+                                case 'middle':
+                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                                default:
+                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                            }
+                        }
+                        else {
+                            shim = -t.getWidth()/2;
+                        }
+                        var val = this.u2p(t.value) + shim + 'px';
+                        t._elem.css('left', val);
+                        t.pack();
+                    }
+                }
+                var w;
+                if (lshow) {
+                    w = this._label._elem.outerWidth(true);
+                    this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px');
+                    if (this.name == 'xaxis') {
+                        this._label._elem.css('bottom', '0px');
+                    }
+                    else {
+                        this._label._elem.css('top', '0px');
+                    }
+                    this._label.pack();
+                }
+                // now show the labels under the bars.
+                var b, l, r;
+                for (i=0; i<this.barLabels.length; i++) {
+                    b = this._barLabels[i];
+                    if (b.show) {
+                        w = b.getWidth();
+                        l = this._ticks[i].getLeft() + this._ticks[i].getWidth();
+                        r = this._ticks[i+1].getLeft();
+                        b._elem.css('left', (r+l-w)/2+'px');
+                        b._elem.css('top', this._ticks[i]._elem.css('top'));
+                        b.pack();
+                    }
+                }
+            }
+            else {
+                for (i=0; i<ticks.length; i++) {
+                    var t = ticks[i];
+                    if (t.show && t.showLabel) {                        
+                        var shim;
+                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
+                            // will need to adjust auto positioning based on which axis this is.
+                            var temp = (this.name == 'yaxis') ? 1 : -1;
+                            switch (t.labelPosition) {
+                                case 'auto':
+                                    // position at end
+                                case 'end':
+                                    if (temp * t.angle < 0) {
+                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
+                                    }
+                                    else {
+                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'start':
+                                    if (t.angle > 0) {
+                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
+                                    }
+                                    else {
+                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'middle':
+                                    shim = -t.getHeight()/2;
+                                    break;
+                                default:
+                                    shim = -t.getHeight()/2;
+                                    break;
+                            }
+                        }
+                        else {
+                            shim = -t.getHeight()/2;
+                        }
+                        
+                        var val = this.u2p(t.value) + shim + 'px';
+                        t._elem.css('top', val);
+                        t.pack();
+                    }
+                }
+                if (lshow) {
+                    var h = this._label._elem.outerHeight(true);
+                    this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px');
+                    if (this.name == 'yaxis') {
+                        this._label._elem.css('left', '0px');
+                    }
+                    else {
+                        this._label._elem.css('right', '0px');
+                    }   
+                    this._label.pack();
+                }
+            }
+        }
+    };
+})(jQuery);

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.mekkoRenderer.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.mekkoRenderer.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.mekkoRenderer.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,419 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {
+    /**
+     * Class: $.jqplot.MekkoRenderer
+     * Draws a Mekko style chart which shows 3 dimensional data on a 2 dimensional graph.
+     * the <$.jqplot.MekkoAxisRenderer> should be used with mekko charts.  The mekko renderer
+     * overrides the default legend renderer with it's own $.jqplot.MekkoLegendRenderer
+     * which allows more flexibility to specify number of rows and columns in the legend.
+     * 
+     * Data is specified per bar in the chart.  You can specify data as an array of y values, or as 
+     * an array of [label, value] pairs.  Note that labels are used only on the first series.  
+     * Labels on subsequent series are ignored:
+     * 
+     * > bar1 = [['shirts', 8],['hats', 14],['shoes', 6],['gloves', 16],['dolls', 12]];
+     * > bar2 = [15,6,9,13,6];
+     * > bar3 = [['grumpy',4],['sneezy',2],['happy',7],['sleepy',9],['doc',7]];
+     * 
+     * If you want to place labels for each bar under the axis, you use the barLabels option on 
+     * the axes.  The bar labels can be styled with the ".jqplot-mekko-barLabel" css class.
+     * 
+     * > barLabels = ['Mickey Mouse', 'Donald Duck', 'Goofy'];
+     * > axes:{xaxis:{barLabels:barLabels}}
+     * 
+     */
+    
+    
+    $.jqplot.MekkoRenderer = function(){
+        this.shapeRenderer = new $.jqplot.ShapeRenderer();
+        // prop: borderColor
+        // color of the borders between areas on the chart
+        this.borderColor = null;
+        // prop: showBorders
+        // True to draw borders lines between areas on the chart.
+        // False will draw borders lines with the same color as the area.
+        this.showBorders = true;
+    };
+    
+    // called with scope of series.
+    $.jqplot.MekkoRenderer.prototype.init = function(options, plot) {
+        this.fill = false;
+        this.fillRect = true;
+        this.strokeRect = true;
+        this.shadow = false;
+        // width of bar on x axis.
+        this._xwidth = 0;
+        this._xstart = 0;
+        $.extend(true, this.renderer, options);
+        // set the shape renderer options
+        var opts = {lineJoin:'miter', lineCap:'butt', isarc:false, fillRect:this.fillRect, strokeRect:this.strokeRect};
+        this.renderer.shapeRenderer.init(opts);
+        plot.axes.x2axis._series.push(this);
+    };
+    
+    // Method: setGridData
+    // converts the user data values to grid coordinates and stores them
+    // in the gridData array.  Will convert user data into appropriate
+    // rectangles.
+    // Called with scope of a series.
+    $.jqplot.MekkoRenderer.prototype.setGridData = function(plot) {
+        // recalculate the grid data
+        var xp = this._xaxis.series_u2p;
+        var yp = this._yaxis.series_u2p;
+        var data = this._plotData;
+        this.gridData = [];
+        // figure out width on x axis.
+        // this._xwidth = this._sumy / plot._sumy * this.canvas.getWidth();
+        this._xwidth = xp(this._sumy) - xp(0);
+        if (this.index>0) {
+            this._xstart = plot.series[this.index-1]._xstart + plot.series[this.index-1]._xwidth;
+        }
+        var totheight = this.canvas.getHeight();
+        var sumy = 0;
+        var cury;
+        var curheight;
+        for (var i=0; i<data.length; i++) {
+            if (data[i] != null) {
+                sumy += data[i][1];
+                cury = totheight - (sumy / this._sumy * totheight);
+                curheight = data[i][1] / this._sumy * totheight;
+                this.gridData.push([this._xstart, cury, this._xwidth, curheight]);
+            }
+        }
+    };
+    
+    // Method: makeGridData
+    // converts any arbitrary data values to grid coordinates and
+    // returns them.  This method exists so that plugins can use a series'
+    // linerenderer to generate grid data points without overwriting the
+    // grid data associated with that series.
+    // Called with scope of a series.
+    $.jqplot.MekkoRenderer.prototype.makeGridData = function(data, plot) {
+        // recalculate the grid data
+        // figure out width on x axis.
+        var xp = this._xaxis.series_u2p;
+        var totheight = this.canvas.getHeight();
+        var sumy = 0;
+        var cury;
+        var curheight;
+        var gd = [];
+        for (var i=0; i<data.length; i++) {
+            if (data[i] != null) {
+                sumy += data[i][1];
+                cury = totheight - (sumy / this._sumy * totheight);
+                curheight = data[i][1] / this._sumy * totheight;
+                gd.push([this._xstart, cury, this._xwidth, curheight]);
+            }
+        }
+        return gd;
+    };
+    
+
+    // called within scope of series.
+    $.jqplot.MekkoRenderer.prototype.draw = function(ctx, gd, options) {
+        var i;
+        var opts = (options != undefined) ? options : {};
+        var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
+        var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors);
+        ctx.save();
+        if (gd.length) {
+            if (showLine) {
+                for (i=0; i<gd.length; i++){
+                    opts.fillStyle = colorGenerator.next();
+                    if (this.renderer.showBorders) {
+                        opts.strokeStyle = this.renderer.borderColor;
+                    }
+                    else {
+                        opts.strokeStyle = opts.fillStyle;
+                    }
+                    this.renderer.shapeRenderer.draw(ctx, gd[i], opts);
+                }
+            }
+        }
+        
+        ctx.restore();
+    };  
+    
+    $.jqplot.MekkoRenderer.prototype.drawShadow = function(ctx, gd, options) {
+        // This is a no-op, no shadows on mekko charts.
+    };
+    
+    /**
+     * Class: $.jqplot.MekkoLegendRenderer
+     * Legend renderer used by mekko charts with options for 
+     * controlling number or rows and columns as well as placement
+     * outside of plot area.
+     * 
+     */
+    $.jqplot.MekkoLegendRenderer = function(){
+        //
+    };
+    
+    $.jqplot.MekkoLegendRenderer.prototype.init = function(options) {
+        // prop: numberRows
+        // Maximum number of rows in the legend.  0 or null for unlimited.
+        this.numberRows = null;
+        // prop: numberColumns
+        // Maximum number of columns in the legend.  0 or null for unlimited.
+        this.numberColumns = null;
+        // this will override the placement option on the Legend object
+        this.placement = "outside";
+        $.extend(true, this, options);
+    };
+    
+    // called with scope of legend
+    $.jqplot.MekkoLegendRenderer.prototype.draw = function() {
+        var legend = this;
+        if (this.show) {
+            var series = this._series;
+            var ss = 'position:absolute;';
+            ss += (this.background) ? 'background:'+this.background+';' : '';
+            ss += (this.border) ? 'border:'+this.border+';' : '';
+            ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
+            ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : '';
+            ss += (this.textColor) ? 'color:'+this.textColor+';' : '';
+            this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>');
+            // Mekko charts  legends don't go by number of series, but by number of data points
+            // in the series.  Refactor things here for that.
+            
+            var pad = false, 
+                reverse = true,    // mekko charts are always stacked, so reverse
+                nr, nc;
+            var s = series[0];
+            var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors);
+            
+            if (s.show) {
+                var pd = s.data;
+                if (this.numberRows) {
+                    nr = this.numberRows;
+                    if (!this.numberColumns){
+                        nc = Math.ceil(pd.length/nr);
+                    }
+                    else{
+                        nc = this.numberColumns;
+                    }
+                }
+                else if (this.numberColumns) {
+                    nc = this.numberColumns;
+                    nr = Math.ceil(pd.length/this.numberColumns);
+                }
+                else {
+                    nr = pd.length;
+                    nc = 1;
+                }
+                
+                var i, j, tr, td1, td2, lt, rs, color;
+                var idx = 0;    
+                
+                for (i=0; i<nr; i++) {
+                    if (reverse){
+                        tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem);
+                    }
+                    else{
+                        tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem);
+                    }
+                    for (j=0; j<nc; j++) {
+                        if (idx < pd.length) {
+                            lt = this.labels[idx] || pd[idx][0].toString();
+                            color = colorGenerator.next();
+                            if (!reverse){
+                                if (i>0){
+                                    pad = true;
+                                }
+                                else{
+                                    pad = false;
+                                }
+                            }
+                            else{
+                                if (i == nr -1){
+                                    pad = false;
+                                }
+                                else{
+                                    pad = true;
+                                }
+                            }
+                            rs = (pad) ? this.rowSpacing : '0';
+                
+                            td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+
+                                '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+
+                                '</div></td>');
+                            td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
+                            if (this.escapeHtml){
+                                td2.text(lt);
+                            }
+                            else {
+                                td2.html(lt);
+                            }
+                            if (reverse) {
+                                td2.prependTo(tr);
+                                td1.prependTo(tr);
+                            }
+                            else {
+                                td1.appendTo(tr);
+                                td2.appendTo(tr);
+                            }
+                            pad = true;
+                        }
+                        idx++;
+                    }   
+                }
+            }
+        }
+        return this._elem;
+    };
+    
+    $.jqplot.MekkoLegendRenderer.prototype.pack = function(offsets) {
+        if (this.show) {
+            // fake a grid for positioning
+            var grid = {_top:offsets.top, _left:offsets.left, _right:offsets.right, _bottom:this._plotDimensions.height - offsets.bottom};        
+            if (this.placement == 'insideGrid') {
+                switch (this.location) {
+                    case 'nw':
+                        var a = grid._left + this.xoffset;
+                        var b = grid._top + this.yoffset;
+                        this._elem.css('left', a);
+                        this._elem.css('top', b);
+                        break;
+                    case 'n':
+                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+                        var b = grid._top + this.yoffset;
+                        this._elem.css('left', a);
+                        this._elem.css('top', b);
+                        break;
+                    case 'ne':
+                        var a = offsets.right + this.xoffset;
+                        var b = grid._top + this.yoffset;
+                        this._elem.css({right:a, top:b});
+                        break;
+                    case 'e':
+                        var a = offsets.right + this.xoffset;
+                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+                        this._elem.css({right:a, top:b});
+                        break;
+                    case 'se':
+                        var a = offsets.right + this.xoffset;
+                        var b = offsets.bottom + this.yoffset;
+                        this._elem.css({right:a, bottom:b});
+                        break;
+                    case 's':
+                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+                        var b = offsets.bottom + this.yoffset;
+                        this._elem.css({left:a, bottom:b});
+                        break;
+                    case 'sw':
+                        var a = grid._left + this.xoffset;
+                        var b = offsets.bottom + this.yoffset;
+                        this._elem.css({left:a, bottom:b});
+                        break;
+                    case 'w':
+                        var a = grid._left + this.xoffset;
+                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+                        this._elem.css({left:a, top:b});
+                        break;
+                    default:  // same as 'se'
+                        var a = grid._right - this.xoffset;
+                        var b = grid._bottom + this.yoffset;
+                        this._elem.css({right:a, bottom:b});
+                        break;
+                }
+                
+            }
+            else {
+                switch (this.location) {
+                    case 'nw':
+                        var a = this._plotDimensions.width - grid._left + this.xoffset;
+                        var b = grid._top + this.yoffset;
+                        this._elem.css('right', a);
+                        this._elem.css('top', b);
+                        break;
+                    case 'n':
+                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+                        var b = this._plotDimensions.height - grid._top + this.yoffset;
+                        this._elem.css('left', a);
+                        this._elem.css('bottom', b);
+                        break;
+                    case 'ne':
+                        var a = this._plotDimensions.width - offsets.right + this.xoffset;
+                        var b = grid._top + this.yoffset;
+                        this._elem.css({left:a, top:b});
+                        break;
+                    case 'e':
+                        var a = this._plotDimensions.width - offsets.right + this.xoffset;
+                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+                        this._elem.css({left:a, top:b});
+                        break;
+                    case 'se':
+                        var a = this._plotDimensions.width - offsets.right + this.xoffset;
+                        var b = offsets.bottom + this.yoffset;
+                        this._elem.css({left:a, bottom:b});
+                        break;
+                    case 's':
+                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+                        var b = this._plotDimensions.height - offsets.bottom + this.yoffset;
+                        this._elem.css({left:a, top:b});
+                        break;
+                    case 'sw':
+                        var a = this._plotDimensions.width - grid._left + this.xoffset;
+                        var b = offsets.bottom + this.yoffset;
+                        this._elem.css({right:a, bottom:b});
+                        break;
+                    case 'w':
+                        var a = this._plotDimensions.width - grid._left + this.xoffset;
+                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+                        this._elem.css({right:a, top:b});
+                        break;
+                    default:  // same as 'se'
+                        var a = grid._right - this.xoffset;
+                        var b = grid._bottom + this.yoffset;
+                        this._elem.css({right:a, bottom:b});
+                        break;
+                }
+            }
+        } 
+    };
+    
+    // setup default renderers for axes and legend so user doesn't have to
+    // called with scope of plot
+    function preInit(target, data, options) {
+        options = options || {};
+        options.axesDefaults = options.axesDefaults || {};
+        options.legend = options.legend || {};
+        options.seriesDefaults = options.seriesDefaults || {};
+        var setopts = false;
+        if (options.seriesDefaults.renderer == $.jqplot.MekkoRenderer) {
+            setopts = true;
+        }
+        else if (options.series) {
+            for (var i=0; i < options.series.length; i++) {
+                if (options.series[i].renderer == $.jqplot.MekkoRenderer) {
+                    setopts = true;
+                }
+            }
+        }
+        
+        if (setopts) {
+            options.axesDefaults.renderer = $.jqplot.MekkoAxisRenderer;
+            options.legend.renderer = $.jqplot.MekkoLegendRenderer;
+            options.legend.preDraw = true;
+        }
+    }
+    
+    $.jqplot.preInitHooks.push(preInit);
+    
+})(jQuery);    
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.meterGaugeRenderer.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.meterGaugeRenderer.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.meterGaugeRenderer.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,1129 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {
+    /**
+     * Class: $.jqplot.MeterGaugeRenderer
+     * Plugin renderer to draw a meter gauge chart.
+     * 
+     * Data consists of a single series with 1 data point to position the gauge needle.
+     * 
+     * To use this renderer, you need to include the 
+     * meter gauge renderer plugin, for example:
+     * 
+     * > <script type="text/javascript" src="plugins/jqplot.meterGaugeRenderer.js"></script>
+     * 
+     * Properties described here are passed into the $.jqplot function
+     * as options on the series renderer.  For example:
+     * 
+     * > plot0 = $.jqplot('chart0',[[18]],{
+     * >     title: 'Network Speed',
+     * >     seriesDefaults: {
+     * >         renderer: $.jqplot.MeterGaugeRenderer,
+     * >         rendererOptions: {
+     * >             label: 'MB/s'
+     * >         }
+     * >     }
+     * > });
+     * 
+     * A meterGauge plot does not support events.
+     */
+    $.jqplot.MeterGaugeRenderer = function(){
+        $.jqplot.LineRenderer.call(this);
+    };
+    
+    $.jqplot.MeterGaugeRenderer.prototype = new $.jqplot.LineRenderer();
+    $.jqplot.MeterGaugeRenderer.prototype.constructor = $.jqplot.MeterGaugeRenderer;
+    
+    // called with scope of a series
+    $.jqplot.MeterGaugeRenderer.prototype.init = function(options) {
+        // Group: Properties
+        //
+        // prop: diameter
+        // Outer diameter of the meterGauge, auto computed by default
+        this.diameter = null;
+        // prop: padding
+        // padding between the meterGauge and plot edges, auto
+        // calculated by default.
+        this.padding = null;
+        // prop: shadowOffset
+        // offset of the shadow from the gauge ring and offset of 
+        // each succesive stroke of the shadow from the last.
+        this.shadowOffset = 2;
+        // prop: shadowAlpha
+        // transparency of the shadow (0 = transparent, 1 = opaque)
+        this.shadowAlpha = 0.07;
+        // prop: shadowDepth
+        // number of strokes to apply to the shadow, 
+        // each stroke offset shadowOffset from the last.
+        this.shadowDepth = 4;
+        // prop: background
+        // background color of the inside of the gauge.
+        this.background = "#efefef";
+        // prop: ringColor
+        // color of the outer ring, hub, and needle of the gauge.
+        this.ringColor = "#BBC6D0";
+        // needle color not implemented yet.
+        this.needleColor = "#C3D3E5";
+        // prop: tickColor
+        // color of the tick marks around the gauge.
+        this.tickColor = "989898";
+        // prop: ringWidth
+        // width of the ring around the gauge.  Auto computed by default.
+        this.ringWidth = null;
+        // prop: min
+        // Minimum value on the gauge.  Auto computed by default
+        this.min;
+        // prop: max
+        // Maximum value on the gauge. Auto computed by default
+        this.max;
+        // prop: ticks
+        // Array of tick values. Auto computed by default.
+        this.ticks = [];
+        // prop: showTicks
+        // true to show ticks around gauge.
+        this.showTicks = true;
+        // prop: showTickLabels
+        // true to show tick labels next to ticks.
+        this.showTickLabels = true;
+        // prop: label
+        // A gauge label like 'kph' or 'Volts'
+        this.label = null;
+        // prop: labelHeightAdjust
+        // Number of Pixels to offset the label up (-) or down (+) from its default position.
+        this.labelHeightAdjust = 0;
+        // prop: labelPosition
+        // Where to position the label, either 'inside' or 'bottom'.
+        this.labelPosition = 'inside';
+        // prop: intervals
+        // Array of ranges to be drawn around the gauge.
+        // Array of form:
+        // > [value1, value2, ...]
+        // indicating the values for the first, second, ... intervals.
+        this.intervals = [];
+        // prop: intervalColors
+        // Array of colors to use for the intervals.
+        this.intervalColors = [ "#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"];
+        // prop: intervalInnerRadius
+        // Radius of the inner circle of the interval ring.
+        this.intervalInnerRadius =  null;
+        // prop: intervalOuterRadius
+        // Radius of the outer circle of the interval ring.
+        this.intervalOuterRadius = null;
+        this.tickRenderer = $.jqplot.MeterGaugeTickRenderer;
+        // ticks spaced every 1, 2, 2.5, 5, 10, 20, .1, .2, .25, .5, etc.
+        this.tickPositions = [1, 2, 2.5, 5, 10];
+        // prop: tickSpacing
+        // Degrees between ticks.  This is a target number, if 
+        // incompatible span and ticks are supplied, a suitable
+        // spacing close to this value will be computed.
+        this.tickSpacing = 30;
+        this.numberMinorTicks = null;
+        // prop: hubRadius
+        // Radius of the hub at the bottom center of gauge which the needle attaches to.
+        // Auto computed by default
+        this.hubRadius = null;
+        // prop: tickPadding
+        // padding of the tick marks to the outer ring and the tick labels to marks.
+        // Auto computed by default.
+        this.tickPadding = null;
+        // prop: needleThickness
+        // Maximum thickness the needle.  Auto computed by default.
+        this.needleThickness = null;
+        // prop: needlePad
+        // Padding between needle and inner edge of the ring when the needle is at the min or max gauge value.
+        this.needlePad = 6;
+        // prop: pegNeedle
+        // True will stop needle just below/above the  min/max values if data is below/above min/max,
+        // as if the meter is "pegged".
+        this.pegNeedle = true;
+        
+        $.extend(true, this, options);
+        this.type = null;
+        this.numberTicks = null;
+        this.tickInterval = null;
+        // span, the sweep (in degrees) from min to max.  This gauge is 
+        // a semi-circle.
+        this.span = 180;
+        // get rid of this nonsense
+        // this.innerSpan = this.span;
+        if (this.type == 'circular') {
+            this.semiCircular = false;
+        }
+        else if (this.type != 'circular') {
+            this.semiCircular = true;
+        }
+        else {
+            this.semiCircular = (this.span <= 180) ? true : false;
+        }
+        this._tickPoints = [];
+        // reference to label element.
+        this._labelElm = null;
+        
+        // start the gauge at the beginning of the span
+        this.startAngle = (90 + (360 - this.span)/2) * Math.PI/180;
+        this.endAngle = (90 - (360 - this.span)/2) * Math.PI/180;
+        
+        this.setmin = !!(this.min == null);
+        this.setmax = !!(this.max == null);
+        
+        // if given intervals and is an array of values, create labels and colors.
+        if (this.intervals.length) {
+            if (this.intervals[0].length == null || this.intervals.length == 1) {
+                for (var i=0; i<this.intervals.length; i++) {
+                    this.intervals[i] = [this.intervals[i], this.intervals[i], this.intervalColors[i]];
+                }
+            }
+            else if (this.intervals[0].length == 2) {
+                for (i=0; i<this.intervals.length; i++) {
+                    this.intervals[i] = [this.intervals[i][0], this.intervals[i][1], this.intervalColors[i]];
+                }
+            }
+        }
+        
+        // compute min, max and ticks if not supplied:
+        if (this.ticks.length) {
+            if (this.ticks[0].length == null || this.ticks[0].length == 1) {
+                for (var i=0; i<this.ticks.length; i++) {
+                    this.ticks[i] = [this.ticks[i], this.ticks[i]];
+                }
+            }
+            this.min = (this.min == null) ? this.ticks[0][0] : this.min;
+            this.max = (this.max == null) ? this.ticks[this.ticks.length-1][0] : this.max;
+            this.setmin = false;
+            this.setmax = false;
+            this.numberTicks = this.ticks.length;
+            this.tickInterval = this.ticks[1][0] - this.ticks[0][0];
+            this.tickFactor = Math.floor(parseFloat((Math.log(this.tickInterval)/Math.log(10)).toFixed(11)));
+            // use the first interal to calculate minor ticks;
+            this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor);
+            if (!this.numberMinorTicks) {
+                this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor-1);
+            }
+            if (!this.numberMinorTicks) {
+                this.numberMinorTicks = 1;
+            }
+        }
+        
+        else if (this.intervals.length) {
+            this.min = (this.min == null) ? 0 : this.min;
+            this.setmin = false;
+            if (this.max == null) {
+                if (this.intervals[this.intervals.length-1][0] >= this.data[0][1]) {
+                    this.max = this.intervals[this.intervals.length-1][0];
+                    this.setmax = false;
+                }
+            }
+            else {
+                this.setmax = false;
+            }
+        }
+        
+        else {
+            // no ticks and no intervals supplied, put needle in middle
+            this.min = (this.min == null) ? 0 : this.min;
+            this.setmin = false;
+            if (this.max == null) {
+                this.max = this.data[0][1] * 1.25;
+                this.setmax = true;
+            }
+            else {
+                this.setmax = false;
+            }
+        }
+    };
+    
+    $.jqplot.MeterGaugeRenderer.prototype.setGridData = function(plot) {
+        // set gridData property.  This will hold angle in radians of each data point.
+        var stack = [];
+        var td = [];
+        var sa = this.startAngle;
+        for (var i=0; i<this.data.length; i++){
+            stack.push(this.data[i][1]);
+            td.push([this.data[i][0]]);
+            if (i>0) {
+                stack[i] += stack[i-1];
+            }
+        }
+        var fact = Math.PI*2/stack[stack.length - 1];
+        
+        for (var i=0; i<stack.length; i++) {
+            td[i][1] = stack[i] * fact;
+        }
+        this.gridData = td;
+    };
+    
+    $.jqplot.MeterGaugeRenderer.prototype.makeGridData = function(data, plot) {
+        var stack = [];
+        var td = [];
+        var sa = this.startAngle;
+        for (var i=0; i<data.length; i++){
+            stack.push(data[i][1]);
+            td.push([data[i][0]]);
+            if (i>0) {
+                stack[i] += stack[i-1];
+            }
+        }
+        var fact = Math.PI*2/stack[stack.length - 1];
+        
+        for (var i=0; i<stack.length; i++) {
+            td[i][1] = stack[i] * fact;
+        }
+        return td;
+    };
+
+        
+    function getnmt(pos, interval, fact) {
+        var temp;
+        for (i=pos.length-1; i>=0; i--) {
+            temp = interval/(pos[i] * Math.pow(10, fact));
+            if (temp == 4 || temp == 5) {
+                return temp - 1;
+            }
+        }
+        return null;
+    }
+    
+    // called with scope of series
+    $.jqplot.MeterGaugeRenderer.prototype.draw = function (ctx, gd, options) {
+        var i;
+        var opts = (options != undefined) ? options : {};
+        // offset and direction of offset due to legend placement
+        var offx = 0;
+        var offy = 0;
+        var trans = 1;
+        if (options.legendInfo && options.legendInfo.placement == 'inside') {
+            var li = options.legendInfo;
+            switch (li.location) {
+                case 'nw':
+                    offx = li.width + li.xoffset;
+                    break;
+                case 'w':
+                    offx = li.width + li.xoffset;
+                    break;
+                case 'sw':
+                    offx = li.width + li.xoffset;
+                    break;
+                case 'ne':
+                    offx = li.width + li.xoffset;
+                    trans = -1;
+                    break;
+                case 'e':
+                    offx = li.width + li.xoffset;
+                    trans = -1;
+                    break;
+                case 'se':
+                    offx = li.width + li.xoffset;
+                    trans = -1;
+                    break;
+                case 'n':
+                    offy = li.height + li.yoffset;
+                    break;
+                case 's':
+                    offy = li.height + li.yoffset;
+                    trans = -1;
+                    break;
+                default:
+                    break;
+            }
+        }
+        
+        
+            
+        // pre-draw so can get it's dimensions.
+        if (this.label) {
+            this._labelElem = $('<div class="jqplot-meterGauge-label" style="position:absolute;">'+this.label+'</div>');
+            this.canvas._elem.after(this._labelElem);
+        }
+        
+        var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
+        var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
+        var fill = (opts.fill != undefined) ? opts.fill : this.fill;
+        var cw = ctx.canvas.width;
+        var ch = ctx.canvas.height;
+        if (this.padding == null) {
+            this.padding = Math.round(Math.min(cw, ch)/30);
+        }
+        var w = cw - offx - 2 * this.padding;
+        var h = ch - offy - 2 * this.padding;
+        if (this.labelPosition == 'bottom' && this.label) {
+            h -= this._labelElem.outerHeight(true);
+        }
+        var mindim = Math.min(w,h);
+        var d = mindim;
+            
+        if (!this.diameter) {
+            if (this.semiCircular) {
+                if ( w >= 2*h) {
+                    if (!this.ringWidth) {
+                        this.ringWidth = 2*h/35;
+                    }
+                    this.needleThickness = this.needleThickness || 2+Math.pow(this.ringWidth, 0.8);
+                    this.innerPad = this.ringWidth/2 + this.needleThickness/2 + this.needlePad;
+                    this.diameter = 2 * (h - 2*this.innerPad);
+                }
+                else {
+                    if (!this.ringWidth) {
+                        this.ringWidth = w/35;
+                    }
+                    this.needleThickness = this.needleThickness || 2+Math.pow(this.ringWidth, 0.8);
+                    this.innerPad = this.ringWidth/2 + this.needleThickness/2 + this.needlePad;
+                    this.diameter = w - 2*this.innerPad;
+                }
+                // center taking into account legend and over draw for gauge bottom below hub.
+                // this will be center of hub.
+                this._center = [(cw - trans * offx)/2 + trans * offx,  (ch + trans*offy - this.padding - this.ringWidth - this.innerPad)];
+            }
+            else {
+                if (!this.ringWidth) {
+                    this.ringWidth = d/35;
+                }
+                this.needleThickness = this.needleThickness || 2+Math.pow(this.ringWidth, 0.8);
+                this.innerPad = 0;
+                this.diameter = d - this.ringWidth;
+                // center in middle of canvas taking into account legend.
+                // will be center of hub.
+                this._center = [(cw-trans*offx)/2 + trans * offx, (ch-trans*offy)/2 + trans * offy];
+            }
+        }
+        
+        if (this._labelElem && this.labelPosition == 'bottom') {
+            this._center[1] -= this._labelElem.outerHeight(true);
+        }
+        
+        this._radius = this.diameter/2;
+        
+        this.tickSpacing = 6000/this.diameter;
+        
+        if (!this.hubRadius) {
+            this.hubRadius = this.diameter/18;
+        }
+        
+        this.shadowOffset = 0.5 + this.ringWidth/9;
+        this.shadowWidth = this.ringWidth*1;
+        
+        this.tickPadding = 3 + Math.pow(this.diameter/20, 0.7);
+        this.tickOuterRadius = this._radius - this.ringWidth/2 - this.tickPadding;
+        this.tickLength = (this.showTicks) ? this._radius/13 : 0;
+        
+        if (this.ticks.length == 0) {
+            // no ticks, lets make some.
+            var max = this.max,
+                min = this.min,
+                setmax = this.setmax,
+                setmin = this.setmin,
+                ti = (max - min) * this.tickSpacing / this.span;
+            var tf = Math.floor(parseFloat((Math.log(ti)/Math.log(10)).toFixed(11)));
+            var tp = (ti/Math.pow(10, tf));
+            (tp > 2 && tp <= 2.5) ? tp = 2.5 : tp = Math.ceil(tp);
+            var t = this.tickPositions;
+            var tpindex, nt;
+    
+            for (i=0; i<t.length; i++) {
+                if (tp == t[i] || i && t[i-1] < tp && tp < t[i]) { 
+                    ti = t[i]*Math.pow(10, tf);
+                    tpindex = i;
+                }
+            }
+        
+            for (i=0; i<t.length; i++) {
+                if (tp == t[i] || i && t[i-1] < tp && tp < t[i]) { 
+                    ti = t[i]*Math.pow(10, tf);
+                    nt = Math.ceil((max - min) / ti);
+                }
+            }
+        
+            // both max and min are free
+            if (setmax && setmin) {
+                var tmin = (min > 0) ? min - min % ti : min - min % ti - ti;
+                if (!this.forceZero) {
+                    var diff = Math.min(min - tmin, 0.8*ti);
+                    var ntp = Math.floor(diff/t[tpindex]);
+                    if (ntp > 1) {
+                        tmin = tmin + t[tpindex] * (ntp-1);
+                        if (parseInt(tmin, 10) != tmin && parseInt(tmin-t[tpindex], 10) == tmin-t[tpindex]) {
+                            tmin = tmin - t[tpindex];
+                        }
+                    }
+                }
+                if (min == tmin) {
+                    min -= ti;
+                }
+                else {
+                    // tmin should always be lower than dataMin
+                    if (min - tmin > 0.23*ti) {
+                        min = tmin;
+                    }
+                    else {
+                        min = tmin -ti;
+                        nt += 1;
+                    }
+                }
+                nt += 1;
+                var tmax = min + (nt - 1) * ti;
+                if (max >= tmax) { 
+                    tmax += ti;
+                    nt += 1;
+                }
+                // now tmax should always be mroe than dataMax
+                if (tmax - max < 0.23*ti) { 
+                    tmax += ti;
+                    nt += 1;
+                }
+                this.max = max = tmax;
+                this.min = min;    
+
+                this.tickInterval = ti;
+                this.numberTicks = nt;
+                var it;
+                for (i=0; i<nt; i++) {
+                    it = parseFloat((min+i*ti).toFixed(11));
+                    this.ticks.push([it, it]);
+                }
+                this.max = this.ticks[nt-1][1];
+            
+                this.tickFactor = tf;      
+                // determine number of minor ticks
+
+                this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor);     
+        
+                if (!this.numberMinorTicks) {
+                    this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor-1);
+                }
+            }
+            // max is free, min is fixed
+            else if (setmax) {
+                var tmax = min + (nt - 1) * ti;
+                if (max >= tmax) {
+                    max = tmax + ti;
+                    nt += 1;
+                }
+                else {
+                    max = tmax;
+                }
+
+                this.tickInterval = this.tickInterval || ti;
+                this.numberTicks = this.numberTicks || nt;
+                var it;
+                for (i=0; i<this.numberTicks; i++) {
+                    it = parseFloat((min+i*this.tickInterval).toFixed(11));
+                    this.ticks.push([it, it]);
+                }
+                this.max = this.ticks[this.numberTicks-1][1];
+            
+                this.tickFactor = tf;
+                // determine number of minor ticks
+                this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor);
+        
+                if (!this.numberMinorTicks) {
+                    this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor-1);
+                }
+            }
+            
+            // not setting max or min
+            if (!setmax && !setmin) {
+                var range = this.max - this.min;
+                tf = Math.floor(parseFloat((Math.log(range)/Math.log(10)).toFixed(11))) - 1;
+                var nticks = [5,6,4,7,3,8,9,10,2], res, numticks, nonSigDigits=0, sigRange;
+                // check to see how many zeros are at the end of the range
+                if (range > 1) {
+                    var rstr = String(range);
+                    if (rstr.search(/\./) == -1) {
+                         var pos = rstr.search(/0+$/);
+                         nonSigDigits = (pos > 0) ? rstr.length - pos - 1 : 0;
+                    }
+                }
+                sigRange = range/Math.pow(10, nonSigDigits);
+                for (i=0; i<nticks.length; i++) {
+                    res = sigRange/(nticks[i]-1);
+                    if (res == parseInt(res, 10)) {
+                        this.numberTicks = nticks[i];
+                        this.tickInterval = range/(this.numberTicks-1);
+                        this.tickFactor = tf+1;
+                        break;
+                    }
+                }
+                var it;
+                for (i=0; i<this.numberTicks; i++) {
+                    it = parseFloat((this.min+i*this.tickInterval).toFixed(11));
+                    this.ticks.push([it, it]);
+                }
+                // determine number of minor ticks
+                this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor);
+        
+                if (!this.numberMinorTicks) {
+                    this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor-1);
+                }
+                
+                if (!this.numberMinorTicks) {
+                    this.numberMinorTicks = 1;
+                    var nums = [4, 5, 3, 6, 2];
+                    for (i=0; i<5; i++) {
+                        temp = this.tickInterval/nums[i];
+                        if (temp == parseInt(temp)) {
+                            this.numberMinorTicks = nums[i]-1;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        
+
+        var r = this._radius,
+            sa = this.startAngle,
+            ea = this.endAngle,       
+            pi = Math.PI,
+            hpi = Math.PI/2;
+            
+        if (this.semiCircular) {
+            var overAngle = Math.atan(this.innerPad/r),
+                outersa = this.outerStartAngle = sa - overAngle,
+                outerea = this.outerEndAngle = ea + overAngle,
+                hubsa = this.hubStartAngle = sa - Math.atan(this.innerPad/this.hubRadius*2),
+                hubea = this.hubEndAngle = ea + Math.atan(this.innerPad/this.hubRadius*2);
+
+            ctx.save();            
+            
+            ctx.translate(this._center[0], this._center[1]);
+            ctx.lineJoin = "round";
+            ctx.lineCap = "round";
+            
+            // draw the innerbackground
+            ctx.save();
+            ctx.beginPath();  
+            ctx.fillStyle = this.background;
+            ctx.arc(0, 0, r, outersa, outerea, false);
+            ctx.closePath();
+            ctx.fill();
+            ctx.restore();
+            
+            // draw the shadow
+            // the outer ring.
+            var shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')';
+            ctx.save();
+            for (var i=0; i<this.shadowDepth; i++) {
+                ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));
+                ctx.beginPath();  
+                ctx.strokeStyle = shadowColor;
+                ctx.lineWidth = this.shadowWidth;
+                ctx.arc(0 ,0, r, outersa, outerea, false);
+                ctx.closePath();
+                ctx.stroke();
+            }
+            ctx.restore();
+            
+            // the inner hub.
+            ctx.save();
+            var tempd = parseInt((this.shadowDepth+1)/2, 10);
+            for (var i=0; i<tempd; i++) {
+                ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));
+                ctx.beginPath();  
+                ctx.fillStyle = shadowColor;
+                ctx.arc(0 ,0, this.hubRadius, hubsa, hubea, false);
+                ctx.closePath();
+                ctx.fill();
+            }
+            ctx.restore();
+            
+            // draw the outer ring.
+            ctx.save();
+            ctx.beginPath();  
+            ctx.strokeStyle = this.ringColor;
+            ctx.lineWidth = this.ringWidth;
+            ctx.arc(0 ,0, r, outersa, outerea, false);
+            ctx.closePath();
+            ctx.stroke();
+            ctx.restore();
+            
+            // draw the hub
+            
+            ctx.save();
+            ctx.beginPath();  
+            ctx.fillStyle = this.ringColor;
+            ctx.arc(0 ,0, this.hubRadius,hubsa, hubea, false);
+            ctx.closePath();
+            ctx.fill();
+            ctx.restore();
+            
+            // draw the ticks
+            if (this.showTicks) {
+                ctx.save();
+                var orad = this.tickOuterRadius,
+                    tl = this.tickLength,
+                    mtl = tl/2,
+                    nmt = this.numberMinorTicks,
+                    ts = this.span * Math.PI / 180 / (this.ticks.length-1),
+                    mts = ts/(nmt + 1);
+                
+                for (i = 0; i<this.ticks.length; i++) {
+                    ctx.beginPath();
+                    ctx.lineWidth = 1.5 + this.diameter/360;
+                    ctx.strokeStyle = this.ringColor;
+                    var wps = ts*i+sa;
+                    ctx.moveTo(-orad * Math.cos(ts*i+sa), orad * Math.sin(ts*i+sa));
+                    ctx.lineTo(-(orad-tl) * Math.cos(ts*i+sa), (orad - tl) * Math.sin(ts*i+sa));
+                    this._tickPoints.push([(orad-tl) * Math.cos(ts*i+sa) + this._center[0] + this.canvas._offsets.left, (orad - tl) * Math.sin(ts*i+sa) + this._center[1] + this.canvas._offsets.top, ts*i+sa]);
+                    ctx.stroke();
+                    ctx.lineWidth = 1.0 + this.diameter/440;
+                    if (i<this.ticks.length-1) {
+                        for (var j=1; j<=nmt; j++) {
+                            ctx.beginPath();
+                            ctx.moveTo(-orad * Math.cos(ts*i+mts*j+sa), orad * Math.sin(ts*i+mts*j+sa));
+                            ctx.lineTo(-(orad-mtl) * Math.cos(ts*i+mts*j+sa), (orad-mtl) * Math.sin(ts*i+mts*j+sa));
+                            ctx.stroke();
+                        }   
+                    }
+                }
+                ctx.restore();
+            }
+            
+            // draw the tick labels
+            if (this.showTickLabels) {
+                var elem, l, t, ew, dim, maxdim=0;
+                var tp = this.tickPadding * (1 - 1/(this.diameter/80+1));
+                for (i=0; i<this.ticks.length; i++) {
+                    elem = $('<div class="jqplot-meterGauge-tick" style="position:absolute;">'+this.ticks[i][1]+'</div>');
+                    this.canvas._elem.after(elem);
+                    ew = elem.outerWidth(true);
+                    eh = elem.outerHeight(true);
+                    l = this._tickPoints[i][0] - ew * (this._tickPoints[i][2]-Math.PI)/Math.PI - tp * Math.cos(this._tickPoints[i][2]);
+                    t = this._tickPoints[i][1] - eh/2 + eh/2 * Math.pow(Math.abs((Math.sin(this._tickPoints[i][2]))), 0.5) + tp/3 * Math.pow(Math.abs((Math.sin(this._tickPoints[i][2]))), 0.5) ;
+                    // t = this._tickPoints[i][1] - eh/2 - eh/2 * Math.sin(this._tickPoints[i][2]) - tp/2 * Math.sin(this._tickPoints[i][2]);
+                    elem.css({left:l, top:t});
+                    dim  = ew*Math.cos(this._tickPoints[i][2]) + eh*Math.sin(Math.PI/2+this._tickPoints[i][2]/2);
+                    maxdim = (dim > maxdim) ? dim : maxdim;
+                }
+            }
+            
+            // draw the gauge label
+            if (this.label && this.labelPosition == 'inside') {
+                var l = this._center[0] + this.canvas._offsets.left;
+                var tp = this.tickPadding * (1 - 1/(this.diameter/80+1));
+                var t = 0.5*(this._center[1] + this.canvas._offsets.top - this.hubRadius) + 0.5*(this._center[1] + this.canvas._offsets.top - this.tickOuterRadius + this.tickLength + tp) + this.labelHeightAdjust;
+                // this._labelElem = $('<div class="jqplot-meterGauge-label" style="position:absolute;">'+this.label+'</div>');
+                // this.canvas._elem.after(this._labelElem);
+                l -= this._labelElem.outerWidth(true)/2;
+                t -= this._labelElem.outerHeight(true)/2;
+                this._labelElem.css({left:l, top:t});
+            }
+            
+            else if (this.label && this.labelPosition == 'bottom') {
+                var l = this._center[0] + this.canvas._offsets.left - this._labelElem.outerWidth(true)/2;
+                var t = this._center[1] + this.canvas._offsets.top + this.innerPad + + this.ringWidth + this.padding + this.labelHeightAdjust;
+                this._labelElem.css({left:l, top:t});
+                
+            }
+            
+            // draw the intervals
+            
+            ctx.save();
+            var inner = this.intervalInnerRadius || this.hubRadius * 1.5;
+            if (this.intervalOuterRadius == null) {
+                if (this.showTickLabels) {
+                    var outer = (this.tickOuterRadius - this.tickLength - this.tickPadding - this.diameter/8);
+                }
+                else {
+                    var outer = (this.tickOuterRadius - this.tickLength - this.diameter/16);
+                }
+            }
+            else {
+                var outer = this.intervalOuterRadius;
+            }
+            var range = this.max - this.min;
+            var intrange = this.intervals[this.intervals.length-1] - this.min;
+            var start, end, span = this.span*Math.PI/180;
+            for (i=0; i<this.intervals.length; i++) {
+                start = (i == 0) ? sa : sa + (this.intervals[i-1][0] - this.min)*span/range;
+                if (start < 0) {
+                    start = 0;
+                }
+                end = sa + (this.intervals[i][0] - this.min)*span/range;
+                if (end < 0) {
+                    end = 0;
+                }
+                ctx.beginPath();
+                ctx.fillStyle = this.intervals[i][2];
+                ctx.arc(0, 0, inner, start, end, false);
+                ctx.lineTo(outer*Math.cos(end), outer*Math.sin(end));
+                ctx.arc(0, 0, outer, end, start, true);
+                ctx.lineTo(inner*Math.cos(start), inner*Math.sin(start));
+                ctx.closePath();
+                ctx.fill();
+            }
+            ctx.restore();
+            
+            // draw the needle
+            var datapoint = this.data[0][1];
+            var dataspan = this.max - this.min;
+            if (this.pegNeedle) {
+                if (this.data[0][1] > this.max + dataspan*3/this.span) {
+                    datapoint = this.max + dataspan*3/this.span;
+                }
+                if (this.data[0][1] < this.min - dataspan*3/this.span) {
+                    datapoint = this.min - dataspan*3/this.span;
+                }
+            }
+            var dataang = (datapoint - this.min)/dataspan * this.span * Math.PI/180 + this.startAngle;
+            
+            
+            ctx.save();
+            ctx.beginPath();
+            ctx.fillStyle = this.ringColor;
+            ctx.strokeStyle = this.ringColor;
+            this.needleLength = (this.tickOuterRadius - this.tickLength) * 0.85;
+            this.needleThickness = (this.needleThickness < 2) ? 2 : this.needleThickness;
+            var endwidth = this.needleThickness * 0.4;
+
+            
+            var dl = this.needleLength/10;
+            var dt = (this.needleThickness - endwidth)/10;
+            var templ;
+            for (var i=0; i<10; i++) {
+                templ = this.needleThickness - i*dt;
+                ctx.moveTo(dl*i*Math.cos(dataang), dl*i*Math.sin(dataang));
+                ctx.lineWidth = templ;
+                ctx.lineTo(dl*(i+1)*Math.cos(dataang), dl*(i+1)*Math.sin(dataang));
+                ctx.stroke();
+            }
+            
+            ctx.restore();
+        }
+        else {
+            this._center = [(cw - trans * offx)/2 + trans * offx, (ch - trans*offy)/2 + trans * offy];
+        }               
+    };
+    
+    $.jqplot.MeterGaugeAxisRenderer = function() {
+        $.jqplot.LinearAxisRenderer.call(this);
+    };
+    
+    $.jqplot.MeterGaugeAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
+    $.jqplot.MeterGaugeAxisRenderer.prototype.constructor = $.jqplot.MeterGaugeAxisRenderer;
+        
+    
+    // There are no traditional axes on a gauge chart.  We just need to provide
+    // dummy objects with properties so the plot will render.
+    // called with scope of axis object.
+    $.jqplot.MeterGaugeAxisRenderer.prototype.init = function(options){
+        //
+        this.tickRenderer = $.jqplot.MeterGaugeTickRenderer;
+        $.extend(true, this, options);
+        // I don't think I'm going to need _dataBounds here.
+        // have to go Axis scaling in a way to fit chart onto plot area
+        // and provide u2p and p2u functionality for mouse cursor, etc.
+        // for convienence set _dataBounds to 0 and 100 and
+        // set min/max to 0 and 100.
+        this._dataBounds = {min:0, max:100};
+        this.min = 0;
+        this.max = 100;
+        this.showTicks = false;
+        this.ticks = [];
+        this.showMark = false;
+        this.show = false; 
+    };
+    
+    $.jqplot.MeterGaugeLegendRenderer = function(){
+        $.jqplot.TableLegendRenderer.call(this);
+    };
+    
+    $.jqplot.MeterGaugeLegendRenderer.prototype = new $.jqplot.TableLegendRenderer();
+    $.jqplot.MeterGaugeLegendRenderer.prototype.constructor = $.jqplot.MeterGaugeLegendRenderer;
+    
+    /**
+     * Class: $.jqplot.MeterGaugeLegendRenderer
+     *Meter gauges don't typically have a legend, this overrides the default legend renderer.
+     */
+    $.jqplot.MeterGaugeLegendRenderer.prototype.init = function(options) {
+
+        // Maximum number of rows in the legend.  0 or null for unlimited.
+        this.numberRows = null;
+        // Maximum number of columns in the legend.  0 or null for unlimited.
+        this.numberColumns = null;
+        $.extend(true, this, options);
+    };
+    
+    // called with context of legend
+    $.jqplot.MeterGaugeLegendRenderer.prototype.draw = function() {
+        var legend = this;
+        if (this.show) {
+            var series = this._series;
+            var ss = 'position:absolute;';
+            ss += (this.background) ? 'background:'+this.background+';' : '';
+            ss += (this.border) ? 'border:'+this.border+';' : '';
+            ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
+            ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : '';
+            ss += (this.textColor) ? 'color:'+this.textColor+';' : '';
+            ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : '';
+            ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : '';
+            ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : '';
+            ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : '';
+            this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>');
+            // MeterGauge charts legends don't go by number of series, but by number of data points
+            // in the series.  Refactor things here for that.
+            
+            var pad = false, 
+                reverse = false,
+                nr, nc;
+            var s = series[0];
+            
+            if (s.show) {
+                var pd = s.data;
+                if (this.numberRows) {
+                    nr = this.numberRows;
+                    if (!this.numberColumns){
+                        nc = Math.ceil(pd.length/nr);
+                    }
+                    else{
+                        nc = this.numberColumns;
+                    }
+                }
+                else if (this.numberColumns) {
+                    nc = this.numberColumns;
+                    nr = Math.ceil(pd.length/this.numberColumns);
+                }
+                else {
+                    nr = pd.length;
+                    nc = 1;
+                }
+                
+                var i, j, tr, td1, td2, lt, rs, color;
+                var idx = 0;    
+                
+                for (i=0; i<nr; i++) {
+                    if (reverse){
+                        tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem);
+                    }
+                    else{
+                        tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem);
+                    }
+                    for (j=0; j<nc; j++) {
+                        if (idx < pd.length){
+                            lt = this.labels[idx] || pd[idx][0].toString();
+                            color = colorGenerator.next();
+                            if (!reverse){
+                                if (i>0){
+                                    pad = true;
+                                }
+                                else{
+                                    pad = false;
+                                }
+                            }
+                            else{
+                                if (i == nr -1){
+                                    pad = false;
+                                }
+                                else{
+                                    pad = true;
+                                }
+                            }
+                            rs = (pad) ? this.rowSpacing : '0';
+                
+                            td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+
+                                '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+
+                                '</div></td>');
+                            td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
+                            if (this.escapeHtml){
+                                td2.text(lt);
+                            }
+                            else {
+                                td2.html(lt);
+                            }
+                            if (reverse) {
+                                td2.prependTo(tr);
+                                td1.prependTo(tr);
+                            }
+                            else {
+                                td1.appendTo(tr);
+                                td2.appendTo(tr);
+                            }
+                            pad = true;
+                        }
+                        idx++;
+                    }   
+                }
+            }
+        }
+        return this._elem;                
+    };
+    
+    // $.jqplot.MeterGaugeLegendRenderer.prototype.pack = function(offsets) {
+    //     if (this.show) {
+    //         // fake a grid for positioning
+    //         var grid = {_top:offsets.top, _left:offsets.left, _right:offsets.right, _bottom:this._plotDimensions.height - offsets.bottom};        
+    //         if (this.placement == 'insideGrid') {
+    //             switch (this.location) {
+    //                 case 'nw':
+    //                     var a = grid._left + this.xoffset;
+    //                     var b = grid._top + this.yoffset;
+    //                     this._elem.css('left', a);
+    //                     this._elem.css('top', b);
+    //                     break;
+    //                 case 'n':
+    //                     var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+    //                     var b = grid._top + this.yoffset;
+    //                     this._elem.css('left', a);
+    //                     this._elem.css('top', b);
+    //                     break;
+    //                 case 'ne':
+    //                     var a = offsets.right + this.xoffset;
+    //                     var b = grid._top + this.yoffset;
+    //                     this._elem.css({right:a, top:b});
+    //                     break;
+    //                 case 'e':
+    //                     var a = offsets.right + this.xoffset;
+    //                     var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+    //                     this._elem.css({right:a, top:b});
+    //                     break;
+    //                 case 'se':
+    //                     var a = offsets.right + this.xoffset;
+    //                     var b = offsets.bottom + this.yoffset;
+    //                     this._elem.css({right:a, bottom:b});
+    //                     break;
+    //                 case 's':
+    //                     var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+    //                     var b = offsets.bottom + this.yoffset;
+    //                     this._elem.css({left:a, bottom:b});
+    //                     break;
+    //                 case 'sw':
+    //                     var a = grid._left + this.xoffset;
+    //                     var b = offsets.bottom + this.yoffset;
+    //                     this._elem.css({left:a, bottom:b});
+    //                     break;
+    //                 case 'w':
+    //                     var a = grid._left + this.xoffset;
+    //                     var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+    //                     this._elem.css({left:a, top:b});
+    //                     break;
+    //                 default:  // same as 'se'
+    //                     var a = grid._right - this.xoffset;
+    //                     var b = grid._bottom + this.yoffset;
+    //                     this._elem.css({right:a, bottom:b});
+    //                     break;
+    //             }
+    //             
+    //         }
+    //         else {
+    //             switch (this.location) {
+    //                 case 'nw':
+    //                     var a = this._plotDimensions.width - grid._left + this.xoffset;
+    //                     var b = grid._top + this.yoffset;
+    //                     this._elem.css('right', a);
+    //                     this._elem.css('top', b);
+    //                     break;
+    //                 case 'n':
+    //                     var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+    //                     var b = this._plotDimensions.height - grid._top + this.yoffset;
+    //                     this._elem.css('left', a);
+    //                     this._elem.css('bottom', b);
+    //                     break;
+    //                 case 'ne':
+    //                     var a = this._plotDimensions.width - offsets.right + this.xoffset;
+    //                     var b = grid._top + this.yoffset;
+    //                     this._elem.css({left:a, top:b});
+    //                     break;
+    //                 case 'e':
+    //                     var a = this._plotDimensions.width - offsets.right + this.xoffset;
+    //                     var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+    //                     this._elem.css({left:a, top:b});
+    //                     break;
+    //                 case 'se':
+    //                     var a = this._plotDimensions.width - offsets.right + this.xoffset;
+    //                     var b = offsets.bottom + this.yoffset;
+    //                     this._elem.css({left:a, bottom:b});
+    //                     break;
+    //                 case 's':
+    //                     var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+    //                     var b = this._plotDimensions.height - offsets.bottom + this.yoffset;
+    //                     this._elem.css({left:a, top:b});
+    //                     break;
+    //                 case 'sw':
+    //                     var a = this._plotDimensions.width - grid._left + this.xoffset;
+    //                     var b = offsets.bottom + this.yoffset;
+    //                     this._elem.css({right:a, bottom:b});
+    //                     break;
+    //                 case 'w':
+    //                     var a = this._plotDimensions.width - grid._left + this.xoffset;
+    //                     var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+    //                     this._elem.css({right:a, top:b});
+    //                     break;
+    //                 default:  // same as 'se'
+    //                     var a = grid._right - this.xoffset;
+    //                     var b = grid._bottom + this.yoffset;
+    //                     this._elem.css({right:a, bottom:b});
+    //                     break;
+    //             }
+    //         }
+    //     } 
+    // };
+    
+    // setup default renderers for axes and legend so user doesn't have to
+    // called with scope of plot
+    function preInit(target, data, options) {
+        options = options || {};
+        options.axesDefaults = options.axesDefaults || {};
+        options.legend = options.legend || {};
+        options.seriesDefaults = options.seriesDefaults || {};
+        options.grid = options.grid || {};
+        options.gridPadding = options.gridPadding || {};
+           
+        // only set these if there is a gauge series
+        var setopts = false;
+        if (options.seriesDefaults.renderer == $.jqplot.MeterGaugeRenderer) {
+            setopts = true;
+        }
+        else if (options.series) {
+            for (var i=0; i < options.series.length; i++) {
+                if (options.series[i].renderer == $.jqplot.MeterGaugeRenderer) {
+                    setopts = true;
+                }
+            }
+        }
+        
+        if (setopts) {
+            options.axesDefaults.renderer = $.jqplot.MeterGaugeAxisRenderer;
+            options.legend.renderer = $.jqplot.MeterGaugeLegendRenderer;
+            options.legend.preDraw = true;
+            options.grid.background = options.grid.background || 'white';
+            options.grid.drawGridlines = false;
+            options.grid.borderWidth = (options.grid.borderWidth != null) ? options.grid.borderWidth : 0;
+            options.grid.shadow = (options.grid.shadow != null) ? options.grid.shadow : false;
+            options.gridPadding.top = (options.gridPadding.top != null) ? options.gridPadding.top : 0;
+            options.gridPadding.bottom = (options.gridPadding.bottom != null) ? options.gridPadding.bottom : 0;
+            options.gridPadding.left = (options.gridPadding.left != null) ? options.gridPadding.left : 0;
+            options.gridPadding.right = (options.gridPadding.right != null) ? options.gridPadding.right : 0;
+        }
+    }
+    
+    // called with scope of plot
+    function postParseOptions(options) {
+        //
+    }
+    
+    $.jqplot.preInitHooks.push(preInit);
+    $.jqplot.postParseOptionsHooks.push(postParseOptions);
+    
+    $.jqplot.MeterGaugeTickRenderer = function() {
+        $.jqplot.AxisTickRenderer.call(this);
+    };
+    
+    $.jqplot.MeterGaugeTickRenderer.prototype = new $.jqplot.AxisTickRenderer();
+    $.jqplot.MeterGaugeTickRenderer.prototype.constructor = $.jqplot.MeterGaugeTickRenderer;
+    
+})(jQuery);
+    
+    
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.ohlcRenderer.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.ohlcRenderer.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.ohlcRenderer.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,343 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {
+    /**
+     * Class: $.jqplot.OHLCRenderer
+     * jqPlot Plugin to draw Open Hi Low Close, Candlestick and Hi Low Close charts.
+     * 
+     * To use this plugin, include the renderer js file in 
+     * your source:
+     * 
+     * > <script type="text/javascript" src="plugins/jqplot.ohlcRenderer.js"></script>
+     * 
+     * You will most likely want to use a date axis renderer
+     * for the x axis also, so include the date axis render js file also:
+     * 
+     * > <script type="text/javascript" src="plugins/jqplot.dateAxisRenderer.js"></script>
+     * 
+     * Then you set the renderer in the series options on your plot:
+     * 
+     * > series: [{renderer:$.jqplot.OHLCRenderer}]
+     * 
+     * For OHLC and candlestick charts, data should be specified
+     * like so:
+     * 
+     * > dat = [['07/06/2009',138.7,139.68,135.18,135.4], ['06/29/2009',143.46,144.66,139.79,140.02], ...]
+     * 
+     * If the data array has only 4 values per point instead of 5,
+     * the renderer will create a Hi Low Close chart instead.  In that case,
+     * data should be supplied like:
+     * 
+     * > dat = [['07/06/2009',139.68,135.18,135.4], ['06/29/2009',144.66,139.79,140.02], ...]
+     * 
+     * To generate a candlestick chart instead of an OHLC chart,
+     * set the "candlestick" option to true:
+     * 
+     * > series: [{renderer:$.jqplot.OHLCRenderer, rendererOptions:{candleStick:true}}],
+     * 
+     */
+    $.jqplot.OHLCRenderer = function(){
+        // subclass line renderer to make use of some of it's methods.
+        $.jqplot.LineRenderer.call(this);
+        // prop: candleStick
+        // true to render chart as candleStick.
+        // Must have an open price, cannot be a hlc chart.
+        this.candleStick = false;
+        // prop: tickLength
+        // length of the line in pixels indicating open and close price.
+        // Default will auto calculate based on plot width and 
+        // number of points displayed.
+        this.tickLength = 'auto';
+        // prop: bodyWidth
+        // width of the candlestick body in pixels.  Default will auto calculate
+        // based on plot width and number of candlesticks displayed.
+        this.bodyWidth = 'auto';
+        // prop: openColor
+        // color of the open price tick mark.  Default is series color.
+        this.openColor = null;
+        // prop: closeColor
+        // color of the close price tick mark.  Default is series color.
+        this.closeColor = null;
+        // prop: wickColor
+        // color of the hi-lo line thorugh the candlestick body.
+        // Default is the series color.
+        this.wickColor = null;
+        // prop: fillUpBody
+        // true to render an "up" day (close price greater than open price)
+        // with a filled candlestick body.
+        this.fillUpBody = false;
+        // prop: fillDownBody
+        // true to render a "down" day (close price lower than open price)
+        // with a filled candlestick body.
+        this.fillDownBody = true;
+        // prop: upBodyColor
+        // Color of candlestick body of an "up" day.  Default is series color.
+        this.upBodyColor = null;
+        // prop: downBodyColor
+        // Color of candlestick body on a "down" day.  Default is series color.
+        this.downBodyColor = null;
+        // prop: hlc
+        // true if is a hi-low-close chart (no open price).
+        // This is determined automatically from the series data.
+        this.hlc = false;
+        this._tickLength;
+        this._bodyWidth;
+    };
+    
+    $.jqplot.OHLCRenderer.prototype = new $.jqplot.LineRenderer();
+    $.jqplot.OHLCRenderer.prototype.constructor = $.jqplot.OHLCRenderer;
+    
+    // called with scope of series.
+    $.jqplot.OHLCRenderer.prototype.init = function(options) {
+        // prop: lineWidth
+        // Width of the hi-low line and open/close ticks.
+        this.lineWidth = 1.5;
+        $.jqplot.LineRenderer.prototype.init.call(this, options);
+        // set the yaxis data bounds here to account for hi and low values
+        var db = this._yaxis._dataBounds;
+        var d = this._plotData;
+        // if data points have less than 5 values, force a hlc chart.
+        if (d[0].length < 5) {
+            this.renderer.hlc = true;
+
+            for (var j=0; j<d.length; j++) { 
+                if (d[j][2] < db.min || db.min == null) {
+                    db.min = d[j][2];
+                }
+                if (d[j][1] > db.max || db.max == null) {
+                    db.max = d[j][1];
+                }             
+            }
+        }
+        else {
+            for (var j=0; j<d.length; j++) { 
+                if (d[j][3] < db.min || db.min == null) {
+                    db.min = d[j][3];
+                }
+                if (d[j][2] > db.max || db.max == null) {
+                    db.max = d[j][2];
+                }             
+            }
+        }
+        
+    };
+    
+    // called within scope of series.
+    $.jqplot.OHLCRenderer.prototype.draw = function(ctx, gd, options) {
+        var d = this.data;
+        var xmin = this._xaxis.min;
+        var xmax = this._xaxis.max;
+        // index of last value below range of plot.
+        var xminidx = 0;
+        // index of first value above range of plot.
+        var xmaxidx = d.length;
+        var xp = this._xaxis.series_u2p;
+        var yp = this._yaxis.series_u2p;
+        var i, prevColor, ops, b, h, w, a, points;
+        var o;
+        var r = this.renderer;
+        var opts = (options != undefined) ? options : {};
+        var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
+        var fill = (opts.fill != undefined) ? opts.fill : this.fill;
+        var fillAndStroke = (opts.fillAndStroke != undefined) ? opts.fillAndStroke : this.fillAndStroke;
+        r.bodyWidth = (opts.bodyWidth != undefined) ? opts.bodyWidth : r.bodyWidth;
+        r.tickLength = (opts.tickLength != undefined) ? opts.tickLength : r.tickLength;
+        ctx.save();
+        if (this.show) {
+            var x, open, hi, low, close;
+            // need to get widths based on number of points shown,
+            // not on total number of points.  Use the results 
+            // to speed up drawing in next step.
+            for (var i=0; i<d.length; i++) {
+                if (d[i][0] < xmin) {
+                    xminidx = i;
+                }
+                else if (d[i][0] < xmax) {
+                    xmaxidx = i+1;
+                }
+            }
+            
+            if (r.candleStick) {
+                if (typeof(r.bodyWidth) == 'number') {
+                    r._bodyWidth = r.bodyWidth;
+                }
+                else {
+                    r._bodyWidth = Math.min(20, ctx.canvas.width/(xmaxidx - xminidx)/2);
+                }
+            }
+            else {
+                if (typeof(r.tickLength) == 'number') {
+                    r._tickLength = r.tickLength;
+                }
+                else {
+                    r._tickLength = Math.min(10, ctx.canvas.width/(xmaxidx - xminidx)/4);
+                }
+            }
+            
+            for (var i=xminidx; i<xmaxidx; i++) {
+                x = xp(d[i][0]);
+                if (r.hlc) {
+                    open = null;
+                    hi = yp(d[i][1]);
+                    low = yp(d[i][2]);
+                    close = yp(d[i][3]);
+                }
+                else {
+                    open = yp(d[i][1]);
+                    hi = yp(d[i][2]);
+                    low = yp(d[i][3]);
+                    close = yp(d[i][4]);
+                }
+                o = {};
+                if (r.candleStick && !r.hlc) {
+                    w = r._bodyWidth;
+                    a = x - w/2;
+                    // draw candle
+                    // determine if candle up or down
+                    // up, remember grid coordinates increase downward
+                    if (close < open) {
+                        // draw wick
+                        if (r.wickColor) {
+                            o.color = r.wickColor;
+                        }
+                        else if (r.downBodyColor) {
+                            o.color = r.upBodyColor;
+                        }
+                        ops = $.extend(true, {}, opts, o);
+                        r.shapeRenderer.draw(ctx, [[x, hi], [x, close]], ops); 
+                        r.shapeRenderer.draw(ctx, [[x, open], [x, low]], ops); 
+                        o = {};
+                        b = close;
+                        h = open - close;
+                        // if color specified, use it
+                        if (r.fillUpBody) {
+                            o.fillRect = true;
+                        }
+                        else {
+                            o.strokeRect = true;
+                            w = w - this.lineWidth;
+                            a = x - w/2;
+                        }
+                        if (r.upBodyColor) {
+                            o.color = r.upBodyColor;
+                            o.fillStyle = r.upBodyColor;
+                        }
+                        points = [a, b, w, h];
+                    }
+                    // down
+                    else if (close >  open) {
+                        // draw wick
+                        if (r.wickColor) {
+                            o.color = r.wickColor;
+                        }
+                        else if (r.downBodyColor) {
+                            o.color = r.downBodyColor;
+                        }
+                        ops = $.extend(true, {}, opts, o);
+                        r.shapeRenderer.draw(ctx, [[x, hi], [x, open]], ops); 
+                        r.shapeRenderer.draw(ctx, [[x, close], [x, low]], ops);
+                         
+                        o = {};
+                        
+                        b = open;
+                        h = close - open;
+                        // if color specified, use it
+                        if (r.fillDownBody) {
+                            o.fillRect = true;
+                        }
+                        else {
+                            o.strokeRect = true;
+                            w = w - this.lineWidth;
+                            a = x - w/2;
+                        }
+                        if (r.downBodyColor) {
+                            o.color = r.downBodyColor;
+                            o.fillStyle = r.downBodyColor;
+                        }
+                        points = [a, b, w, h];
+                    }
+                    // even, open = close
+                    else  {
+                        // draw wick
+                        if (r.wickColor) {
+                            o.color = r.wickColor;
+                        }
+                        ops = $.extend(true, {}, opts, o);
+                        r.shapeRenderer.draw(ctx, [[x, hi], [x, low]], ops); 
+                        o = {};
+                        o.fillRect = false;
+                        o.strokeRect = false;
+                        a = [x - w/2, open];
+                        b = [x + w/2, close];
+                        w = null;
+                        h = null;
+                        points = [a, b];
+                    }
+                    ops = $.extend(true, {}, opts, o);
+                    r.shapeRenderer.draw(ctx, points, ops);
+                }
+                else {
+                    prevColor = opts.color;
+                    if (r.openColor) {
+                        opts.color = r.openColor;
+                    }
+                    // draw open tick
+                    if (!r.hlc) {
+                        r.shapeRenderer.draw(ctx, [[x-r._tickLength, open], [x, open]], opts);    
+                    }
+                    opts.color = prevColor;
+                    // draw wick
+                    if (r.wickColor) {
+                        opts.color = r.wickColor;
+                    }
+                    r.shapeRenderer.draw(ctx, [[x, hi], [x, low]], opts); 
+                    opts.color  = prevColor;
+                    // draw close tick
+                    if (r.closeColor) {
+                        opts.color = r.closeColor;
+                    }
+                    r.shapeRenderer.draw(ctx, [[x, close], [x+r._tickLength, close]], opts); 
+                    opts.color = prevColor;
+                }
+            }
+        }
+        
+        ctx.restore();
+    };  
+    
+    $.jqplot.OHLCRenderer.prototype.drawShadow = function(ctx, gd, options) {
+        // This is a no-op, shadows drawn with lines.
+    };
+    
+    // called with scope of plot.
+    $.jqplot.OHLCRenderer.checkOptions = function(target, data, options) {
+        // provide some sensible highlighter options by default
+        // These aren't good for hlc, only for ohlc or candlestick
+        if (!options.highlighter) {
+            options.highlighter = {
+                showMarker:false,
+                tooltipAxes: 'y',
+                yvalues: 4,
+                formatString:'<table class="jqplot-highlighter"><tr><td>date:</td><td>%s</td></tr><tr><td>open:</td><td>%s</td></tr><tr><td>hi:</td><td>%s</td></tr><tr><td>low:</td><td>%s</td></tr><tr><td>close:</td><td>%s</td></tr></table>'
+            };
+        }
+    };
+    
+    //$.jqplot.preInitHooks.push($.jqplot.OHLCRenderer.checkOptions);
+    
+})(jQuery);    
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.pieRenderer.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.pieRenderer.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.pieRenderer.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,766 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {
+    /**
+     * Class: $.jqplot.PieRenderer
+     * Plugin renderer to draw a pie chart.
+     * x values, if present, will be used as slice labels.
+     * y values give slice size.
+     * 
+     * To use this renderer, you need to include the 
+     * pie renderer plugin, for example:
+     * 
+     * > <script type="text/javascript" src="plugins/jqplot.pieRenderer.js"></script>
+     * 
+     * Properties described here are passed into the $.jqplot function
+     * as options on the series renderer.  For example:
+     * 
+     * > plot2 = $.jqplot('chart2', [s1, s2], {
+     * >     seriesDefaults: {
+     * >         renderer:$.jqplot.PieRenderer,
+     * >         rendererOptions:{
+     * >              sliceMargin: 2,
+     * >              startAngle: -90
+     * >          }
+     * >      }
+     * > });
+     * 
+     * A pie plot will trigger events on the plot target
+     * according to user interaction.  All events return the event object,
+     * the series index, the point (slice) index, and the point data for 
+     * the appropriate slice.
+     * 
+     * 'jqplotDataMouseOver' - triggered when user mouseing over a slice.
+     * 'jqplotDataHighlight' - triggered the first time user mouses over a slice,
+     * if highlighting is enabled.
+     * 'jqplotDataUnhighlight' - triggered when a user moves the mouse out of
+     * a highlighted slice.
+     * 'jqplotDataClick' - triggered when the user clicks on a slice.
+     * 'jqplotDataRightClick' - tiggered when the user right clicks on a slice if
+     * the "captureRightClick" option is set to true on the plot.
+     */
+    $.jqplot.PieRenderer = function(){
+        $.jqplot.LineRenderer.call(this);
+    };
+    
+    $.jqplot.PieRenderer.prototype = new $.jqplot.LineRenderer();
+    $.jqplot.PieRenderer.prototype.constructor = $.jqplot.PieRenderer;
+    
+    // called with scope of a series
+    $.jqplot.PieRenderer.prototype.init = function(options, plot) {
+        // Group: Properties
+        //
+        // prop: diameter
+        // Outer diameter of the pie, auto computed by default
+        this.diameter = null;
+        // prop: padding
+        // padding between the pie and plot edges, legend, etc.
+        this.padding = 20;
+        // prop: sliceMargin
+        // angular spacing between pie slices in degrees.
+        this.sliceMargin = 0;
+        // prop: fill
+        // true or false, wether to fil the slices.
+        this.fill = true;
+        // prop: shadowOffset
+        // offset of the shadow from the slice and offset of 
+        // each succesive stroke of the shadow from the last.
+        this.shadowOffset = 2;
+        // prop: shadowAlpha
+        // transparency of the shadow (0 = transparent, 1 = opaque)
+        this.shadowAlpha = 0.07;
+        // prop: shadowDepth
+        // number of strokes to apply to the shadow, 
+        // each stroke offset shadowOffset from the last.
+        this.shadowDepth = 5;
+        // prop: highlightMouseOver
+        // True to highlight slice when moused over.
+        // This must be false to enable highlightMouseDown to highlight when clicking on a slice.
+        this.highlightMouseOver = true;
+        // prop: highlightMouseDown
+        // True to highlight when a mouse button is pressed over a slice.
+        // This will be disabled if highlightMouseOver is true.
+        this.highlightMouseDown = false;
+        // prop: highlightColors
+        // an array of colors to use when highlighting a slice.
+        this.highlightColors = [];
+        // prop: dataLabels
+        // Either 'label', 'value', 'percent' or an array of labels to place on the pie slices.
+        // Defaults to percentage of each pie slice.
+        this.dataLabels = 'percent';
+        // prop: showDataLabels
+        // true to show data labels on slices.
+        this.showDataLabels = false;
+        // prop: dataLabelFormatString
+        // Format string for data labels.  If none, '%s' is used for "label" and for arrays, '%d' for value and '%d%%' for percentage.
+        this.dataLabelFormatString = null;
+        // prop: dataLabelThreshold
+        // Threshhold in percentage (0 - 100) of pie area, below which no label will be displayed.
+        // This applies to all label types, not just to percentage labels.
+        this.dataLabelThreshold = 3;
+        // prop: dataLabelPositionFactor
+        // A Multiplier (0-1) of the pie radius which controls position of label on slice.
+        // Increasing will slide label toward edge of pie, decreasing will slide label toward center of pie.
+        this.dataLabelPositionFactor = 0.52;
+        // prop: dataLabelNudge
+        // Number of pixels to slide the label away from (+) or toward (-) the center of the pie.
+        this.dataLabelNudge = 2;
+        // prop: dataLabelCenterOn
+        // True to center the data label at its position.
+        // False to set the inside facing edge of the label at its position.
+        this.dataLabelCenterOn = true;
+        // prop: startAngle
+        // Angle to start drawing pie in degrees.  
+        // According to orientation of canvas coordinate system:
+        // 0 = on the positive x axis
+        // -90 = on the positive y axis.
+        // 90 = on the negaive y axis.
+        // 180 or - 180 = on the negative x axis.
+        this.startAngle = 0;
+        this.tickRenderer = $.jqplot.PieTickRenderer;
+        // Used as check for conditions where pie shouldn't be drawn.
+        this._drawData = true;
+        
+        // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
+        if (options.highlightMouseDown && options.highlightMouseOver == null) {
+            options.highlightMouseOver = false;
+        }
+        
+        $.extend(true, this, options);
+        if (this.diameter != null) {
+            this.diameter = this.diameter - this.sliceMargin;
+        }
+        this._diameter = null;
+        this._radius = null;
+        // array of [start,end] angles arrays, one for each slice.  In radians.
+        this._sliceAngles = [];
+        // index of the currenty highlighted point, if any
+        this._highlightedPoint = null;
+        
+        // set highlight colors if none provided
+        if (this.highlightColors.length == 0) {
+            for (var i=0; i<this.seriesColors.length; i++){
+                var rgba = $.jqplot.getColorComponents(this.seriesColors[i]);
+                var newrgb = [rgba[0], rgba[1], rgba[2]];
+                var sum = newrgb[0] + newrgb[1] + newrgb[2];
+                for (var j=0; j<3; j++) {
+                    // when darkening, lowest color component can be is 60.
+                    newrgb[j] = (sum > 570) ?  newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]);
+                    newrgb[j] = parseInt(newrgb[j], 10);
+                }
+                this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')');
+            }
+        }
+        
+        this.highlightColorGenerator = new $.jqplot.ColorGenerator(this.highlightColors);
+        
+        plot.postParseOptionsHooks.addOnce(postParseOptions);
+        plot.postInitHooks.addOnce(postInit);
+        plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
+        plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown);
+        plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp);
+        plot.eventListenerHooks.addOnce('jqplotClick', handleClick);
+        plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick);
+        plot.postDrawHooks.addOnce(postPlotDraw);
+    };
+    
+    $.jqplot.PieRenderer.prototype.setGridData = function(plot) {
+        // set gridData property.  This will hold angle in radians of each data point.
+        var stack = [];
+        var td = [];
+        var sa = this.startAngle/180*Math.PI;
+        var tot = 0;
+        // don't know if we have any valid data yet, so set plot to not draw.
+        this._drawData = false;
+        for (var i=0; i<this.data.length; i++){
+            if (this.data[i][1] != 0) {
+                // we have data, O.K. to draw.
+                this._drawData = true;
+            }
+            stack.push(this.data[i][1]);
+            td.push([this.data[i][0]]);
+            if (i>0) {
+                stack[i] += stack[i-1];
+            }
+            tot += this.data[i][1];
+        }
+        var fact = Math.PI*2/stack[stack.length - 1];
+        
+        for (var i=0; i<stack.length; i++) {
+            td[i][1] = stack[i] * fact;
+            td[i][2] = this.data[i][1]/tot;
+        }
+        this.gridData = td;
+    };
+    
+    $.jqplot.PieRenderer.prototype.makeGridData = function(data, plot) {
+        var stack = [];
+        var td = [];
+        var tot = 0;
+        var sa = this.startAngle/180*Math.PI;
+        // don't know if we have any valid data yet, so set plot to not draw.
+        this._drawData = false;
+        for (var i=0; i<data.length; i++){
+            if (this.data[i][1] != 0) {
+                // we have data, O.K. to draw.
+                this._drawData = true;
+            }
+            stack.push(data[i][1]);
+            td.push([data[i][0]]);
+            if (i>0) {
+                stack[i] += stack[i-1];
+            }
+            tot += data[i][1];
+        }
+        var fact = Math.PI*2/stack[stack.length - 1];
+        
+        for (var i=0; i<stack.length; i++) {
+            td[i][1] = stack[i] * fact;
+            td[i][2] = data[i][1]/tot;
+        }
+        return td;
+    };
+    
+    $.jqplot.PieRenderer.prototype.drawSlice = function (ctx, ang1, ang2, color, isShadow) {
+        if (this._drawData) {
+            var r = this._diameter / 2;
+            var fill = this.fill;
+            var lineWidth = this.lineWidth;
+            ctx.save();
+            ctx.translate(this._center[0], this._center[1]);
+            ctx.translate(this.sliceMargin*Math.cos((ang1+ang2)/2), this.sliceMargin*Math.sin((ang1+ang2)/2));
+    
+            if (isShadow) {
+                for (var i=0; i<this.shadowDepth; i++) {
+                    ctx.save();
+                    ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));
+                    doDraw();
+                }
+            }
+    
+            else {
+                doDraw();
+            }
+        }
+    
+        function doDraw () {
+            // Fix for IE and Chrome that can't seem to draw circles correctly.
+            // ang2 should always be <= 2 pi since that is the way the data is converted.
+             if (ang2 > 6.282 + this.startAngle) {
+                ang2 = 6.282 + this.startAngle;
+                if (ang1 > ang2) {
+                    ang1 = 6.281 + this.startAngle;
+                }
+            }
+            // Fix for IE, where it can't seem to handle 0 degree angles.  Also avoids
+            // ugly line on unfilled pies.
+            if (ang1 >= ang2) {
+                return;
+            }            
+        
+            ctx.beginPath();  
+            ctx.fillStyle = color;
+            ctx.strokeStyle = color;
+            ctx.lineWidth = lineWidth;
+            ctx.arc(0, 0, r, ang1, ang2, false);
+            ctx.lineTo(0,0);
+            ctx.closePath();
+        
+            if (fill) {
+                ctx.fill();
+            }
+            else {
+                ctx.stroke();
+            }
+        }
+        
+        if (isShadow) {
+            for (var i=0; i<this.shadowDepth; i++) {
+                ctx.restore();
+            }
+        }
+        
+        ctx.restore();
+    };
+    
+    // called with scope of series
+    $.jqplot.PieRenderer.prototype.draw = function (ctx, gd, options, plot) {
+        var i;
+        var opts = (options != undefined) ? options : {};
+        // offset and direction of offset due to legend placement
+        var offx = 0;
+        var offy = 0;
+        var trans = 1;
+        var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors);
+        if (options.legendInfo && options.legendInfo.placement == 'insideGrid') {
+            var li = options.legendInfo;
+            switch (li.location) {
+                case 'nw':
+                    offx = li.width + li.xoffset;
+                    break;
+                case 'w':
+                    offx = li.width + li.xoffset;
+                    break;
+                case 'sw':
+                    offx = li.width + li.xoffset;
+                    break;
+                case 'ne':
+                    offx = li.width + li.xoffset;
+                    trans = -1;
+                    break;
+                case 'e':
+                    offx = li.width + li.xoffset;
+                    trans = -1;
+                    break;
+                case 'se':
+                    offx = li.width + li.xoffset;
+                    trans = -1;
+                    break;
+                case 'n':
+                    offy = li.height + li.yoffset;
+                    break;
+                case 's':
+                    offy = li.height + li.yoffset;
+                    trans = -1;
+                    break;
+                default:
+                    break;
+            }
+        }
+        
+        var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
+        var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
+        var fill = (opts.fill != undefined) ? opts.fill : this.fill;
+        var cw = ctx.canvas.width;
+        var ch = ctx.canvas.height;
+        var w = cw - offx - 2 * this.padding;
+        var h = ch - offy - 2 * this.padding;
+        var mindim = Math.min(w,h);
+        var d = mindim;
+        // this._diameter = this.diameter || d;
+        this._diameter = this.diameter  || d - this.sliceMargin;
+
+        var r = this._radius = this._diameter/2;
+        var sa = this.startAngle / 180 * Math.PI;
+        this._center = [(cw - trans * offx)/2 + trans * offx, (ch - trans*offy)/2 + trans * offy];
+        
+        if (this.shadow) {
+            var shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')';
+            for (var i=0; i<gd.length; i++) {
+                var ang1 = (i == 0) ? sa : gd[i-1][1] + sa;
+                // Adjust ang1 and ang2 for sliceMargin
+                ang1 += this.sliceMargin/180*Math.PI;
+                this.renderer.drawSlice.call (this, ctx, ang1, gd[i][1]+sa, shadowColor, true);
+            }
+            
+        }
+        for (var i=0; i<gd.length; i++) {
+            var ang1 = (i == 0) ? sa : gd[i-1][1] + sa;
+            // Adjust ang1 and ang2 for sliceMargin
+            ang1 += this.sliceMargin/180*Math.PI;
+            var ang2 = gd[i][1] + sa;
+            this._sliceAngles.push([ang1, ang2]);
+                      
+            this.renderer.drawSlice.call (this, ctx, ang1, ang2, colorGenerator.next(), false);
+        
+            if (this.showDataLabels && gd[i][2]*100 >= this.dataLabelThreshold) {
+                var fstr, avgang = (ang1+ang2)/2, label;
+            
+                if (this.dataLabels == 'label') {
+                    fstr = this.dataLabelFormatString || '%s';
+                    label = $.jqplot.sprintf(fstr, gd[i][0]);
+                }
+                else if (this.dataLabels == 'value') {
+                    fstr = this.dataLabelFormatString || '%d';
+                    label = $.jqplot.sprintf(fstr, this.data[i][1]);
+                }
+                else if (this.dataLabels == 'percent') {
+                    fstr = this.dataLabelFormatString || '%d%%';
+                    label = $.jqplot.sprintf(fstr, gd[i][2]*100);
+                }
+                else if (this.dataLabels.constructor == Array) {
+                    fstr = this.dataLabelFormatString || '%s';
+                    label = $.jqplot.sprintf(fstr, this.dataLabels[i]);
+                }
+            
+                var fact = (this._radius ) * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge;
+            
+                var x = this._center[0] + Math.cos(avgang) * fact + this.canvas._offsets.left;
+                var y = this._center[1] + Math.sin(avgang) * fact + this.canvas._offsets.top;
+            
+                var labelelem = $('<div class="jqplot-pie-series jqplot-data-label" style="position:absolute;">' + label + '</div>').insertBefore(plot.eventCanvas._elem);
+                if (this.dataLabelCenterOn) {
+                    x -= labelelem.width()/2;
+                    y -= labelelem.height()/2;
+                }
+                else {
+                    x -= labelelem.width() * Math.sin(avgang/2);
+                    y -= labelelem.height()/2;
+                }
+                x = Math.round(x);
+                y = Math.round(y);
+                labelelem.css({left: x, top: y});
+            }
+        }
+               
+    };
+    
+    $.jqplot.PieAxisRenderer = function() {
+        $.jqplot.LinearAxisRenderer.call(this);
+    };
+    
+    $.jqplot.PieAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
+    $.jqplot.PieAxisRenderer.prototype.constructor = $.jqplot.PieAxisRenderer;
+        
+    
+    // There are no traditional axes on a pie chart.  We just need to provide
+    // dummy objects with properties so the plot will render.
+    // called with scope of axis object.
+    $.jqplot.PieAxisRenderer.prototype.init = function(options){
+        //
+        this.tickRenderer = $.jqplot.PieTickRenderer;
+        $.extend(true, this, options);
+        // I don't think I'm going to need _dataBounds here.
+        // have to go Axis scaling in a way to fit chart onto plot area
+        // and provide u2p and p2u functionality for mouse cursor, etc.
+        // for convienence set _dataBounds to 0 and 100 and
+        // set min/max to 0 and 100.
+        this._dataBounds = {min:0, max:100};
+        this.min = 0;
+        this.max = 100;
+        this.showTicks = false;
+        this.ticks = [];
+        this.showMark = false;
+        this.show = false; 
+    };
+    
+    
+    
+    
+    $.jqplot.PieLegendRenderer = function(){
+        $.jqplot.TableLegendRenderer.call(this);
+    };
+    
+    $.jqplot.PieLegendRenderer.prototype = new $.jqplot.TableLegendRenderer();
+    $.jqplot.PieLegendRenderer.prototype.constructor = $.jqplot.PieLegendRenderer;
+    
+    /**
+     * Class: $.jqplot.PieLegendRenderer
+     * Legend Renderer specific to pie plots.  Set by default
+     * when user creates a pie plot.
+     */
+    $.jqplot.PieLegendRenderer.prototype.init = function(options) {
+        // Group: Properties
+        //
+        // prop: numberRows
+        // Maximum number of rows in the legend.  0 or null for unlimited.
+        this.numberRows = null;
+        // prop: numberColumns
+        // Maximum number of columns in the legend.  0 or null for unlimited.
+        this.numberColumns = null;
+        $.extend(true, this, options);
+    };
+    
+    // called with context of legend
+    $.jqplot.PieLegendRenderer.prototype.draw = function() {
+        var legend = this;
+        if (this.show) {
+            var series = this._series;
+            var ss = 'position:absolute;';
+            ss += (this.background) ? 'background:'+this.background+';' : '';
+            ss += (this.border) ? 'border:'+this.border+';' : '';
+            ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
+            ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : '';
+            ss += (this.textColor) ? 'color:'+this.textColor+';' : '';
+            ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : '';
+            ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : '';
+            ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : '';
+            ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : '';
+            this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>');
+            // Pie charts legends don't go by number of series, but by number of data points
+            // in the series.  Refactor things here for that.
+            
+            var pad = false, 
+                reverse = false,
+                nr, nc;
+            var s = series[0];
+            var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors);
+            
+            if (s.show) {
+                var pd = s.data;
+                if (this.numberRows) {
+                    nr = this.numberRows;
+                    if (!this.numberColumns){
+                        nc = Math.ceil(pd.length/nr);
+                    }
+                    else{
+                        nc = this.numberColumns;
+                    }
+                }
+                else if (this.numberColumns) {
+                    nc = this.numberColumns;
+                    nr = Math.ceil(pd.length/this.numberColumns);
+                }
+                else {
+                    nr = pd.length;
+                    nc = 1;
+                }
+                
+                var i, j, tr, td1, td2, lt, rs, color;
+                var idx = 0;    
+                
+                for (i=0; i<nr; i++) {
+                    if (reverse){
+                        tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem);
+                    }
+                    else{
+                        tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem);
+                    }
+                    for (j=0; j<nc; j++) {
+                        if (idx < pd.length){
+                            lt = this.labels[idx] || pd[idx][0].toString();
+                            color = colorGenerator.next();
+                            if (!reverse){
+                                if (i>0){
+                                    pad = true;
+                                }
+                                else{
+                                    pad = false;
+                                }
+                            }
+                            else{
+                                if (i == nr -1){
+                                    pad = false;
+                                }
+                                else{
+                                    pad = true;
+                                }
+                            }
+                            rs = (pad) ? this.rowSpacing : '0';
+                
+                            td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+
+                                '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+
+                                '</div></td>');
+                            td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
+                            if (this.escapeHtml){
+                                td2.text(lt);
+                            }
+                            else {
+                                td2.html(lt);
+                            }
+                            if (reverse) {
+                                td2.prependTo(tr);
+                                td1.prependTo(tr);
+                            }
+                            else {
+                                td1.appendTo(tr);
+                                td2.appendTo(tr);
+                            }
+                            pad = true;
+                        }
+                        idx++;
+                    }   
+                }
+            }
+        }
+        return this._elem;                
+    };
+    
+    $.jqplot.PieRenderer.prototype.handleMove = function(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            plot.target.trigger('jqplotDataMouseOver', ins);
+            if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
+                plot.target.trigger('jqplotDataHighlight', ins);
+                highlight (plot, ins[0], ins[1]);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    };
+    
+    
+    // this.eventCanvas._elem.bind($.jqplot.eventListenerHooks[i][0], {plot:this}, $.jqplot.eventListenerHooks[i][1]);
+    
+    // setup default renderers for axes and legend so user doesn't have to
+    // called with scope of plot
+    function preInit(target, data, options) {
+        options = options || {};
+        options.axesDefaults = options.axesDefaults || {};
+        options.legend = options.legend || {};
+        options.seriesDefaults = options.seriesDefaults || {};
+        // only set these if there is a pie series
+        var setopts = false;
+        if (options.seriesDefaults.renderer == $.jqplot.PieRenderer) {
+            setopts = true;
+        }
+        else if (options.series) {
+            for (var i=0; i < options.series.length; i++) {
+                if (options.series[i].renderer == $.jqplot.PieRenderer) {
+                    setopts = true;
+                }
+            }
+        }
+        
+        if (setopts) {
+            options.axesDefaults.renderer = $.jqplot.PieAxisRenderer;
+            options.legend.renderer = $.jqplot.PieLegendRenderer;
+            options.legend.preDraw = true;
+            options.seriesDefaults.pointLabels = {show: false};
+        }
+    }
+    
+    function postInit(target, data, options) {
+        for (i=0; i<this.series.length; i++) {
+            if (this.series[i].renderer.constructor == $.jqplot.PieRenderer) {
+                // don't allow mouseover and mousedown at same time.
+                if (this.series[i].highlightMouseOver) {
+                    this.series[i].highlightMouseDown = false;
+                }
+            }
+        }
+        this.target.bind('mouseout', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
+    }
+    
+    // called with scope of plot
+    function postParseOptions(options) {
+        for (var i=0; i<this.series.length; i++) {
+            this.series[i].seriesColors = this.seriesColors;
+            this.series[i].colorGenerator = this.colorGenerator;
+        }
+    }
+    
+    function highlight (plot, sidx, pidx) {
+        var s = plot.series[sidx];
+        var canvas = plot.plugins.pieRenderer.highlightCanvas;
+        canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height);
+        s._highlightedPoint = pidx;
+        plot.plugins.pieRenderer.highlightedSeriesIndex = sidx;
+        s.renderer.drawSlice.call(s, canvas._ctx, s._sliceAngles[pidx][0], s._sliceAngles[pidx][1], s.highlightColorGenerator.get(pidx), false);
+    }
+    
+    function unhighlight (plot) {
+        var canvas = plot.plugins.pieRenderer.highlightCanvas;
+        canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
+        for (var i=0; i<plot.series.length; i++) {
+            plot.series[i]._highlightedPoint = null;
+        }
+        plot.plugins.pieRenderer.highlightedSeriesIndex = null;
+        plot.target.trigger('jqplotDataUnhighlight');
+    }
+ 
+    function handleMove(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var evt1 = jQuery.Event('jqplotDataMouseOver');
+            evt1.pageX = ev.pageX;
+            evt1.pageY = ev.pageY;
+            plot.target.trigger(evt1, ins);
+            if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
+                var evt = jQuery.Event('jqplotDataHighlight');
+                evt.pageX = ev.pageX;
+                evt.pageY = ev.pageY;
+                plot.target.trigger(evt, ins);
+                highlight (plot, ins[0], ins[1]);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    } 
+    
+    function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
+                var evt = jQuery.Event('jqplotDataHighlight');
+                evt.pageX = ev.pageX;
+                evt.pageY = ev.pageY;
+                plot.target.trigger(evt, ins);
+                highlight (plot, ins[0], ins[1]);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    }
+    
+    function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
+        var idx = plot.plugins.pieRenderer.highlightedSeriesIndex;
+        if (idx != null && plot.series[idx].highlightMouseDown) {
+            unhighlight(plot);
+        }
+    }
+    
+    function handleClick(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var evt = jQuery.Event('jqplotDataClick');
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            plot.target.trigger(evt, ins);
+        }
+    }
+    
+    function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var idx = plot.plugins.pieRenderer.highlightedSeriesIndex;
+            if (idx != null && plot.series[idx].highlightMouseDown) {
+                unhighlight(plot);
+            }
+            var evt = jQuery.Event('jqplotDataRightClick');
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            plot.target.trigger(evt, ins);
+        }
+    }    
+    
+    // called within context of plot
+    // create a canvas which we can draw on.
+    // insert it before the eventCanvas, so eventCanvas will still capture events.
+    function postPlotDraw() {
+        this.plugins.pieRenderer = {highlightedSeriesIndex:null};
+        this.plugins.pieRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
+        
+        // do we have any data labels?  if so, put highlight canvas before those
+        var labels = $(this.targetId+' .jqplot-data-label');
+        if (labels.length) {
+            $(labels[0]).before(this.plugins.pieRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pieRenderer-highlight-canvas', this._plotDimensions));
+        }
+        // else put highlight canvas before event canvas.
+        else {
+            this.eventCanvas._elem.before(this.plugins.pieRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pieRenderer-highlight-canvas', this._plotDimensions));
+        }
+        
+        var hctx = this.plugins.pieRenderer.highlightCanvas.setContext();
+    }
+    
+    $.jqplot.preInitHooks.push(preInit);
+    
+    $.jqplot.PieTickRenderer = function() {
+        $.jqplot.AxisTickRenderer.call(this);
+    };
+    
+    $.jqplot.PieTickRenderer.prototype = new $.jqplot.AxisTickRenderer();
+    $.jqplot.PieTickRenderer.prototype.constructor = $.jqplot.PieTickRenderer;
+    
+})(jQuery);
+    
+    
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.pointLabels.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.pointLabels.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.pointLabels.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,325 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {
+    
+    /**
+     * Class: $.jqplot.PointLabels
+     * Plugin for putting labels at the data points.
+     * 
+     * To use this plugin, include the js
+     * file in your source:
+     * 
+     * > <script type="text/javascript" src="plugins/jqplot.pointLabels.js"></script>
+     * 
+     * By default, the last value in the data ponit array in the data series is used
+     * for the label.  For most series renderers, extra data can be added to the 
+     * data point arrays and the last value will be used as the label.
+     * 
+     * For instance, 
+     * this series:
+     * 
+     * > [[1,4], [3,5], [7,2]]
+     * 
+     * Would, by default, use the y values in the labels.
+     * Extra data can be added to the series like so:
+     * 
+     * > [[1,4,'mid'], [3 5,'hi'], [7,2,'low']]
+     * 
+     * And now the point labels would be 'mid', 'low', and 'hi'.
+     * 
+     * Options to the point labels and a custom labels array can be passed into the
+     * "pointLabels" option on the series option like so:
+     * 
+     * > series:[{pointLabels:{
+     * >    labels:['mid', 'hi', 'low'],
+     * >    location:'se',
+     * >    ypadding: 12
+     * >    }
+     * > }]
+     * 
+     * A custom labels array in the options takes precendence over any labels
+     * in the series data.  If you have a custom labels array in the options,
+     * but still want to use values from the series array as labels, set the
+     * "labelsFromSeries" option to true.
+     * 
+     * By default, html entities (<, >, etc.) are escaped in point labels.  
+     * If you want to include actual html markup in the labels, 
+     * set the "escapeHTML" option to false.
+     * 
+     */
+    $.jqplot.PointLabels = function(options) {
+        // Group: Properties
+        //
+        // prop: show
+        // show the labels or not.
+        this.show = $.jqplot.config.enablePlugins;
+        // prop: location
+        // compass location where to position the label around the point.
+        // 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'
+        this.location = 'n';
+        // prop: labelsFromSeries
+        // true to use labels within data point arrays.
+        this.labelsFromSeries = false;
+        // prop: seriesLabelIndex
+        // array index for location of labels within data point arrays.
+        // if null, will use the last element of the data point array.
+        this.seriesLabelIndex = null;
+        // prop: labels
+        // array of arrays of labels, one array for each series.
+        this.labels = [];
+        // actual labels that will get displayed.
+        // needed to preserve user specified labels in labels array.
+        this._labels = [];
+        // prop: stackedValue
+        // true to display value as stacked in a stacked plot.
+        // no effect if labels is specified.
+        this.stackedValue = false;
+        // prop: ypadding
+        // vertical padding in pixels between point and label
+        this.ypadding = 6;
+        // prop: xpadding
+        // horizontal padding in pixels between point and label
+        this.xpadding = 6;
+        // prop: escapeHTML
+        // true to escape html entities in the labels.
+        // If you want to include markup in the labels, set to false.
+        this.escapeHTML = true;
+        // prop: edgeTolerance
+        // Number of pixels that the label must be away from an axis
+        // boundary in order to be drawn.  Negative values will allow overlap
+        // with the grid boundaries.
+        this.edgeTolerance = -5;
+        // prop: formatter
+        // A class of a formatter for the tick text.  sprintf by default.
+        this.formatter = $.jqplot.DefaultTickFormatter;
+        // prop: formatString
+        // string passed to the formatter.
+        this.formatString = '';
+        // prop: hideZeros
+        // true to not show a label for a value which is 0.
+        this.hideZeros = false;
+        this._elems = [];
+        
+        $.extend(true, this, options);
+    };
+    
+    var locations = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w'];
+    var locationIndicies = {'nw':0, 'n':1, 'ne':2, 'e':3, 'se':4, 's':5, 'sw':6, 'w':7};
+    var oppositeLocations = ['se', 's', 'sw', 'w', 'nw', 'n', 'ne', 'e'];
+    
+    // called with scope of a series
+    $.jqplot.PointLabels.init = function (target, data, seriesDefaults, opts){
+        var options = $.extend(true, {}, seriesDefaults, opts);
+        options.pointLabels = options.pointLabels || {};
+        if (this.renderer.constructor == $.jqplot.BarRenderer && this.barDirection == 'horizontal' && !options.pointLabels.location) {
+            options.pointLabels.location = 'e';
+        }
+        // add a pointLabels attribute to the series plugins
+        this.plugins.pointLabels = new $.jqplot.PointLabels(options.pointLabels);
+        this.plugins.pointLabels.setLabels.call(this);
+    };
+    
+    // called with scope of series
+    $.jqplot.PointLabels.prototype.setLabels = function() {   
+        var p = this.plugins.pointLabels; 
+        var labelIdx;
+        if (p.seriesLabelIndex != null) {
+            labelIdx = p.seriesLabelIndex;
+        }
+        else if (this.renderer.constructor == $.jqplot.BarRenderer && this.barDirection == 'horizontal') {
+            labelIdx = 0;
+        }
+        else {
+            labelIdx = this._plotData[0].length -1;
+        }
+        p._labels = [];
+        if (p.labels.length == 0 || p.labelsFromSeries) {    
+            if (p.stackedValue) {
+                if (this._plotData.length && this._plotData[0].length){
+                    // var idx = p.seriesLabelIndex || this._plotData[0].length -1;
+                    for (var i=0; i<this._plotData.length; i++) {
+                        p._labels.push(this._plotData[i][labelIdx]);
+                    }
+                }
+            }
+            else {
+                var d = this.data;
+                if (this.renderer.constructor == $.jqplot.BarRenderer && this.waterfall) {
+                    d = this._data;
+                }
+                if (d.length && d[0].length) {
+                    // var idx = p.seriesLabelIndex || d[0].length -1;
+                    for (var i=0; i<d.length; i++) {
+                        p._labels.push(d[i][labelIdx]);
+                    }
+                }
+            }
+        }
+        else if (p.labels.length){
+            p._labels = p.labels;
+        }
+    };
+    
+    $.jqplot.PointLabels.prototype.xOffset = function(elem, location, padding) {
+        location = location || this.location;
+        padding = padding || this.xpadding;
+        var offset;
+        
+        switch (location) {
+            case 'nw':
+                offset = -elem.outerWidth(true) - this.xpadding;
+                break;
+            case 'n':
+                offset = -elem.outerWidth(true)/2;
+                break;
+            case 'ne':
+                offset =  this.xpadding;
+                break;
+            case 'e':
+                offset = this.xpadding;
+                break;
+            case 'se':
+                offset = this.xpadding;
+                break;
+            case 's':
+                offset = -elem.outerWidth(true)/2;
+                break;
+            case 'sw':
+                offset = -elem.outerWidth(true) - this.xpadding;
+                break;
+            case 'w':
+                offset = -elem.outerWidth(true) - this.xpadding;
+                break;
+            default: // same as 'nw'
+                offset = -elem.outerWidth(true) - this.xpadding;
+                break;
+        }
+        return offset; 
+    };
+    
+    $.jqplot.PointLabels.prototype.yOffset = function(elem, location, padding) {
+        location = location || this.location;
+        padding = padding || this.xpadding;
+        var offset;
+        
+        switch (location) {
+            case 'nw':
+                offset = -elem.outerHeight(true) - this.ypadding;
+                break;
+            case 'n':
+                offset = -elem.outerHeight(true) - this.ypadding;
+                break;
+            case 'ne':
+                offset = -elem.outerHeight(true) - this.ypadding;
+                break;
+            case 'e':
+                offset = -elem.outerHeight(true)/2;
+                break;
+            case 'se':
+                offset = this.ypadding;
+                break;
+            case 's':
+                offset = this.ypadding;
+                break;
+            case 'sw':
+                offset = this.ypadding;
+                break;
+            case 'w':
+                offset = -elem.outerHeight(true)/2;
+                break;
+            default: // same as 'nw'
+                offset = -elem.outerHeight(true) - this.ypadding;
+                break;
+        }
+        return offset; 
+    };
+    
+    // called with scope of series
+    $.jqplot.PointLabels.draw = function (sctx, options) {
+        var p = this.plugins.pointLabels;
+        // set labels again in case they have changed.
+        p.setLabels.call(this);
+        // remove any previous labels
+        for (var i=0; i<p._elems.length; i++) {
+            p._elems[i].remove();
+        }
+        if (p.show) {
+            var ax = '_'+this._stackAxis+'axis';
+        
+            if (!p.formatString) {
+                p.formatString = this[ax]._ticks[0].formatString;
+                p.formatter = this[ax]._ticks[0].formatter;
+            }
+        
+            var pd = this._plotData;
+            var xax = this._xaxis;
+            var yax = this._yaxis;
+
+            for (var i=p._labels.length-1; i>=0; i--) {
+                var label = p._labels[i];
+                
+                if (p.hideZeros && parseInt(p._labels[i], 10) == 0) {
+                    label = '';
+                }
+                
+                if (label != null) {
+                    label = p.formatter(p.formatString, label);
+                } 
+                var elem = $('<div class="jqplot-point-label jqplot-series-'+this.index+' jqplot-point-'+i+'" style="position:absolute"></div>');
+                elem.insertAfter(sctx.canvas);
+                p._elems.push(elem);
+                if (p.escapeHTML) {
+                    elem.text(label);
+                }
+                else {
+                    elem.html(label);
+                }
+                var location = p.location;
+                if (this.waterfall && parseInt(label, 10) < 0) {
+                    location = oppositeLocations[locationIndicies[location]];
+                }
+                var ell = xax.u2p(pd[i][0]) + p.xOffset(elem, location);
+                var elt = yax.u2p(pd[i][1]) + p.yOffset(elem, location);
+                if (this.renderer.constructor == $.jqplot.BarRenderer) {
+                    if (this.barDirection == "vertical") {
+                        ell += this._barNudge;
+                    }
+                    else {
+                        elt -= this._barNudge;
+                    }
+                }
+                elem.css('left', ell);
+                elem.css('top', elt);
+                var elr = ell + $(elem).width();
+                var elb = elt + $(elem).height();
+                var et = p.edgeTolerance;
+                var scl = $(sctx.canvas).position().left;
+                var sct = $(sctx.canvas).position().top;
+                var scr = sctx.canvas.width + scl;
+                var scb = sctx.canvas.height + sct;
+                // if label is outside of allowed area, remove it
+                if (ell - et < scl || elt - et < sct || elr + et > scr || elb + et > scb) {
+                    $(elem).detach();
+                }
+            }
+        }
+    };
+    
+    $.jqplot.postSeriesInitHooks.push($.jqplot.PointLabels.init);
+    $.jqplot.postDrawSeriesHooks.push($.jqplot.PointLabels.draw);
+})(jQuery);
\ No newline at end of file

Added: gnucash/trunk/src/report/jqplot/plugins/jqplot.trendline.js
===================================================================
--- gnucash/trunk/src/report/jqplot/plugins/jqplot.trendline.js	                        (rev 0)
+++ gnucash/trunk/src/report/jqplot/plugins/jqplot.trendline.js	2011-03-24 21:30:09 UTC (rev 20473)
@@ -0,0 +1,208 @@
+/**
+ * Copyright (c) 2009 - 2010 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT and GPL version 2.0 licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * The author would appreciate an email letting him know of any substantial
+ * use of jqPlot.  You can reach the author at: chris at jqplot dot com 
+ * or see http://www.jqplot.com/info.php .  This is, of course, 
+ * not required.
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * Thanks for using jqPlot!
+ * 
+ */
+(function($) {
+    
+    /**
+     * Class: $.jqplot.Trendline
+     * Plugin which will automatically compute and draw trendlines for plotted data.
+     */
+    $.jqplot.Trendline = function() {
+        // Group: Properties
+        
+        // prop: show
+        // Wether or not to show the trend line.
+        this.show = $.jqplot.config.enablePlugins;
+        // prop: color
+        // CSS color spec for the trend line.
+        // By default this wil be the same color as the primary line.
+        this.color = '#666666';
+        // prop: renderer
+        // Renderer to use to draw the trend line.
+        // The data series that is plotted may not be rendered as a line.
+        // Therefore, we use our own line renderer here to draw a trend line.
+        this.renderer = new $.jqplot.LineRenderer();
+        // prop: rendererOptions
+        // Options to pass to the line renderer.
+        // By default, markers are not shown on trend lines.
+        this.rendererOptions = {marker:{show:false}};
+        // prop: label
+        // Label for the trend line to use in the legend.
+        this.label = '';
+        // prop: type
+        // Either 'exponential', 'exp', or 'linear'.
+        this.type = 'linear';
+        // prop: shadow
+        // true or false, wether or not to show the shadow.
+        this.shadow = true;
+        // prop: markerRenderer
+        // Renderer to use to draw markers on the line.
+        // I think this is wrong.
+        this.markerRenderer = {show:false};
+        // prop: lineWidth
+        // Width of the trend line.
+        this.lineWidth = 1.5;
+        // prop: shadowAngle
+        // Angle of the shadow on the trend line.
+        this.shadowAngle = 45;
+        // prop: shadowOffset
+        // pixel offset for each stroke of the shadow.
+        this.shadowOffset = 1.0;
+        // prop: shadowAlpha
+        // Alpha transparency of the shadow.
+        this.shadowAlpha = 0.07;
+        // prop: shadowDepth
+        // number of strokes to make of the shadow.
+        this.shadowDepth = 3;
+        this.isTrendline = true;
+        
+    };
+    
+    $.jqplot.postSeriesInitHooks.push(parseTrendLineOptions);
+    $.jqplot.postDrawSeriesHooks.push(drawTrendline);
+    $.jqplot.addLegendRowHooks.push(addTrendlineLegend);
+    
+    // called witin scope of the legend object
+    // current series passed in
+    // must return null or an object {label:label, color:color}
+    function addTrendlineLegend(series) {
+        var lt = series.trendline.label.toString();
+        var ret = null;
+        if (this.renderer.constructor != $.jqplot.PieRenderer && series.trendline.show && lt) {
+            ret = {label:lt, color:series.trendline.color};
+        }
+        return ret;
+    }
+
+    // called within scope of a series
+    function parseTrendLineOptions (target, data, seriesDefaults, options, plot) {
+        if (this.renderer.constructor == $.jqplot.LineRenderer) {
+            this.trendline = new $.jqplot.Trendline();
+            options = options || {};
+            $.extend(true, this.trendline, {color:this.color}, seriesDefaults.trendline, options.trendline);
+            this.trendline.renderer.init.call(this.trendline, null);
+        }
+    }
+    
+    // called within scope of series object
+    function drawTrendline(sctx, options) {
+        // if we have options, merge trendline options in with precedence
+        options = $.extend(true, {}, this.trendline, options);
+
+        if (options.show && this.renderer.constructor != $.jqplot.PieRenderer) {
+            var fit;
+            // this.renderer.setGridData.call(this);
+            var data = options.data || this.data;
+            fit = fitData(data, this.trendline.type);
+            var gridData = options.gridData || this.renderer.makeGridData.call(this, fit.data);
+            this.trendline.renderer.draw.call(this.trendline, sctx, gridData, {showLine:true, shadow:this.trendline.shadow});
+        }
+    }
+    
+    function regression(x, y, typ)  {
+        var type = (typ == null) ? 'linear' : typ;
+        var N = x.length;
+        var slope;
+        var intercept;  
+        var SX = 0;
+        var SY = 0;
+        var SXX = 0;
+        var SXY = 0;
+        var SYY = 0;
+        var Y = [];
+        var X = [];
+    
+        if (type == 'linear') {
+            X = x;
+            Y = y;
+        }
+        else if (type == 'exp' || type == 'exponential') {
+            for ( var i=0; i<y.length; i++) {
+                // ignore points <= 0, log undefined.
+                if (y[i] <= 0) {
+                    N--;
+                }
+                else {
+                    X.push(x[i]);
+                    Y.push(Math.log(y[i]));
+                }
+            }
+        }
+
+        for ( var i = 0; i < N; i++) {
+            SX = SX + X[i];
+            SY = SY + Y[i];
+            SXY = SXY + X[i]* Y[i];
+            SXX = SXX + X[i]* X[i];
+            SYY = SYY + Y[i]* Y[i];
+        }
+
+        slope = (N*SXY - SX*SY)/(N*SXX - SX*SX);
+        intercept = (SY - slope*SX)/N;
+
+        return [slope, intercept];
+    }
+
+    function linearRegression(X,Y) {
+        var ret;
+        ret = regression(X,Y,'linear');
+        return [ret[0],ret[1]];
+    }
+
+    function expRegression(X,Y) {
+        var ret;
+        var x = X;
+        var y = Y;
+        ret = regression(x, y,'exp');
+        var base = Math.exp(ret[0]);
+        var coeff = Math.exp(ret[1]);
+        return [base, coeff];
+    }
+
+    function fitData(data, typ) {
+        var type = (typ == null) ?  'linear' : typ;
+        var ret;
+        var res;
+        var x = [];
+        var y = [];
+        var ypred = [];
+        
+        for (i=0; i<data.length; i++){
+            if (data[i] != null && data[i][0] != null && data[i][1] != null) {
+                x.push(data[i][0]);
+                y.push(data[i][1]);
+            }
+        }
+        
+        if (type == 'linear') {
+            ret = linearRegression(x,y);
+            for ( var i=0; i<x.length; i++){
+                res = ret[0]*x[i] + ret[1];
+                ypred.push([x[i], res]);
+            }
+        }
+        else if (type == 'exp' || type == 'exponential') {
+            ret = expRegression(x,y);
+            for ( var i=0; i<x.length; i++){
+                res = ret[1]*Math.pow(ret[0],x[i]);
+                ypred.push([x[i], res]);
+            }
+        }
+        return {data: ypred, slope: ret[0], intercept: ret[1]};
+    } 
+
+})(jQuery);
\ No newline at end of file



More information about the gnucash-changes mailing list