Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeezyT <timo@ezdesign.de>2011-05-24 20:21:01 +0400
committerBeezyT <timo@ezdesign.de>2011-05-24 20:21:01 +0400
commit47b784ebc16a840541a5b8e808075472e7abd794 (patch)
tree2da48881a32d6a1a736fdd538c7fe5a8471d830d /libs/jqplot
parent7463e20b893faf2a44941144e5b67afa7a95fb79 (diff)
refs #2431 added jqplot again
git-svn-id: http://dev.piwik.org/svn/trunk@4801 59fd770c-687e-43c8-a1e3-f5a4ff64c105
Diffstat (limited to 'libs/jqplot')
-rw-r--r--libs/jqplot/MIT-LICENSE.txt21
-rw-r--r--libs/jqplot/changes.txt369
-rw-r--r--libs/jqplot/excanvas.js1434
-rw-r--r--libs/jqplot/gpl-2.0.txt280
-rw-r--r--libs/jqplot/jqPlotCssStyling.txt53
-rw-r--r--libs/jqplot/jqPlotOptions.txt276
-rw-r--r--libs/jqplot/jqplot.axisLabelRenderer.js96
-rw-r--r--libs/jqplot/jqplot.axisTickRenderer.js169
-rwxr-xr-xlibs/jqplot/jqplot.canvasGridRenderer.js358
-rwxr-xr-xlibs/jqplot/jqplot.core.js3396
-rw-r--r--libs/jqplot/jqplot.divTitleRenderer.js114
-rwxr-xr-xlibs/jqplot/jqplot.lineRenderer.js497
-rw-r--r--libs/jqplot/jqplot.linearAxisRenderer.js905
-rw-r--r--libs/jqplot/jqplot.linearTickGenerator.js116
-rw-r--r--libs/jqplot/jqplot.markerRenderer.js228
-rw-r--r--libs/jqplot/jqplot.shadowRenderer.js131
-rw-r--r--libs/jqplot/jqplot.shapeRenderer.js157
-rw-r--r--libs/jqplot/jqplot.sprintf.js287
-rw-r--r--libs/jqplot/jqplot.tableLegendRenderer.js292
-rw-r--r--libs/jqplot/jqplot.themeEngine.js914
-rw-r--r--libs/jqplot/jquery.jqplot.css226
-rw-r--r--libs/jqplot/jquery.jqplot.js75
-rw-r--r--libs/jqplot/jsdate.js1437
-rw-r--r--libs/jqplot/jsl.conf143
-rw-r--r--libs/jqplot/optionsTutorial.txt239
-rw-r--r--libs/jqplot/plugins/jqplot.BezierCurveRenderer.js312
-rwxr-xr-xlibs/jqplot/plugins/jqplot.barRenderer.js709
-rw-r--r--libs/jqplot/plugins/jqplot.blockRenderer.js234
-rwxr-xr-xlibs/jqplot/plugins/jqplot.bubbleRenderer.js754
-rw-r--r--libs/jqplot/plugins/jqplot.canvasAxisLabelRenderer.js191
-rw-r--r--libs/jqplot/plugins/jqplot.canvasAxisTickRenderer.js230
-rwxr-xr-xlibs/jqplot/plugins/jqplot.canvasOverlay.js619
-rw-r--r--libs/jqplot/plugins/jqplot.canvasTextRenderer.js420
-rw-r--r--libs/jqplot/plugins/jqplot.categoryAxisRenderer.js636
-rw-r--r--libs/jqplot/plugins/jqplot.ciParser.js115
-rwxr-xr-xlibs/jqplot/plugins/jqplot.cursor.js1051
-rw-r--r--libs/jqplot/plugins/jqplot.dateAxisRenderer.js439
-rwxr-xr-xlibs/jqplot/plugins/jqplot.donutRenderer.js910
-rwxr-xr-xlibs/jqplot/plugins/jqplot.dragable.js224
-rw-r--r--libs/jqplot/plugins/jqplot.enhancedLegendRenderer.js199
-rwxr-xr-xlibs/jqplot/plugins/jqplot.funnelRenderer.js938
-rwxr-xr-xlibs/jqplot/plugins/jqplot.highlighter.js421
-rw-r--r--libs/jqplot/plugins/jqplot.json2.js475
-rw-r--r--libs/jqplot/plugins/jqplot.logAxisRenderer.js446
-rw-r--r--libs/jqplot/plugins/jqplot.mekkoAxisRenderer.js610
-rw-r--r--libs/jqplot/plugins/jqplot.mekkoRenderer.js436
-rw-r--r--libs/jqplot/plugins/jqplot.meterGaugeRenderer.js1142
-rw-r--r--libs/jqplot/plugins/jqplot.ohlcRenderer.js372
-rwxr-xr-xlibs/jqplot/plugins/jqplot.pieRenderer.js803
-rw-r--r--libs/jqplot/plugins/jqplot.pointLabels.js352
-rw-r--r--libs/jqplot/plugins/jqplot.trendline.js220
-rw-r--r--libs/jqplot/usage.txt126
52 files changed, 25597 insertions, 0 deletions
diff --git a/libs/jqplot/MIT-LICENSE.txt b/libs/jqplot/MIT-LICENSE.txt
new file mode 100644
index 0000000000..da4732ec39
--- /dev/null
+++ b/libs/jqplot/MIT-LICENSE.txt
@@ -0,0 +1,21 @@
+Title: MIT License
+
+Copyright (c) 2009-2011 Chris Leonello
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. \ No newline at end of file
diff --git a/libs/jqplot/changes.txt b/libs/jqplot/changes.txt
new file mode 100644
index 0000000000..65f7938b3d
--- /dev/null
+++ b/libs/jqplot/changes.txt
@@ -0,0 +1,369 @@
+Title: Change Log
+
+1.0.0b1:
+* Much improved tick generation algorithm to get precise rounded
+ tick values (Thanks Scott Prahl!).
+* Auto compute tick format string if none is provided.
+* Much better "slicing" of pie charts when using "sliceMargin" option to set
+ a gap between the slices.
+* Expanded canvasOverlay plugin to create arbitrary dashed and solid
+ horizontal and vertical lines on top of plot.
+* Added defaultColors and defaultNegativeColors options to $.jqplot.config.
+* Fixed issue #318, highlighter & bar renderer incompatability.
+* Improve highlighter tooltip positioning with negative bars.
+* Fixed #305, mispelling of jqlotDragStart and jqlotDragStop. MUST NOW BIND
+ TO jqplotDragStart and jqplotDragStop.
+* Fixed #290, some variables left in global scope.
+* Fixed #289, OHLC line widths hard coded at 1.5. Now set by lineWidth option.
+* Fixed #296 for determining databounds on log axes.
+* Updated to jQuery 1.5.1
+* Fixed waterfall plot to ensure first and last bars always fill to zero.
+* Added lineJoin and lineCap option to series lines.
+* Bar widths now based on width of grid, not plot target for better scaling.
+* Added looseZoom option to cursor so zooming can produce well rounded ticks.
+* Added forceTickAt0 and forceTickAt100 options to ensure there will always
+ be a tick at 0 or 100 in the plot.
+* Fixed bug where cursor legend didn't honor series showLabel option.
+
+
+1.0.0a:
+
+* Series can now be moved forward or backward in stack to e.g. bring a line
+ forward when mousing over a point.
+* Can now move outside of grid area while zooming. Can have zoom
+ constrained to grid area or allow zooming outside.
+* Fixed issue #142 with tooltip drawn on top of event canvas, hiding
+ mouse events.
+* Fixed #147 where pie slices with 0 value not rendering properly in IE.
+* Fixed #130 where stack data not sorted properly.
+* Fixed bug with null values not handled properly in category axes.
+* Fixed #156 where pie charts not rendering on QTWebKit.
+* Now using feature detection for canvas and canvas text capability
+ rather than browser version.
+* Added enahncedLegendRenderer plugin to allow multi row/column legends
+ and clickable labels to show/hide series.
+* Added fillToValue option to allow filled line plot to fill to an
+ arbitrary value.
+* Added block plot plugin.
+* Added funnel type charts.
+* Added meter gauge type charts.
+* Added plot theming support.
+* $.jqplot.config.enablePlugins now false by default.
+* Implemented highlighting on bar, pie, donut, funnel, etc. charts.
+* Fix to pointlabels plugin to align labels properly on multi series plots.
+* Added custom error handling to display error message in plot area.
+* Fixed issue where would call to draw grid border of 0 width would
+ result in a default border being drawn.
+* Added options to place legend outside of grid and shrink grid so everything
+ stays within plot div.
+* Fixed bug in color generator so now calls to get() continually cycle
+ through colors just like next().
+* Added defaultAxisStart option.
+* Added gradient fills to bubbles.
+* Added bubble charts.
+* Added showLabels option to bubble charts.
+* Pass bubble radius to event callback in bubble charts.
+* Fixed #207, typo in docs.
+* Fixed #206 where "value" pie slice data labels were displaying wrong
+ value.
+* Fixed #147 with 0 value slices in IE6.
+* Fixed issue #241, disabled varyBarColor option in stacked charts.
+* Added dataRenderer option to allow custom processors for JSON, AJAX
+ and anywhere else you might want to get data.
+* Fixed null value handling so plot now properly skip or join over nulls.
+* Fixed showTicks and showTickMarks option conflicts.
+* Fixed issue #185 where pointLabels plugin incompatibility could crash
+ pie, donut and other plots.
+* Fixed #23 and #143 to obey gridPadding option.
+* Fixed #233 with highlighter tooltip separator.
+* Fixed #224 where type checking failing on GWT.
+* Fixed #272 with pie highlighting not working on replot.
+* Memory performance improvements.
+* Changes to build script so everything should build when pulled from repo.
+* Fixed issue #275, IE 6/7 don't support array indexing of strings.
+* Added event listener hooks for mouseUp, mouseDown, etc. to all line plots.
+* Fixed bug with highlighter not working when null in data.
+* Updated to jQuery 1.4.4
+* Fixed bug where donut plots showed value of radians of slice instead
+ of actual data.
+* Reverted to excanvas r3 so IE8 no longer has to emulate IE7.
+* Added tooltipContentEditor option to highlighter, allowing callback
+ to manipulate tooltip content at run time (thanks Tim Bunce!).
+* Fixed bug where axes scale not resetting.
+* Fixed bug with date axes where data bounds not properly set.
+* Fixed issue where tick marks disappear if grid lines turned off.
+* Updated replot method to allow passing in axes options for more control.
+* Added experimental support for "broken" axes.
+* Fixed bug with pies where pies with 0 valued slices did not draw correctly.
+* Added canvasOverlay plugin to allow drawing of arbitrary shapes on a canvas
+ over the plot.
+* Added option to display arbitrary text/html (message, animated gif, etc.) if
+ plot is constructed without data. Allow a "data loading" indicator to be shown.
+* Added resetAxisValues method to manually update axis ticks without
+ redrawing the plot.
+* Fix to labels on negative bars so label postiion of 'n' will be below a negative bar,
+ just as it is above a positive bar (thanks guigod!).
+* Added thousands separator character (') to sprintf formatting (thanks yuichi1004!).
+* Re-factored date parsing/formatting to use new jsDate module which does not
+ extend the Date prototype.
+
+
+0.9.7:
+
+* Added Mekko chart plot type with enhanced legend and axes support.
+* Implemented vertical waterfall charts. Can create waterfall plot as
+ option to bar chart. See examples folder of distribution.
+* Enhanced plot labels for waterfall style.
+* Enhanced bar plots so you can now color each bar of a series
+ independently with the "varyBarColor" option.
+* Re-factored series drawing so that each series and series shadow drawn
+ on it's own canvas. Allows series to be redrawn independently of each other.
+* Added additional default series colors.
+* Added useNegativeColors option to turn off negative color array and use
+ only seriesColors array to define all bar/filled line colors.
+* Fix css for cursor legend.
+* Modified shape renderer so rectangles can be stroked and filled.
+* Re-factored date methods out of dateAxisRenderer so that date formatter
+ and methods can be accesses outside of dateAxisRenderer plugin.
+* Fixed #132, now trigger series change event on plot target instead of drag canvas.
+* Fixes issue #116 where some source files had mix of tabs and spaces
+ for indentation. Should have been all spaces.
+* Fixed issue #126, some links broken in docs section of web site.
+* Fixed issue #90, trendline plugin incompatibility with pie renderer.
+* Updated samples in examples folder of distribution to include navigation
+ links if web server is set up to process .html files with php.
+
+
+0.9.6:
+
+* New, easier to use, replot() method for placing plots in tabs, accordions,
+ resizable containers or for changing plot parameters programmatically.
+* Updated legend renderer for pie charts to draw swatches which will
+ print correctly.
+* Fixed issue #118 with patch from taum so autoscale option will
+ honor tickInterval and numberTicks options
+* Fix to plot diameter calculation for initially hidden plots.
+* Added examples for making plots in jQuery UI tabs and accordions.
+* Fixed issue #120 where pie chart with single slice not displaying
+ correctly in IE and Chrome
+
+
+0.9.5.2:
+
+* Fixed #102 where double clicking on plot that has zoom enabled, but
+ has not been zoomed resulted in error.
+* Fixed bug where candlestick coloring options not working.
+* Added option to turn individual series labels off in the legend.
+
+
+0.9.5.1:
+
+* Fixed bug where tooltip not working with OHLC and candlestick charts.
+* Added additional marker styles: plus, X and dash.
+
+
+0.9.5:
+
+* Implemented "zoomProxy". zoomProxy allows zooming one plot from another
+ such as an overview plot.
+* Zooming can now be constrained to just x or y axis.
+* Enhanced cursor plugin with vertical "dataTracking" line. This is a line
+ at the cursor location with a readout of data points at the line location
+ which are displayed in the chart legend.
+* Changed cursor tooltip format string. Now one format string is used for
+ entire tooltip.
+* Added mechanisms to specify plot size when plot target is hidden or plot
+ height/width otherwise cannot be determined from markup.
+* Added $.jqplot.config object to specify jqplot wide configuration options.
+ These include enablePlugins to globally set the default plugin state on/off
+ and defaultHeight/defaultWidth to specify default plot height/width.
+* Added fillToZero option which forces filled charts to fill to zero as opposed
+ to axis minimum. Thus negative filled bar/line values will fill upwards to
+ zero axis value.
+* Added option to disable stacking on individual lines.
+* Changed targetId property of the plot object so it now includes a "#" before
+ the id string.
+* Improved tick and body sizing of Open Hi Low Close and candlestick charts.
+* Removed lots of web site related files from the repository. This means that,
+ if working from the sources, user's won't be able to build the jqplot web
+ site and the docs/tests that are hosted on that site. The minified and
+ compressed distribution packages will build fine.
+* Lots of examples were added to a separate examples directory to better show
+ functionality of jqPlot for local testing with the distribution.
+* Many various bug fixes and other minor enhancements.
+
+
+0.9.4:
+
+* Implemented axis labels. Labels can be rendered in div tags or as canvas
+ elements supporting rotated text.
+* Improved rotated axis label positioning so labels will start or end at a
+ tick position.
+* Fixed bug where an empty data series would hang plot rendering.
+* completed issue #66 for misc. improvements to documentation.
+* Fixed issue #64 where the same ID's were assigned to cursor and highlighter
+ elements.
+* Added option to legend to encode special HTML characters.
+* Fixed undesirable behavior where point labels for points off the plot
+ were being rendered.
+* Added edgeTolerance option to point label renderer to control rendering of
+ labels near plot edges.
+
+
+0.9.3:
+
+* Preliminary support for axis labels. Currently rendered into DIV tags,
+ so no rotated label support. This feature is currently experimental.
+* Fixed bug #52, needed space in tick div tag between style and class declarations
+ or plot failed in certain application doctypes.
+* Fixed issue #54, miter style line join for chart lines causing spikes at steep
+ changes in slope. Changed miter style to round.
+* Added examples for new autoscaling algorithm.
+* Fixed bug #57, category axis labels disappear on redraw()
+* Improved algorithm which controlled maximum number of labels that would display
+ on a category axis.
+* Fixed bug #45 where null values causing errors in plotData and gridData.
+* Fixed issue #60 where seriesColors option was not working.
+
+
+0.9.2:
+
+* Fixed bug #45 where a plot could crash if series had different numbers of points.
+* Fixed issue #50, added option to turn off sorting of series data.
+* Fixed issue #31, implemented a better axis autoscaling algorithm and added an autoscale option.
+
+0.9.1:
+
+* Fixed bug #40, when axis pad, padMax, padMin set to 0, graph would fail to render.
+* Fixed bug #41 where pie and bar charts not rendered correctly on redraw().
+* Fixed bug #11, filled stacked line plots not rendering correctly in IE.
+* Fixed bug #42 where stacked charts not rendering with string date axis ticks.
+* Fixed bug in redraw() method where axes ticks were not reset.
+* Fixed "jqplotPreRedrawEvent" that should have been named "jqplotPostRedraw" event.
+
+0.9.0:
+
+* Added Open Hi Low Close charts, Candlestick charts and Hi Low Close charts.
+* Added support for arbitrary labels on the data points.
+* Enhanced highlighter plugin to allow custom formatting control of entire tooltip.
+* Enhanced highlighter to support multiple y values in a data point.
+* Fixed bug #38 where series with a single point with a negative value would fail.
+* Improvements to examples to show what plugins to include.
+* Expanded documentation for some of the plugins.
+
+0.8.5:
+
+* Added zooming ability with double click or single click options to reset zoom.
+* Modified default tick spacing algorithm for date axes to give more space to ticks.
+* Fixed bug #2 where tickInterval wasn't working properly.
+* Added neighborThreshold option to control how close mouse must be to
+ point to trigger neighbor detection.
+* Added double click event handler on plot.
+
+0.8.0:
+
+* Support for up to 9 y axes.
+* Added option to control padding at max/min bounds of axes separately.
+* Closed issue #21, added options to control grid line color and width.
+* Closed issue #20, added options to filled line charts to stoke above
+ fill and customize fill color and transparency.
+* Improved structure of on line documentation to make usage and options
+ docs default.
+* Added much documentation on options and css styling.
+
+0.7.1:
+
+* Bug fix release
+* Fixed bug #6, missing semi-colons messing up some javascript compressors.
+* Fixed bug #13 where 2D ticks array of [values, labels] would fail to
+ renderer with DateAxisRenderer.
+* Fixes bug #16 where pie renderer overwriting options for all plot types
+ and crashing non pie plots.
+* Fixes bug #17 constrainTo dragable option mispelled as "contstrainTo".
+ Fixed dragable color issue when used with trend lines.
+
+0.7.0:
+
+* Pie chart support
+* Enabled tooltipLocation option in highlighter.
+* Highlighter Tooltip will account for mark size and highlight size when
+ positioning itself.
+* Added ability to show just x, y or both axes in highlighter tooltip.
+* Added customization of separator between axes values in highlighter tooltip.
+* Modified how shadows are drawn for lines, bars and markers. Now drawn first,
+ so they are always behind the object.
+* Adjustments to shadow parameters on lines to account for new shadow positioning.
+* Added a ColorGenerator class to robustly return next available color
+ for a plot with wrap around to first color at end.
+* Udates to docs about css file.
+* Fixed bug with String x values in series and IE error on sorting (Category Axis).
+* Added cursor changes in dragable plugin when cursor near dragable point.
+
+0.6.6b:
+
+* Added excanvas.js and excanvas.min.js to compressed distributions.
+* Added example/test html pages I had locally into repository and to
+ compressed distributions.
+
+0.6.6a:
+
+* Removed absolute positioning from dom element and put back into css file.
+* Duplicate of 0.6.6 with a suffix to unambiguously differentiate between
+ previously posted 0.6.6 release.
+
+0.6.6:
+
+* Fixed bug #5, trend line plugin failing when no trend line options specified.
+* Added absolute position css spec to axis tick dom element.
+* Enhancement to category axes, more intuitive handling of series with
+ missing data values.
+
+0.6.5:
+
+* Fixed bug #4, series of unequal data length not rendering correctly.
+ This is a bugfix release only.
+
+0.6.4:
+
+* Fixed bug (issue #1 in tracker) where flat line data series (all x and/or y
+ values are euqal) or single value data series would crash.
+
+0.6.3:
+
+* Support for stacked line (a.k.a. area) and stacked bar (horizontal and
+ vertical) charts.
+* Refactored barRenderer to use default shape and shadow renderers.
+* Added info (contacts & support information) page to web site.
+
+0.6.2:
+
+* This is a minor upgrade to docs and build only. No functionality has changed.
+* Ant build script generates entire site, examples, tests and distribution.
+* Improvements to documentation.
+
+0.6.1:
+
+* New sprintf implementation from Ash Searle that implements %g.
+* Fix to sprintf e/f formats.
+* Created new format specifier, %p and %P to preserve significance.
+* Modified p/P format to better display larger numbers.
+* Fixed and simplified significant digits calculation for sprintf.
+* Added option to have cursor tooltip follow the mouse or not.
+* Added options to change size of highlight.
+* Updates to handle dates like '6-May-09'.
+* Mods to improve look of web site.
+* Updates to documentation.
+* Added license and copyright statement to source files.
+
+0.6.0:
+
+* Added rotated text support. Uses native canvas text functionality in
+ browsers that support it or draws text on canvas with Hershey font
+* metrics for non-supporting browsers.
+* Removed lots of lint in js code.
+* Moved tick css from js code into css file.
+* Fix to tick positioning css. y axis ticks were positioned to wrong side of axis div.
+* Re-factored axis tick renderer instantiation into the axes renderers themselves.
+
+
+For changes prior to 0.6.0 release, please see change log at http://bitbucket.org/cleonello/jqplot/changesets/ \ No newline at end of file
diff --git a/libs/jqplot/excanvas.js b/libs/jqplot/excanvas.js
new file mode 100644
index 0000000000..f3b70f470d
--- /dev/null
+++ b/libs/jqplot/excanvas.js
@@ -0,0 +1,1434 @@
+// Memory Leaks patch from http://explorercanvas.googlecode.com/svn/trunk/
+// svn : r73
+// ------------------------------------------------------------------
+// Copyright 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+// Known Issues:
+//
+// * Patterns only support repeat.
+// * Radial gradient are not implemented. The VML version of these look very
+// different from the canvas one.
+// * Clipping paths are not implemented.
+// * Coordsize. The width and height attribute have higher priority than the
+// width and height style values which isn't correct.
+// * Painting mode isn't implemented.
+// * Canvas width/height should is using content-box by default. IE in
+// Quirks mode will draw the canvas using border-box. Either change your
+// doctype to HTML5
+// (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)
+// or use Box Sizing Behavior from WebFX
+// (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)
+// * Non uniform scaling does not correctly scale strokes.
+// * Optimize. There is always room for speed improvements.
+
+// Only add this code if we do not already have a canvas implementation
+if (!document.createElement('canvas').getContext) {
+
+(function() {
+
+ // alias some functions to make (compiled) code shorter
+ var m = Math;
+ var mr = m.round;
+ var ms = m.sin;
+ var mc = m.cos;
+ var abs = m.abs;
+ var sqrt = m.sqrt;
+
+ // this is used for sub pixel precision
+ var Z = 10;
+ var Z2 = Z / 2;
+
+ var IE_VERSION = +navigator.userAgent.match(/MSIE ([\d.]+)?/)[1];
+
+ /**
+ * This funtion is assigned to the <canvas> elements as element.getContext().
+ * @this {HTMLElement}
+ * @return {CanvasRenderingContext2D_}
+ */
+ function getContext() {
+ return this.context_ ||
+ (this.context_ = new CanvasRenderingContext2D_(this));
+ }
+
+ var slice = Array.prototype.slice;
+
+ /**
+ * Binds a function to an object. The returned function will always use the
+ * passed in {@code obj} as {@code this}.
+ *
+ * Example:
+ *
+ * g = bind(f, obj, a, b)
+ * g(c, d) // will do f.call(obj, a, b, c, d)
+ *
+ * @param {Function} f The function to bind the object to
+ * @param {Object} obj The object that should act as this when the function
+ * is called
+ * @param {*} var_args Rest arguments that will be used as the initial
+ * arguments when the function is called
+ * @return {Function} A new function that has bound this
+ */
+ function bind(f, obj, var_args) {
+ var a = slice.call(arguments, 2);
+ return function() {
+ return f.apply(obj, a.concat(slice.call(arguments)));
+ };
+ }
+
+ function encodeHtmlAttribute(s) {
+ return String(s).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
+ }
+
+ function addNamespace(doc, prefix, urn) {
+ if (!doc.namespaces[prefix]) {
+ doc.namespaces.add(prefix, urn, '#default#VML');
+ }
+ }
+
+ function addNamespacesAndStylesheet(doc) {
+ addNamespace(doc, 'g_vml_', 'urn:schemas-microsoft-com:vml');
+ addNamespace(doc, 'g_o_', 'urn:schemas-microsoft-com:office:office');
+
+ // Setup default CSS. Only add one style sheet per document
+ if (!doc.styleSheets['ex_canvas_']) {
+ var ss = doc.createStyleSheet();
+ ss.owningElement.id = 'ex_canvas_';
+ ss.cssText = 'canvas{display:inline-block;overflow:hidden;' +
+ // default size is 300x150 in Gecko and Opera
+ 'text-align:left;width:300px;height:150px}';
+ }
+ }
+
+ // Add namespaces and stylesheet at startup.
+ addNamespacesAndStylesheet(document);
+
+ var G_vmlCanvasManager_ = {
+ init: function(opt_doc) {
+ var doc = opt_doc || document;
+ // Create a dummy element so that IE will allow canvas elements to be
+ // recognized.
+ doc.createElement('canvas');
+ doc.attachEvent('onreadystatechange', bind(this.init_, this, doc));
+ },
+
+ init_: function(doc) {
+ // find all canvas elements
+ var els = doc.getElementsByTagName('canvas');
+ for (var i = 0; i < els.length; i++) {
+ this.initElement(els[i]);
+ }
+ },
+
+ /**
+ * Public initializes a canvas element so that it can be used as canvas
+ * element from now on. This is called automatically before the page is
+ * loaded but if you are creating elements using createElement you need to
+ * make sure this is called on the element.
+ * @param {HTMLElement} el The canvas element to initialize.
+ * @return {HTMLElement} the element that was created.
+ */
+ initElement: function(el) {
+ if (!el.getContext) {
+ el.getContext = getContext;
+
+ // Add namespaces and stylesheet to document of the element.
+ addNamespacesAndStylesheet(el.ownerDocument);
+
+ // Remove fallback content. There is no way to hide text nodes so we
+ // just remove all childNodes. We could hide all elements and remove
+ // text nodes but who really cares about the fallback content.
+ el.innerHTML = '';
+
+ // do not use inline function because that will leak memory
+ el.attachEvent('onpropertychange', onPropertyChange);
+ el.attachEvent('onresize', onResize);
+
+ var attrs = el.attributes;
+ if (attrs.width && attrs.width.specified) {
+ // TODO: use runtimeStyle and coordsize
+ // el.getContext().setWidth_(attrs.width.nodeValue);
+ el.style.width = attrs.width.nodeValue + 'px';
+ } else {
+ el.width = el.clientWidth;
+ }
+ if (attrs.height && attrs.height.specified) {
+ // TODO: use runtimeStyle and coordsize
+ // el.getContext().setHeight_(attrs.height.nodeValue);
+ el.style.height = attrs.height.nodeValue + 'px';
+ } else {
+ el.height = el.clientHeight;
+ }
+ //el.getContext().setCoordsize_()
+ }
+ return el;
+ },
+
+ // Memory Leaks patch : see http://code.google.com/p/explorercanvas/issues/detail?id=82
+ uninitElement: function(el){
+ if (el.getContext) {
+ var ctx = el.getContext();
+ delete ctx.element_;
+ delete ctx.canvas;
+ el.innerHTML = "";
+ //el.outerHTML = "";
+ el.context_ = null;
+ el.getContext = null;
+ el.detachEvent("onpropertychange", onPropertyChange);
+ el.detachEvent("onresize", onResize);
+ }
+ }
+ };
+
+ function onPropertyChange(e) {
+ var el = e.srcElement;
+
+ switch (e.propertyName) {
+ case 'width':
+ el.getContext().clearRect();
+ el.style.width = el.attributes.width.nodeValue + 'px';
+ // In IE8 this does not trigger onresize.
+ el.firstChild.style.width = el.clientWidth + 'px';
+ break;
+ case 'height':
+ el.getContext().clearRect();
+ el.style.height = el.attributes.height.nodeValue + 'px';
+ el.firstChild.style.height = el.clientHeight + 'px';
+ break;
+ }
+ }
+
+ function onResize(e) {
+ var el = e.srcElement;
+ if (el.firstChild) {
+ el.firstChild.style.width = el.clientWidth + 'px';
+ el.firstChild.style.height = el.clientHeight + 'px';
+ }
+ }
+
+ G_vmlCanvasManager_.init();
+
+ // precompute "00" to "FF"
+ var decToHex = [];
+ for (var i = 0; i < 16; i++) {
+ for (var j = 0; j < 16; j++) {
+ decToHex[i * 16 + j] = i.toString(16) + j.toString(16);
+ }
+ }
+
+ function createMatrixIdentity() {
+ return [
+ [1, 0, 0],
+ [0, 1, 0],
+ [0, 0, 1]
+ ];
+ }
+
+ function matrixMultiply(m1, m2) {
+ var result = createMatrixIdentity();
+
+ for (var x = 0; x < 3; x++) {
+ for (var y = 0; y < 3; y++) {
+ var sum = 0;
+
+ for (var z = 0; z < 3; z++) {
+ sum += m1[x][z] * m2[z][y];
+ }
+
+ result[x][y] = sum;
+ }
+ }
+ return result;
+ }
+
+ function copyState(o1, o2) {
+ o2.fillStyle = o1.fillStyle;
+ o2.lineCap = o1.lineCap;
+ o2.lineJoin = o1.lineJoin;
+ o2.lineWidth = o1.lineWidth;
+ o2.miterLimit = o1.miterLimit;
+ o2.shadowBlur = o1.shadowBlur;
+ o2.shadowColor = o1.shadowColor;
+ o2.shadowOffsetX = o1.shadowOffsetX;
+ o2.shadowOffsetY = o1.shadowOffsetY;
+ o2.strokeStyle = o1.strokeStyle;
+ o2.globalAlpha = o1.globalAlpha;
+ o2.font = o1.font;
+ o2.textAlign = o1.textAlign;
+ o2.textBaseline = o1.textBaseline;
+ o2.arcScaleX_ = o1.arcScaleX_;
+ o2.arcScaleY_ = o1.arcScaleY_;
+ o2.lineScale_ = o1.lineScale_;
+ }
+
+ var colorData = {
+ aliceblue: '#F0F8FF',
+ antiquewhite: '#FAEBD7',
+ aquamarine: '#7FFFD4',
+ azure: '#F0FFFF',
+ beige: '#F5F5DC',
+ bisque: '#FFE4C4',
+ black: '#000000',
+ blanchedalmond: '#FFEBCD',
+ blueviolet: '#8A2BE2',
+ brown: '#A52A2A',
+ burlywood: '#DEB887',
+ cadetblue: '#5F9EA0',
+ chartreuse: '#7FFF00',
+ chocolate: '#D2691E',
+ coral: '#FF7F50',
+ cornflowerblue: '#6495ED',
+ cornsilk: '#FFF8DC',
+ crimson: '#DC143C',
+ cyan: '#00FFFF',
+ darkblue: '#00008B',
+ darkcyan: '#008B8B',
+ darkgoldenrod: '#B8860B',
+ darkgray: '#A9A9A9',
+ darkgreen: '#006400',
+ darkgrey: '#A9A9A9',
+ darkkhaki: '#BDB76B',
+ darkmagenta: '#8B008B',
+ darkolivegreen: '#556B2F',
+ darkorange: '#FF8C00',
+ darkorchid: '#9932CC',
+ darkred: '#8B0000',
+ darksalmon: '#E9967A',
+ darkseagreen: '#8FBC8F',
+ darkslateblue: '#483D8B',
+ darkslategray: '#2F4F4F',
+ darkslategrey: '#2F4F4F',
+ darkturquoise: '#00CED1',
+ darkviolet: '#9400D3',
+ deeppink: '#FF1493',
+ deepskyblue: '#00BFFF',
+ dimgray: '#696969',
+ dimgrey: '#696969',
+ dodgerblue: '#1E90FF',
+ firebrick: '#B22222',
+ floralwhite: '#FFFAF0',
+ forestgreen: '#228B22',
+ gainsboro: '#DCDCDC',
+ ghostwhite: '#F8F8FF',
+ gold: '#FFD700',
+ goldenrod: '#DAA520',
+ grey: '#808080',
+ greenyellow: '#ADFF2F',
+ honeydew: '#F0FFF0',
+ hotpink: '#FF69B4',
+ indianred: '#CD5C5C',
+ indigo: '#4B0082',
+ ivory: '#FFFFF0',
+ khaki: '#F0E68C',
+ lavender: '#E6E6FA',
+ lavenderblush: '#FFF0F5',
+ lawngreen: '#7CFC00',
+ lemonchiffon: '#FFFACD',
+ lightblue: '#ADD8E6',
+ lightcoral: '#F08080',
+ lightcyan: '#E0FFFF',
+ lightgoldenrodyellow: '#FAFAD2',
+ lightgreen: '#90EE90',
+ lightgrey: '#D3D3D3',
+ lightpink: '#FFB6C1',
+ lightsalmon: '#FFA07A',
+ lightseagreen: '#20B2AA',
+ lightskyblue: '#87CEFA',
+ lightslategray: '#778899',
+ lightslategrey: '#778899',
+ lightsteelblue: '#B0C4DE',
+ lightyellow: '#FFFFE0',
+ limegreen: '#32CD32',
+ linen: '#FAF0E6',
+ magenta: '#FF00FF',
+ mediumaquamarine: '#66CDAA',
+ mediumblue: '#0000CD',
+ mediumorchid: '#BA55D3',
+ mediumpurple: '#9370DB',
+ mediumseagreen: '#3CB371',
+ mediumslateblue: '#7B68EE',
+ mediumspringgreen: '#00FA9A',
+ mediumturquoise: '#48D1CC',
+ mediumvioletred: '#C71585',
+ midnightblue: '#191970',
+ mintcream: '#F5FFFA',
+ mistyrose: '#FFE4E1',
+ moccasin: '#FFE4B5',
+ navajowhite: '#FFDEAD',
+ oldlace: '#FDF5E6',
+ olivedrab: '#6B8E23',
+ orange: '#FFA500',
+ orangered: '#FF4500',
+ orchid: '#DA70D6',
+ palegoldenrod: '#EEE8AA',
+ palegreen: '#98FB98',
+ paleturquoise: '#AFEEEE',
+ palevioletred: '#DB7093',
+ papayawhip: '#FFEFD5',
+ peachpuff: '#FFDAB9',
+ peru: '#CD853F',
+ pink: '#FFC0CB',
+ plum: '#DDA0DD',
+ powderblue: '#B0E0E6',
+ rosybrown: '#BC8F8F',
+ royalblue: '#4169E1',
+ saddlebrown: '#8B4513',
+ salmon: '#FA8072',
+ sandybrown: '#F4A460',
+ seagreen: '#2E8B57',
+ seashell: '#FFF5EE',
+ sienna: '#A0522D',
+ skyblue: '#87CEEB',
+ slateblue: '#6A5ACD',
+ slategray: '#708090',
+ slategrey: '#708090',
+ snow: '#FFFAFA',
+ springgreen: '#00FF7F',
+ steelblue: '#4682B4',
+ tan: '#D2B48C',
+ thistle: '#D8BFD8',
+ tomato: '#FF6347',
+ turquoise: '#40E0D0',
+ violet: '#EE82EE',
+ wheat: '#F5DEB3',
+ whitesmoke: '#F5F5F5',
+ yellowgreen: '#9ACD32'
+ };
+
+
+ function getRgbHslContent(styleString) {
+ var start = styleString.indexOf('(', 3);
+ var end = styleString.indexOf(')', start + 1);
+ var parts = styleString.substring(start + 1, end).split(',');
+ // add alpha if needed
+ if (parts.length != 4 || styleString.charAt(3) != 'a') {
+ parts[3] = 1;
+ }
+ return parts;
+ }
+
+ function percent(s) {
+ return parseFloat(s) / 100;
+ }
+
+ function clamp(v, min, max) {
+ return Math.min(max, Math.max(min, v));
+ }
+
+ function hslToRgb(parts){
+ var r, g, b, h, s, l;
+ h = parseFloat(parts[0]) / 360 % 360;
+ if (h < 0)
+ h++;
+ s = clamp(percent(parts[1]), 0, 1);
+ l = clamp(percent(parts[2]), 0, 1);
+ if (s == 0) {
+ r = g = b = l; // achromatic
+ } else {
+ var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ var p = 2 * l - q;
+ r = hueToRgb(p, q, h + 1 / 3);
+ g = hueToRgb(p, q, h);
+ b = hueToRgb(p, q, h - 1 / 3);
+ }
+
+ return '#' + decToHex[Math.floor(r * 255)] +
+ decToHex[Math.floor(g * 255)] +
+ decToHex[Math.floor(b * 255)];
+ }
+
+ function hueToRgb(m1, m2, h) {
+ if (h < 0)
+ h++;
+ if (h > 1)
+ h--;
+
+ if (6 * h < 1)
+ return m1 + (m2 - m1) * 6 * h;
+ else if (2 * h < 1)
+ return m2;
+ else if (3 * h < 2)
+ return m1 + (m2 - m1) * (2 / 3 - h) * 6;
+ else
+ return m1;
+ }
+
+ var processStyleCache = {};
+
+ function processStyle(styleString) {
+ if (styleString in processStyleCache) {
+ return processStyleCache[styleString];
+ }
+
+ var str, alpha = 1;
+
+ styleString = String(styleString);
+ if (styleString.charAt(0) == '#') {
+ str = styleString;
+ } else if (/^rgb/.test(styleString)) {
+ var parts = getRgbHslContent(styleString);
+ var str = '#', n;
+ for (var i = 0; i < 3; i++) {
+ if (parts[i].indexOf('%') != -1) {
+ n = Math.floor(percent(parts[i]) * 255);
+ } else {
+ n = +parts[i];
+ }
+ str += decToHex[clamp(n, 0, 255)];
+ }
+ alpha = +parts[3];
+ } else if (/^hsl/.test(styleString)) {
+ var parts = getRgbHslContent(styleString);
+ str = hslToRgb(parts);
+ alpha = parts[3];
+ } else {
+ str = colorData[styleString] || styleString;
+ }
+ return processStyleCache[styleString] = {color: str, alpha: alpha};
+ }
+
+ var DEFAULT_STYLE = {
+ style: 'normal',
+ variant: 'normal',
+ weight: 'normal',
+ size: 10,
+ family: 'sans-serif'
+ };
+
+ // Internal text style cache
+ var fontStyleCache = {};
+
+ function processFontStyle(styleString) {
+ if (fontStyleCache[styleString]) {
+ return fontStyleCache[styleString];
+ }
+
+ var el = document.createElement('div');
+ var style = el.style;
+ try {
+ style.font = styleString;
+ } catch (ex) {
+ // Ignore failures to set to invalid font.
+ }
+
+ return fontStyleCache[styleString] = {
+ style: style.fontStyle || DEFAULT_STYLE.style,
+ variant: style.fontVariant || DEFAULT_STYLE.variant,
+ weight: style.fontWeight || DEFAULT_STYLE.weight,
+ size: style.fontSize || DEFAULT_STYLE.size,
+ family: style.fontFamily || DEFAULT_STYLE.family
+ };
+ }
+
+ function getComputedStyle(style, element) {
+ var computedStyle = {};
+
+ for (var p in style) {
+ computedStyle[p] = style[p];
+ }
+
+ // Compute the size
+ var canvasFontSize = parseFloat(element.currentStyle.fontSize),
+ fontSize = parseFloat(style.size);
+
+ if (typeof style.size == 'number') {
+ computedStyle.size = style.size;
+ } else if (style.size.indexOf('px') != -1) {
+ computedStyle.size = fontSize;
+ } else if (style.size.indexOf('em') != -1) {
+ computedStyle.size = canvasFontSize * fontSize;
+ } else if(style.size.indexOf('%') != -1) {
+ computedStyle.size = (canvasFontSize / 100) * fontSize;
+ } else if (style.size.indexOf('pt') != -1) {
+ computedStyle.size = fontSize / .75;
+ } else {
+ computedStyle.size = canvasFontSize;
+ }
+
+ // Different scaling between normal text and VML text. This was found using
+ // trial and error to get the same size as non VML text.
+ computedStyle.size *= 0.981;
+
+ return computedStyle;
+ }
+
+ function buildStyle(style) {
+ return style.style + ' ' + style.variant + ' ' + style.weight + ' ' +
+ style.size + 'px ' + style.family;
+ }
+
+ var lineCapMap = {
+ 'butt': 'flat',
+ 'round': 'round'
+ };
+
+ function processLineCap(lineCap) {
+ return lineCapMap[lineCap] || 'square';
+ }
+
+ /**
+ * This class implements CanvasRenderingContext2D interface as described by
+ * the WHATWG.
+ * @param {HTMLElement} canvasElement The element that the 2D context should
+ * be associated with
+ */
+ function CanvasRenderingContext2D_(canvasElement) {
+ this.m_ = createMatrixIdentity();
+
+ this.mStack_ = [];
+ this.aStack_ = [];
+ this.currentPath_ = [];
+
+ // Canvas context properties
+ this.strokeStyle = '#000';
+ this.fillStyle = '#000';
+
+ this.lineWidth = 1;
+ this.lineJoin = 'miter';
+ this.lineCap = 'butt';
+ this.miterLimit = Z * 1;
+ this.globalAlpha = 1;
+ this.font = '10px sans-serif';
+ this.textAlign = 'left';
+ this.textBaseline = 'alphabetic';
+ this.canvas = canvasElement;
+
+ var cssText = 'width:' + canvasElement.clientWidth + 'px;height:' +
+ canvasElement.clientHeight + 'px;overflow:hidden;position:absolute';
+ var el = canvasElement.ownerDocument.createElement('div');
+ el.style.cssText = cssText;
+ canvasElement.appendChild(el);
+
+ var overlayEl = el.cloneNode(false);
+ // Use a non transparent background.
+ overlayEl.style.backgroundColor = 'red';
+ overlayEl.style.filter = 'alpha(opacity=0)';
+ canvasElement.appendChild(overlayEl);
+
+ this.element_ = el;
+ this.arcScaleX_ = 1;
+ this.arcScaleY_ = 1;
+ this.lineScale_ = 1;
+ }
+
+ var contextPrototype = CanvasRenderingContext2D_.prototype;
+ contextPrototype.clearRect = function() {
+ if (this.textMeasureEl_) {
+ this.textMeasureEl_.removeNode(true);
+ this.textMeasureEl_ = null;
+ }
+ this.element_.innerHTML = '';
+ };
+
+ contextPrototype.beginPath = function() {
+ // TODO: Branch current matrix so that save/restore has no effect
+ // as per safari docs.
+ this.currentPath_ = [];
+ };
+
+ contextPrototype.moveTo = function(aX, aY) {
+ var p = getCoords(this, aX, aY);
+ this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y});
+ this.currentX_ = p.x;
+ this.currentY_ = p.y;
+ };
+
+ contextPrototype.lineTo = function(aX, aY) {
+ var p = getCoords(this, aX, aY);
+ this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y});
+
+ this.currentX_ = p.x;
+ this.currentY_ = p.y;
+ };
+
+ contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
+ aCP2x, aCP2y,
+ aX, aY) {
+ var p = getCoords(this, aX, aY);
+ var cp1 = getCoords(this, aCP1x, aCP1y);
+ var cp2 = getCoords(this, aCP2x, aCP2y);
+ bezierCurveTo(this, cp1, cp2, p);
+ };
+
+ // Helper function that takes the already fixed cordinates.
+ function bezierCurveTo(self, cp1, cp2, p) {
+ self.currentPath_.push({
+ type: 'bezierCurveTo',
+ cp1x: cp1.x,
+ cp1y: cp1.y,
+ cp2x: cp2.x,
+ cp2y: cp2.y,
+ x: p.x,
+ y: p.y
+ });
+ self.currentX_ = p.x;
+ self.currentY_ = p.y;
+ }
+
+ contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
+ // the following is lifted almost directly from
+ // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes
+
+ var cp = getCoords(this, aCPx, aCPy);
+ var p = getCoords(this, aX, aY);
+
+ var cp1 = {
+ x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_),
+ y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_)
+ };
+ var cp2 = {
+ x: cp1.x + (p.x - this.currentX_) / 3.0,
+ y: cp1.y + (p.y - this.currentY_) / 3.0
+ };
+
+ bezierCurveTo(this, cp1, cp2, p);
+ };
+
+ contextPrototype.arc = function(aX, aY, aRadius,
+ aStartAngle, aEndAngle, aClockwise) {
+ aRadius *= Z;
+ var arcType = aClockwise ? 'at' : 'wa';
+
+ var xStart = aX + mc(aStartAngle) * aRadius - Z2;
+ var yStart = aY + ms(aStartAngle) * aRadius - Z2;
+
+ var xEnd = aX + mc(aEndAngle) * aRadius - Z2;
+ var yEnd = aY + ms(aEndAngle) * aRadius - Z2;
+
+ // IE won't render arches drawn counter clockwise if xStart == xEnd.
+ if (xStart == xEnd && !aClockwise) {
+ xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
+ // that can be represented in binary
+ }
+
+ var p = getCoords(this, aX, aY);
+ var pStart = getCoords(this, xStart, yStart);
+ var pEnd = getCoords(this, xEnd, yEnd);
+
+ this.currentPath_.push({type: arcType,
+ x: p.x,
+ y: p.y,
+ radius: aRadius,
+ xStart: pStart.x,
+ yStart: pStart.y,
+ xEnd: pEnd.x,
+ yEnd: pEnd.y});
+
+ };
+
+ contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
+ this.moveTo(aX, aY);
+ this.lineTo(aX + aWidth, aY);
+ this.lineTo(aX + aWidth, aY + aHeight);
+ this.lineTo(aX, aY + aHeight);
+ this.closePath();
+ };
+
+ contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
+ var oldPath = this.currentPath_;
+ this.beginPath();
+
+ this.moveTo(aX, aY);
+ this.lineTo(aX + aWidth, aY);
+ this.lineTo(aX + aWidth, aY + aHeight);
+ this.lineTo(aX, aY + aHeight);
+ this.closePath();
+ this.stroke();
+
+ this.currentPath_ = oldPath;
+ };
+
+ contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
+ var oldPath = this.currentPath_;
+ this.beginPath();
+
+ this.moveTo(aX, aY);
+ this.lineTo(aX + aWidth, aY);
+ this.lineTo(aX + aWidth, aY + aHeight);
+ this.lineTo(aX, aY + aHeight);
+ this.closePath();
+ this.fill();
+
+ this.currentPath_ = oldPath;
+ };
+
+ contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
+ var gradient = new CanvasGradient_('gradient');
+ gradient.x0_ = aX0;
+ gradient.y0_ = aY0;
+ gradient.x1_ = aX1;
+ gradient.y1_ = aY1;
+ return gradient;
+ };
+
+ contextPrototype.createRadialGradient = function(aX0, aY0, aR0,
+ aX1, aY1, aR1) {
+ var gradient = new CanvasGradient_('gradientradial');
+ gradient.x0_ = aX0;
+ gradient.y0_ = aY0;
+ gradient.r0_ = aR0;
+ gradient.x1_ = aX1;
+ gradient.y1_ = aY1;
+ gradient.r1_ = aR1;
+ return gradient;
+ };
+
+ contextPrototype.drawImage = function(image, var_args) {
+ var dx, dy, dw, dh, sx, sy, sw, sh;
+
+ // to find the original width we overide the width and height
+ var oldRuntimeWidth = image.runtimeStyle.width;
+ var oldRuntimeHeight = image.runtimeStyle.height;
+ image.runtimeStyle.width = 'auto';
+ image.runtimeStyle.height = 'auto';
+
+ // get the original size
+ var w = image.width;
+ var h = image.height;
+
+ // and remove overides
+ image.runtimeStyle.width = oldRuntimeWidth;
+ image.runtimeStyle.height = oldRuntimeHeight;
+
+ if (arguments.length == 3) {
+ dx = arguments[1];
+ dy = arguments[2];
+ sx = sy = 0;
+ sw = dw = w;
+ sh = dh = h;
+ } else if (arguments.length == 5) {
+ dx = arguments[1];
+ dy = arguments[2];
+ dw = arguments[3];
+ dh = arguments[4];
+ sx = sy = 0;
+ sw = w;
+ sh = h;
+ } else if (arguments.length == 9) {
+ sx = arguments[1];
+ sy = arguments[2];
+ sw = arguments[3];
+ sh = arguments[4];
+ dx = arguments[5];
+ dy = arguments[6];
+ dw = arguments[7];
+ dh = arguments[8];
+ } else {
+ throw Error('Invalid number of arguments');
+ }
+
+ var d = getCoords(this, dx, dy);
+
+ var w2 = sw / 2;
+ var h2 = sh / 2;
+
+ var vmlStr = [];
+
+ var W = 10;
+ var H = 10;
+
+ // For some reason that I've now forgotten, using divs didn't work
+ vmlStr.push(' <g_vml_:group',
+ ' coordsize="', Z * W, ',', Z * H, '"',
+ ' coordorigin="0,0"' ,
+ ' style="width:', W, 'px;height:', H, 'px;position:absolute;');
+
+ // If filters are necessary (rotation exists), create them
+ // filters are bog-slow, so only create them if abbsolutely necessary
+ // The following check doesn't account for skews (which don't exist
+ // in the canvas spec (yet) anyway.
+
+ if (this.m_[0][0] != 1 || this.m_[0][1] ||
+ this.m_[1][1] != 1 || this.m_[1][0]) {
+ var filter = [];
+
+ // Note the 12/21 reversal
+ filter.push('M11=', this.m_[0][0], ',',
+ 'M12=', this.m_[1][0], ',',
+ 'M21=', this.m_[0][1], ',',
+ 'M22=', this.m_[1][1], ',',
+ 'Dx=', mr(d.x / Z), ',',
+ 'Dy=', mr(d.y / Z), '');
+
+ // Bounding box calculation (need to minimize displayed area so that
+ // filters don't waste time on unused pixels.
+ var max = d;
+ var c2 = getCoords(this, dx + dw, dy);
+ var c3 = getCoords(this, dx, dy + dh);
+ var c4 = getCoords(this, dx + dw, dy + dh);
+
+ max.x = m.max(max.x, c2.x, c3.x, c4.x);
+ max.y = m.max(max.y, c2.y, c3.y, c4.y);
+
+ vmlStr.push('padding:0 ', mr(max.x / Z), 'px ', mr(max.y / Z),
+ 'px 0;filter:progid:DXImageTransform.Microsoft.Matrix(',
+ filter.join(''), ", sizingmethod='clip');");
+
+ } else {
+ vmlStr.push('top:', mr(d.y / Z), 'px;left:', mr(d.x / Z), 'px;');
+ }
+
+ vmlStr.push(' ">' ,
+ '<g_vml_:image src="', image.src, '"',
+ ' style="width:', Z * dw, 'px;',
+ ' height:', Z * dh, 'px"',
+ ' cropleft="', sx / w, '"',
+ ' croptop="', sy / h, '"',
+ ' cropright="', (w - sx - sw) / w, '"',
+ ' cropbottom="', (h - sy - sh) / h, '"',
+ ' />',
+ '</g_vml_:group>');
+
+ this.element_.insertAdjacentHTML('BeforeEnd', vmlStr.join(''));
+ };
+
+ contextPrototype.stroke = function(aFill) {
+ var lineStr = [];
+ var lineOpen = false;
+
+ var W = 10;
+ var H = 10;
+
+ lineStr.push('<g_vml_:shape',
+ ' filled="', !!aFill, '"',
+ ' style="position:absolute;width:', W, 'px;height:', H, 'px;"',
+ ' coordorigin="0,0"',
+ ' coordsize="', Z * W, ',', Z * H, '"',
+ ' stroked="', !aFill, '"',
+ ' path="');
+
+ var newSeq = false;
+ var min = {x: null, y: null};
+ var max = {x: null, y: null};
+
+ for (var i = 0; i < this.currentPath_.length; i++) {
+ var p = this.currentPath_[i];
+ var c;
+
+ switch (p.type) {
+ case 'moveTo':
+ c = p;
+ lineStr.push(' m ', mr(p.x), ',', mr(p.y));
+ break;
+ case 'lineTo':
+ lineStr.push(' l ', mr(p.x), ',', mr(p.y));
+ break;
+ case 'close':
+ lineStr.push(' x ');
+ p = null;
+ break;
+ case 'bezierCurveTo':
+ lineStr.push(' c ',
+ mr(p.cp1x), ',', mr(p.cp1y), ',',
+ mr(p.cp2x), ',', mr(p.cp2y), ',',
+ mr(p.x), ',', mr(p.y));
+ break;
+ case 'at':
+ case 'wa':
+ lineStr.push(' ', p.type, ' ',
+ mr(p.x - this.arcScaleX_ * p.radius), ',',
+ mr(p.y - this.arcScaleY_ * p.radius), ' ',
+ mr(p.x + this.arcScaleX_ * p.radius), ',',
+ mr(p.y + this.arcScaleY_ * p.radius), ' ',
+ mr(p.xStart), ',', mr(p.yStart), ' ',
+ mr(p.xEnd), ',', mr(p.yEnd));
+ break;
+ }
+
+
+ // TODO: Following is broken for curves due to
+ // move to proper paths.
+
+ // Figure out dimensions so we can do gradient fills
+ // properly
+ if (p) {
+ if (min.x == null || p.x < min.x) {
+ min.x = p.x;
+ }
+ if (max.x == null || p.x > max.x) {
+ max.x = p.x;
+ }
+ if (min.y == null || p.y < min.y) {
+ min.y = p.y;
+ }
+ if (max.y == null || p.y > max.y) {
+ max.y = p.y;
+ }
+ }
+ }
+ lineStr.push(' ">');
+
+ if (!aFill) {
+ appendStroke(this, lineStr);
+ } else {
+ appendFill(this, lineStr, min, max);
+ }
+
+ lineStr.push('</g_vml_:shape>');
+
+ this.element_.insertAdjacentHTML('beforeEnd', lineStr.join(''));
+ };
+
+ function appendStroke(ctx, lineStr) {
+ var a = processStyle(ctx.strokeStyle);
+ var color = a.color;
+ var opacity = a.alpha * ctx.globalAlpha;
+ var lineWidth = ctx.lineScale_ * ctx.lineWidth;
+
+ // VML cannot correctly render a line if the width is less than 1px.
+ // In that case, we dilute the color to make the line look thinner.
+ if (lineWidth < 1) {
+ opacity *= lineWidth;
+ }
+
+ lineStr.push(
+ '<g_vml_:stroke',
+ ' opacity="', opacity, '"',
+ ' joinstyle="', ctx.lineJoin, '"',
+ ' miterlimit="', ctx.miterLimit, '"',
+ ' endcap="', processLineCap(ctx.lineCap), '"',
+ ' weight="', lineWidth, 'px"',
+ ' color="', color, '" />'
+ );
+ }
+
+ function appendFill(ctx, lineStr, min, max) {
+ var fillStyle = ctx.fillStyle;
+ var arcScaleX = ctx.arcScaleX_;
+ var arcScaleY = ctx.arcScaleY_;
+ var width = max.x - min.x;
+ var height = max.y - min.y;
+ if (fillStyle instanceof CanvasGradient_) {
+ // TODO: Gradients transformed with the transformation matrix.
+ var angle = 0;
+ var focus = {x: 0, y: 0};
+
+ // additional offset
+ var shift = 0;
+ // scale factor for offset
+ var expansion = 1;
+
+ if (fillStyle.type_ == 'gradient') {
+ var x0 = fillStyle.x0_ / arcScaleX;
+ var y0 = fillStyle.y0_ / arcScaleY;
+ var x1 = fillStyle.x1_ / arcScaleX;
+ var y1 = fillStyle.y1_ / arcScaleY;
+ var p0 = getCoords(ctx, x0, y0);
+ var p1 = getCoords(ctx, x1, y1);
+ var dx = p1.x - p0.x;
+ var dy = p1.y - p0.y;
+ angle = Math.atan2(dx, dy) * 180 / Math.PI;
+
+ // The angle should be a non-negative number.
+ if (angle < 0) {
+ angle += 360;
+ }
+
+ // Very small angles produce an unexpected result because they are
+ // converted to a scientific notation string.
+ if (angle < 1e-6) {
+ angle = 0;
+ }
+ } else {
+ var p0 = getCoords(ctx, fillStyle.x0_, fillStyle.y0_);
+ focus = {
+ x: (p0.x - min.x) / width,
+ y: (p0.y - min.y) / height
+ };
+
+ width /= arcScaleX * Z;
+ height /= arcScaleY * Z;
+ var dimension = m.max(width, height);
+ shift = 2 * fillStyle.r0_ / dimension;
+ expansion = 2 * fillStyle.r1_ / dimension - shift;
+ }
+
+ // We need to sort the color stops in ascending order by offset,
+ // otherwise IE won't interpret it correctly.
+ var stops = fillStyle.colors_;
+ stops.sort(function(cs1, cs2) {
+ return cs1.offset - cs2.offset;
+ });
+
+ var length = stops.length;
+ var color1 = stops[0].color;
+ var color2 = stops[length - 1].color;
+ var opacity1 = stops[0].alpha * ctx.globalAlpha;
+ var opacity2 = stops[length - 1].alpha * ctx.globalAlpha;
+
+ var colors = [];
+ for (var i = 0; i < length; i++) {
+ var stop = stops[i];
+ colors.push(stop.offset * expansion + shift + ' ' + stop.color);
+ }
+
+ // When colors attribute is used, the meanings of opacity and o:opacity2
+ // are reversed.
+ lineStr.push('<g_vml_:fill type="', fillStyle.type_, '"',
+ ' method="none" focus="100%"',
+ ' color="', color1, '"',
+ ' color2="', color2, '"',
+ ' colors="', colors.join(','), '"',
+ ' opacity="', opacity2, '"',
+ ' g_o_:opacity2="', opacity1, '"',
+ ' angle="', angle, '"',
+ ' focusposition="', focus.x, ',', focus.y, '" />');
+ } else if (fillStyle instanceof CanvasPattern_) {
+ if (width && height) {
+ var deltaLeft = -min.x;
+ var deltaTop = -min.y;
+ lineStr.push('<g_vml_:fill',
+ ' position="',
+ deltaLeft / width * arcScaleX * arcScaleX, ',',
+ deltaTop / height * arcScaleY * arcScaleY, '"',
+ ' type="tile"',
+ // TODO: Figure out the correct size to fit the scale.
+ //' size="', w, 'px ', h, 'px"',
+ ' src="', fillStyle.src_, '" />');
+ }
+ } else {
+ var a = processStyle(ctx.fillStyle);
+ var color = a.color;
+ var opacity = a.alpha * ctx.globalAlpha;
+ lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity,
+ '" />');
+ }
+ }
+
+ contextPrototype.fill = function() {
+ this.stroke(true);
+ };
+
+ contextPrototype.closePath = function() {
+ this.currentPath_.push({type: 'close'});
+ };
+
+ function getCoords(ctx, aX, aY) {
+ var m = ctx.m_;
+ return {
+ x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2,
+ y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2
+ };
+ };
+
+ contextPrototype.save = function() {
+ var o = {};
+ copyState(this, o);
+ this.aStack_.push(o);
+ this.mStack_.push(this.m_);
+ this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
+ };
+
+ contextPrototype.restore = function() {
+ if (this.aStack_.length) {
+ copyState(this.aStack_.pop(), this);
+ this.m_ = this.mStack_.pop();
+ }
+ };
+
+ function matrixIsFinite(m) {
+ return isFinite(m[0][0]) && isFinite(m[0][1]) &&
+ isFinite(m[1][0]) && isFinite(m[1][1]) &&
+ isFinite(m[2][0]) && isFinite(m[2][1]);
+ }
+
+ function setM(ctx, m, updateLineScale) {
+ if (!matrixIsFinite(m)) {
+ return;
+ }
+ ctx.m_ = m;
+
+ if (updateLineScale) {
+ // Get the line scale.
+ // Determinant of this.m_ means how much the area is enlarged by the
+ // transformation. So its square root can be used as a scale factor
+ // for width.
+ var det = m[0][0] * m[1][1] - m[0][1] * m[1][0];
+ ctx.lineScale_ = sqrt(abs(det));
+ }
+ }
+
+ contextPrototype.translate = function(aX, aY) {
+ var m1 = [
+ [1, 0, 0],
+ [0, 1, 0],
+ [aX, aY, 1]
+ ];
+
+ setM(this, matrixMultiply(m1, this.m_), false);
+ };
+
+ contextPrototype.rotate = function(aRot) {
+ var c = mc(aRot);
+ var s = ms(aRot);
+
+ var m1 = [
+ [c, s, 0],
+ [-s, c, 0],
+ [0, 0, 1]
+ ];
+
+ setM(this, matrixMultiply(m1, this.m_), false);
+ };
+
+ contextPrototype.scale = function(aX, aY) {
+ this.arcScaleX_ *= aX;
+ this.arcScaleY_ *= aY;
+ var m1 = [
+ [aX, 0, 0],
+ [0, aY, 0],
+ [0, 0, 1]
+ ];
+
+ setM(this, matrixMultiply(m1, this.m_), true);
+ };
+
+ contextPrototype.transform = function(m11, m12, m21, m22, dx, dy) {
+ var m1 = [
+ [m11, m12, 0],
+ [m21, m22, 0],
+ [dx, dy, 1]
+ ];
+
+ setM(this, matrixMultiply(m1, this.m_), true);
+ };
+
+ contextPrototype.setTransform = function(m11, m12, m21, m22, dx, dy) {
+ var m = [
+ [m11, m12, 0],
+ [m21, m22, 0],
+ [dx, dy, 1]
+ ];
+
+ setM(this, m, true);
+ };
+
+ /**
+ * The text drawing function.
+ * The maxWidth argument isn't taken in account, since no browser supports
+ * it yet.
+ */
+ contextPrototype.drawText_ = function(text, x, y, maxWidth, stroke) {
+ var m = this.m_,
+ delta = 1000,
+ left = 0,
+ right = delta,
+ offset = {x: 0, y: 0},
+ lineStr = [];
+
+ var fontStyle = getComputedStyle(processFontStyle(this.font),
+ this.element_);
+
+ var fontStyleString = buildStyle(fontStyle);
+
+ var elementStyle = this.element_.currentStyle;
+ var textAlign = this.textAlign.toLowerCase();
+ switch (textAlign) {
+ case 'left':
+ case 'center':
+ case 'right':
+ break;
+ case 'end':
+ textAlign = elementStyle.direction == 'ltr' ? 'right' : 'left';
+ break;
+ case 'start':
+ textAlign = elementStyle.direction == 'rtl' ? 'right' : 'left';
+ break;
+ default:
+ textAlign = 'left';
+ }
+
+ // 1.75 is an arbitrary number, as there is no info about the text baseline
+ switch (this.textBaseline) {
+ case 'hanging':
+ case 'top':
+ offset.y = fontStyle.size / 1.75;
+ break;
+ case 'middle':
+ break;
+ default:
+ case null:
+ case 'alphabetic':
+ case 'ideographic':
+ case 'bottom':
+ offset.y = -fontStyle.size / 2.25;
+ break;
+ }
+
+ switch(textAlign) {
+ case 'right':
+ left = delta;
+ right = 0.05;
+ break;
+ case 'center':
+ left = right = delta / 2;
+ break;
+ }
+
+ var d = getCoords(this, x + offset.x, y + offset.y);
+
+ lineStr.push('<g_vml_:line from="', -left ,' 0" to="', right ,' 0.05" ',
+ ' coordsize="100 100" coordorigin="0 0"',
+ ' filled="', !stroke, '" stroked="', !!stroke,
+ '" style="position:absolute;width:1px;height:1px;">');
+
+ if (stroke) {
+ appendStroke(this, lineStr);
+ } else {
+ // TODO: Fix the min and max params.
+ appendFill(this, lineStr, {x: -left, y: 0},
+ {x: right, y: fontStyle.size});
+ }
+
+ var skewM = m[0][0].toFixed(3) + ',' + m[1][0].toFixed(3) + ',' +
+ m[0][1].toFixed(3) + ',' + m[1][1].toFixed(3) + ',0,0';
+
+ var skewOffset = mr(d.x / Z) + ',' + mr(d.y / Z);
+
+ lineStr.push('<g_vml_:skew on="t" matrix="', skewM ,'" ',
+ ' offset="', skewOffset, '" origin="', left ,' 0" />',
+ '<g_vml_:path textpathok="true" />',
+ '<g_vml_:textpath on="true" string="',
+ encodeHtmlAttribute(text),
+ '" style="v-text-align:', textAlign,
+ ';font:', encodeHtmlAttribute(fontStyleString),
+ '" /></g_vml_:line>');
+
+ this.element_.insertAdjacentHTML('beforeEnd', lineStr.join(''));
+ };
+
+ contextPrototype.fillText = function(text, x, y, maxWidth) {
+ this.drawText_(text, x, y, maxWidth, false);
+ };
+
+ contextPrototype.strokeText = function(text, x, y, maxWidth) {
+ this.drawText_(text, x, y, maxWidth, true);
+ };
+
+ contextPrototype.measureText = function(text) {
+ if (!this.textMeasureEl_) {
+ var s = '<span style="position:absolute;' +
+ 'top:-20000px;left:0;padding:0;margin:0;border:none;' +
+ 'white-space:pre;"></span>';
+ this.element_.insertAdjacentHTML('beforeEnd', s);
+ this.textMeasureEl_ = this.element_.lastChild;
+ }
+ var doc = this.element_.ownerDocument;
+ this.textMeasureEl_.innerHTML = '';
+ this.textMeasureEl_.style.font = this.font;
+ // Don't use innerHTML or innerText because they allow markup/whitespace.
+ this.textMeasureEl_.appendChild(doc.createTextNode(text));
+ return {width: this.textMeasureEl_.offsetWidth};
+ };
+
+ /******** STUBS ********/
+ contextPrototype.clip = function() {
+ // TODO: Implement
+ };
+
+ contextPrototype.arcTo = function() {
+ // TODO: Implement
+ };
+
+ contextPrototype.createPattern = function(image, repetition) {
+ return new CanvasPattern_(image, repetition);
+ };
+
+ // Gradient / Pattern Stubs
+ function CanvasGradient_(aType) {
+ this.type_ = aType;
+ this.x0_ = 0;
+ this.y0_ = 0;
+ this.r0_ = 0;
+ this.x1_ = 0;
+ this.y1_ = 0;
+ this.r1_ = 0;
+ this.colors_ = [];
+ }
+
+ CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
+ aColor = processStyle(aColor);
+ this.colors_.push({offset: aOffset,
+ color: aColor.color,
+ alpha: aColor.alpha});
+ };
+
+ function CanvasPattern_(image, repetition) {
+ assertImageIsValid(image);
+ switch (repetition) {
+ case 'repeat':
+ case null:
+ case '':
+ this.repetition_ = 'repeat';
+ break
+ case 'repeat-x':
+ case 'repeat-y':
+ case 'no-repeat':
+ this.repetition_ = repetition;
+ break;
+ default:
+ throwException('SYNTAX_ERR');
+ }
+
+ this.src_ = image.src;
+ this.width_ = image.width;
+ this.height_ = image.height;
+ }
+
+ function throwException(s) {
+ throw new DOMException_(s);
+ }
+
+ function assertImageIsValid(img) {
+ if (!img || img.nodeType != 1 || img.tagName != 'IMG') {
+ throwException('TYPE_MISMATCH_ERR');
+ }
+ if (img.readyState != 'complete') {
+ throwException('INVALID_STATE_ERR');
+ }
+ }
+
+ function DOMException_(s) {
+ this.code = this[s];
+ this.message = s +': DOM Exception ' + this.code;
+ }
+ var p = DOMException_.prototype = new Error;
+ p.INDEX_SIZE_ERR = 1;
+ p.DOMSTRING_SIZE_ERR = 2;
+ p.HIERARCHY_REQUEST_ERR = 3;
+ p.WRONG_DOCUMENT_ERR = 4;
+ p.INVALID_CHARACTER_ERR = 5;
+ p.NO_DATA_ALLOWED_ERR = 6;
+ p.NO_MODIFICATION_ALLOWED_ERR = 7;
+ p.NOT_FOUND_ERR = 8;
+ p.NOT_SUPPORTED_ERR = 9;
+ p.INUSE_ATTRIBUTE_ERR = 10;
+ p.INVALID_STATE_ERR = 11;
+ p.SYNTAX_ERR = 12;
+ p.INVALID_MODIFICATION_ERR = 13;
+ p.NAMESPACE_ERR = 14;
+ p.INVALID_ACCESS_ERR = 15;
+ p.VALIDATION_ERR = 16;
+ p.TYPE_MISMATCH_ERR = 17;
+
+ // set up externs
+ G_vmlCanvasManager = G_vmlCanvasManager_;
+ CanvasRenderingContext2D = CanvasRenderingContext2D_;
+ CanvasGradient = CanvasGradient_;
+ CanvasPattern = CanvasPattern_;
+ DOMException = DOMException_;
+})();
+
+} // if
diff --git a/libs/jqplot/gpl-2.0.txt b/libs/jqplot/gpl-2.0.txt
new file mode 100644
index 0000000000..864c6b8b7d
--- /dev/null
+++ b/libs/jqplot/gpl-2.0.txt
@@ -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
diff --git a/libs/jqplot/jqPlotCssStyling.txt b/libs/jqplot/jqPlotCssStyling.txt
new file mode 100644
index 0000000000..041035d2ed
--- /dev/null
+++ b/libs/jqplot/jqPlotCssStyling.txt
@@ -0,0 +1,53 @@
+Title: jqPlot CSS Customization
+
+Much of the styling of jqPlot is done by css. The jqPlot css file is, unremarkably,
+jquery.jqplot.css and resides in the same directory as jqPlot itself.
+
+There exist some styling related javascript properties on the plot objects themselves
+(like fontStyle, fontSize, etc.). These can be set with the options object at plot creation.
+Generally, setting these options is *NOT* the preferred way to customize the look of the
+plot. Use the css file instead. *These options are deprecated and may disappear*. The
+exceptions are certain background and color options which control attributes of something
+renderered on a canvas. This would be line color, grid background, etc. These must
+be set by the options object. For a list of available options, see <jqPlot Options>.
+
+Objects in the plot that can be customized by css are given a css class like ".jqplot-*".
+For example, the plot title will have a ".jqplot-title" class, the axes ".jqplot-axis", etc.
+
+Currently assigned classes in jqPlot
+are as follows:
+
+.jqplot-target - Styles for the plot target div. These will be cascaded down
+to all plot elements according to css rules.
+
+.jqplot-axis - Styles for all axes
+
+.jqplot-xaxis - Styles applied to the primary x axis only.
+
+.jqplot-yaxis - Styles applied to the primary y axis only.
+
+.jqplot-x2axis, .jqplot-x3axis, ... - Styles applied to the 2nd, 3rd, etc. x axis only.
+
+.jqplot-y2axis, .jqplot-y3axis, ... - Styles applied to the 2nd, 3rd, etc.y axis only.
+
+.jqplot-axis-tick - Styles applied to all axis ticks
+
+.jqplot-xaxis-tick - Styles applied to primary x axis ticks only.
+
+.jqplot-x2axis-tick - Styles applied to secondary x axis ticks only.
+
+.jqplot-yaxis-tick - Styles applied to primary y axis ticks only.
+
+.jqplot-y2axis-tick - Styles applied to secondary y axis ticks only.
+
+table.jqplot-table-legend - Styles applied to the legend box table.
+
+.jqplot-title - Styles applied to the title.
+
+.jqplot-cursor-tooltip - Styles applied to the cursor tooltip
+
+.jqplot-highlighter-tooltip - Styles applied to the highlighter tooltip.
+
+div.jqplot-table-legend-swatch - the div element used for the colored swatch on the legend.
+
+Note that axes will be assigned 2 classes like: class=".jqplot-axis .jqplot-xaxis". \ No newline at end of file
diff --git a/libs/jqplot/jqPlotOptions.txt b/libs/jqplot/jqPlotOptions.txt
new file mode 100644
index 0000000000..d2d1e68ccf
--- /dev/null
+++ b/libs/jqplot/jqPlotOptions.txt
@@ -0,0 +1,276 @@
+Title: jqPlot Options
+
+**This document is out of date. While the options described here should still be
+relavent and valid, it has not been updated for many new options. Sorry for
+this inconvenience.**
+
+This document describes the options available to jqPlot. These are set with the
+third argument to the $.jqplot('target', data, options) function. Options are
+using the following convention:
+
+{{{
+property: default, // notes
+}}}
+
+This document is not complete! Not all options are shown! Also, Options marked
+with ** in the notes are post 0.7.1 additions. They will be available in the next
+release. Further information about the options can be found in the online API
+documentation. For details on how the options relate to the API documentation,
+see the <Options Tutorial> in the optionsTutorial.txt file.
+
+{{{
+options =
+{
+ seriesColors: [ "#4bb2c5", "#c5b47f", "#EAA228", "#579575", "#839557", "#958c12",
+ "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc"], // colors that will
+ // be assigned to the series. If there are more series than colors, colors
+ // will wrap around and start at the beginning again.
+
+ stackSeries: false, // if true, will create a stack plot.
+ // Currently supported by line and bar graphs.
+
+ title: '', // Title for the plot. Can also be specified as an object like:
+
+ title: {
+ text: '', // title for the plot,
+ show: true,
+ },
+
+ axesDefaults: {
+ show: false, // wether or not to renderer the axis. Determined automatically.
+ min: null, // minimum numerical value of the axis. Determined automatically.
+ max: null, // maximum numverical value of the axis. Determined automatically.
+ pad: 1.2, // a factor multiplied by the data range on the axis to give the
+ // axis range so that data points don't fall on the edges of the axis.
+ ticks: [], // a 1D [val1, val2, ...], or 2D [[val, label], [val, label], ...]
+ // array of ticks to use. Computed automatically.
+ numberTicks: undefined,
+ renderer: $.jqplot.LinearAxisRenderer, // renderer to use to draw the axis,
+ rendererOptions: {}, // options to pass to the renderer. LinearAxisRenderer
+ // has no options,
+ tickOptions: {
+ mark: 'outside', // Where to put the tick mark on the axis
+ // 'outside', 'inside' or 'cross',
+ showMark: true,
+ showGridline: true, // wether to draw a gridline (across the whole grid) at this tick,
+ markSize: 4, // length the tick will extend beyond the grid in pixels. For
+ // 'cross', length will be added above and below the grid boundary,
+ show: true, // wether to show the tick (mark and label),
+ showLabel: true, // wether to show the text label at the tick,
+ formatString: '', // format string to use with the axis tick formatter
+ }
+ showTicks: true, // wether or not to show the tick labels,
+ showTickMarks: true, // wether or not to show the tick marks
+ },
+
+ axes: {
+ xaxis: {
+ // same options as axesDefaults
+ },
+ yaxis: {
+ // same options as axesDefaults
+ },
+ x2axis: {
+ // same options as axesDefaults
+ },
+ y2axis: {
+ // same options as axesDefaults
+ }
+ },
+
+ seriesDefaults: {
+ show: true, // wether to render the series.
+ xaxis: 'xaxis', // either 'xaxis' or 'x2axis'.
+ yaxis: 'yaxis', // either 'yaxis' or 'y2axis'.
+ label: '', // label to use in the legend for this line.
+ color: '', // CSS color spec to use for the line. Determined automatically.
+ lineWidth: 2.5, // Width of the line in pixels.
+ shadow: true, // show shadow or not.
+ shadowAngle: 45, // angle (degrees) of the shadow, clockwise from x axis.
+ shadowOffset: 1.25, // offset from the line of the shadow.
+ shadowDepth: 3, // Number of strokes to make when drawing shadow. Each
+ // stroke offset by shadowOffset from the last.
+ shadowAlpha: 0.1, // Opacity of the shadow.
+ showLine: true, // whether to render the line segments or not.
+ showMarker: true, // render the data point markers or not.
+ fill: false, // fill under the line,
+ fillAndStroke: false, // **stroke a line at top of fill area.
+ fillColor: undefined, // **custom fill color for filled lines (default is line color).
+ fillAlpha: undefined, // **custom alpha to apply to fillColor.
+ renderer: $.jqplot.LineRenderer], // renderer used to draw the series.
+ rendererOptions: {}, // options passed to the renderer. LineRenderer has no options.
+ markerRenderer: $.jqplot.MarkerRenderer, // renderer to use to draw the data
+ // point markers.
+ markerOptions: {
+ show: true, // wether to show data point markers.
+ style: 'filledCircle', // circle, diamond, square, filledCircle.
+ // filledDiamond or filledSquare.
+ lineWidth: 2, // width of the stroke drawing the marker.
+ size: 9, // size (diameter, edge length, etc.) of the marker.
+ color: '#666666' // color of marker, set to color of line by default.
+ shadow: true, // wether to draw shadow on marker or not.
+ shadowAngle: 45, // angle of the shadow. Clockwise from x axis.
+ shadowOffset: 1, // offset from the line of the shadow,
+ shadowDepth: 3, // Number of strokes to make when drawing shadow. Each stroke
+ // offset by shadowOffset from the last.
+ shadowAlpha: 0.07 // Opacity of the shadow
+ }
+ },
+
+ series:[
+ {Each series has same options as seriesDefaults},
+ {You can override each series individually here}
+ ],
+
+ legend: {
+ show: false,
+ location: 'ne', // compass direction, nw, n, ne, e, se, s, sw, w.
+ xoffset: 12, // pixel offset of the legend box from the x (or x2) axis.
+ yoffset: 12, // pixel offset of the legend box from the y (or y2) axis.
+ },
+
+ grid: {
+ drawGridLines: true, // wether to draw lines across the grid or not.
+ gridLineColor: '#cccccc' // **Color of the grid lines.
+ background: '#fffdf6', // CSS color spec for background color of grid.
+ borderColor: '#999999', // CSS color spec for border around grid.
+ borderWidth: 2.0, // pixel width of border around grid.
+ shadow: true, // draw a shadow for grid.
+ shadowAngle: 45, // angle of the shadow. Clockwise from x axis.
+ shadowOffset: 1.5, // offset from the line of the shadow.
+ shadowWidth: 3, // width of the stroke for the shadow.
+ shadowDepth: 3, // Number of strokes to make when drawing shadow.
+ // Each stroke offset by shadowOffset from the last.
+ shadowAlpha: 0.07 // Opacity of the shadow
+ renderer: $.jqplot.CanvasGridRenderer, // renderer to use to draw the grid.
+ rendererOptions: {} // options to pass to the renderer. Note, the default
+ // CanvasGridRenderer takes no additional options.
+ },
+
+ // Plugin and renderer options.
+
+ // BarRenderer.
+ // With BarRenderer, you can specify additional options in the rendererOptions object
+ // on the series or on the seriesDefaults object. Note, some options are respecified
+ // (like shadowDepth) to override lineRenderer defaults from which BarRenderer inherits.
+
+ seriesDefaults: {
+ rendererOptions: {
+ barPadding: 8, // number of pixels between adjacent bars in the same
+ // group (same category or bin).
+ barMargin: 10, // number of pixels between adjacent groups of bars.
+ barDirection: 'vertical', // vertical or horizontal.
+ barWidth: null, // width of the bars. null to calculate automatically.
+ shadowOffset: 2, // offset from the bar edge to stroke the shadow.
+ shadowDepth: 5, // nuber of strokes to make for the shadow.
+ shadowAlpha: 0.8, // transparency of the shadow.
+ }
+ },
+
+ // Cursor
+ // Options are passed to the cursor plugin through the "cursor" object at the top
+ // level of the options object.
+
+ cursor: {
+ style: 'crosshair', // A CSS spec for the cursor type to change the
+ // cursor to when over plot.
+ show: true,
+ showTooltip: true, // show a tooltip showing cursor position.
+ followMouse: false, // wether tooltip should follow the mouse or be stationary.
+ tooltipLocation: 'se', // location of the tooltip either relative to the mouse
+ // (followMouse=true) or relative to the plot. One of
+ // the compass directions, n, ne, e, se, etc.
+ tooltipOffset: 6, // pixel offset of the tooltip from the mouse or the axes.
+ showTooltipGridPosition: false, // show the grid pixel coordinates of the mouse
+ // in the tooltip.
+ showTooltipUnitPosition: true, // show the coordinates in data units of the mouse
+ // in the tooltip.
+ tooltipFormatString: '%.4P', // sprintf style format string for tooltip values.
+ useAxesFormatters: true, // wether to use the same formatter and formatStrings
+ // as used by the axes, or to use the formatString
+ // specified on the cursor with sprintf.
+ tooltipAxesGroups: [], // show only specified axes groups in tooltip. Would specify like:
+ // [['xaxis', 'yaxis'], ['xaxis', 'y2axis']]. By default, all axes
+ // combinations with for the series in the plot are shown.
+
+ },
+
+ // Dragable
+ // Dragable options are specified with the "dragable" object at the top level
+ // of the options object.
+
+ dragable: {
+ color: undefined, // custom color to use for the dragged point and dragged line
+ // section. default will use a transparent variant of the line color.
+ constrainTo: 'none', // Constrain dragging motion to an axis: 'x', 'y', or 'none'.
+ },
+
+ // Highlighter
+ // Highlighter options are specified with the "highlighter" object at the top level
+ // of the options object.
+
+ highlighter: {
+ lineWidthAdjust: 2.5, // pixels to add to the size line stroking the data point marker
+ // when showing highlight. Only affects non filled data point markers.
+ sizeAdjust: 5, // pixels to add to the size of filled markers when drawing highlight.
+ showTooltip: true, // show a tooltip with data point values.
+ tooltipLocation: 'nw', // location of tooltip: n, ne, e, se, s, sw, w, nw.
+ fadeTooltip: true, // use fade effect to show/hide tooltip.
+ tooltipFadeSpeed: "fast"// slow, def, fast, or a number of milliseconds.
+ tooltipOffset: 2, // pixel offset of tooltip from the highlight.
+ tooltipAxes: 'both', // which axis values to display in the tooltip, x, y or both.
+ tooltipSeparator: ', ' // separator between values in the tooltip.
+ useAxesFormatters: true // use the same format string and formatters as used in the axes to
+ // display values in the tooltip.
+ tooltipFormatString: '%.5P' // sprintf format string for the tooltip. only used if
+ // useAxesFormatters is false. Will use sprintf formatter with
+ // this string, not the axes formatters.
+ },
+
+ // LogAxisRenderer
+ // LogAxisRenderer add 2 options to the axes object. These options are specified directly on
+ // the axes or axesDefaults object.
+
+ axesDefaults: {
+ base: 10, // the logarithmic base.
+ tickDistribution: 'even', // 'even' or 'power'. 'even' will produce with even visiual (pixel)
+ // spacing on the axis. 'power' will produce ticks spaced by
+ // increasing powers of the log base.
+ },
+
+ // PieRenderer
+ // PieRenderer accepts options from the rendererOptions object of the series or seriesDefaults object.
+
+ seriesDefaults: {
+ rendererOptions: {
+ diameter: undefined, // diameter of pie, auto computed by default.
+ padding: 20, // padding between pie and neighboring legend or plot margin.
+ sliceMargin: 0, // gap between slices.
+ fill: true, // render solid (filled) slices.
+ shadowOffset: 2, // offset of the shadow from the chart.
+ shadowDepth: 5, // Number of strokes to make when drawing shadow. Each stroke
+ // offset by shadowOffset from the last.
+ shadowAlpha: 0.07 // Opacity of the shadow
+ }
+ },
+
+ // Trendline
+ // Trendline takes options on the trendline object of the series or seriesDefaults object.
+
+ seriesDefaults: {
+ trendline: {
+ show: true, // show the trend line
+ color: '#666666', // CSS color spec for the trend line.
+ label: '', // label for the trend line.
+ type: 'linear', // 'linear', 'exponential' or 'exp'
+ shadow: true, // show the trend line shadow.
+ lineWidth: 1.5, // width of the trend line.
+ shadowAngle: 45, // angle of the shadow. Clockwise from x axis.
+ shadowOffset: 1.5, // offset from the line of the shadow.
+ shadowDepth: 3, // Number of strokes to make when drawing shadow.
+ // Each stroke offset by shadowOffset from the last.
+ shadowAlpha: 0.07 // Opacity of the shadow
+ }
+ }
+}
+}}} \ No newline at end of file
diff --git a/libs/jqplot/jqplot.axisLabelRenderer.js b/libs/jqplot/jqplot.axisLabelRenderer.js
new file mode 100644
index 0000000000..8a0286883b
--- /dev/null
+++ b/libs/jqplot/jqplot.axisLabelRenderer.js
@@ -0,0 +1,96 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(function($) {
+ // 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(ctx, plot) {
+ // Memory Leaks patch
+ if (this._elem) {
+ this._elem.emptyForce();
+ this._elem = null;
+ }
+
+ 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() {
+ };
+})(jQuery); \ No newline at end of file
diff --git a/libs/jqplot/jqplot.axisTickRenderer.js b/libs/jqplot/jqplot.axisTickRenderer.js
new file mode 100644
index 0000000000..70bbe749ce
--- /dev/null
+++ b/libs/jqplot/jqplot.axisTickRenderer.js
@@ -0,0 +1,169 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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 to prepend to the tick label.
+ // Prefix is prepended to the formatted tick label.
+ 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;
+ this._breakTick = false;
+
+ $.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.prefix + this.formatter(this.formatString, this.value);
+ }
+ var style = {position: 'absolute'};
+ if (Number(this.label)) {
+ style['whitSpace'] = 'nowrap';
+ }
+
+ // Memory Leaks patch
+ if (this._elem) {
+ this._elem.emptyForce();
+ this._elem = null;
+ }
+
+
+ var elem = document.createElement('div');
+ this._elem = $(elem);
+ this._elem.addClass("jqplot-"+this.axis+"-tick");
+ this._elem.text(this.label);
+ this._elem.css(style);
+
+
+ // 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);
+ }
+ if (this._breakTick) {
+ this._elem.addClass('jqplot-breakTick');
+ }
+
+ elem = null;
+
+ 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() {
+ };
+})(jQuery); \ No newline at end of file
diff --git a/libs/jqplot/jqplot.canvasGridRenderer.js b/libs/jqplot/jqplot.canvasGridRenderer.js
new file mode 100755
index 0000000000..0ef0cc3372
--- /dev/null
+++ b/libs/jqplot/jqplot.canvasGridRenderer.js
@@ -0,0 +1,358 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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(plot) {
+ var elem;
+ // Memory Leaks patch
+ if (this._elem) {
+ if ($.jqplot.use_excanvas) {
+ elem = this._elem.get(0);
+ window.G_vmlCanvasManager.uninitElement(elem);
+ elem = null;
+ }
+
+ this._elem.emptyForce();
+ this._elem = null;
+ }
+
+ elem = plot.canvasManager.getCanvas();
+
+ 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 });
+
+ elem = plot.canvasManager.initCanvas(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;
+ // avoid memory leak
+ elem = null;
+ 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 (true) {
+ ctx.save();
+ ctx.lineJoin = 'miter';
+ ctx.lineCap = 'butt';
+ ctx.lineWidth = this.gridLineWidth;
+ ctx.strokeStyle = this.gridLineColor;
+ var b, e, s, m;
+ 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 && this.drawGridlines) {
+ 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 && this.drawGridlines) {
+ 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 && this.drawGridlines) {
+ 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 && this.drawGridlines) {
+ 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;
+ }
+ }
+ }
+ t = null;
+ }
+ axis = null;
+ ticks = null;
+ }
+ // 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});
+ }
+ t = null;
+ }
+ t0 = null;
+ }
+ axis = null;
+ ticks = null;
+ }
+
+ 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();
+ ctx = null;
+ axes = null;
+ };
+})(jQuery); \ No newline at end of file
diff --git a/libs/jqplot/jqplot.core.js b/libs/jqplot/jqplot.core.js
new file mode 100755
index 0000000000..8978db5fb9
--- /dev/null
+++ b/libs/jqplot/jqplot.core.js
@@ -0,0 +1,3396 @@
+/**
+ * Title: jqPlot Charts
+ *
+ * Pure JavaScript plotting plugin for jQuery.
+ *
+ * About: Version
+ *
+ * @VERSION
+ *
+ * About: Copyright & License
+ *
+ * Copyright (c) 2009-2011 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.
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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.2 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 lt IE 9]><script language="javascript" type="text/javascript" src="excanvas.js"></script><![endif]-->
+ * > <script language="javascript" type="text/javascript" src="jquery-1.4.4.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;
+
+ $.fn.emptyForce = function() {
+ for ( var i = 0, elem; (elem = $(this)[i]) != null; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ if ( elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ }
+
+ // Remove any remaining nodes
+ if ($.jqplot_use_excanvas) {
+ elem.outerHTML = "";
+ }
+ else {
+ while ( elem.firstChild ) {
+ elem.removeChild( elem.firstChild );
+ }
+ }
+
+ elem = null;
+ }
+
+ return $(this);
+ };
+
+ $.fn.removeChildForce = function(parent) {
+ while ( parent.firstChild ) {
+ this.removeChildForce( parent.firstChild );
+ parent.removeChild( parent.firstChild );
+ }
+ };
+
+ // Memory Leaks patch : garbage collector
+ $.gcInit = function() {
+ var gc = $("<div id='gc_jqPlotSalsaGarbage' style='display:none;visibility:hidden;'></div>");
+ $("body").append(gc);
+ $._gc = gc;
+ };
+
+ $.gcCollect = function(elem) {
+ $._gc.append(elem);
+ };
+
+ $.gcClear = function() {
+ if ($.browser.msie) {
+ $._gc.children().each(function() {
+ this.outerHTML = "";
+ });
+ }
+
+ $._gc.empty();
+ };
+
+ $(document).ready(function() {
+ $.gcInit();
+ });
+
+ /**
+ * 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 (jQuery.isArray(data)) {
+ _data = data;
+ _options = null;
+ }
+
+ else if (typeof(data) === '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;
+ }
+ };
+
+ // canvas manager to reuse canvases on the plot.
+ // Should help solve problem of canvases not being freed and
+ // problem of waiting forever for firefox to decide to free memory.
+ $.jqplot.CanvasManager = function() {
+ // canvases are managed globally so that they can be reused
+ // across plots after they have been freed
+ if (typeof $.jqplot.CanvasManager.canvases == 'undefined') {
+ $.jqplot.CanvasManager.canvases = [];
+ $.jqplot.CanvasManager.free = [];
+ }
+
+ var myCanvases = [];
+
+ this.getCanvas = function() {
+ var canvas;
+ var makeNew = true;
+
+ if (!$.jqplot.use_excanvas) {
+ for (var i = 0, l = $.jqplot.CanvasManager.canvases.length; i < l; i++) {
+ if ($.jqplot.CanvasManager.free[i] === true) {
+ makeNew = false;
+ canvas = $.jqplot.CanvasManager.canvases[i];
+ $.jqplot.CanvasManager.free[i] = false;
+ myCanvases.push(i);
+ break;
+ }
+ }
+ }
+
+ if (makeNew) {
+ canvas = document.createElement('canvas');
+ myCanvases.push($.jqplot.CanvasManager.canvases.length);
+ $.jqplot.CanvasManager.canvases.push(canvas);
+ $.jqplot.CanvasManager.free.push(false);
+ }
+
+ return canvas;
+ };
+
+ // this method has to be used after settings the dimesions
+ // on the element returned by getCanvas()
+ this.initCanvas = function(canvas) {
+ if ($.jqplot.use_excanvas) {
+ return window.G_vmlCanvasManager.initElement(canvas);
+ }
+ return canvas;
+ }
+
+ this.freeAllCanvases = function() {
+ for (var i = 0; i < myCanvases.length; i++) {
+ this.freeCanvas(myCanvases[i]);
+ }
+ myCanvases = [];
+ };
+
+ this.freeCanvas = function(idx) {
+ if ($.jqplot.use_excanvas) {
+ // excanvas can't be reused, but properly unset
+ window.G_vmlCanvasManager.uninitElement($.jqplot.CanvasManager.canvases[idx]);
+ $.jqplot.CanvasManager.canvases[idx] = null;
+ } else {
+ var canvas = $.jqplot.CanvasManager.canvases[idx];
+ canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
+ $(canvas).unbind().removeAttr('class').removeAttr('style')
+ .removeAttr('width').removeAttr('height');
+ $.jqplot.CanvasManager.free[idx] = true;
+ }
+ };
+
+ };
+
+
+ // Convienence function that won't hang IE of FF without FireBug.
+ $.jqplot.log = function() {
+ if (window.console) {
+ console.log.apply(console, arguments);
+ }
+ };
+
+ $.jqplot.config = {
+ 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",
+ defaultColors: [ "#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
+ defaultNegativeColors: [ "#498991", "#C08840", "#9F9274", "#546D61", "#646C4A", "#6F6621", "#6E3F5F", "#4F64B0", "#A89050", "#C45923", "#187399", "#945381", "#959E5C", "#C7AF7B", "#478396", "#907294"]
+ };
+
+
+ $.jqplot.arrayMax = function( array ){
+ return Math.max.apply( Math, array );
+ };
+
+ $.jqplot.arrayMin = function( array ){
+ return Math.min.apply( Math, array );
+ };
+
+ $.jqplot.enablePlugins = $.jqplot.config.enablePlugins;
+
+ // canvas related tests taken from modernizer:
+ // Copyright (c) 2009 - 2010 Faruk Ates.
+ // http://www.modernizr.com
+
+ $.jqplot.support_canvas = function() {
+ if (typeof $.jqplot.support_canvas.result == 'undefined') {
+ $.jqplot.support_canvas.result = !!document.createElement('canvas').getContext;
+ }
+ return $.jqplot.support_canvas.result;
+ };
+
+ $.jqplot.support_canvas_text = function() {
+ if (typeof $.jqplot.support_canvas_text.result == 'undefined') {
+ $.jqplot.support_canvas_text.result = !!(document.createElement('canvas').getContext && typeof document.createElement('canvas').getContext('2d').fillText == 'function');
+ }
+ return $.jqplot.support_canvas_text.result;
+ };
+
+ $.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);
+ // avoid memory leak;
+ elem = null;
+ 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 2 x axes, 'xaxis' and 'x2axis', and
+ // 9 yaxes, 'yaxis', 'y2axis'. 'y3axis', ... 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};
+ // statistics (min, max, mean) as well as actual data intervals for each series attached to axis.
+ // holds collection of {intervals:[], min:, max:, mean: } objects for each series on axis.
+ this._intervalStats = [];
+ // 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;
+ // hold original user options.
+ this._options = {};
+ }
+
+ 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, plot) {
+ // Memory Leaks patch
+ if (this.__ticks) {
+ this.__ticks = null;
+ }
+
+ return this.renderer.draw.call(this, ctx, plot);
+
+ };
+
+ 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(opts) {
+ $.extend(true, this, {min: null, max: null, numberTicks: null, tickInterval: null, _ticks: [], ticks: []}, opts);
+ this.resetDataBounds();
+ };
+
+ Axis.prototype.resetDataBounds = function() {
+ // Go through all the series attached to this axis and find
+ // the min/max bounds for this axis.
+ var db = this._dataBounds;
+ db.min = null;
+ db.max = null;
+ // check for when to force min 0 on bar series plots.
+ var doforce = (this.show) ? true : false;
+ for (var i=0; i<this._series.length; i++) {
+ var s = this._series[i];
+ var d = s._plotData;
+ var minyidx = 1, maxyidx = 1;
+
+ if (s._type != null && s._type == 'ohlc') {
+ minyidx = 3;
+ maxyidx = 2;
+ }
+
+ 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][minyidx] != null && d[j][minyidx] < db.min) || db.min == null) {
+ db.min = d[j][minyidx];
+ }
+ if ((d[j][maxyidx] != null && d[j][maxyidx] > db.max) || db.max == null) {
+ db.max = d[j][maxyidx];
+ }
+ }
+ }
+
+ // Hack to not pad out bottom of bar plots unless user has specified a padding.
+ // every series will have a chance to set doforce to false. once it is set to
+ // false, it cannot be reset to true.
+ // If any series attached to axis is not a bar, wont force 0.
+ if (doforce && s.renderer.constructor !== $.jqplot.BarRenderer) {
+ doforce = false;
+ }
+
+ else if (doforce && this._options.hasOwnProperty('forceTickAt0') && this._options.forceTickAt0 == false) {
+ doforce = false;
+ }
+
+ else if (doforce && s.renderer.constructor === $.jqplot.BarRenderer) {
+ if (s.barDirection == 'vertical' && this.name != 'xaxis' && this.name != 'x2axis') {
+ if (this._options.pad != null || this._options.padMin != null) {
+ doforce = false;
+ }
+ }
+
+ else if (s.barDirection == 'horizontal' && (this.name == 'xaxis' || this.name == 'x2axis')) {
+ if (this._options.pad != null || this._options.padMin != null) {
+ doforce = false;
+ }
+ }
+
+ }
+ }
+
+ if (doforce && this.renderer.constructor === $.jqplot.LinearAxisRenderer && db.min >= 0) {
+ this.padMin = 1.0;
+ this.forceTickAt0 = true;
+ }
+ };
+
+ /**
+ * 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: lineJoin
+ // Canvas lineJoin style between segments of series.
+ this.lineJoin = 'round';
+ // prop: lineCap
+ // Canvas lineCap style at ends of line.
+ this.lineCap = 'round';
+ // 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
+ // 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;
+ this._type = '';
+ }
+
+ 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;
+
+ var j, data, gridData;
+
+ // hooks get called even if series not shown
+ // we don't clear canvas here, it would wipe out all other series as well.
+ for (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]);
+ }
+ data = [];
+ if (options.data) {
+ data = options.data;
+ }
+ else if (!this._stack) {
+ data = this.data;
+ }
+ else {
+ data = this._plotData;
+ }
+ gridData = options.gridData || this.renderer.makeGridData.call(this, data, plot);
+ this.renderer.draw.call(this, sctx, gridData, options, plot);
+ }
+
+ for (j=0; j<$.jqplot.postDrawSeriesHooks.length; j++) {
+ $.jqplot.postDrawSeriesHooks[j].call(this, sctx, options);
+ }
+
+ sctx = opts = plot = j = data = gridData = null;
+ };
+
+ Series.prototype.drawShadow = function(sctx, opts, plot) {
+ var options = (opts == undefined) ? {} : opts;
+ sctx = (sctx == undefined) ? this.shadowCanvas._ctx : sctx;
+
+ var j, data, gridData;
+
+ // hooks get called even if series not shown
+ // we don't clear canvas here, it would wipe out all other series as well.
+ for (j=0; j<$.jqplot.preDrawSeriesShadowHooks.length; j++) {
+ $.jqplot.preDrawSeriesShadowHooks[j].call(this, sctx, options);
+ }
+ if (this.shadow) {
+ this.renderer.setGridData.call(this, plot);
+
+ data = [];
+ if (options.data) {
+ data = options.data;
+ }
+ else if (!this._stack) {
+ data = this.data;
+ }
+ else {
+ data = this._plotData;
+ }
+ gridData = options.gridData || this.renderer.makeGridData.call(this, data, plot);
+
+ this.renderer.drawShadow.call(this, sctx, gridData, options);
+ }
+
+ for (j=0; j<$.jqplot.postDrawSeriesShadowHooks.length; j++) {
+ $.jqplot.postDrawSeriesShadowHooks[j].call(this, sctx, options);
+ }
+
+ sctx = opts = plot = j = data = gridData = null;
+
+ };
+
+ // 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,plot) {
+ this._offsets = offsets;
+ return this.renderer.createElement.call(this, plot);
+ };
+
+ 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, plot) {
+ this._offsets = offsets;
+ var klass = 'jqplot';
+ if (clss != undefined) {
+ klass = clss;
+ }
+ var elem;
+
+ // if (this._elem) {
+ // // Memory Leaks patch
+ // if ($.jqplot.use_excanvas) {
+ // window.G_vmlCanvasManager.uninitElement(this._elem.get(0));
+ // }
+ // }
+ // else {
+ // // don't use the canvas manager with excanvas.
+ // if ($.jqplot.use_excanvas) {
+ // elem = document.createElement('canvas');
+ // }
+ // else {
+ // elem = plot.canvasManager.getCanvas();
+ // }
+
+ // }
+
+ elem = plot.canvasManager.getCanvas();
+
+ // if new plotDimensions supplied, use them.
+ if (plotDimensions != null) {
+ 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);
+
+ elem = plot.canvasManager.initCanvas(elem);
+
+ elem = null;
+ return this._elem;
+ };
+
+ $.jqplot.GenericCanvas.prototype.setContext = function() {
+ this._ctx = this._elem.get(0).getContext("2d");
+ return this._ctx;
+ };
+
+ // Memory Leaks patch
+ $.jqplot.GenericCanvas.prototype.resetCanvas = function() {
+ if (this._elem) {
+ if ($.jqplot.use_excanvas) {
+ window.G_vmlCanvasManager.uninitElement(this._elem.get(0));
+ }
+
+ //this._elem.remove();
+ this._elem.emptyForce();
+ }
+
+ this._ctx = null;
+ };
+
+ $.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;
+ // prop noDataIndicator
+ // Options to set up a mock plot with a data loading indicator if no data is specified.
+ this.noDataIndicator = {
+ show: false,
+ indicator: 'Loading Data...',
+ axes: {
+ xaxis: {
+ min: 0,
+ max: 10,
+ tickInterval: 2,
+ show: true
+ },
+ yaxis: {
+ min: 0,
+ max: 12,
+ tickInterval: 3,
+ show: true
+ }
+ }
+ };
+ // 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: {},
+ 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:null, right:null, bottom:null, left:null};
+ this._defaultGridPadding = {top:10, right:10, bottom:23, 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 = $.jqplot.config.defaultColors;
+ this.negativeSeriesColors = $.jqplot.config.defaultNegativeColors;
+ // 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;
+
+ this.canvasManager = new $.jqplot.CanvasManager();
+
+ // Group: methods
+ //
+ // method: init
+ // sets the plot target, checks data and applies user
+ // options to plot.
+ this.init = function(target, data, options) {
+ options = 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 = h = 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 = w = 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 && jQuery.isFunction(options.dataRenderer)) {
+ if (options.dataRendererOptions) {
+ this.dataRendererOptions = options.dataRendererOptions;
+ }
+ this.dataRenderer = options.dataRenderer;
+ data = this.dataRenderer(data, this, this.dataRendererOptions);
+ }
+
+ if (options.noDataIndicator && jQuery.isPlainObject(options.noDataIndicator)) {
+ $.extend(true, this.noDataIndicator, options.noDataIndicator);
+ }
+
+ if (data == null || jQuery.isArray(data) == false || data.length == 0 || jQuery.isArray(data[0]) == false || data[0].length == 0) {
+
+ if (this.noDataIndicator.show == false) {
+ throw{
+ name: "DataError",
+ message: "No data to plot."
+ };
+ }
+
+ else {
+ // have to be descructive here in order for plot to not try and render series.
+ // This means that $.jqplot() will have to be called again when there is data.
+ //delete options.series;
+
+ for (var ax in this.noDataIndicator.axes) {
+ for (var prop in this.noDataIndicator.axes[ax]) {
+ this.axes[ax][prop] = this.noDataIndicator.axes[ax][prop];
+ }
+ }
+
+ this.postDrawHooks.add(function() {
+ var eh = this.eventCanvas.getHeight();
+ var ew = this.eventCanvas.getWidth();
+ var temp = $('<div class="jqplot-noData-container" style="position:absolute;"></div>');
+ this.target.append(temp);
+ temp.height(eh);
+ temp.width(ew);
+ temp.css('top', this.eventCanvas._offsets.top);
+ temp.css('left', this.eventCanvas._offsets.left);
+
+ var temp2 = $('<div class="jqplot-noData-contents" style="text-align:center; position:relative; margin-left:auto; margin-right:auto;"></div>');
+ temp.append(temp2);
+ temp2.html(this.noDataIndicator.indicator);
+ var th = temp2.height();
+ var tw = temp2.width();
+ temp2.height(th);
+ temp2.width(tw);
+ temp2.css('top', (eh - th)/2 + 'px');
+ });
+
+ }
+ }
+
+ 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, options) {
+ var opts = options || {};
+ var ax = axes || this.axes;
+ if (ax === true) {
+ ax = this.axes;
+ }
+ if (jQuery.isArray(ax)) {
+ for (var i = 0; i < ax.length; i++) {
+ this.axes[ax[i]].resetScale(opts[ax[i]]);
+ }
+ }
+ else if (typeof(ax) === 'object') {
+ for (var name in ax) {
+ this.axes[name].resetScale(opts[name]);
+ }
+ }
+ };
+ // 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.
+
+ //
+ // Wont have options here
+ /*
+ 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._height = this.target.height();
+ 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) {
+ // Memory Leaks patch : clear ticks elements
+ var t = this.axes[name]._ticks;
+ for (var i = 0; i < t.length; i++) {
+ var el = t[i]._elem;
+ if (el) {
+ // if canvas renderer
+ if ($.jqplot.use_excanvas) {
+ window.G_vmlCanvasManager.uninitElement(el.get(0));
+ }
+ el.emptyForce();
+ el = null;
+ t._elem = null;
+ }
+ }
+ t = null;
+
+ 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];
+ }
+ };
+
+ // this.setData = function(seriesIndex, newdata) {
+ // // if newdata is null, assume all data is passed in as first argument
+ // if (newdata == null) {
+
+ // }
+ // };
+
+ // 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];
+ axis._options = $.extend(true, {}, this.options.axesDefaults, this.options.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 (!jQuery.isArray(data[0])) {
+ // 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 === $.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: destroy
+ // Releases all resources occupied by the plot
+ this.destroy = function() {
+ this.canvasManager.freeAllCanvases();
+ this.target[0].innerHTML = '';
+ };
+
+ // 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 || {};
+ var clear = opts.clear || true;
+ var resetAxes = opts.resetAxes || false;
+ this.target.trigger('jqplotPreReplot');
+
+ if (clear) {
+ this.canvasManager.freeAllCanvases();
+ // Memory Leaks patch
+ this.target.find("table.jqplot-table-legend,table.jqplot-legend").each( function() {
+ $(this).unbind();
+
+ $(this).find(".jqplot-seriesToggle").each( function() {
+ $(this).unbind();
+ });
+ $.gcCollect(this);
+ });
+
+ this.target.find(".jqplot-title").each( function() {
+ $(this).unbind();
+ $.gcCollect(this);
+ });
+
+ $.gcClear();
+
+ this.eventCanvas._elem.unbind();
+ this.target.unbind();
+
+ // Couple of posts on Stack Overflow indicate that empty() doesn't
+ // always cear up the dom and release memory. Sometimes setting
+ // innerHTML property to null is needed. Particularly on IE, may
+ // have to directly set it to null, bypassing jQuery.
+ this.target.empty();
+ }
+ this.reInitialize();
+ if (resetAxes) {
+ this.resetAxesScale(resetAxes, opts.axes);
+ }
+ 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) {
+ // Couple of posts on Stack Overflow indicate that empty() doesn't
+ // always cear up the dom and release memory. Sometimes setting
+ // innerHTML property to null is needed. Particularly on IE, may
+ // have to directly set it to null, bypassing jQuery.
+ 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', null, this));
+ 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, this));
+ 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 (this._gridPadding[arr[n]] == null && gridPadding[arr[n]] > 0) {
+ this._gridPadding[arr[n]] = gridPadding[arr[n]];
+ }
+ else if (this._gridPadding[arr[n]] == null) {
+ this._gridPadding[arr[n]] = this._defaultGridPadding[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));
+ 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', null, this));
+ 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', null, this));
+ 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', null, this));
+ 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();
+ if (this.series.length) {
+ $(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=0; j<s._barPoints.length; j++) {
+ points = s._barPoints[j];
+ p = s.gridData[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,
+ cv;
+
+ // 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);
+ }
+ }
+ options = idx = i = series = ctx = null;
+ };
+
+ // 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, move, keep;
+ // 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=[], serelem, shadelem;
+ 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 (jQuery.isArray(colors)) {
+ 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) {
+ colors = colors || $.jqplot.config.defaultColors;
+ 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.charAt(0)+h.charAt(0)+h.charAt(1)+h.charAt(1)+h.charAt(2)+h.charAt(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 (var 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 (var 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(230, 230, 250)', lavenderblush: 'rgb(255, 240, 245)', lawngreen: 'rgb(124, 252, 0)', lemonchiffon: 'rgb(255, 250, 205)', lightblue: 'rgb(173, 216, 230)', lightcoral: 'rgb(240, 128, 128)', lightcyan: 'rgb(224, 255, 255)', lightgoldenrodyellow: 'rgb(250, 250, 210)', lightgray: 'rgb(211, 211, 211)', lightgreen: 'rgb(144, 238, 144)', lightgrey: 'rgb(211, 211, 211)', lightpink: 'rgb(255, 182, 193)', lightsalmon: 'rgb(255, 160, 122)', lightseagreen: 'rgb( 32, 178, 170)', lightskyblue: 'rgb(135, 206, 250)', lightslategray: 'rgb(119, 136, 153)', lightslategrey: 'rgb(119, 136, 153)', lightsteelblue: 'rgb(176, 196, 222)', lightyellow: 'rgb(255, 255, 224)', lime: 'rgb( 0, 255, 0)', limegreen: 'rgb( 50, 205, 50)', linen: 'rgb(250, 240, 230)', magenta: 'rgb(255, 0, 255)', maroon: 'rgb(128, 0, 0)', mediumaquamarine: 'rgb(102, 205, 170)', mediumblue: 'rgb( 0, 0, 205)', mediumorchid: 'rgb(186, 85, 211)', mediumpurple: 'rgb(147, 112, 219)', mediumseagreen: 'rgb( 60, 179, 113)', mediumslateblue: 'rgb(123, 104, 238)', mediumspringgreen: 'rgb( 0, 250, 154)', mediumturquoise: 'rgb( 72, 209, 204)', mediumvioletred: 'rgb(199, 21, 133)', midnightblue: 'rgb( 25, 25, 112)', mintcream: 'rgb(245, 255, 250)', mistyrose: 'rgb(255, 228, 225)', moccasin: 'rgb(255, 228, 181)', navajowhite: 'rgb(255, 222, 173)', navy: 'rgb( 0, 0, 128)', oldlace: 'rgb(253, 245, 230)', olive: 'rgb(128, 128, 0)', olivedrab: 'rgb(107, 142, 35)', orange: 'rgb(255, 165, 0)', orangered: 'rgb(255, 69, 0)', orchid: 'rgb(218, 112, 214)', palegoldenrod: 'rgb(238, 232, 170)', palegreen: 'rgb(152, 251, 152)', paleturquoise: 'rgb(175, 238, 238)', palevioletred: 'rgb(219, 112, 147)', papayawhip: 'rgb(255, 239, 213)', peachpuff: 'rgb(255, 218, 185)', peru: 'rgb(205, 133, 63)', pink: 'rgb(255, 192, 203)', plum: 'rgb(221, 160, 221)', powderblue: 'rgb(176, 224, 230)', purple: 'rgb(128, 0, 128)', red: 'rgb(255, 0, 0)', rosybrown: 'rgb(188, 143, 143)', royalblue: 'rgb( 65, 105, 225)', saddlebrown: 'rgb(139, 69, 19)', salmon: 'rgb(250, 128, 114)', sandybrown: 'rgb(244, 164, 96)', seagreen: 'rgb( 46, 139, 87)', seashell: 'rgb(255, 245, 238)', sienna: 'rgb(160, 82, 45)', silver: 'rgb(192, 192, 192)', skyblue: 'rgb(135, 206, 235)', slateblue: 'rgb(106, 90, 205)', slategray: 'rgb(112, 128, 144)', slategrey: 'rgb(112, 128, 144)', snow: 'rgb(255, 250, 250)', springgreen: 'rgb( 0, 255, 127)', steelblue: 'rgb( 70, 130, 180)', tan: 'rgb(210, 180, 140)', teal: 'rgb( 0, 128, 128)', thistle: 'rgb(216, 191, 216)', tomato: 'rgb(255, 99, 71)', turquoise: 'rgb( 64, 224, 208)', violet: 'rgb(238, 130, 238)', wheat: 'rgb(245, 222, 179)', white: 'rgb(255, 255, 255)', whitesmoke: 'rgb(245, 245, 245)', yellow: 'rgb(255, 255, 0)', yellowgreen: 'rgb(154, 205, 50)'};
+
+
+})(jQuery); \ No newline at end of file
diff --git a/libs/jqplot/jqplot.divTitleRenderer.js b/libs/jqplot/jqplot.divTitleRenderer.js
new file mode 100644
index 0000000000..79d7d3e42c
--- /dev/null
+++ b/libs/jqplot/jqplot.divTitleRenderer.js
@@ -0,0 +1,114 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(function($) {
+ // 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() {
+ // Memory Leaks patch
+ if (this._elem) {
+ this._elem.emptyForce();
+ this._elem = null;
+ }
+
+ var r = this.renderer;
+ var elem = document.createElement('div');
+ this._elem = $(elem);
+ this._elem.addClass('jqplot-title');
+
+ if (!this.text) {
+ this.show = false;
+ this._elem.height(0);
+ this._elem.width(0);
+ }
+ 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 styles = {position:'absolute', top:'0px', left:'0px'};
+
+ if (this._plotWidth) {
+ styles['width'] = this._plotWidth+'px';
+ }
+ if (this.fontSize) {
+ styles['fontSize'] = this.fontSize;
+ }
+ if (this.textAlign) {
+ styles['textAlign'] = this.textAlign;
+ }
+ else {
+ styles['textAlign'] = 'center';
+ }
+ if (color) {
+ styles['color'] = color;
+ }
+ if (this.paddingBottom) {
+ styles['paddingBottom'] = this.paddingBottom;
+ }
+ if (this.fontFamily) {
+ styles['fontFamily'] = this.fontFamily;
+ }
+
+ this._elem.css(styles);
+ this._elem.text(this.text);
+
+
+ // 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);
+ // }
+ }
+
+ elem = null;
+
+ return this._elem;
+ };
+
+ $.jqplot.DivTitleRenderer.prototype.pack = function() {
+ // nothing to do here
+ };
+})(jQuery); \ No newline at end of file
diff --git a/libs/jqplot/jqplot.lineRenderer.js b/libs/jqplot/jqplot.lineRenderer.js
new file mode 100755
index 0000000000..ad722661bb
--- /dev/null
+++ b/libs/jqplot/jqplot.lineRenderer.js
@@ -0,0 +1,497 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(function($) {
+ // 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 || {};
+ this._type='line';
+ 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:this.lineJoin, lineCap:this.lineCap, 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:this.lineJoin, lineCap:this.lineCap, fill:this.fill, isarc:false, angle:this.shadowAngle, offset:shadow_offset, alpha:this.shadowAlpha, depth:this.shadowDepth, lineWidth:this.lineWidth, 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 (disable) the highlighter plugin
+ if (this.highlighter) {
+ this.highlighter.show = false;
+ }
+ }
+
+ if (!this.isTrendline && plot) {
+ plot.plugins.lineRenderer = {};
+ 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]);
+ var 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 (var 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() {
+ // Memory Leaks patch
+ if (this.plugins.lineRenderer && this.plugins.lineRenderer.highlightCanvas) {
+ this.plugins.lineRenderer.highlightCanvas.resetCanvas();
+ this.plugins.lineRenderer.highlightCanvas = null;
+ }
+
+ 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, this));
+ 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);
+ canvas = null;
+ }
+
+ 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');
+ canvas = null;
+ }
+
+
+ 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);
+ }
+ }
+
+})(jQuery); \ No newline at end of file
diff --git a/libs/jqplot/jqplot.linearAxisRenderer.js b/libs/jqplot/jqplot.linearAxisRenderer.js
new file mode 100644
index 0000000000..c050e17722
--- /dev/null
+++ b/libs/jqplot/jqplot.linearAxisRenderer.js
@@ -0,0 +1,905 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(function($) {
+ // 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){
+ // prop: breakPoints
+ // EXPERIMENTAL!! Use at your own risk!
+ // Works only with linear axes and the default tick renderer.
+ // Array of [start, stop] points to create a broken axis.
+ // Broken axes have a "jump" in them, which is an immediate
+ // transition from a smaller value to a larger value.
+ // Currently, axis ticks MUST be manually assigned if using breakPoints
+ // by using the axis ticks array option.
+ this.breakPoints = null;
+ // prop: breakTickLabel
+ // Label to use at the axis break if breakPoints are specified.
+ this.breakTickLabel = "&asymp;";
+ // prop: forceTickAt0
+ // This will ensure that there is always a tick mark at 0.
+ // If data range is strictly positive or negative,
+ // this will force 0 to be inside the axis bounds unless
+ // the appropriate axis pad (pad, padMin or padMax) is set
+ // to 0, then this will force an axis min or max value at 0.
+ // This has know effect when any of the following options
+ // are set: autoscale, min, max, numberTicks or tickInterval.
+ this.forceTickAt0 = false;
+ // prop: forceTickAt100
+ // This will ensure that there is always a tick mark at 100.
+ // If data range is strictly above or below 100,
+ // this will force 100 to be inside the axis bounds unless
+ // the appropriate axis pad (pad, padMin or padMax) is set
+ // to 0, then this will force an axis min or max value at 100.
+ // This has know effect when any of the following options
+ // are set: autoscale, min, max, numberTicks or tickInterval.
+ this.forceTickAt100 = false;
+ this._autoFormatString = '';
+ this._overrideFormatString = false;
+ $.extend(true, this, options);
+ if (this.breakPoints) {
+ if (!$.isArray(this.breakPoints)) {
+ this.breakPoints = null;
+ }
+ else if (this.breakPoints.length < 2 || this.breakPoints[1] <= this.breakPoints[0]) {
+ this.breakPoints = null;
+ }
+ }
+ this.resetDataBounds();
+ };
+
+ // called with scope of axis
+ $.jqplot.LinearAxisRenderer.prototype.draw = function(ctx, plot) {
+ 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) {
+ // Memory Leaks patch
+ //this._elem.empty();
+ this._elem.emptyForce();
+ this._elem = null;
+ }
+
+ var elem = document.createElement('div');
+ this._elem = $(elem);
+ this._elem.addClass('jqplot-axis jqplot-'+this.name);
+ this._elem.css('posiiton', 'absolute');
+ elem = null;
+
+
+ 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) {
+ elem = this._label.draw(ctx, plot);
+ elem.appendTo(this._elem);
+ }
+
+ var t = this._ticks;
+ var tick;
+ for (var i=0; i<t.length; i++) {
+ tick = t[i];
+ if (tick.show && tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
+ this._elem.append(tick.draw(ctx, plot));
+ }
+ }
+ tick = null;
+ t = null;
+ elem = null;
+ }
+ 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._autoFormatString = '';
+ if (this._overrideFormatString && this.tickOptions && this.tickOptions.formatString) {
+ this.tickOptions.formatString = '';
+ }
+
+ // 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;
+ var tick;
+ for (var i=0; i<t.length; i++) {
+ tick = t[i];
+ if (!tick._breakTick && tick.show && 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;
+ }
+ }
+ }
+ tick = null;
+ t = null;
+
+ 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];
+ if (this.breakPoints) {
+ if (ut[0] == this.breakPoints[0]) {
+ t.label = this.breakTickLabel;
+ t._breakTick = true;
+ t.showGridline = false;
+ t.showMark = false;
+ }
+ else if (ut[0] > this.breakPoints[0] && ut[0] <= this.breakPoints[1]) {
+ t.show = false;
+ t.showGridline = false;
+ t.label = ut[1];
+ }
+ else {
+ t.label = ut[1];
+ }
+ }
+ else {
+ t.label = ut[1];
+ }
+ t.setTick(ut[0], this.name);
+ this._ticks.push(t);
+ }
+
+ else {
+ t.value = ut;
+ if (this.breakPoints) {
+ if (ut == this.breakPoints[0]) {
+ t.label = this.breakTickLabel;
+ t._breakTick = true;
+ t.showGridline = false;
+ t.showMark = false;
+ }
+ else if (ut > this.breakPoints[0] && ut <= this.breakPoints[1]) {
+ t.show = false;
+ t.showGridline = 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.autoscale && this.min != null && this.max != null && this.numberTicks != null) {
+ // console.log('doing this');
+ // 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);
+
+ var range = max - min;
+ var rmin, rmax;
+ var temp;
+
+ // Doing complete autoscaling
+ if (this.min == null && this.max == null && this.numberTicks == null && this.tickInterval == null && !this.autoscale) {
+ // check to see if we can override tick format string with autocalculated one
+ if (this.tickOptions == null || !this.tickOptions.formatString) {
+ this._overrideFormatString = true;
+ }
+
+
+ // Check if user must have tick at 0 or 100 and ensure they are in range.
+ // The autoscaling algorithm will always place ticks at 0 and 100 if they are in range.
+ if (this.forceTickAt0) {
+ if (min > 0) {
+ min = 0;
+ }
+ if (max < 0) {
+ max = 0;
+ }
+ }
+
+ if (this.forceTickAt100) {
+ if (min > 100) {
+ min = 100;
+ }
+ if (max < 100) {
+ max = 100;
+ }
+ }
+
+ // console.log(this.name);
+ var threshold = 30;
+ var tdim = Math.max(dim, threshold+1);
+ var scalefact = (tdim-threshold)/300.0;
+ // scalefact = 1;
+ var ret = $.jqplot.LinearTickGenerator(min, max, scalefact);
+ // calculate a padded max and min, points should be less than these
+ // so that they aren't too close to the edges of the plot.
+ // User can adjust how much padding is allowed with pad, padMin and PadMax options.
+ var tumin = min + range*(this.padMin - 1);
+ var tumax = max - range*(this.padMax - 1);
+
+ if (min <=tumin || max >= tumax) {
+ tumin = min - range*(this.padMin - 1);
+ tumax = max + range*(this.padMax - 1);
+ ret = $.jqplot.LinearTickGenerator(tumin, tumax, scalefact);
+ }
+
+
+ // if (ret[2] > max_number_ticks) {
+ // ret[4] = Math.ceil(r[2]/max_number_ticks) * ret[4];
+
+ // }
+
+ this.min = ret[0];
+ this.max = ret[1];
+ this.numberTicks = ret[2];
+ this._autoFormatString = ret[3];
+ //this.tickInterval = Math.abs(this.max - this.min)/(this.numberTicks - 1);
+ this.tickInterval = ret[4];
+ // console.log('numberticks: %s, interval: %s', ret[2], ret[4]);
+ }
+
+ // User has specified some axis scale related option, can use auto algorithm
+ else {
+
+ // 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;
+ }
+
+ // 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 && this._autoFormatString == '') {
+ // 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;
+ }
+ }
+ }
+ }
+ }
+
+ }
+
+ if (this._overrideFormatString && this._autoFormatString != '') {
+ this.tickOptions = this.tickOptions || {};
+ this.tickOptions.formatString = this._autoFormatString;
+ }
+
+ 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);
+ t = null;
+ }
+ }
+ ticks = null;
+ };
+
+ // Used to reset just the values of the ticks and then repack, which will
+ // recalculate the positioning functions. It is assuemd that the
+ // number of ticks is the same and the values of the new array are at the
+ // proper interval.
+ // This method needs to be called with the scope of an axis object, like:
+ //
+ // > plot.axes.yaxis.renderer.resetTickValues.call(plot.axes.yaxis, yarr);
+ //
+ $.jqplot.LinearAxisRenderer.prototype.resetTickValues = function(opts) {
+ if ($.isArray(opts) && opts.length == this._ticks.length) {
+ var t;
+ for (var i=0; i<opts.length; i++) {
+ t = this._ticks[i];
+ t.value = opts[i];
+ t.label = t.formatter(t.formatString, opts[i]);
+ t.label = t.prefix + t.label;
+ t._elem.html(t.label);
+ }
+ t = null;
+ this.min = $.jqplot.arrayMin(opts);
+ this.max = $.jqplot.arrayMax(opts);
+ this.pack();
+ }
+ // Not implemented yet.
+ // else if ($.isPlainObject(opts)) {
+ //
+ // }
+ };
+
+ // called with scope of axis
+ $.jqplot.LinearAxisRenderer.prototype.pack = function(pos, offsets) {
+ // Add defaults for repacking from resetTickValues function.
+ pos = pos || {};
+ offsets = offsets || this._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.
+ if (this.breakPoints) {
+ unitlength = unitlength - this.breakPoints[1] + this.breakPoints[0];
+
+ this.p2u = function(p){
+ return (p - offmin) * unitlength / pixellength + min;
+ };
+
+ this.u2p = function(u){
+ if (u > this.breakPoints[0] && u < this.breakPoints[1]){
+ u = this.breakPoints[0];
+ }
+ if (u <= this.breakPoints[0]) {
+ return (u - min) * pixellength / unitlength + offmin;
+ }
+ else {
+ return (u - this.breakPoints[1] + this.breakPoints[0] - min) * pixellength / unitlength + offmin;
+ }
+ };
+
+ if (this.name.charAt(0) == 'x'){
+ this.series_u2p = function(u){
+ if (u > this.breakPoints[0] && u < this.breakPoints[1]){
+ u = this.breakPoints[0];
+ }
+ if (u <= this.breakPoints[0]) {
+ return (u - min) * pixellength / unitlength;
+ }
+ else {
+ return (u - this.breakPoints[1] + this.breakPoints[0] - min) * pixellength / unitlength;
+ }
+ };
+ this.series_p2u = function(p){
+ return p * unitlength / pixellength + min;
+ };
+ }
+
+ else {
+ this.series_u2p = function(u){
+ if (u > this.breakPoints[0] && u < this.breakPoints[1]){
+ u = this.breakPoints[0];
+ }
+ if (u >= this.breakPoints[1]) {
+ return (u - max) * pixellength / unitlength;
+ }
+ else {
+ return (u + this.breakPoints[1] - this.breakPoints[0] - max) * pixellength / unitlength;
+ }
+ };
+ this.series_p2u = function(p){
+ return p * unitlength / pixellength + max;
+ };
+ }
+ }
+ else {
+ 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 (var 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 (var 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();
+ }
+ }
+ }
+
+ ticks = null;
+ };
+})(jQuery);
diff --git a/libs/jqplot/jqplot.linearTickGenerator.js b/libs/jqplot/jqplot.linearTickGenerator.js
new file mode 100644
index 0000000000..75527bf794
--- /dev/null
+++ b/libs/jqplot/jqplot.linearTickGenerator.js
@@ -0,0 +1,116 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(function($) {
+ /**
+ * The following code was generaously given to me a while back by Scott Prahl.
+ * He did a good job at computing axes min, max and number of ticks for the
+ * case where the user has not set any scale related parameters (tickInterval,
+ * numberTicks, min or max). I had ignored this use case for a long time,
+ * focusing on the more difficult case where user has set some option controlling
+ * tick generation. Anyway, about time I got this into jqPlot.
+ * Thanks Scott!!
+ */
+
+ /**
+ * Copyright (c) 2010 Scott Prahl
+ * The next three routines are 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.
+ */
+
+ // A good format string depends on the interval. If the interval is greater
+ // than 1 then there is no need to show any decimal digits. If it is < 1.0, then
+ // use the magnitude of the interval to determine the number of digits to show.
+ function bestFormatString (interval)
+ {
+ interval = Math.abs(interval);
+ if (interval > 1) {return '%d';}
+
+ var expv = -Math.floor(Math.log(interval)/Math.LN10);
+ return '%.' + expv + 'f';
+ }
+
+ // This will return an interval of form 2 * 10^n, 5 * 10^n or 10 * 10^n
+ function bestLinearInterval(range, scalefact) {
+ var expv = Math.floor(Math.log(range)/Math.LN10);
+ var magnitude = Math.pow(10, expv);
+ // 0 < f < 10
+ var f = range / magnitude;
+ // console.log('f: %s, scaled: %s ', f, f/scalefact);
+ // for large plots, scalefact will decrease f and increase number of ticks.
+ // for small plots, scalefact will increase f and decrease number of ticks.
+ f = f/scalefact;
+
+ // for large plots, smaller interval, more ticks.
+ if (f<=0.38) {return 0.1*magnitude;}
+ if (f<=1.6) {return 0.2*magnitude;}
+ if (f<=4.0) {return 0.5*magnitude;}
+ if (f<=8.0) {return magnitude;}
+ // for very small plots, larger interval, less ticks in number ticks
+ if (f<=16.0) {return 2*magnitude;}
+ return 5*magnitude;
+ }
+
+ // Given the min and max for a dataset, return suitable endpoints
+ // for the graphing, a good number for the number of ticks, and a
+ // format string so that extraneous digits are not displayed.
+ // returned is an array containing [min, max, nTicks, format]
+ $.jqplot.LinearTickGenerator = function(axis_min, axis_max, scalefact) {
+ // if endpoints are equal try to include zero otherwise include one
+ if (axis_min == axis_max) {
+ axis_max = (axis_max) ? 0 : 1;
+ }
+
+ scalefact = scalefact || 1.0;
+
+ // make sure range is positive
+ if (axis_max < axis_min) {
+ var a = axis_max;
+ axis_max = axis_min;
+ axis_min = a;
+ }
+
+ var ss = bestLinearInterval(axis_max - axis_min, scalefact);
+ var r = [];
+
+ // Figure out the axis min, max and number of ticks
+ // the min and max will be some multiple of the tick interval,
+ // 1*10^n, 2*10^n or 5*10^n. This gaurantees that, if the
+ // axis min is negative, 0 will be a tick.
+ r[0] = Math.floor(axis_min / ss) * ss; // min
+ r[1] = Math.ceil(axis_max / ss) * ss; // max
+ r[2] = Math.round((r[1]-r[0])/ss+1.0); // number of ticks
+ r[3] = bestFormatString(ss); // format string
+ r[4] = ss; // tick Interval
+ return r;
+ };
+
+})(jQuery); \ No newline at end of file
diff --git a/libs/jqplot/jqplot.markerRenderer.js b/libs/jqplot/jqplot.markerRenderer.js
new file mode 100644
index 0000000000..b11ecf601c
--- /dev/null
+++ b/libs/jqplot/jqplot.markerRenderer.js
@@ -0,0 +1,228 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(function($) {
+ // 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);
+ };
+
+ $.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);
+ };
+
+ $.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);
+ };
+
+ $.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);
+ };
+
+ $.jqplot.MarkerRenderer.prototype.drawLine = function(p1, p2, ctx, fill, options) {
+ var points = [p1, p2];
+ if (this.shadow) {
+ this.shadowRenderer.draw(ctx, points);
+ }
+ this.shapeRenderer.draw(ctx, points, options);
+ };
+
+ $.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);
+ };
+
+ $.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);
+ };
+
+ $.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;
+ case 'line':
+ this.drawLine(x, y, ctx, false, options);
+ break;
+ default:
+ this.drawDiamond(x,y,ctx, false, options);
+ break;
+ }
+ }
+ };
+})(jQuery); \ No newline at end of file
diff --git a/libs/jqplot/jqplot.shadowRenderer.js b/libs/jqplot/jqplot.shadowRenderer.js
new file mode 100644
index 0000000000..a8fc02728a
--- /dev/null
+++ b/libs/jqplot/jqplot.shadowRenderer.js
@@ -0,0 +1,131 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(function($) {
+ // 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 if (points && points.length){
+ 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();
+ };
+})(jQuery); \ No newline at end of file
diff --git a/libs/jqplot/jqplot.shapeRenderer.js b/libs/jqplot/jqplot.shapeRenderer.js
new file mode 100644
index 0000000000..6a96577fe5
--- /dev/null
+++ b/libs/jqplot/jqplot.shapeRenderer.js
@@ -0,0 +1,157 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(function($) {
+ // 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.lineJoin || 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 if (points && points.length){
+ 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();
+ };
+})(jQuery); \ No newline at end of file
diff --git a/libs/jqplot/jqplot.sprintf.js b/libs/jqplot/jqplot.sprintf.js
new file mode 100644
index 0000000000..5a9112a2d1
--- /dev/null
+++ b/libs/jqplot/jqplot.sprintf.js
@@ -0,0 +1,287 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(function($) {
+ /**
+ * 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 thousand_separate(value) {
+ var value_str = new String(value);
+ for (var i=10; i>0; i--) {
+ if (value_str == (value_str = value_str.replace(/^(\d+)(\d{3})/, "$1"+$.jqplot.sprintf.thousandsSeparator+"$2"))) break;
+ }
+ return value_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, thousandSeparation = 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;
+ case '\'': thousandSeparation = 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;
+ var number_str = thousandSeparation ? thousand_separate(String(Math.abs(number))): String(Math.abs(number));
+ value = prefix + pad(number_str, precision, '0', false);
+ //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;
+ var number_str = thousandSeparation ? thousand_separate(String(Math.abs(number))): String(Math.abs(number));
+ value = prefix + pad(number_str, 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];
+ var number_str = Math.abs(number)[method](precision);
+ number_str = thousandSeparation ? thousand_separate(number_str): number_str;
+ value = prefix + number_str;
+ 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.thousandsSeparator = ',';
+
+ $.jqplot.sprintf.regex = /%%|%(\d+\$)?([-+#0&\' ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([nAscboxXuidfegpEGP])/g;
+
+})(jQuery);
diff --git a/libs/jqplot/jqplot.tableLegendRenderer.js b/libs/jqplot/jqplot.tableLegendRenderer.js
new file mode 100644
index 0000000000..946ab57a61
--- /dev/null
+++ b/libs/jqplot/jqplot.tableLegendRenderer.js
@@ -0,0 +1,292 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(function($) {
+ // 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';
+ var tr,
+ elem;
+ if (reverse){
+ tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem);
+ }
+ else{
+ 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) {
+ elem = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
+ elem.appendTo(tr);
+ if (this.escapeHtml) {
+ elem.text(label);
+ }
+ else {
+ elem.html(label);
+ }
+ }
+ tr = null;
+ elem = null;
+ };
+
+ // called with scope of legend
+ $.jqplot.TableLegendRenderer.prototype.draw = function() {
+ if (this._elem) {
+ this._elem.emptyForce();
+ this._elem = null;
+ }
+
+ if (this.show) {
+ var series = this._series;
+ // make a table. one line label per row.
+ var elem = document.createElement('table');
+ this._elem = $(elem);
+ this._elem.addClass('jqplot-table-legend');
+
+ var ss = {position:'absolute'};
+ if (this.background) {
+ ss['background'] = this.background;
+ }
+ if (this.border) {
+ ss['border'] = this.border;
+ }
+ if (this.fontSize) {
+ ss['fontSize'] = this.fontSize;
+ }
+ if (this.fontFamily) {
+ ss['fontFamily'] = this.fontFamily;
+ }
+ if (this.textColor) {
+ ss['textColor'] = this.textColor;
+ }
+ if (this.marginTop != null) {
+ ss['marginTop'] = this.marginTop;
+ }
+ if (this.marginBottom != null) {
+ ss['marginBottom'] = this.marginBottom;
+ }
+ if (this.marginLeft != null) {
+ ss['marginLeft'] = this.marginLeft;
+ }
+ if (this.marginRight != null) {
+ ss['marginRight'] = this.marginRight;
+ }
+
+
+ var pad = false,
+ reverse = false,
+ s;
+ 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;
+ }
+ }
+ lt = null;
+ }
+ }
+ }
+ 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;
+ }
+ }
+ }
+ };
+})(jQuery); \ No newline at end of file
diff --git a/libs/jqplot/jqplot.themeEngine.js b/libs/jqplot/jqplot.themeEngine.js
new file mode 100644
index 0000000000..2c28ea4cd0
--- /dev/null
+++ b/libs/jqplot/jqplot.themeEngine.js
@@ -0,0 +1,914 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(function($) {
+ /**
+ * 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, nn;
+
+ 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 (var 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 (var 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 (var 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;
+ };
+
+
+})(jQuery); \ No newline at end of file
diff --git a/libs/jqplot/jquery.jqplot.css b/libs/jqplot/jquery.jqplot.css
new file mode 100644
index 0000000000..c9bf6f923b
--- /dev/null
+++ b/libs/jqplot/jquery.jqplot.css
@@ -0,0 +1,226 @@
+/*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-yaxis-tick.jqplot-breakTick {
+ right: -20px;
+ margin-right: 0px;
+ padding:1px 5px 1px 5px;
+/* background-color: white;*/
+ z-index: 2;
+ font-size: 1.5em;
+}
+
+.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);
+}
+
+div.jqplot-noData-container {
+ text-align: center;
+ background-color: rgba(96%, 96%, 96%, 0.3);
+}
diff --git a/libs/jqplot/jquery.jqplot.js b/libs/jqplot/jquery.jqplot.js
new file mode 100644
index 0000000000..f10dc79809
--- /dev/null
+++ b/libs/jqplot/jquery.jqplot.js
@@ -0,0 +1,75 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, the author would appreciate an email letting him
+ * know of any substantial use of jqPlot. You can reach the author at:
+ * chris at jqplot or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+
+ /**
+ *
+ * This is a boot loader for the source version of jqplot.
+ * It will load all of the necessary core jqplot files that
+ * are concated together in the distribution.
+ *
+ */
+
+(function(){
+ var getRootNode = function(){
+ // figure out the path to this loader
+ if(this["document"] && this["document"]["getElementsByTagName"]){
+ var scripts = document.getElementsByTagName("script");
+ var pat = /jquery\.jqplot\.js/i;
+ for(var i = 0; i < scripts.length; i++){
+ var src = scripts[i].getAttribute("src");
+ if(!src){ continue; }
+ var m = src.match(pat);
+ if(m){
+ return {
+ node: scripts[i],
+ root: src.substring(0, m.index)
+ };
+ }
+ }
+ }
+ };
+
+
+ var files = ['jqplot.core.js', 'jqplot.linearTickGenerator.js', 'jqplot.linearAxisRenderer.js', 'jqplot.axisTickRenderer.js', 'jqplot.axisLabelRenderer.js', 'jqplot.tableLegendRenderer.js', 'jqplot.lineRenderer.js', 'jqplot.markerRenderer.js', 'jqplot.divTitleRenderer.js', 'jqplot.canvasGridRenderer.js', 'jqplot.shadowRenderer.js', 'jqplot.shapeRenderer.js', 'jqplot.sprintf.js', 'jsdate.js', 'jqplot.themeEngine.js'];
+ var rn = getRootNode().root;
+ for (var i=0; i<files.length; i++) {
+ var pp = rn+files[i];
+ try {
+ document.write("<scr"+"ipt type='text/javascript' src='"+pp+"'></scr"+"ipt>");
+ } catch (e) {
+ var script = document.createElement("script");
+ script.src = pp;
+ document.getElementsByTagName("head")[0].appendChild(script);
+ // avoid memory leak
+ script = null;
+ }
+ }
+
+})(); \ No newline at end of file
diff --git a/libs/jqplot/jsdate.js b/libs/jqplot/jsdate.js
new file mode 100644
index 0000000000..38c55fa671
--- /dev/null
+++ b/libs/jqplot/jsdate.js
@@ -0,0 +1,1437 @@
+
+/**
+ * @fileOverview Date parsing and formatting operations without extending the Date built-in object.
+ * @author Chris Leonello
+ * @version #VERSION#
+ * @date #DATE#
+ */
+
+(function($) {
+
+ /**
+ * @description
+ * <p>Object with extended date parsing and formatting capabilities.
+ * This library borrows many concepts and ideas from the Date Instance
+ * Methods by Ken Snyder along with some parts of Ken's actual code.</p>
+ *
+ * <p>jsDate takes a different approach by not extending the built-in
+ * Date Object, improving date parsing, allowing for multiple formatting
+ * syntaxes and multiple and more easily expandable localization.</p>
+ *
+ * @author Chris Leonello
+ * @date #date#
+ * @version #VERSION#
+ * @copyright (c) 2010 Chris Leonello
+ * jsDate 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.
+ *
+ * <p>Ken's origianl Date Instance Methods and copyright notice:</p>
+ * <pre>
+ * Ken Snyder (ken d snyder at gmail dot com)
+ * 2008-09-10
+ * version 2.0.2 (http://kendsnyder.com/sandbox/date/)
+ * Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/)
+ * </pre>
+ *
+ * @class
+ * @name jsDate
+ * @param {String | Number | Array | Date&nbsp;Object | Options&nbsp;Object} arguments Optional arguments, either a parsable date/time string,
+ * a JavaScript timestamp, an array of numbers of form [year, month, day, hours, minutes, seconds, milliseconds],
+ * a Date object, or an options object of form {syntax: "perl", date:some Date} where all options are optional.
+ */
+
+ var jsDate = function () {
+
+ this.syntax = jsDate.config.syntax;
+ this._type = "jsDate";
+ this.utcOffset = new Date().getTimezoneOffset * 60000;
+ this.proxy = new Date();
+ this.options = {};
+ this.locale = jsDate.regional.getLocale();
+ this.formatString = '';
+ this.defaultCentury = jsDate.config.defaultCentury;
+
+ switch ( arguments.length ) {
+ case 0:
+ break;
+ case 1:
+ // other objects either won't have a _type property or,
+ // if they do, it shouldn't be set to "jsDate", so
+ // assume it is an options argument.
+ if (get_type(arguments[0]) == "[object Object]" && arguments[0]._type != "jsDate") {
+ var opts = this.options = arguments[0];
+ this.syntax = opts.syntax || this.syntax;
+ this.defaultCentury = opts.defaultCentury || this.defaultCentury;
+ this.proxy = jsDate.createDate(opts.date);
+ }
+ else {
+ this.proxy = jsDate.createDate(arguments[0]);
+ }
+ break;
+ default:
+ var a = [];
+ for ( var i=0; i<arguments.length; i++ ) {
+ a.push(arguments[i]);
+ }
+ this.proxy = new Date( this.utcOffset );
+ this.proxy.setFullYear.apply( this.proxy, a.slice(0,3) );
+ if ( a.slice(3).length ) {
+ this.proxy.setHours.apply( this.proxy, a.slice(3) );
+ }
+ break;
+ }
+ };
+
+ /**
+ * @namespace Configuration options that will be used as defaults for all instances on the page.
+ * @property {String} defaultLocale The default locale to use [en].
+ * @property {String} syntax The default syntax to use [perl].
+ */
+ jsDate.config = {
+ defaultLocale: 'en',
+ syntax: 'perl',
+ defaultCentury: 1900
+ };
+
+ /**
+ * Add an arbitrary amount to the currently stored date
+ *
+ * @param {Number} number
+ * @param {String} unit
+ * @returns {jsDate}
+ */
+
+ jsDate.prototype.add = function(number, unit) {
+ var factor = multipliers[unit] || multipliers.day;
+ if (typeof factor == 'number') {
+ this.proxy.setTime(this.proxy.getTime() + (factor * number));
+ } else {
+ factor.add(this, number);
+ }
+ return this;
+ };
+
+ /**
+ * Create a new jqplot.date object with the same date
+ *
+ * @returns {jsDate}
+ */
+
+ jsDate.prototype.clone = function() {
+ return new jsDate(this.proxy.getTime());
+ };
+
+ /**
+ * Find the difference between this jsDate and another date.
+ *
+ * @param {String| Number| Array| jsDate&nbsp;Object| Date&nbsp;Object} dateObj
+ * @param {String} unit
+ * @param {Boolean} allowDecimal
+ * @returns {Number} Number of units difference between dates.
+ */
+
+ jsDate.prototype.diff = function(dateObj, unit, allowDecimal) {
+ // ensure we have a Date object
+ dateObj = new jsDate(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.proxy.getTime() - dateObj.proxy.getTime()) / factor;
+ } else {
+ // run function
+ var unitDiff = factor.diff(this.proxy, dateObj.proxy);
+ }
+ // if decimals are not allowed, round toward zero
+ return (allowDecimal ? unitDiff : Math[unitDiff > 0 ? 'floor' : 'ceil'](unitDiff));
+ };
+
+ /**
+ * Get the abbreviated name of the current week day
+ *
+ * @returns {String}
+ */
+
+ jsDate.prototype.getAbbrDayName = function() {
+ return jsDate.regional[this.locale]["dayNamesShort"][this.proxy.getDay()];
+ };
+
+ /**
+ * Get the abbreviated name of the current month
+ *
+ * @returns {String}
+ */
+
+ jsDate.prototype.getAbbrMonthName = function() {
+ return jsDate.regional[this.locale]["monthNamesShort"][this.proxy.getMonth()];
+ };
+
+ /**
+ * Get UPPER CASE AM or PM for the current time
+ *
+ * @returns {String}
+ */
+
+ jsDate.prototype.getAMPM = function() {
+ return this.proxy.getHours() >= 12 ? 'PM' : 'AM';
+ };
+
+ /**
+ * Get lower case am or pm for the current time
+ *
+ * @returns {String}
+ */
+
+ jsDate.prototype.getAmPm = function() {
+ return this.proxy.getHours() >= 12 ? 'pm' : 'am';
+ };
+
+ /**
+ * Get the century (19 for 20th Century)
+ *
+ * @returns {Integer} Century (19 for 20th century).
+ */
+ jsDate.prototype.getCentury = function() {
+ return parseInt(this.proxy.getFullYear()/100, 10);
+ };
+
+ /**
+ * Implements Date functionality
+ */
+ jsDate.prototype.getDate = function() {
+ return this.proxy.getDate();
+ };
+
+ /**
+ * Implements Date functionality
+ */
+ jsDate.prototype.getDay = function() {
+ return this.proxy.getDay();
+ };
+
+ /**
+ * Get the Day of week 1 (Monday) thru 7 (Sunday)
+ *
+ * @returns {Integer} Day of week 1 (Monday) thru 7 (Sunday)
+ */
+ jsDate.prototype.getDayOfWeek = function() {
+ var dow = this.proxy.getDay();
+ return dow===0?7:dow;
+ };
+
+ /**
+ * Get the day of the year
+ *
+ * @returns {Integer} 1 - 366, day of the year
+ */
+ jsDate.prototype.getDayOfYear = function() {
+ var d = this.proxy;
+ var ms = d - new Date('' + d.getFullYear() + '/1/1 GMT');
+ ms += d.getTimezoneOffset()*60000;
+ d = null;
+ return parseInt(ms/60000/60/24, 10)+1;
+ };
+
+ /**
+ * Get the name of the current week day
+ *
+ * @returns {String}
+ */
+
+ jsDate.prototype.getDayName = function() {
+ return jsDate.regional[this.locale]["dayNames"][this.proxy.getDay()];
+ };
+
+ /**
+ * Get the week number of the given year, starting with the first Sunday as the first week
+ * @returns {Integer} Week number (13 for the 13th full week of the year).
+ */
+ jsDate.prototype.getFullWeekOfYear = function() {
+ var d = this.proxy;
+ var doy = this.getDayOfYear();
+ var rdow = 6-d.getDay();
+ var woy = parseInt((doy+rdow)/7, 10);
+ return woy;
+ };
+
+ /**
+ * Implements Date functionality
+ */
+ jsDate.prototype.getFullYear = function() {
+ return this.proxy.getFullYear();
+ };
+
+ /**
+ * Get the GMT offset in hours and minutes (e.g. +06:30)
+ *
+ * @returns {String}
+ */
+
+ jsDate.prototype.getGmtOffset = function() {
+ // divide the minutes offset by 60
+ var hours = this.proxy.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 + addZeros(Math.floor(hours), 2) + ':' + addZeros((hours % 1) * 60, 2);
+ };
+
+ /**
+ * Implements Date functionality
+ */
+ jsDate.prototype.getHours = function() {
+ return this.proxy.getHours();
+ };
+
+ /**
+ * Get the current hour on a 12-hour scheme
+ *
+ * @returns {Integer}
+ */
+
+ jsDate.prototype.getHours12 = function() {
+ var hours = this.proxy.getHours();
+ return hours > 12 ? hours - 12 : (hours == 0 ? 12 : hours);
+ };
+
+
+ jsDate.prototype.getIsoWeek = function() {
+ var d = this.proxy;
+ var woy = d.getWeekOfYear();
+ var dow1_1 = (new Date('' + d.getFullYear() + '/1/1')).getDay();
+ // First week is 01 and not 00 as in the case of %U and %W,
+ // so we add 1 to the final result except if day 1 of the year
+ // is a Monday (then %W returns 01).
+ // We also need to subtract 1 if the day 1 of the year is
+ // Friday-Sunday, so the resulting equation becomes:
+ var idow = woy + (dow1_1 > 4 || dow1_1 <= 1 ? 0 : 1);
+ if(idow == 53 && (new Date('' + d.getFullYear() + '/12/31')).getDay() < 4)
+ {
+ idow = 1;
+ }
+ else if(idow === 0)
+ {
+ d = new jsDate(new Date('' + (d.getFullYear()-1) + '/12/31'));
+ idow = d.getIsoWeek();
+ }
+ d = null;
+ return idow;
+ };
+
+ /**
+ * Implements Date functionality
+ */
+ jsDate.prototype.getMilliseconds = function() {
+ return this.proxy.getMilliseconds();
+ };
+
+ /**
+ * Implements Date functionality
+ */
+ jsDate.prototype.getMinutes = function() {
+ return this.proxy.getMinutes();
+ };
+
+ /**
+ * Implements Date functionality
+ */
+ jsDate.prototype.getMonth = function() {
+ return this.proxy.getMonth();
+ };
+
+ /**
+ * Get the name of the current month
+ *
+ * @returns {String}
+ */
+
+ jsDate.prototype.getMonthName = function() {
+ return jsDate.regional[this.locale]["monthNames"][this.proxy.getMonth()];
+ };
+
+ /**
+ * Get the number of the current month, 1-12
+ *
+ * @returns {Integer}
+ */
+
+ jsDate.prototype.getMonthNumber = function() {
+ return this.proxy.getMonth() + 1;
+ };
+
+ /**
+ * Implements Date functionality
+ */
+ jsDate.prototype.getSeconds = function() {
+ return this.proxy.getSeconds();
+ };
+
+ /**
+ * Return a proper two-digit year integer
+ *
+ * @returns {Integer}
+ */
+
+ jsDate.prototype.getShortYear = function() {
+ return this.proxy.getYear() % 100;
+ };
+
+ /**
+ * Implements Date functionality
+ */
+ jsDate.prototype.getTime = function() {
+ return this.proxy.getTime();
+ };
+
+ /**
+ * Get the timezone abbreviation
+ *
+ * @returns {String} Abbreviation for the timezone
+ */
+ jsDate.prototype.getTimezoneAbbr = function() {
+ return this.proxy.toString().replace(/^.*\(([^)]+)\)$/, '$1');
+ };
+
+ /**
+ * Get the browser-reported name for the current timezone (e.g. MDT, Mountain Daylight Time)
+ *
+ * @returns {String}
+ */
+ jsDate.prototype.getTimezoneName = function() {
+ var match = /(?:\((.+)\)$| ([A-Z]{3}) )/.exec(this.toString());
+ return match[1] || match[2] || 'GMT' + this.getGmtOffset();
+ };
+
+ /**
+ * Implements Date functionality
+ */
+ jsDate.prototype.getTimezoneOffset = function() {
+ return this.proxy.getTimezoneOffset();
+ };
+
+
+ /**
+ * Get the week number of the given year, starting with the first Monday as the first week
+ * @returns {Integer} Week number (13 for the 13th week of the year).
+ */
+ jsDate.prototype.getWeekOfYear = function() {
+ var doy = this.getDayOfYear();
+ var rdow = 7 - this.getDayOfWeek();
+ var woy = parseInt((doy+rdow)/7, 10);
+ return woy;
+ };
+
+ /**
+ * Get the current date as a Unix timestamp
+ *
+ * @returns {Integer}
+ */
+
+ jsDate.prototype.getUnix = function() {
+ return Math.round(this.proxy.getTime() / 1000, 0);
+ };
+
+ /**
+ * Implements Date functionality
+ */
+ jsDate.prototype.getYear = function() {
+ return this.proxy.getYear();
+ };
+
+ /**
+ * Return a date one day ahead (or any other unit)
+ *
+ * @param {String} unit Optional, year | month | day | week | hour | minute | second | millisecond
+ * @returns {jsDate}
+ */
+
+ jsDate.prototype.next = function(unit) {
+ unit = unit || 'day';
+ return this.clone().add(1, unit);
+ };
+
+ /**
+ * Set the jsDate instance to a new date.
+ *
+ * @param {String | Number | Array | Date Object | jsDate Object | Options Object} arguments Optional arguments,
+ * either a parsable date/time string,
+ * a JavaScript timestamp, an array of numbers of form [year, month, day, hours, minutes, seconds, milliseconds],
+ * a Date object, jsDate Object or an options object of form {syntax: "perl", date:some Date} where all options are optional.
+ */
+ jsDate.prototype.set = function() {
+ switch ( arguments.length ) {
+ case 0:
+ this.proxy = new Date();
+ break;
+ case 1:
+ // other objects either won't have a _type property or,
+ // if they do, it shouldn't be set to "jsDate", so
+ // assume it is an options argument.
+ if (get_type(arguments[0]) == "[object Object]" && arguments[0]._type != "jsDate") {
+ var opts = this.options = arguments[0];
+ this.syntax = opts.syntax || this.syntax;
+ this.defaultCentury = opts.defaultCentury || this.defaultCentury;
+ this.proxy = jsDate.createDate(opts.date);
+ }
+ else {
+ this.proxy = jsDate.createDate(arguments[0]);
+ }
+ break;
+ default:
+ var a = [];
+ for ( var i=0; i<arguments.length; i++ ) {
+ a.push(arguments[i]);
+ }
+ this.proxy = new Date( this.utcOffset );
+ this.proxy.setFullYear.apply( this.proxy, a.slice(0,3) );
+ if ( a.slice(3).length ) {
+ this.proxy.setHours.apply( this.proxy, a.slice(3) );
+ }
+ break;
+ }
+ };
+
+ /**
+ * Sets the day of the month for a specified date according to local time.
+ * @param {Integer} dayValue An integer from 1 to 31, representing the day of the month.
+ */
+ jsDate.prototype.setDate = function(n) {
+ return this.proxy.setDate(n);
+ };
+
+ /**
+ * Sets the full year for a specified date according to local time.
+ * @param {Integer} yearValue The numeric value of the year, for example, 1995.
+ * @param {Integer} monthValue Optional, between 0 and 11 representing the months January through December.
+ * @param {Integer} dayValue Optional, between 1 and 31 representing the day of the month. If you specify the dayValue parameter, you must also specify the monthValue.
+ */
+ jsDate.prototype.setFullYear = function() {
+ return this.proxy.setFullYear.apply(this.proxy, arguments);
+ };
+
+ /**
+ * Sets the hours for a specified date according to local time.
+ *
+ * @param {Integer} hoursValue An integer between 0 and 23, representing the hour.
+ * @param {Integer} minutesValue Optional, An integer between 0 and 59, representing the minutes.
+ * @param {Integer} secondsValue Optional, An integer between 0 and 59, representing the seconds.
+ * If you specify the secondsValue parameter, you must also specify the minutesValue.
+ * @param {Integer} msValue Optional, A number between 0 and 999, representing the milliseconds.
+ * If you specify the msValue parameter, you must also specify the minutesValue and secondsValue.
+ */
+ jsDate.prototype.setHours = function() {
+ return this.proxy.setHours.apply(this.proxy, arguments);
+ };
+
+ /**
+ * Implements Date functionality
+ */
+ jsDate.prototype.setMilliseconds = function(n) {
+ return this.proxy.setMilliseconds(n);
+ };
+
+ /**
+ * Implements Date functionality
+ */
+ jsDate.prototype.setMinutes = function() {
+ return this.proxy.setMinutes.apply(this.proxy, arguments);
+ };
+
+ /**
+ * Implements Date functionality
+ */
+ jsDate.prototype.setMonth = function() {
+ return this.proxy.setMonth.apply(this.proxy, arguments);
+ };
+
+ /**
+ * Implements Date functionality
+ */
+ jsDate.prototype.setSeconds = function() {
+ return this.proxy.setSeconds.apply(this.proxy, arguments);
+ };
+
+ /**
+ * Implements Date functionality
+ */
+ jsDate.prototype.setTime = function(n) {
+ return this.proxy.setTime(n);
+ };
+
+ /**
+ * Implements Date functionality
+ */
+ jsDate.prototype.setYear = function() {
+ return this.proxy.setYear.apply(this.proxy, arguments);
+ };
+
+ /**
+ * Provide a formatted string representation of this date.
+ *
+ * @param {String} formatString A format string.
+ * See: {@link jsDate.formats}.
+ * @returns {String} Date String.
+ */
+
+ jsDate.prototype.strftime = function(formatString) {
+ formatString = formatString || this.formatString || jsDate.regional[this.locale]['formatString'];
+ return jsDate.strftime(this, formatString, this.syntax);
+ };
+
+ /**
+ * Return a String representation of this jsDate object.
+ * @returns {String} Date string.
+ */
+
+ jsDate.prototype.toString = function() {
+ return this.proxy.toString();
+ };
+
+ /**
+ * Convert the current date to an 8-digit integer (%Y%m%d)
+ *
+ * @returns {Integer}
+ */
+
+ jsDate.prototype.toYmdInt = function() {
+ return (this.proxy.getFullYear() * 10000) + (this.getMonthNumber() * 100) + this.proxy.getDate();
+ };
+
+ /**
+ * @namespace Holds localizations for month/day names.
+ * <p>jsDate attempts to detect locale when loaded and defaults to 'en'.
+ * If a localization is detected which is not available, jsDate defaults to 'en'.
+ * Additional localizations can be added after jsDate loads. After adding a localization,
+ * call the jsDate.regional.getLocale() method. Currently, en, fr and de are defined.</p>
+ *
+ * <p>Localizations must be an object and have the following properties defined: monthNames, monthNamesShort, dayNames, dayNamesShort and Localizations are added like:</p>
+ * <pre class="code">
+ * jsDate.regional['en'] = {
+ * monthNames : 'January February March April May June July August September October November December'.split(' '),
+ * monthNamesShort : 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' '),
+ * dayNames : 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' '),
+ * dayNamesShort : 'Sun Mon Tue Wed Thu Fri Sat'.split(' ')
+ * };
+ * </pre>
+ * <p>After adding localizations, call <code>jsDate.regional.getLocale();</code> to update the locale setting with the
+ * new localizations.</p>
+ */
+
+ jsDate.regional = {
+ 'en': {
+ monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'],
+ monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun','Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
+ dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
+ dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+ formatString: '%Y-%m-%d %H:%M:%S'
+ },
+
+ 'fr': {
+ monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre'],
+ monthNamesShort: ['Jan','Fév','Mar','Avr','Mai','Jun','Jul','Aoû','Sep','Oct','Nov','Déc'],
+ dayNames: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'],
+ dayNamesShort: ['Dim','Lun','Mar','Mer','Jeu','Ven','Sam'],
+ formatString: '%Y-%m-%d %H:%M:%S'
+ },
+
+ 'de': {
+ monthNames: ['Januar','Februar','März','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember'],
+ monthNamesShort: ['Jan','Feb','Mär','Apr','Mai','Jun','Jul','Aug','Sep','Okt','Nov','Dez'],
+ dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'],
+ dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'],
+ formatString: '%Y-%m-%d %H:%M:%S'
+ },
+
+ 'es': {
+ monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio', 'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'],
+ monthNamesShort: ['Ene','Feb','Mar','Abr','May','Jun', 'Jul','Ago','Sep','Oct','Nov','Dic'],
+ dayNames: ['Domingo','Lunes','Martes','Mi&eacute;rcoles','Jueves','Viernes','S&aacute;bado'],
+ dayNamesShort: ['Dom','Lun','Mar','Mi&eacute;','Juv','Vie','S&aacute;b'],
+ formatString: '%Y-%m-%d %H:%M:%S'
+ },
+
+ 'ru': {
+ monthNames: ['Январь','Февраль','Март','Апрель','Май','Июнь','Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'],
+ monthNamesShort: ['Янв','Фев','Мар','Апр','Май','Июн','Июл','Авг','Сен','Окт','Ноя','Дек'],
+ dayNames: ['воскресенье','понедельник','вторник','среда','четверг','пятница','суббота'],
+ dayNamesShort: ['вск','пнд','втр','срд','чтв','птн','сбт'],
+ formatString: '%Y-%m-%d %H:%M:%S'
+ },
+
+ 'ar': {
+ monthNames: ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'آذار', 'حزيران','تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'],
+ monthNamesShort: ['1','2','3','4','5','6','7','8','9','10','11','12'],
+ dayNames: ['السبت', 'الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة'],
+ dayNamesShort: ['سبت', 'أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة'],
+ formatString: '%Y-%m-%d %H:%M:%S'
+ },
+
+ 'pt': {
+ monthNames: ['Janeiro','Fevereiro','Mar&ccedil;o','Abril','Maio','Junho','Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'],
+ monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'],
+ dayNames: ['Domingo','Segunda-feira','Ter&ccedil;a-feira','Quarta-feira','Quinta-feira','Sexta-feira','S&aacute;bado'],
+ dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','S&aacute;b'],
+ formatString: '%Y-%m-%d %H:%M:%S'
+ },
+
+ 'pt-BR': {
+ monthNames: ['Janeiro','Fevereiro','Mar&ccedil;o','Abril','Maio','Junho', 'Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'],
+ monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'],
+ dayNames: ['Domingo','Segunda-feira','Ter&ccedil;a-feira','Quarta-feira','Quinta-feira','Sexta-feira','S&aacute;bado'],
+ dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','S&aacute;b'],
+ formatString: '%Y-%m-%d %H:%M:%S'
+ }
+
+
+ };
+
+ // Set english variants to 'en'
+ jsDate.regional['en-US'] = jsDate.regional['en-GB'] = jsDate.regional['en'];
+
+ /**
+ * Try to determine the users locale based on the lang attribute of the html page. Defaults to 'en'
+ * if it cannot figure out a locale of if the locale does not have a localization defined.
+ * @returns {String} locale
+ */
+
+ jsDate.regional.getLocale = function () {
+ var l = jsDate.config.defaultLocale;
+
+ if ( document && document.getElementsByTagName('html') && document.getElementsByTagName('html')[0].lang ) {
+ l = document.getElementsByTagName('html')[0].lang;
+ if (!jsDate.regional.hasOwnProperty(l)) {
+ l = jsDate.config.defaultLocale;
+ }
+ }
+
+ return l;
+ };
+
+ // ms in day
+ var day = 24 * 60 * 60 * 1000;
+
+ // padd a number with zeros
+ var addZeros = function(num, digits) {
+ num = String(num);
+ var i = digits - num.length;
+ var s = String(Math.pow(10, i)).slice(1);
+ return s.concat(num);
+ };
+
+ // representations used for calculating differences between dates.
+ // This borrows heavily from Ken Snyder's work.
+ 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.
+ // This comes from Ken Snyders work.
+ //
+ 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 jsDate instance and a format code and return the formatted value.
+ // This is a somewhat modified version of Ken Snyder's method.
+ //
+ var format = function(d, code, syntax) {
+ // if shorcut codes are used, recursively expand those.
+ if (jsDate.formats[syntax]["shortcuts"][code]) {
+ return jsDate.strftime(d, jsDate.formats[syntax]["shortcuts"][code], syntax);
+ } else {
+ // get the format code function and addZeros() argument
+ var getter = (jsDate.formats[syntax]["codes"][code] || '').split('.');
+ var nbr = d['get' + getter[0]] ? d['get' + getter[0]]() : '';
+ if (getter[1]) {
+ nbr = addZeros(nbr, getter[1]);
+ }
+ return nbr;
+ }
+ };
+
+ /**
+ * @static
+ * Static function for convert a date to a string according to a given format. Also acts as namespace for strftime format codes.
+ * <p>strftime formatting can be accomplished without creating a jsDate object by calling jsDate.strftime():</p>
+ * <pre class="code">
+ * var formattedDate = jsDate.strftime('Feb 8, 2006 8:48:32', '%Y-%m-%d %H:%M:%S');
+ * </pre>
+ * @param {String | Number | Array | jsDate&nbsp;Object | Date&nbsp;Object} date A parsable date string, JavaScript time stamp, Array of form [year, month, day, hours, minutes, seconds, milliseconds], jsDate Object or Date object.
+ * @param {String} formatString String with embedded date formatting codes.
+ * See: {@link jsDate.formats}.
+ * @param {String} syntax Optional syntax to use [default perl].
+ * @param {String} locale Optional locale to use.
+ * @returns {String} Formatted representation of the date.
+ */
+ //
+ // Logic as implemented here is very similar to Ken Snyder's Date Instance Methods.
+ //
+ jsDate.strftime = function(d, formatString, syntax, locale) {
+ var syn = 'perl';
+ var loc = jsDate.regional.getLocale();
+
+ // check if syntax and locale are available or reversed
+ if (syntax && jsDate.formats.hasOwnProperty(syntax)) {
+ syn = syntax;
+ }
+ else if (syntax && jsDate.regional.hasOwnProperty(syntax)) {
+ loc = syntax;
+ }
+
+ if (locale && jsDate.formats.hasOwnProperty(locale)) {
+ syn = locale;
+ }
+ else if (locale && jsDate.regional.hasOwnProperty(locale)) {
+ loc = locale;
+ }
+
+ if (get_type(d) != "[object Object]" || d._type != "jsDate") {
+ d = new jsDate(d);
+ d.locale = loc;
+ }
+ if (!formatString) {
+ formatString = d.formatString || jsDate.regional[loc]['formatString'];
+ }
+ // default the format string to year-month-day
+ var source = formatString || '%Y-%m-%d',
+ result = '',
+ match;
+ // replace each format code
+ while (source.length > 0) {
+ if (match = source.match(jsDate.formats[syn].codes.matcher)) {
+ result += source.slice(0, match.index);
+ result += (match[1] || '') + format(d, match[2], syn);
+ source = source.slice(match.index + match[0].length);
+ } else {
+ result += source;
+ source = '';
+ }
+ }
+ return result;
+ };
+
+ /**
+ * @namespace
+ * Namespace to hold format codes and format shortcuts. "perl" and "php" format codes
+ * and shortcuts are defined by default. Additional codes and shortcuts can be
+ * added like:
+ *
+ * <pre class="code">
+ * jsDate.formats["perl"] = {
+ * "codes": {
+ * matcher: /someregex/,
+ * Y: "fullYear", // name of "get" method without the "get",
+ * ..., // more codes
+ * },
+ * "shortcuts": {
+ * F: '%Y-%m-%d',
+ * ..., // more shortcuts
+ * }
+ * };
+ * </pre>
+ *
+ * <p>Additionally, ISO and SQL shortcuts are defined and can be accesses via:
+ * <code>jsDate.formats.ISO</code> and <code>jsDate.formats.SQL</code>
+ */
+
+ jsDate.formats = {
+ ISO:'%Y-%m-%dT%H:%M:%S.%N%G',
+ SQL:'%Y-%m-%d %H:%M:%S'
+ };
+
+ /**
+ * Perl format codes and shortcuts for strftime.
+ *
+ * A hash (object) of codes where each code must be an array where the first member is
+ * the name of a Date.prototype or jsDate.prototype function to call
+ * and optionally a second member indicating the number to pass to addZeros()
+ *
+ * <p>The following format codes are defined:</p>
+ *
+ * <pre class="code">
+ * 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)
+ *
+ * == 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 03:31:00 PM %I:%M:%S %p
+ *
+ * == Characters ==
+ * %n \n Newline
+ * %t \t Tab
+ * %% % Percent Symbol
+ * </pre>
+ *
+ * <p>Formatting 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.</p>
+ *
+ * <p>Format codes and format shortcuts can be redefined after the jsDate
+ * module is imported.</p>
+ *
+ * <p>Note that if you redefine the whole hash (object), you must supply a "matcher"
+ * regex for the parser. The default matcher is:</p>
+ *
+ * <code>/()%(#?(%|[a-z]))/i</code>
+ *
+ * <p>which corresponds to the Perl syntax used by default.</p>
+ *
+ * <p>By customizing the matcher and format codes, nearly any strftime functionality is possible.</p>
+ */
+
+ jsDate.formats.perl = {
+ codes: {
+ //
+ // 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',
+ // 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: {
+ // 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',
+ '%': '%'
+ }
+ };
+
+ /**
+ * PHP format codes and shortcuts for strftime.
+ *
+ * A hash (object) of codes where each code must be an array where the first member is
+ * the name of a Date.prototype or jsDate.prototype function to call
+ * and optionally a second member indicating the number to pass to addZeros()
+ *
+ * <p>The following format codes are defined:</p>
+ *
+ * <pre class="code">
+ * Code Result Description
+ * === Days ===
+ * %a Sun through Sat An abbreviated textual representation of the day
+ * %A Sunday - Saturday A full textual representation of the day
+ * %d 01 to 31 Two-digit day of the month (with leading zeros)
+ * %e 1 to 31 Day of the month, with a space preceding single digits.
+ * %j 001 to 366 Day of the year, 3 digits with leading zeros
+ * %u 1 - 7 (Mon - Sun) ISO-8601 numeric representation of the day of the week
+ * %w 0 - 6 (Sun - Sat) Numeric representation of the day of the week
+ *
+ * === Week ===
+ * %U 13 Full Week number, starting with the first Sunday as the first week
+ * %V 01 through 53 ISO-8601:1988 week number, starting with the first week of the year
+ * with at least 4 weekdays, with Monday being the start of the week
+ * %W 46 A numeric representation of the week of the year,
+ * starting with the first Monday as the first week
+ * === Month ===
+ * %b Jan through Dec Abbreviated month name, based on the locale
+ * %B January - December Full month name, based on the locale
+ * %h Jan through Dec Abbreviated month name, based on the locale (an alias of %b)
+ * %m 01 - 12 (Jan - Dec) Two digit representation of the month
+ *
+ * === Year ===
+ * %C 19 Two digit century (year/100, truncated to an integer)
+ * %y 09 for 2009 Two digit year
+ * %Y 2038 Four digit year
+ *
+ * === Time ===
+ * %H 00 through 23 Two digit representation of the hour in 24-hour format
+ * %I 01 through 12 Two digit representation of the hour in 12-hour format
+ * %l 1 through 12 Hour in 12-hour format, with a space preceeding single digits
+ * %M 00 through 59 Two digit representation of the minute
+ * %p AM/PM UPPER-CASE 'AM' or 'PM' based on the given time
+ * %P am/pm lower-case 'am' or 'pm' based on the given time
+ * %r 09:34:17 PM Same as %I:%M:%S %p
+ * %R 00:35 Same as %H:%M
+ * %S 00 through 59 Two digit representation of the second
+ * %T 21:34:17 Same as %H:%M:%S
+ * %X 03:59:16 Preferred time representation based on locale, without the date
+ * %z -0500 or EST Either the time zone offset from UTC or the abbreviation
+ * %Z -0500 or EST The time zone offset/abbreviation option NOT given by %z
+ *
+ * === Time and Date ===
+ * %D 02/05/09 Same as %m/%d/%y
+ * %F 2009-02-05 Same as %Y-%m-%d (commonly used in database datestamps)
+ * %s 305815200 Unix Epoch Time timestamp (same as the time() function)
+ * %x 02/05/09 Preferred date representation, without the time
+ *
+ * === Miscellaneous ===
+ * %n --- A newline character (\n)
+ * %t --- A Tab character (\t)
+ * %% --- A literal percentage character (%)
+ * </pre>
+ */
+
+ jsDate.formats.php = {
+ codes: {
+ //
+ // 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,
+ // day
+ a: 'AbbrDayName',
+ A: 'DayName',
+ d: 'Date.2',
+ e: 'Date',
+ j: 'DayOfYear.3',
+ u: 'DayOfWeek',
+ w: 'Day',
+ // week
+ U: 'FullWeekOfYear.2',
+ V: 'IsoWeek.2',
+ W: 'WeekOfYear.2',
+ // month
+ b: 'AbbrMonthName',
+ B: 'MonthName',
+ m: 'MonthNumber.2',
+ h: 'AbbrMonthName',
+ // year
+ C: 'Century.2',
+ y: 'ShortYear.2',
+ Y: 'FullYear',
+ // time
+ H: 'Hours.2',
+ I: 'Hours12.2',
+ l: 'Hours12',
+ p: 'AMPM',
+ P: 'AmPm',
+ M: 'Minutes.2',
+ S: 'Seconds.2',
+ s: 'Unix',
+ O: 'TimezoneOffset',
+ z: 'GmtOffset',
+ Z: 'TimezoneAbbr'
+ },
+
+ shortcuts: {
+ D: '%m/%d/%y',
+ F: '%Y-%m-%d',
+ T: '%H:%M:%S',
+ X: '%H:%M:%S',
+ x: '%m/%d/%y',
+ R: '%H:%M',
+ r: '%I:%M:%S %p',
+ t: '\t',
+ n: '\n',
+ '%': '%'
+ }
+ };
+ //
+ // Conceptually, the logic implemented here is similar to Ken Snyder's Date Instance Methods.
+ // I use his idea of a set of parsers which can be regular expressions or functions,
+ // iterating through those, and then seeing if Date.parse() will create a date.
+ // The parser expressions and functions are a little different and some bugs have been
+ // worked out. Also, a lot of "pre-parsing" is done to fix implementation
+ // variations of Date.parse() between browsers.
+ //
+ jsDate.createDate = function(date) {
+ // if passing in multiple arguments, try Date constructor
+ if (date == null) {
+ return new 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 * 1000);
+ // If the passed value is an integer, interpret it as a javascript timestamp
+ if (typeof date == 'number') {
+ return new Date(date);
+ }
+
+ // Before passing strings into Date.parse(), have to normalize them for certain conditions.
+ // If strings are not formatted staccording to the EcmaScript spec, results from Date parse will be implementation dependent.
+ //
+ // For example:
+ // * FF and Opera assume 2 digit dates are pre y2k, Chome assumes <50 is pre y2k, 50+ is 21st century.
+ // * Chrome will correctly parse '1984-1-25' into localtime, FF and Opera will not parse.
+ // * Both FF, Chrome and Opera will parse '1984/1/25' into localtime.
+
+ // remove leading and trailing spaces
+ var parsable = String(date).replace(/^\s*(.+)\s*$/g, '$1');
+
+ // replace dahses (-) with slashes (/) in dates like n[nnn]/n[n]/n[nnn]
+ parsable = parsable.replace(/^([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,4})/, "$1/$2/$3");
+
+ /////////
+ // Need to check for '15-Dec-09' also.
+ // FF will not parse, but Chrome will.
+ // Chrome will set date to 2009 as well.
+ /////////
+
+ // first check for 'dd-mmm-yyyy' or 'dd/mmm/yyyy' like '15-Dec-2010'
+ parsable = parsable.replace(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{4})/i, "$1 $2 $3");
+
+ // Now check for 'dd-mmm-yy' or 'dd/mmm/yy' and normalize years to default century.
+ var match = parsable.match(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{2})\D*/i);
+ if (match && match.length > 3) {
+ var m3 = parseFloat(match[3]);
+ var ny = jsDate.config.defaultCentury + m3;
+ ny = String(ny);
+
+ // now replace 2 digit year with 4 digit year
+ parsable = parsable.replace(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{2})\D*/i, match[1] +' '+ match[2] +' '+ ny);
+
+ }
+
+ // Check for '1/19/70 8:14PM'
+ // where starts with mm/dd/yy or yy/mm/dd and have something after
+ // Check if 1st postiion is greater than 31, assume it is year.
+ // Assme all 2 digit years are 1900's.
+ // Finally, change them into US style mm/dd/yyyy representations.
+ match = parsable.match(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})[^0-9]/);
+
+ function h1(parsable, match) {
+ var m1 = parseFloat(match[1]);
+ var m2 = parseFloat(match[2]);
+ var m3 = parseFloat(match[3]);
+ var cent = jsDate.config.defaultCentury;
+ var ny, nd, nm, str;
+
+ if (m1 > 31) { // first number is a year
+ nd = m3;
+ nm = m2;
+ ny = cent + m1;
+ }
+
+ else { // last number is the year
+ nd = m2;
+ nm = m1;
+ ny = cent + m3;
+ }
+
+ str = nm+'/'+nd+'/'+ny;
+
+ // now replace 2 digit year with 4 digit year
+ return parsable.replace(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})/, str);
+
+ }
+
+ if (match && match.length > 3) {
+ parsable = h1(parsable, match);
+ }
+
+ // Now check for '1/19/70' with nothing after and do as above
+ var match = parsable.match(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})$/);
+
+ if (match && match.length > 3) {
+ parsable = h1(parsable, match);
+ }
+
+
+ var i = 0;
+ var length = jsDate.matchers.length;
+ var pattern,
+ ms,
+ current = parsable;
+ while (i < length) {
+ ms = Date.parse(current);
+ if (!isNaN(ms)) {
+ return new Date(ms);
+ }
+ pattern = jsDate.matchers[i];
+ if (typeof pattern == 'function') {
+ var obj = pattern.call(jsDate, current);
+ if (obj instanceof Date) {
+ return obj;
+ }
+ } else {
+ current = parsable.replace(pattern[0], pattern[1]);
+ }
+ i++;
+ }
+ return NaN;
+ };
+
+
+ /**
+ * @static
+ * Handy static utility function to return the number of days in a given month.
+ * @param {Integer} year Year
+ * @param {Integer} month Month (1-12)
+ * @returns {Integer} Number of days in the month.
+ */
+ //
+ // handy utility method Borrowed right from Ken Snyder's Date Instance Mehtods.
+ //
+ jsDate.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];
+ };
+
+
+ //
+ // An Array of regular expressions or functions that will attempt to match the date string.
+ // Functions are called with scope of a jsDate instance.
+ //
+ jsDate.matchers = [
+ // convert dd.mmm.yyyy to mm/dd/yyyy (world date to US date).
+ [/(3[01]|[0-2]\d)\s*\.\s*(1[0-2]|0\d)\s*\.\s*([1-9]\d{3})/, '$2/$1/$3'],
+ // convert yyyy-mm-dd to mm/dd/yyyy (ISO date to US date).
+ [/([1-9]\d{3})\s*-\s*(1[0-2]|0\d)\s*-\s*(3[01]|[0-2]\d)/, '$2/$3/$1'],
+ // Handle 12 hour or 24 hour time with milliseconds am/pm and optional date part.
+ function(str) {
+ 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 = this.createDate(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;
+ }
+ },
+ // Handle ISO timestamp with time zone.
+ function(str) {
+ 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 = this.createDate(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;
+ }
+ },
+ // Try to match ambiguous strings like 12/8/22.
+ // Use FF date assumption that 2 digit years are 20th century (i.e. 1900's).
+ // This may be redundant with pre processing of date already performed.
+ 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 cent = jsDate.config.defaultCentury;
+ var m1 = parseFloat(match[1]);
+ var m3 = parseFloat(match[3]);
+ var ny, nd, nm;
+ if (m1 > 31) { // first number is a year
+ nd = m3;
+ ny = cent + m1;
+ }
+
+ else { // last number is the year
+ nd = m1;
+ ny = cent + m3;
+ }
+
+ var nm = inArray(match[2], jsDate.regional[this.locale]["monthNamesShort"]);
+
+ if (nm == -1) {
+ nm = inArray(match[2], jsDate.regional[this.locale]["monthNames"]);
+ }
+
+ d.setFullYear(ny, nm, nd);
+ d.setHours(0,0,0,0);
+ return d;
+ }
+
+ else {
+ return str;
+ }
+ }
+ ];
+
+ //
+ // I think John Reisig published this method on his blog, ejohn.
+ //
+ function inArray( elem, array ) {
+ if ( array.indexOf ) {
+ return array.indexOf( elem );
+ }
+
+ for ( var i = 0, length = array.length; i < length; i++ ) {
+ if ( array[ i ] === elem ) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ //
+ // Thanks to Kangax, Christian Sciberras and Stack Overflow for this method.
+ //
+ function get_type(thing){
+ if(thing===null) return "[object Null]"; // special case
+ return Object.prototype.toString.call(thing);
+ }
+
+ $.jsDate = jsDate;
+
+})(jQuery); \ No newline at end of file
diff --git a/libs/jqplot/jsl.conf b/libs/jqplot/jsl.conf
new file mode 100644
index 0000000000..ce1f7f461a
--- /dev/null
+++ b/libs/jqplot/jsl.conf
@@ -0,0 +1,143 @@
+#
+# Configuration File for JavaScript Lint 0.3.0
+# Developed by Matthias Miller (http://www.JavaScriptLint.com)
+#
+# This configuration file can be used to lint a collection of scripts, or to enable
+# or disable warnings for scripts that are linted via the command line.
+#
+
+### Warnings
+# Enable or disable warnings based on requirements.
+# Use "+WarningName" to display or "-WarningName" to suppress.
+#
++no_return_value # function {0} does not always return a value
++duplicate_formal # duplicate formal argument {0}
++equal_as_assign # test for equality (==) mistyped as assignment (=)?{0}
++var_hides_arg # variable {0} hides argument
+-redeclared_var # redeclaration of {0} {1}
+-anon_no_return_value # anonymous function does not always return a value
++missing_semicolon # missing semicolon
++meaningless_block # meaningless block; curly braces have no impact
++comma_separated_stmts # multiple statements separated by commas (use semicolons?)
++unreachable_code # unreachable code
++missing_break # missing break statement
++missing_break_for_last_case # missing break statement for last case in switch
+-comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==)
+-inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement
++useless_void # use of the void type may be unnecessary (void is always undefined)
++multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs
++use_of_label # use of label
++block_without_braces # block statement without curly braces
++leading_decimal_point # leading decimal point may indicate a number or an object member
++trailing_decimal_point # trailing decimal point may indicate a number or an object member
++octal_number # leading zeros make an octal number
++nested_comment # nested comment
++misplaced_regex # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma
++ambiguous_newline # unexpected end of line; it is ambiguous whether these lines are part of the same statement
++empty_statement # empty statement or extra semicolon
+-missing_option_explicit # the "option explicit" control comment is missing
++partial_option_explicit # the "option explicit" control comment, if used, must be in the first script tag
++dup_option_explicit # duplicate "option explicit" control comment
++useless_assign # useless assignment
++ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity
++ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent)
+-missing_default_case # missing default case in switch statement
++duplicate_case_in_switch # duplicate case in switch statements
++default_not_at_end # the default case is not at the end of the switch statement
++legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax
++jsl_cc_not_understood # couldn't understand control comment using /*jsl:keyword*/ syntax
++useless_comparison # useless comparison; comparing identical expressions
++with_statement # with statement hides undeclared variables; use temporary variable instead
++trailing_comma_in_array # extra comma is not recommended in array initializers
++assign_to_function_call # assignment to a function call
+-parseint_missing_radix # parseInt missing radix parameter
+
+
+### Output format
+# Customize the format of the error message.
+# __FILE__ indicates current file path
+# __FILENAME__ indicates current file name
+# __LINE__ indicates current line
+# __ERROR__ indicates error message
+#
+# Visual Studio syntax (default):
++output-format __FILE__(__LINE__): __ERROR__
+# Alternative syntax:
+#+output-format __FILE__:__LINE__: __ERROR__
+
+
+### Context
+# Show the in-line position of the error.
+# Use "+context" to display or "-context" to suppress.
+#
++context
+
+
+### Semicolons
+# By default, assignments of an anonymous function to a variable or
+# property (such as a function prototype) must be followed by a semicolon.
+#
++lambda_assign_requires_semicolon
+
+
+### Control Comments
+# Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for
+# the /*@keyword@*/ control comments and JScript conditional comments. (The latter is
+# enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason,
+# although legacy control comments are enabled by default for backward compatibility.
+#
++legacy_control_comments
+
+
+### JScript Function Extensions
+# JScript allows member functions to be defined like this:
+# function MyObj() { /*constructor*/ }
+# function MyObj.prototype.go() { /*member function*/ }
+#
+# It also allows events to be attached like this:
+# function window::onload() { /*init page*/ }
+#
+# This is a Microsoft-only JavaScript extension. Enable this setting to allow them.
+#
+-jscript_function_extensions
+
+
+### Defining identifiers
+# By default, "option explicit" is enabled on a per-file basis.
+# To enable this for all files, use "+always_use_option_explicit"
++always_use_option_explicit
+
+# Define certain identifiers of which the lint is not aware.
+# (Use this in conjunction with the "undeclared identifier" warning.)
+#
+# Common uses for webpages might be:
++define window
++define document
++define jQuery
++define console
+
+
+### Files
+# Specify which files to lint
+# Use "+recurse" to enable recursion (disabled by default).
+# To add a set of files, use "+process FileName", "+process Folder\Path\*.js",
+# or "+process Folder\Path\*.htm".
+#
+#+process jsl-test.js
++process jqplot.axisLabelRenderer.js
++process jqplot.axisTickRenderer.js
++process jqplot.canvasGridRenderer.js
++process jqplot.core.js
++process jqplot.divTitleRenderer.js
++process jqplot.linearAxisRenderer.js
++process jqplot.linearTickGenerator.js
++process jqplot.lineRenderer.js
++process jqplot.markerRenderer.js
++process jqplot.shadowRenderer.js
++process jqplot.shapeRenderer.js
++process jqplot.sprintf.js
++process jqplot.tableLegendRenderer.js
++process jqplot.themeEngine.js
++process jquery.jqplot.js
++process jsdate.js
++process plugins/*.js \ No newline at end of file
diff --git a/libs/jqplot/optionsTutorial.txt b/libs/jqplot/optionsTutorial.txt
new file mode 100644
index 0000000000..3e68825e4d
--- /dev/null
+++ b/libs/jqplot/optionsTutorial.txt
@@ -0,0 +1,239 @@
+Title: Options Tutorial
+
+This document will help you understand how jqPlot's options
+relate to the API documentation and the jqPlot object
+itself. For a listing of options available to jqPlot,
+see <jqPlot Options> in the jqPlotOptions.txt file.
+
+The key to effectively using jqPlot is understanding jqPlot's
+options. The online documentation is API documentation. While
+it explains what attributes and methods various objects posses,
+it doesn't explain how to use or set those attributes through
+options. This tutorial will help explain that.
+
+Lets assume you are creating a plot
+like this:
+
+> chart = $.jqplot('chart', dataSeries, optionsObj);
+
+First, note that you shouldn't try to directly set attributes on the
+"chart" object (like chart.grid.shadow) after your call to $.jqplot().
+At best this won't do anything **(see below). You should pass options in via
+the "optionsObj".
+
+the optionsObj really represents the plot object (jqPlot object, not
+to be confused with the $.jqplot function which will create a jqPlot
+object). Attributes you specify on that object will be merged with
+attributes in the jqPlot object. The axes, legend, series, etc. are
+attributes on the jqPlot object. The jqPlot/optionsObj object looks
+something like (only some attributes shown):
+
+> jqPlot-|
+> |-seriesColors
+> |-textColor
+> |-fontFamily
+> |-fontSize
+> |-stackSeries
+> |-series(Array)-|
+> | |-Series1-|
+> | | |-lineWidth
+> | | |-shadow
+> | | |-showLine
+> | | |-showMarker
+> | | |-color
+> | |-Series2...
+> | |-...
+> | |-SeriesN
+> |
+> |-grid(Object)-|
+> | |-drawGridLines
+> | |-background
+> | |-borderColor
+> | |-borderWidth
+> | |-shadow
+> |
+> |-title(Object)-|
+> | |-text
+> | |-show
+> | |-fontFamily
+> | |-fontSize
+> | |-textAlign
+> | |-textColor
+> |
+> |-axes(Object)-|
+> | |-xais-|
+> | | |-min
+> | | |-max
+> | | |-numberTicks
+> | | |-showTicks
+> | | |-showTickMarks
+> | | |-pad
+> |
+> | ... and so on
+
+The optionsObj should follow the same construction as if it were a
+jqPlot object (with some exceptions/shortcuts I'll mention in a
+moment). So generally, when you see something like
+"this.drawGridLines" in the grid properties in the docs, just replace
+"this" with "grid" in your options object. So it becomes
+optionsObj.grid.drawGridLines. Do likewise with the other objects in
+the plot, replacing "this", with the respective attribute on the plot
+like "legend" or "title". Series and Axes are handled a little
+different, because series is an array and axes has 4 distinct children
+"xaxis", "yaxis", "x2axis" and "y2axis".
+
+So, to remove the shadow from the grid and change the grid border size
+you would do:
+
+> optionObj = {grid:{shadow:false, borderWidth:9.0}};
+
+To do the same as above but also make all the text in the plot red you
+would do:
+
+> optionObj = {
+> textColor:"#ff0000",
+> grid:{shadow:false, borderWidth:9.0}
+> }
+
+Here is a more deeply nested example. Say you want to specify a min
+and max on your y axis and use a specific color for your second
+series. That would look like:
+
+> optionsObj = {
+> axes:{yaxis:{min:5, max:230}},
+> series:[{},{color:"#33ff66"}]
+> }
+
+Note that series options are an array in order of the series data you
+sent in to your plot. To get to the second series, you have to put an
+object (even if empty) in place of the first series.
+
+There is a handy shortcut to assign options to all axes or all series
+at one go. Use axesDefaults and seriesDefaults. So, if you wanted
+both x and y axes to start at 0 and you wanted all series to not show
+markers, you could do:
+
+> optionsObj = {axesDefaults:{min:0}, seriesDefaults:{showMarker:false}}
+
+Another shortcut is for the plot title. Normally, you would assign
+options to the title as an object. If you specify a title option as a
+string, it will assign that to the title.text property automatically.
+So these two are equivalent:
+
+> optionsObj = {title:{text:"My Plot"}}
+
+and
+
+> optionsObj = {title:"My Plot"}
+
+Where things need more explaination is with renderers, plugins and
+their options. Briefly, what's renderer, what's a plugin.
+
+A renderer is an object that is used to draw something and gets
+attached to an existing object in the plot in order to draw it. A
+plugin does more than just provide drawing functionality to an
+object. It will do more like calculate a trend line, change the
+cursor, provide event driven functionality, etc. I consider renderers
+plugins, but plugins don't have to be renderers.
+
+So, how do you use renderers, plugins, and specify their options?
+Some common renderes are for bar charts and category axes. If you
+want to render your series as a bar chart with each set of bars
+showing up in a category on the x axis, you do:
+
+> optionsObj = {
+> seriesDefaults:{renderer:$.jqplot.BarRenderer},
+> axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer}}
+> }
+
+This replaces the default renderer used for all series in the plot
+with a bar renderer and the x axis default renderer (but not any other
+axis) with a category renderer.
+
+Now, how would I assign options to those renderers? The renderer's
+attributes may not be present in the pre-existing jqPlot object, they
+may be specific to the renderer. This is done through the
+"rendererOptions" option on the appropriate object. So, if I wanted my
+bars to be 25 pixels wide, I would do:
+
+
+> optionsObj = {
+> seriesDefaults:{
+> renderer:$.jqplot.BarRenderer},
+> rendererOptions:{
+> barWidth:25
+> },
+> axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer}}
+> }
+
+Again, this is using the "seriesDefaults" option, which will apply
+options to all series in the plot. You could do the same on any
+particular series in the plot through the "series" options array.
+
+Plugins are free to add their own options. For example, the
+highlighter plugin has it's own set of options that are unique to it.
+As a result, it responds to options placed in the "highlighter"
+attribute of your options object. So, if I wanted to change the
+highlighter tooltip to fade in and out slowly and be positioned
+directly above the point I'm highlighting:
+
+> optionsObj = {
+> highlighter:{tooltipFadeSpeed:'slow', tooltipLocation:'n'}
+> }
+
+Other plugins, like dragable and trendlines, add their options in with
+the series. This is because both of those plugins can have different
+options for different series in the plot. So, if you wanted to specify the
+color of the dragable and constrain it to drag only on the x axis as well
+as specify the color of the trend line you could do:
+
+> series:[{
+> dragable: {
+> color: '#ff3366',
+> constrainTo: 'x'
+> },
+> trendline: {
+> color: '#cccccc'
+> }
+> }]
+
+This would apply those options to the first series only. If you had 2 series
+and wanted to turn off dragging and trend lines on the second series, you could do:
+
+> series:[{
+> dragable: {
+> color: '#ff3366',
+> constrainTo: 'x'
+> },
+> trendline: {
+> color: '#cccccc'
+> }
+> }, {
+> isDragable: false,
+> trendline:{
+> show: false
+> }
+> }]
+
+Note, series dragability is turned off with the "isDragable" option directly on
+the series itself, not with a suboption of "dragable". This may be improved
+in the future.
+
+I hope this is helpful.
+A few key points to remember:
+
+- When you see "this" in the api docs, you generally replace it with
+the name of the object (in lowercase) you are looking at in your
+options object.
+- seriesDefaults and axesDefaults are convenient shortcuts.
+- to assign options to a renderer, generally use the "rendererOptions"
+- plugins may add their own options attribute, like "highlighter" or
+"cursor".
+
+** Note: you can set attributes after the plot is created (like
+plot.grid.shadow = false), but you'll have to issue the appropriate
+calls to possibly reinitialize and redraw the plot. jqPlot can
+definitely handle this to change the plot after creation (this is how
+the dragable plugin updates the plot data and the trend line plugin
+recomputes itself when data changes). This hasn't been documented
+yet, however. \ No newline at end of file
diff --git a/libs/jqplot/plugins/jqplot.BezierCurveRenderer.js b/libs/jqplot/plugins/jqplot.BezierCurveRenderer.js
new file mode 100644
index 0000000000..96e91e88ef
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.BezierCurveRenderer.js
@@ -0,0 +1,312 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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
diff --git a/libs/jqplot/plugins/jqplot.barRenderer.js b/libs/jqplot/plugins/jqplot.barRenderer.js
new file mode 100755
index 0000000000..5f043e40ad
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.barRenderer.js
@@ -0,0 +1,709 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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 = [];
+ this._type = 'bar';
+
+ // 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;
+ // Ughhh, have to make a copy of options b/c it may be modified later.
+ var opts = $.extend({}, 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;
+ // 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);
+ var nvals = temp[0];
+ var nseries = temp[1];
+ var pos = temp[2];
+ var points = [];
+
+ 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;
+ var base;
+ var xstart;
+ var ystart;
+
+ if (this.barDirection == 'vertical') {
+ for (var i=0; i<gridData.length; i++) {
+ if (this.data[i][1] == null) {
+ continue;
+ }
+ points = [];
+ base = gridData[i][0] + this._barNudge;
+ 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 if (this.waterfall && i == 0 && i < this.gridData.length-1) {
+ if (this._yaxis.min <= 0 && this._yaxis.max >= 0) {
+ ystart = this._yaxis.series_u2p(0);
+ }
+ else if (this._yaxis.min > 0) {
+ ystart = ctx.canvas.height;
+ }
+ else {
+ ystart = 0;
+ }
+ }
+ else if (this.waterfall && i == this.gridData.length - 1) {
+ if (this._yaxis.min <= 0 && this._yaxis.max >= 0) {
+ ystart = this._yaxis.series_u2p(0);
+ }
+ else if (this._yaxis.min > 0) {
+ ystart = ctx.canvas.height;
+ }
+ else {
+ ystart = 0;
+ }
+ }
+ 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;
+ }
+ }
+
+ if (!this.fillToZero || this._plotData[i][1] >= 0) {
+ 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]);
+ }
+ // for negative bars make sure points are always ordered clockwise
+ else {
+ points.push([base-this.barWidth/2, gridData[i][1]]);
+ points.push([base-this.barWidth/2, ystart]);
+ points.push([base+this.barWidth/2, ystart]);
+ points.push([base+this.barWidth/2, gridData[i][1]]);
+ }
+ 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 = [];
+ base = gridData[i][1] - this._barNudge;
+ 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 if (this.waterfall && i == 0 && i < this.gridData.length-1) {
+ if (this._xaxis.min <= 0 && this._xaxis.max >= 0) {
+ xstart = this._xaxis.series_u2p(0);
+ }
+ else if (this._xaxis.min > 0) {
+ xstart = 0;
+ }
+ else {
+ xstart = ctx.canvas.width;
+ }
+ }
+ else if (this.waterfall && i == this.gridData.length - 1) {
+ if (this._xaxis.min <= 0 && this._xaxis.max >= 0) {
+ xstart = this._xaxis.series_u2p(0);
+ }
+ else if (this._xaxis.min > 0) {
+ xstart = 0;
+ }
+ else {
+ xstart = ctx.canvas.width;
+ }
+ }
+ 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, points, 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 (var 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() {
+ // Memory Leaks patch
+ if (this.plugins.barRenderer && this.plugins.barRenderer.highlightCanvas) {
+
+ this.plugins.barRenderer.highlightCanvas.resetCanvas();
+ this.plugins.barRenderer.highlightCanvas = null;
+ }
+
+ 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, this));
+ 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);
+ canvas = null;
+ }
+
+ 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');
+ canvas = null;
+ }
+
+
+ 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
diff --git a/libs/jqplot/plugins/jqplot.blockRenderer.js b/libs/jqplot/plugins/jqplot.blockRenderer.js
new file mode 100644
index 0000000000..d113c3ea5b
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.blockRenderer.js
@@ -0,0 +1,234 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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;
+ this._type = 'block';
+
+ // 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
diff --git a/libs/jqplot/plugins/jqplot.bubbleRenderer.js b/libs/jqplot/plugins/jqplot.bubbleRenderer.js
new file mode 100755
index 0000000000..ffcc1e037e
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.bubbleRenderer.js
@@ -0,0 +1,754 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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 = [];
+ this._type = 'bubble';
+
+ // 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.
+ var comps;
+ 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 = null;
+ var 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 && !$.jqplot.use_excanvas) {
+ 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 = 0,
+ minpidx = 0,
+ maxsidx = 0,
+ maxpidx = 0,
+ maxr = 0,
+ minr = 0,
+ minMaxRadius = 0,
+ maxMaxRadius = 0,
+ maxMult = 0,
+ 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() {
+ // Memory Leaks patch
+ if (this.plugins.bubbleRenderer && this.plugins.bubbleRenderer.highlightCanvas) {
+ this.plugins.bubbleRenderer.highlightCanvas.resetCanvas();
+ this.plugins.bubbleRenderer.highlightCanvas = null;
+ }
+
+ 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));
+ 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
diff --git a/libs/jqplot/plugins/jqplot.canvasAxisLabelRenderer.js b/libs/jqplot/plugins/jqplot.canvasAxisLabelRenderer.js
new file mode 100644
index 0000000000..c900182427
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.canvasAxisLabelRenderer.js
@@ -0,0 +1,191 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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;
+ }
+
+ 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, plot) {
+ // Memory Leaks patch
+ if (this._elem) {
+ if ($.jqplot.use_excanvas) {
+ window.G_vmlCanvasManager.uninitElement(this._elem.get(0));
+ }
+
+ this._elem.emptyForce();
+ this._elem = null;
+ }
+
+ // create a canvas here, but can't draw on it untill it is appended
+ // to dom for IE compatability.
+ var elem = plot.canvasManager.getCanvas();
+
+ this._textRenderer.setText(this.label, ctx);
+ var w = this.getWidth(ctx);
+ var h = this.getHeight(ctx);
+ elem.width = w;
+ elem.height = h;
+ elem.style.width = w;
+ elem.style.height = h;
+
+ elem = plot.canvasManager.initCanvas(elem);
+
+ this._elem = $(elem);
+ this._elem.css({ position: 'absolute'});
+ this._elem.addClass('jqplot-'+this.axis+'-label');
+
+ elem = null;
+ return this._elem;
+ };
+
+ $.jqplot.CanvasAxisLabelRenderer.prototype.pack = function() {
+ this._textRenderer.draw(this._elem.get(0).getContext("2d"), this.label);
+ };
+
+})(jQuery); \ No newline at end of file
diff --git a/libs/jqplot/plugins/jqplot.canvasAxisTickRenderer.js b/libs/jqplot/plugins/jqplot.canvasAxisTickRenderer.js
new file mode 100644
index 0000000000..4771c1a617
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.canvasAxisTickRenderer.js
@@ -0,0 +1,230 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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 to prepend to the tick label.
+ // Prefix is prepended to the formatted tick label.
+ 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;
+ }
+
+ 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, plot) {
+ if (!this.label) {
+ this.label = this.prefix + this.formatter(this.formatString, this.value);
+ }
+
+ // Memory Leaks patch
+ if (this._elem) {
+ if ($.jqplot.use_excanvas) {
+ window.G_vmlCanvasManager.uninitElement(this._elem.get(0));
+ }
+
+ this._elem.emptyForce();
+ this._elem = null;
+ }
+
+ // create a canvas here, but can't draw on it untill it is appended
+ // to dom for IE compatability.
+
+ var elem = plot.canvasManager.getCanvas();
+
+ this._textRenderer.setText(this.label, ctx);
+ var w = this.getWidth(ctx);
+ var h = this.getHeight(ctx);
+ elem.width = w;
+ elem.height = h;
+ elem.style.width = w;
+ elem.style.height = h;
+ elem.style.textAlign = 'left';
+ elem.style.position = 'absolute';
+
+ elem = plot.canvasManager.initCanvas(elem);
+
+ this._elem = $(elem);
+ this._elem.css(this._styles);
+ this._elem.addClass('jqplot-'+this.axis+'-tick');
+
+ elem = null;
+ return this._elem;
+ };
+
+ $.jqplot.CanvasAxisTickRenderer.prototype.pack = function() {
+ this._textRenderer.draw(this._elem.get(0).getContext("2d"), this.label);
+ };
+
+})(jQuery); \ No newline at end of file
diff --git a/libs/jqplot/plugins/jqplot.canvasOverlay.js b/libs/jqplot/plugins/jqplot.canvasOverlay.js
new file mode 100755
index 0000000000..b8832c0971
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.canvasOverlay.js
@@ -0,0 +1,619 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(function($) {
+ // class: $.jqplot.CanvasOverlay
+ $.jqplot.CanvasOverlay = function(opts){
+ var options = opts || {};
+ this.options = {
+ show: $.jqplot.config.enablePlugins,
+ deferDraw: false
+ };
+ // prop: objects
+ this.objects = [];
+ this.objectNames = [];
+ this.canvas = null;
+ this.markerRenderer = new $.jqplot.MarkerRenderer({style:'line'});
+ this.markerRenderer.init();
+ if (options.objects) {
+ var objs = options.objects,
+ obj;
+ for (var i=0; i<objs.length; i++) {
+ obj = objs[i];
+ for (var n in obj) {
+ switch (n) {
+ case 'line':
+ this.addLine(obj[n]);
+ break;
+ case 'horizontalLine':
+ this.addHorizontalLine(obj[n]);
+ break;
+ case 'dashedHorizontalLine':
+ this.addDashedHorizontalLine(obj[n]);
+ break;
+ case 'verticalLine':
+ this.addVerticalLine(obj[n]);
+ break;
+ case 'dashedVerticalLine':
+ this.addDashedVerticalLine(obj[n]);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ $.extend(true, this.options, options);
+ };
+
+ // called with scope of a plot object
+ $.jqplot.CanvasOverlay.postPlotInit = function (target, data, opts) {
+ var options = opts || {};
+ // add a canvasOverlay attribute to the plot
+ this.plugins.canvasOverlay = new $.jqplot.CanvasOverlay(options.canvasOverlay);
+ };
+
+ function Line(options) {
+ this.type = 'line';
+ this.options = {
+ name: null,
+ show: true,
+ lineWidth: 2,
+ lineCap: 'round',
+ color: '#666666',
+ // prop: shadow
+ // wether or not to draw a shadow on the line
+ shadow: true,
+ // prop: shadowAngle
+ // Shadow angle in degrees
+ shadowAngle: 45,
+ // prop: shadowOffset
+ // Shadow offset from line in pixels
+ shadowOffset: 1,
+ // prop: shadowDepth
+ // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
+ shadowDepth: 3,
+ // prop: shadowAlpha
+ // Alpha channel transparency of shadow. 0 = transparent.
+ shadowAlpha: '0.07',
+ xaxis: 'xaxis',
+ yaxis: 'yaxis',
+ start: [],
+ stop: []
+ };
+ $.extend(true, this.options, options);
+ }
+
+ function HorizontalLine(options) {
+ this.type = 'horizontalLine';
+ this.options = {
+ name: null,
+ show: true,
+ lineWidth: 2,
+ lineCap: 'round',
+ color: '#666666',
+ // prop: shadow
+ // wether or not to draw a shadow on the line
+ shadow: true,
+ // prop: shadowAngle
+ // Shadow angle in degrees
+ shadowAngle: 45,
+ // prop: shadowOffset
+ // Shadow offset from line in pixels
+ shadowOffset: 1,
+ // prop: shadowDepth
+ // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
+ shadowDepth: 3,
+ // prop: shadowAlpha
+ // Alpha channel transparency of shadow. 0 = transparent.
+ shadowAlpha: '0.07',
+ // prop: xaxis
+ // For drawing, canvas has to know which x-y axes to scale to.
+ // Specify the desired x axis here.
+ xaxis: 'xaxis',
+ // prop: yaxis
+ // For drawing, canvas has to know which x-y axes to scale to.
+ // Specify the desired y axis here.
+ yaxis: 'yaxis',
+ y: null,
+ xmin: null,
+ xmax: null,
+ xOffset: '6px', // number or string. Number interpreted as units, string as pixels.
+ xminOffset: null,
+ xmaxOffset: null
+ };
+ $.extend(true, this.options, options);
+ }
+
+ function DashedHorizontalLine(options) {
+ this.type = 'dashedHorizontalLine';
+ this.options = {
+ name: null,
+ show: true,
+ lineWidth: 2,
+ lineCap: 'butt',
+ color: '#666666',
+ // prop: shadow
+ // wether or not to draw a shadow on the line
+ shadow: true,
+ // prop: shadowAngle
+ // Shadow angle in degrees
+ shadowAngle: 45,
+ // prop: shadowOffset
+ // Shadow offset from line in pixels
+ shadowOffset: 1,
+ // prop: shadowDepth
+ // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
+ shadowDepth: 3,
+ // prop: shadowAlpha
+ // Alpha channel transparency of shadow. 0 = transparent.
+ shadowAlpha: '0.07',
+ // prop: xaxis
+ // For drawing, canvas has to know which x-y axes to scale to.
+ // Specify the desired x axis here.
+ xaxis: 'xaxis',
+ // prop: yaxis
+ // For drawing, canvas has to know which x-y axes to scale to.
+ // Specify the desired y axis here.
+ yaxis: 'yaxis',
+ y: null,
+ xmin: null,
+ xmax: null,
+ xOffset: '6px', // number or string. Number interpreted as units, string as pixels.
+ xminOffset: null,
+ xmaxOffset: null,
+ // prop: dashPattern
+ // Array of line, space settings in pixels.
+ // Default is 8 pixel of line, 8 pixel of space.
+ // Note, limit to a 2 element array b/c of bug with higher order arrays.
+ dashPattern: [8,8]
+ };
+ $.extend(true, this.options, options);
+ }
+
+ function VerticalLine(options) {
+ this.type = 'verticalLine';
+ this.options = {
+ name: null,
+ show: true,
+ lineWidth: 2,
+ lineCap: 'round',
+ color: '#666666',
+ // prop: shadow
+ // wether or not to draw a shadow on the line
+ shadow: true,
+ // prop: shadowAngle
+ // Shadow angle in degrees
+ shadowAngle: 45,
+ // prop: shadowOffset
+ // Shadow offset from line in pixels
+ shadowOffset: 1,
+ // prop: shadowDepth
+ // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
+ shadowDepth: 3,
+ // prop: shadowAlpha
+ // Alpha channel transparency of shadow. 0 = transparent.
+ shadowAlpha: '0.07',
+ // prop: xaxis
+ // For drawing, canvas has to know which x-y axes to scale to.
+ // Specify the desired x axis here.
+ xaxis: 'xaxis',
+ // prop: yaxis
+ // For drawing, canvas has to know which x-y axes to scale to.
+ // Specify the desired y axis here.
+ yaxis: 'yaxis',
+ x: null,
+ ymin: null,
+ ymax: null,
+ yOffset: '6px', // number or string. Number interpreted as units, string as pixels.
+ yminOffset: null,
+ ymaxOffset: null
+ };
+ $.extend(true, this.options, options);
+ }
+
+ function DashedVerticalLine(options) {
+ this.type = 'dashedVerticalLine';
+ this.options = {
+ name: null,
+ show: true,
+ lineWidth: 2,
+ lineCap: 'butt',
+ color: '#666666',
+ // prop: shadow
+ // wether or not to draw a shadow on the line
+ shadow: true,
+ // prop: shadowAngle
+ // Shadow angle in degrees
+ shadowAngle: 45,
+ // prop: shadowOffset
+ // Shadow offset from line in pixels
+ shadowOffset: 1,
+ // prop: shadowDepth
+ // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
+ shadowDepth: 3,
+ // prop: shadowAlpha
+ // Alpha channel transparency of shadow. 0 = transparent.
+ shadowAlpha: '0.07',
+ // prop: xaxis
+ // For drawing, canvas has to know which x-y axes to scale to.
+ // Specify the desired x axis here.
+ xaxis: 'xaxis',
+ // prop: yaxis
+ // For drawing, canvas has to know which x-y axes to scale to.
+ // Specify the desired y axis here.
+ yaxis: 'yaxis',
+ x: null,
+ ymin: null,
+ ymax: null,
+ yOffset: '6px', // number or string. Number interpreted as units, string as pixels.
+ yminOffset: null,
+ ymaxOffset: null,
+ // prop: dashPattern
+ // Array of line, space settings in pixels.
+ // Default is 8 pixel of line, 8 pixel of space.
+ // Note, limit to a 2 element array b/c of bug with higher order arrays.
+ dashPattern: [8,8]
+ };
+ $.extend(true, this.options, options);
+ }
+
+ $.jqplot.CanvasOverlay.prototype.addLine = function(opts) {
+ var line = new Line(opts);
+ this.objects.push(line);
+ this.objectNames.push(line.options.name);
+ };
+
+ $.jqplot.CanvasOverlay.prototype.addHorizontalLine = function(opts) {
+ var line = new HorizontalLine(opts);
+ this.objects.push(line);
+ this.objectNames.push(line.options.name);
+ };
+
+ $.jqplot.CanvasOverlay.prototype.addDashedHorizontalLine = function(opts) {
+ var line = new DashedHorizontalLine(opts);
+ this.objects.push(line);
+ this.objectNames.push(line.options.name);
+ };
+
+ $.jqplot.CanvasOverlay.prototype.addVerticalLine = function(opts) {
+ var line = new VerticalLine(opts);
+ this.objects.push(line);
+ this.objectNames.push(line.options.name);
+ };
+
+ $.jqplot.CanvasOverlay.prototype.addDashedVerticalLine = function(opts) {
+ var line = new DashedVerticalLine(opts);
+ this.objects.push(line);
+ this.objectNames.push(line.options.name);
+ };
+
+ $.jqplot.CanvasOverlay.prototype.removeObject = function(idx) {
+ // check if integer, remove by index
+ if ($.type(idx) == 'number') {
+ this.objects.splice(idx, 1);
+ this.objectNames.splice(idx, 1);
+ }
+ // if string, remove by name
+ else {
+ var id = $.inArray(idx, this.objectNames);
+ if (id != -1) {
+ this.objects.splice(id, 1);
+ this.objectNames.splice(id, 1);
+ }
+ }
+ };
+
+ $.jqplot.CanvasOverlay.prototype.getObject = function(idx) {
+ // check if integer, remove by index
+ if ($.type(idx) == 'number') {
+ return this.objects[idx];
+ }
+ // if string, remove by name
+ else {
+ var id = $.inArray(idx, this.objectNames);
+ if (id != -1) {
+ return this.objects[id];
+ }
+ }
+ };
+
+ // Set get as alias for getObject.
+ $.jqplot.CanvasOverlay.prototype.get = $.jqplot.CanvasOverlay.prototype.getObject;
+
+ $.jqplot.CanvasOverlay.prototype.clear = function(plot) {
+ this.canvas._ctx.clearRect(0,0,this.canvas.getWidth(), this.canvas.getHeight());
+ };
+
+ $.jqplot.CanvasOverlay.prototype.draw = function(plot) {
+ var obj,
+ objs = this.objects,
+ mr = this.markerRenderer,
+ start,
+ stop;
+ if (this.options.show) {
+ this.canvas._ctx.clearRect(0,0,this.canvas.getWidth(), this.canvas.getHeight());
+ for (var k=0; k<objs.length; k++) {
+ obj = objs[k];
+ var opts = $.extend(true, {}, obj.options);
+ if (obj.options.show) {
+ // style and shadow properties should be set before
+ // every draw of marker renderer.
+ mr.shadow = obj.options.shadow;
+ switch (obj.type) {
+ case 'line':
+ // style and shadow properties should be set before
+ // every draw of marker renderer.
+ mr.style = 'line';
+ opts.closePath = false;
+ start = [plot.axes[obj.options.xaxis].series_u2p(obj.options.start[0]), plot.axes[obj.options.yaxis].series_u2p(obj.options.start[1])];
+ stop = [plot.axes[obj.options.xaxis].series_u2p(obj.options.stop[0]), plot.axes[obj.options.yaxis].series_u2p(obj.options.stop[1])];
+ mr.draw(start, stop, this.canvas._ctx, opts);
+ break;
+ case 'horizontalLine':
+
+ // style and shadow properties should be set before
+ // every draw of marker renderer.
+ if (obj.options.y != null) {
+ mr.style = 'line';
+ opts.closePath = false;
+ var xaxis = plot.axes[obj.options.xaxis],
+ xstart,
+ xstop,
+ y = plot.axes[obj.options.yaxis].series_u2p(obj.options.y),
+ xminoff = obj.options.xminOffset || obj.options.xOffset,
+ xmaxoff = obj.options.xmaxOffset || obj.options.xOffset;
+ if (obj.options.xmin != null) {
+ xstart = xaxis.series_u2p(obj.options.xmin);
+ }
+ else if (xminoff != null) {
+ if ($.type(xminoff) == "number") {
+ xstart = xaxis.series_u2p(xaxis.min + xminoff);
+ }
+ else if ($.type(xminoff) == "string") {
+ xstart = xaxis.series_u2p(xaxis.min) + parseFloat(xminoff);
+ }
+ }
+ if (obj.options.xmax != null) {
+ xstop = xaxis.series_u2p(obj.options.xmax);
+ }
+ else if (xmaxoff != null) {
+ if ($.type(xmaxoff) == "number") {
+ xstop = xaxis.series_u2p(xaxis.max - xmaxoff);
+ }
+ else if ($.type(xmaxoff) == "string") {
+ xstop = xaxis.series_u2p(xaxis.max) - parseFloat(xmaxoff);
+ }
+ }
+ if (xstop != null && xstart != null) {
+ mr.draw([xstart, y], [xstop, y], this.canvas._ctx, opts);
+ }
+ }
+ break;
+
+ case 'dashedHorizontalLine':
+
+ var dashPat = obj.options.dashPattern;
+ var dashPatLen = 0;
+ for (var i=0; i<dashPat.length; i++) {
+ dashPatLen += dashPat[i];
+ }
+
+ // style and shadow properties should be set before
+ // every draw of marker renderer.
+ if (obj.options.y != null) {
+ mr.style = 'line';
+ opts.closePath = false;
+ var xaxis = plot.axes[obj.options.xaxis],
+ xstart,
+ xstop,
+ y = plot.axes[obj.options.yaxis].series_u2p(obj.options.y),
+ xminoff = obj.options.xminOffset || obj.options.xOffset,
+ xmaxoff = obj.options.xmaxOffset || obj.options.xOffset;
+ if (obj.options.xmin != null) {
+ xstart = xaxis.series_u2p(obj.options.xmin);
+ }
+ else if (xminoff != null) {
+ if ($.type(xminoff) == "number") {
+ xstart = xaxis.series_u2p(xaxis.min + xminoff);
+ }
+ else if ($.type(xminoff) == "string") {
+ xstart = xaxis.series_u2p(xaxis.min) + parseFloat(xminoff);
+ }
+ }
+ if (obj.options.xmax != null) {
+ xstop = xaxis.series_u2p(obj.options.xmax);
+ }
+ else if (xmaxoff != null) {
+ if ($.type(xmaxoff) == "number") {
+ xstop = xaxis.series_u2p(xaxis.max - xmaxoff);
+ }
+ else if ($.type(xmaxoff) == "string") {
+ xstop = xaxis.series_u2p(xaxis.max) - parseFloat(xmaxoff);
+ }
+ }
+ if (xstop != null && xstart != null) {
+ var numDash = Math.ceil((xstop - xstart)/dashPatLen);
+ var b=xstart, e;
+ for (var i=0; i<numDash; i++) {
+ for (var j=0; j<dashPat.length; j+=2) {
+ e = b+dashPat[j];
+ mr.draw([b, y], [e, y], this.canvas._ctx, opts);
+ b += dashPat[j];
+ if (j < dashPat.length-1) {
+ b += dashPat[j+1];
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case 'verticalLine':
+
+ // style and shadow properties should be set before
+ // every draw of marker renderer.
+ if (obj.options.x != null) {
+ mr.style = 'line';
+ opts.closePath = false;
+ var yaxis = plot.axes[obj.options.yaxis],
+ ystart,
+ ystop,
+ x = plot.axes[obj.options.xaxis].series_u2p(obj.options.x),
+ yminoff = obj.options.yminOffset || obj.options.yOffset,
+ ymaxoff = obj.options.ymaxOffset || obj.options.yOffset;
+ if (obj.options.ymin != null) {
+ ystart = yaxis.series_u2p(obj.options.ymin);
+ }
+ else if (yminoff != null) {
+ if ($.type(yminoff) == "number") {
+ ystart = yaxis.series_u2p(yaxis.min - yminoff);
+ }
+ else if ($.type(yminoff) == "string") {
+ ystart = yaxis.series_u2p(yaxis.min) - parseFloat(yminoff);
+ }
+ }
+ if (obj.options.ymax != null) {
+ ystop = yaxis.series_u2p(obj.options.ymax);
+ }
+ else if (ymaxoff != null) {
+ if ($.type(ymaxoff) == "number") {
+ ystop = yaxis.series_u2p(yaxis.max + ymaxoff);
+ }
+ else if ($.type(ymaxoff) == "string") {
+ ystop = yaxis.series_u2p(yaxis.max) + parseFloat(ymaxoff);
+ }
+ }
+ if (ystop != null && ystart != null) {
+ mr.draw([x, ystart], [x, ystop], this.canvas._ctx, opts);
+ }
+ }
+ break;
+
+ case 'dashedVerticalLine':
+
+ var dashPat = obj.options.dashPattern;
+ var dashPatLen = 0;
+ for (var i=0; i<dashPat.length; i++) {
+ dashPatLen += dashPat[i];
+ }
+
+ // style and shadow properties should be set before
+ // every draw of marker renderer.
+ if (obj.options.x != null) {
+ mr.style = 'line';
+ opts.closePath = false;
+ var yaxis = plot.axes[obj.options.yaxis],
+ ystart,
+ ystop,
+ x = plot.axes[obj.options.xaxis].series_u2p(obj.options.x),
+ yminoff = obj.options.yminOffset || obj.options.yOffset,
+ ymaxoff = obj.options.ymaxOffset || obj.options.yOffset;
+ if (obj.options.ymin != null) {
+ ystart = yaxis.series_u2p(obj.options.ymin);
+ }
+ else if (yminoff != null) {
+ if ($.type(yminoff) == "number") {
+ ystart = yaxis.series_u2p(yaxis.min - yminoff);
+ }
+ else if ($.type(yminoff) == "string") {
+ ystart = yaxis.series_u2p(yaxis.min) - parseFloat(yminoff);
+ }
+ }
+ if (obj.options.ymax != null) {
+ ystop = yaxis.series_u2p(obj.options.ymax);
+ }
+ else if (ymaxoff != null) {
+ if ($.type(ymaxoff) == "number") {
+ ystop = yaxis.series_u2p(yaxis.max + ymaxoff);
+ }
+ else if ($.type(ymaxoff) == "string") {
+ ystop = yaxis.series_u2p(yaxis.max) + parseFloat(ymaxoff);
+ }
+ }
+
+
+ if (ystop != null && ystart != null) {
+ var numDash = Math.ceil((ystart - ystop)/dashPatLen);
+ var firstDashAdjust = ((numDash * dashPatLen) - (ystart - ystop))/2.0;
+ var b=ystart, e, bs, es;
+ for (var i=0; i<numDash; i++) {
+ for (var j=0; j<dashPat.length; j+=2) {
+ e = b - dashPat[j];
+ if (e < ystop) {
+ e = ystop;
+ }
+ if (b < ystop) {
+ b = ystop;
+ }
+ // es = e;
+ // if (i == 0) {
+ // es += firstDashAdjust;
+ // }
+ mr.draw([x, b], [x, e], this.canvas._ctx, opts);
+ b -= dashPat[j];
+ if (j < dashPat.length-1) {
+ b -= dashPat[j+1];
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+ };
+
+ // 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.CanvasOverlay.postPlotDraw = function() {
+ // Memory Leaks patch
+ if (this.plugins.canvasOverlay && this.plugins.canvasOverlay.highlightCanvas) {
+ this.plugins.canvasOverlay.highlightCanvas.resetCanvas();
+ this.plugins.canvasOverlay.highlightCanvas = null;
+ }
+ this.plugins.canvasOverlay.canvas = new $.jqplot.GenericCanvas();
+
+ this.eventCanvas._elem.before(this.plugins.canvasOverlay.canvas.createElement(this._gridPadding, 'jqplot-overlayCanvas-canvas', this._plotDimensions, this));
+ this.plugins.canvasOverlay.canvas.setContext();
+ if (!this.plugins.canvasOverlay.deferDraw) {
+ this.plugins.canvasOverlay.draw(this);
+ }
+ };
+
+ $.jqplot.postInitHooks.push($.jqplot.CanvasOverlay.postPlotInit);
+ $.jqplot.postDrawHooks.push($.jqplot.CanvasOverlay.postPlotDraw);
+
+})(jQuery); \ No newline at end of file
diff --git a/libs/jqplot/plugins/jqplot.canvasTextRenderer.js b/libs/jqplot/plugins/jqplot.canvasTextRenderer.js
new file mode 100644
index 0000000000..95c19b303f
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.canvasTextRenderer.js
@@ -0,0 +1,420 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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);
+ var 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 (var 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
diff --git a/libs/jqplot/plugins/jqplot.categoryAxisRenderer.js b/libs/jqplot/plugins/jqplot.categoryAxisRenderer.js
new file mode 100644
index 0000000000..e9dc176c55
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.categoryAxisRenderer.js
@@ -0,0 +1,636 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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, plot) {
+ 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();
+ // Memory Leaks patch
+ this._elem.emptyForce();
+ }
+
+ 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, plot);
+ 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;
+ var i;
+
+ 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
diff --git a/libs/jqplot/plugins/jqplot.ciParser.js b/libs/jqplot/plugins/jqplot.ciParser.js
new file mode 100644
index 0000000000..ebbaa9be17
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.ciParser.js
@@ -0,0 +1,115 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(function($) {
+ /**
+ * Class: $.jqplot.ciParser
+ * Data Renderer function which converts a custom JSON data object into jqPlot data format.
+ * Set this as a callable on the jqplot dataRenderer plot option:
+ *
+ * > plot = $.jqplot('mychart', [data], { dataRenderer: $.jqplot.ciParser, ... });
+ *
+ * Where data is an object in JSON format or a JSON encoded string conforming to the
+ * City Index API spec.
+ *
+ * Note that calling the renderer function is handled internally by jqPlot. The
+ * user does not have to call the function. The parameters described below will
+ * automatically be passed to the ciParser function.
+ *
+ * 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,
+ temp,
+ 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
diff --git a/libs/jqplot/plugins/jqplot.cursor.js b/libs/jqplot/plugins/jqplot.cursor.js
new file mode 100755
index 0000000000..85ae21863d
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.cursor.js
@@ -0,0 +1,1051 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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: looseZoom
+ // Will expand zoom range to provide more rounded tick values.
+ // Works only with linear axes and date axes.
+ this.looseZoom = 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();
+ axes[ax]._ticks = [];
+ // fake out tick creation algorithm to make sure original auto
+ // computed format string is used if _overrideFormatString is true
+ axes[ax]._autoFormatString = c._zoom.axes[ax].tickFormatString;
+ }
+ this.redraw();
+ }
+ else {
+ var ctx = this.plugins.cursor.zoomCanvas._ctx;
+ ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
+ ctx = null;
+ }
+ 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;
+
+ // Memory Leaks patch
+ if (c.zoomCanvas) {
+ c.zoomCanvas.resetCanvas();
+ c.zoomCanvas = null;
+ }
+
+ if (c.cursorCanvas) {
+ c.cursorCanvas.resetCanvas();
+ c.cursorCanvas = null;
+ }
+
+ if (c._tooltipElem) {
+ c._tooltipElem.emptyForce();
+ c._tooltipElem = null;
+ }
+
+
+ if (c.zoom) {
+ c.zoomCanvas = new $.jqplot.GenericCanvas();
+ this.eventCanvas._elem.before(c.zoomCanvas.createElement(this._gridPadding, 'jqplot-zoom-canvas', this._plotDimensions, this));
+ c.zoomCanvas.setContext();
+ }
+
+ var elem = document.createElement('div');
+ c._tooltipElem = $(elem);
+ elem = null;
+ c._tooltipElem.addClass('jqplot-cursor-tooltip');
+ c._tooltipElem.css({position:'absolute', display:'none'});
+
+
+ if (c.zoomCanvas) {
+ c.zoomCanvas._elem.before(c._tooltipElem);
+ }
+
+ else {
+ this.eventCanvas._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, this));
+ 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;
+ axes[ax].reset();
+ axes[ax]._ticks = [];
+ // fake out tick creation algorithm to make sure original auto
+ // computed format string is used if _overrideFormatString is true
+ axes[ax]._autoFormatString = cax[ax].tickFormatString;
+ }
+ plot.redraw();
+ cursor._zoom.isZoomed = false;
+ }
+ else {
+ var ctx = cursor.zoomCanvas._ctx;
+ ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
+ ctx = null;
+ }
+ 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, dp, span;
+ var ctx = plot.plugins.cursor.zoomCanvas._ctx;
+ // don't zoom if 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;
+ c._zoom.axes[ax].tickFormatString = (axes[ax].tickOptions != null) ? axes[ax].tickOptions.formatString : '';
+ }
+
+
+ if ((c.constrainZoomTo == 'none') || (c.constrainZoomTo == 'x' && ax.charAt(0) == 'x') || (c.constrainZoomTo == 'y' && ax.charAt(0) == 'y')) {
+ dp = datapos[ax];
+ if (dp != null) {
+ var newmin, newmax;
+ if (dp > start[ax]) {
+ newmin = start[ax];
+ newmax = dp;
+ }
+ else {
+ span = start[ax] - dp;
+ newmin = dp;
+ newmax = start[ax];
+ }
+
+ if (this.looseZoom && (axes[ax].renderer.constructor === $.jqplot.LinearAxisRenderer || axes[ax].renderer.constructor === $.jqplot.DateAxisRenderer)) {
+ var ret = $.jqplot.LinearTickGenerator(newmin, newmax);
+ axes[ax].min = ret[0];
+ axes[ax].max = ret[1];
+ axes[ax]._autoFormatString = ret[3];
+ axes[ax].numberTicks = ret[2];
+ axes[ax].tickInterval = ret[4];
+ // for date axes...
+ axes[ax].daTickInterval = [ret[4]/1000, 'seconds'];
+ }
+ else {
+ axes[ax].min = newmin;
+ axes[ax].max = newmax;
+ 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;
+ ctx = null;
+ }
+ 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));
+ }
+ }
+ }
+ ctx = null;
+ }
+
+ function getIntersectingPoints(plot, x, y) {
+ var ret = {indices:[], data:[]};
+ var s, i, d0, d, j, r, p;
+ 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;
+ }
+
+ elem.css('left', x);
+ elem.css('top', y);
+ elem = null;
+ }
+
+ 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;
+ }
+ elem = null;
+ }
+
+ 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);
+ ctx = null;
+ }
+ 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;
+ 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);
+ ctx = null;
+ }
+ }
+
+ 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);
+ ctx = null;
+ }
+ 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);
+ ctx = null;
+ }
+
+ $.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._elem) {
+ this._elem.emptyForce();
+ this._elem = null;
+ }
+ if (this.show) {
+ var series = this._series, s;
+ // make a table. one line label per row.
+ var elem = document.createElement('div');
+ this._elem = $(elem);
+ elem = null;
+ this._elem.addClass('jqplot-legend jqplot-cursor-legend');
+ this._elem.css('position', 'absolute');
+
+ var pad = false;
+ for (var i = 0; i< series.length; i++) {
+ s = series[i];
+ if (s.show && s.showLabel) {
+ 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;
+ }
+ }
+ }
+ }
+ series = s = null;
+ delete series;
+ delete s;
+ }
+
+ 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);
+ }
+ tr = null;
+ td = null;
+ }
+ return this._elem;
+ };
+
+})(jQuery); \ No newline at end of file
diff --git a/libs/jqplot/plugins/jqplot.dateAxisRenderer.js b/libs/jqplot/plugins/jqplot.dateAxisRenderer.js
new file mode 100644
index 0000000000..0973e2b3fe
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.dateAxisRenderer.js
@@ -0,0 +1,439 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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);
+ this.date = new $.jsDate();
+ };
+
+ $.jqplot.DateAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
+ $.jqplot.DateAxisRenderer.prototype.constructor = $.jqplot.DateAxisRenderer;
+
+ $.jqplot.DateTickFormatter = function(format, val) {
+ if (!format) {
+ format = '%Y/%m/%d';
+ }
+ return $.jsDate.strftime(val, 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,
+ stats,
+ sum,
+ s,
+ d,
+ pd,
+ sd,
+ intv;
+
+ // 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++) {
+ stats = {intervals:[], frequencies:{}, sortedIntervals:[], min:null, max:null, mean:null};
+ sum = 0;
+ s = this._series[i];
+ d = s.data;
+ pd = s._plotData;
+ sd = s._stackData;
+ intv = 0;
+
+ for (var j=0; j<d.length; j++) {
+ if (this.name == 'xaxis' || this.name == 'x2axis') {
+ d[j][0] = new $.jsDate(d[j][0]).getTime();
+ pd[j][0] = new $.jsDate(d[j][0]).getTime();
+ sd[j][0] = new $.jsDate(d[j][0]).getTime();
+ 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];
+ }
+ if (j>0) {
+ intv = Math.abs(d[j][0] - d[j-1][0]);
+ stats.intervals.push(intv);
+ if (stats.frequencies.hasOwnProperty(intv)) {
+ stats.frequencies[intv] += 1;
+ }
+ else {
+ stats.frequencies[intv] = 1;
+ }
+ }
+ sum += intv;
+
+ }
+ else {
+ d[j][1] = new $.jsDate(d[j][1]).getTime();
+ pd[j][1] = new $.jsDate(d[j][1]).getTime();
+ sd[j][1] = new $.jsDate(d[j][1]).getTime();
+ 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];
+ }
+ if (j>0) {
+ intv = Math.abs(d[j][1] - d[j-1][1]);
+ stats.intervals.push(intv);
+ if (stats.frequencies.hasOwnProperty(intv)) {
+ stats.frequencies[intv] += 1;
+ }
+ else {
+ stats.frequencies[intv] = 1;
+ }
+ }
+ }
+ sum += intv;
+ }
+
+ var tempf = 0,
+ tempn=0;
+ for (var n in stats.frequencies) {
+ stats.sortedIntervals.push({interval:n, frequency:stats.frequencies[n]});
+ }
+ stats.sortedIntervals.sort(function(a, b){
+ return b.frequency - a.frequency;
+ });
+
+ stats.min = $.jqplot.arrayMin(stats.intervals);
+ stats.max = $.jqplot.arrayMax(stats.intervals);
+ stats.mean = sum/d.length;
+ this._intervalStats.push(stats);
+ stats = sum = s = d = pd = sd = null;
+ }
+ db = null;
+
+ };
+
+ // 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 iv = this._intervalStats;
+ 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.
+
+ min = ((this.min != null) ? new $.jsDate(this.min).getTime() : db.min);
+ max = ((this.max != null) ? new $.jsDate(this.max).getTime() : db.max);
+
+ var range = max - min;
+
+ 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 = new $.jsDate(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 = new $.jsDate(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!
+ // Doing complete autoscaling, no user options specified
+ ////////
+
+ else if (this.tickInterval == null && this.min == null && this.max == null && this.numberTicks == null) {
+ var ret = $.jqplot.LinearTickGenerator(min, max);
+ // calculate a padded max and min, points should be less than these
+ // so that they aren't too close to the edges of the plot.
+ // User can adjust how much padding is allowed with pad, padMin and PadMax options.
+ var tumin = min + range*(this.padMin - 1);
+ var tumax = max - range*(this.padMax - 1);
+
+ if (min <=tumin || max >= tumax) {
+ tumin = min - range*(this.padMin - 1);
+ tumax = max + range*(this.padMax - 1);
+ ret = $.jqplot.LinearTickGenerator(tumin, tumax);
+ }
+
+ this.min = ret[0];
+ this.max = ret[1];
+ this.numberTicks = ret[2];
+ this.tickInterval = ret[4];
+ this.daTickInterval = [this.tickInterval/1000, 'seconds'];
+
+ for (var i=0; i<this.numberTicks; i++){
+ var min = new $.jsDate(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);
+ }
+ }
+
+ ////////
+ // Some option(s) specified, work around that.
+ ////////
+
+ 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]];
+ }
+ }
+ }
+
+ // 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;
+ }
+
+ range = max - min;
+
+ var optNumTicks = 2 + parseInt(Math.max(0, dim-100)/100, 10);
+
+
+ // Here try to set ticks based on data spacing.
+ // if (this.min == null && this.max == null && this.numberTicks == null && this.tickInterval == null) {
+ // //
+ // }
+
+
+ var rmin, rmax;
+
+ rmin = (this.min != null) ? new $.jsDate(this.min).getTime() : min - range/2*(this.padMin - 1);
+ rmax = (this.max != null) ? new $.jsDate(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 = new $.jsDate(this.max).diff(this.min, this.daTickInterval[1], true);
+ this.numberTicks = Math.ceil(nc/this.daTickInterval[0]) +1;
+ // this.max = new $.jsDate(this.min).add(this.numberTicks-1, this.daTickInterval[1]).getTime();
+ this.max = new $.jsDate(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 = new $.jsDate(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);
+
diff --git a/libs/jqplot/plugins/jqplot.donutRenderer.js b/libs/jqplot/plugins/jqplot.donutRenderer.js
new file mode 100755
index 0000000000..5e156ad6a3
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.donutRenderer.js
@@ -0,0 +1,910 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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 the 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.4;
+ // 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;
+ this._type = 'donut';
+
+ // 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, 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._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() {
+ // Memory Leaks patch
+ if (this.plugins.donutRenderer && this.plugins.donutRenderer.highlightCanvas) {
+ this.plugins.donutRenderer.highlightCanvas.resetCanvas();
+ this.plugins.donutRenderer.highlightCanvas = null;
+ }
+
+ 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, this));
+ }
+ // 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, this));
+ }
+ 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
diff --git a/libs/jqplot/plugins/jqplot.dragable.js b/libs/jqplot/plugins/jqplot.dragable.js
new file mode 100755
index 0000000000..c092be7f65
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.dragable.js
@@ -0,0 +1,224 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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() {
+ // Memory Leaks patch
+ if (this.plugins.dragable && this.plugins.dragable.highlightCanvas) {
+ this.plugins.dragable.highlightCanvas.resetCanvas();
+ this.plugins.dragable.highlightCanvas = null;
+ }
+
+ 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, this));
+ 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('jqplotDragStart', [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('jqplotDragStop', [gridpos, datapos]);
+ }
+ }
+})(jQuery); \ No newline at end of file
diff --git a/libs/jqplot/plugins/jqplot.enhancedLegendRenderer.js b/libs/jqplot/plugins/jqplot.enhancedLegendRenderer.js
new file mode 100644
index 0000000000..d93a17d4f1
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.enhancedLegendRenderer.js
@@ -0,0 +1,199 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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 s;
+ 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.
+ var 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
diff --git a/libs/jqplot/plugins/jqplot.funnelRenderer.js b/libs/jqplot/plugins/jqplot.funnelRenderer.js
new file mode 100755
index 0000000000..c8899d1c46
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.funnelRenderer.js
@@ -0,0 +1,938 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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._type = 'funnel';
+
+ 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 (var 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() {
+ // Memory Leaks patch
+ if (this.plugins.funnelRenderer && this.plugins.funnelRenderer.highlightCanvas) {
+ this.plugins.funnelRenderer.highlightCanvas.resetCanvas();
+ this.plugins.funnelRenderer.highlightCanvas = null;
+ }
+
+ 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, this));
+ }
+ // 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, this));
+ }
+ 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
diff --git a/libs/jqplot/plugins/jqplot.highlighter.js b/libs/jqplot/plugins/jqplot.highlighter.js
new file mode 100755
index 0000000000..275d29a360
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.highlighter.js
@@ -0,0 +1,421 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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; tooltipContentEditor
+ // Function used to edit/augment/replace the formatted tooltip contents.
+ // Called as str = tooltipContentEditor(str, seriesIndex, pointIndex)
+ // where str is the generated tooltip html and seriesIndex and pointIndex identify
+ // the data point being highlighted. Should return the html for the tooltip contents.
+ this.tooltipContentEditor = null;
+ // 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);
+ };
+
+ 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'];
+
+ // 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() {
+ // Memory Leaks patch
+ if (this.plugins.highlighter && this.plugins.highlighter.highlightCanvas) {
+ this.plugins.highlighter.highlightCanvas.resetCanvas();
+ this.plugins.highlighter.highlightCanvas = null;
+ }
+
+ if (this.plugins.highlighter && this.plugins.highlighter._tooltipElem) {
+ this.plugins.highlighter._tooltipElem.emptyForce();
+ this.plugins.highlighter._tooltipElem = null;
+ }
+
+ this.plugins.highlighter.highlightCanvas = new $.jqplot.GenericCanvas();
+
+ this.eventCanvas._elem.before(this.plugins.highlighter.highlightCanvas.createElement(this._gridPadding, 'jqplot-highlight-canvas', this._plotDimensions, this));
+ this.plugins.highlighter.highlightCanvas.setContext();
+
+ var elem = document.createElement('div');
+ this.plugins.highlighter._tooltipElem = $(elem);
+ elem = null;
+ this.plugins.highlighter._tooltipElem.addClass('jqplot-highlighter-tooltip');
+ this.plugins.highlighter._tooltipElem.css({position:'absolute', display:'none'});
+
+ this.eventCanvas._elem.before(this.plugins.highlighter._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 = ystrs.join(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]);
+ }
+ }
+ if ($.isFunction(hl.tooltipContentEditor)) {
+ // args str, seriesIndex, pointIndex are essential so the hook can look up
+ // extra data for the point.
+ str = hl.tooltipContentEditor(str, neighbor.seriesIndex, neighbor.pointIndex, plot);
+ }
+ 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;
+ }
+
+ var loc = locations;
+ if (series.fillToZero && series.fill && neighbor.data[1] < 0) {
+ loc = oppositeLocations;
+ }
+
+ switch (loc[locationIndicies[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();
+ }
+ elem = null;
+
+ }
+
+ 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;
+ ctx = null;
+
+ }
+ else 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
diff --git a/libs/jqplot/plugins/jqplot.json2.js b/libs/jqplot/plugins/jqplot.json2.js
new file mode 100644
index 0000000000..46fb942baa
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.json2.js
@@ -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);
diff --git a/libs/jqplot/plugins/jqplot.logAxisRenderer.js b/libs/jqplot/plugins/jqplot.logAxisRenderer.js
new file mode 100644
index 0000000000..60bdb8a7f5
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.logAxisRenderer.js
@@ -0,0 +1,446 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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] != 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];
+ }
+ }
+ }
+ }
+ };
+
+ $.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)); };
+ var max = trans(this.max);
+ var 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 (var 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 (var 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
diff --git a/libs/jqplot/plugins/jqplot.mekkoAxisRenderer.js b/libs/jqplot/plugins/jqplot.mekkoAxisRenderer.js
new file mode 100644
index 0000000000..8cb65d82ab
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.mekkoAxisRenderer.js
@@ -0,0 +1,610 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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;
+
+ var elem = document.createElement('div');
+ this._elem = $(elem);
+ this._elem.addClass('jqplot-axis jqplot-'+this.name);
+ this._elem.css('position', 'absolute');
+ elem = null;
+
+ 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) {
+ this._elem.append(this._label.draw(ctx));
+ }
+
+ 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)) {
+ this._elem.append(tick.draw(ctx));
+ }
+ }
+ }
+
+ // 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);
+ elem = null;
+ }
+ }
+
+ }
+ 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 (var 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 (var 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 (var 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);
diff --git a/libs/jqplot/plugins/jqplot.mekkoRenderer.js b/libs/jqplot/plugins/jqplot.mekkoRenderer.js
new file mode 100644
index 0000000000..b93bc60aad
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.mekkoRenderer.js
@@ -0,0 +1,436 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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);
+ this._type = 'mekko';
+ };
+
+ // 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++;
+ }
+ }
+
+ tr = null;
+ td1 = null;
+ td2 = null;
+ }
+ }
+ 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
diff --git a/libs/jqplot/plugins/jqplot.meterGaugeRenderer.js b/libs/jqplot/plugins/jqplot.meterGaugeRenderer.js
new file mode 100644
index 0000000000..7a8e5f6494
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.meterGaugeRenderer.js
@@ -0,0 +1,1142 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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;
+ this._type = 'meterGauge';
+
+ $.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 (var 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++) {
+ var temp = this.tickInterval/nums[i];
+ if (temp == parseInt(temp, 10)) {
+ 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, eh, 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
diff --git a/libs/jqplot/plugins/jqplot.ohlcRenderer.js b/libs/jqplot/plugins/jqplot.ohlcRenderer.js
new file mode 100644
index 0000000000..a8616a5755
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.ohlcRenderer.js
@@ -0,0 +1,372 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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;
+ // prop: lineWidth
+ // Width of the hi-low line and open/close ticks.
+ // Must be set in the rendererOptions for the series.
+ this.lineWidth = 1.5;
+ 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) {
+ options = options || {};
+ // lineWidth has to be set on the series, changes in renderer
+ // constructor have no effect. set the default here
+ // if no renderer option for lineWidth is specified.
+ this.lineWidth = options.lineWidth || 1.5;
+ $.jqplot.LineRenderer.prototype.init.call(this, options);
+ this._type = 'ohlc';
+ // 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;
+ }
+ }
+
+ var dwidth = this.gridData[xmaxidx-1][0] - this.gridData[xminidx][0];
+ var nvisiblePoints = xmaxidx - xminidx;
+ try {
+ var dinterval = Math.abs(this._xaxis.series_u2p(parseInt(this._xaxis._intervalStats[0].sortedIntervals[0].interval)) - this._xaxis.series_u2p(0));
+ }
+
+ catch (e) {
+ var dinterval = dwidth / nvisiblePoints;
+ }
+
+ if (r.candleStick) {
+ if (typeof(r.bodyWidth) == 'number') {
+ r._bodyWidth = r.bodyWidth;
+ }
+ else {
+ r._bodyWidth = Math.min(20, dinterval/1.75);
+ }
+ }
+ else {
+ if (typeof(r.tickLength) == 'number') {
+ r._tickLength = r.tickLength;
+ }
+ else {
+ r._tickLength = Math.min(10, dinterval/3.5);
+ }
+ }
+
+ 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
diff --git a/libs/jqplot/plugins/jqplot.pieRenderer.js b/libs/jqplot/plugins/jqplot.pieRenderer.js
new file mode 100755
index 0000000000..f0f1d0b351
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.pieRenderer.js
@@ -0,0 +1,803 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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;
+ this._type = 'pie';
+
+ // 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);
+ 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.0;
+ var fill = this.fill;
+ var lineWidth = this.lineWidth;
+ var sm = this.sliceMargin;
+ if (this.fill == false) {
+ sm += this.lineWidth;
+ }
+ ctx.save();
+ ctx.translate(this._center[0], this._center[1]);
+ var rprime = 0;
+ if (Math.abs(ang2-ang1) > 0) {
+ rprime = parseFloat(sm) / 2.0 / Math.sin((ang2 - ang1)/2.0);
+ }
+ var transx = rprime * Math.cos((ang1 + ang2) / 2.0);
+ var transy = rprime * Math.sin((ang1 + ang2) / 2.0);
+ if ((ang2 - ang1) <= Math.PI) {
+ r -= rprime;
+ }
+ else {
+ r += rprime;
+ }
+ ctx.translate(transx, transy);
+
+ 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(r);
+ }
+ }
+
+ else {
+ doDraw(r);
+ }
+ }
+
+ function doDraw (rad) {
+ // 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, rad, 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];
+
+ // Fixes issue #272. Thanks hugwijst!
+ // reset slice angles array.
+ this._sliceAngles = [];
+
+ 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 (var 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() {
+ // Memory Leaks patch
+ if (this.plugins.pieRenderer && this.plugins.pieRenderer.highlightCanvas) {
+ this.plugins.pieRenderer.highlightCanvas.resetCanvas();
+ this.plugins.pieRenderer.highlightCanvas = null;
+ }
+
+ 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, this));
+ }
+ // 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, this));
+ }
+
+ 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
diff --git a/libs/jqplot/plugins/jqplot.pointLabels.js b/libs/jqplot/plugins/jqplot.pointLabels.js
new file mode 100644
index 0000000000..3fc82b281b
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.pointLabels.js
@@ -0,0 +1,352 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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++) {
+ // Memory Leaks patch
+ // p._elems[i].remove();
+ p._elems[i].emptyForce();
+ }
+ p._elems.splice(0, p._elems.length);
+
+ 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;
+ var elem, helem;
+
+ for (var i=0, l=p._labels.length; i < l; 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);
+ }
+
+ helem = document.createElement('div');
+ p._elems[i] = $(helem);
+
+ elem = p._elems[i];
+
+
+ elem.addClass('jqplot-point-label jqplot-series-'+this.index+' jqplot-point-'+i);
+ elem.css('position', 'absolute');
+ elem.insertAfter(sctx.canvas);
+
+ if (p.escapeHTML) {
+ elem.text(label);
+ }
+ else {
+ elem.html(label);
+ }
+ var location = p.location;
+ if ((this.fillToZero && pd[i][1] < 0) || (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.remove();
+ }
+ elem = null;
+ helem = null;
+ }
+ }
+ };
+
+ $.jqplot.postSeriesInitHooks.push($.jqplot.PointLabels.init);
+ $.jqplot.postDrawSeriesHooks.push($.jqplot.PointLabels.draw);
+})(jQuery); \ No newline at end of file
diff --git a/libs/jqplot/plugins/jqplot.trendline.js b/libs/jqplot/plugins/jqplot.trendline.js
new file mode 100644
index 0000000000..55b3e8b61a
--- /dev/null
+++ b/libs/jqplot/plugins/jqplot.trendline.js
@@ -0,0 +1,220 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: @VERSION
+ *
+ * Copyright (c) 2009-2011 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, 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 .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * 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."
+ *
+ */
+(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
diff --git a/libs/jqplot/usage.txt b/libs/jqplot/usage.txt
new file mode 100644
index 0000000000..de42d22f87
--- /dev/null
+++ b/libs/jqplot/usage.txt
@@ -0,0 +1,126 @@
+Title: jqPlot Usage
+
+Usage Documentation:
+
+Introduction:
+
+jqPlot is a jQuery plugin to generate pure client-side javascript charts in your web pages.
+
+The jqPlot home page is at <http://www.jqplot.com/>.
+
+The project page and downloads are at <http://www.bitbucket.org/cleonello/jqplot/>.
+
+Below are a few examples to demonstrate jqPlot usage. These plots are shown as static images.
+Many more examples of dynamically rendered plots can be seen on the test and examples pages here: <../../tests/>.
+
+Include the Files:
+
+jqPlot requires jQuery (1.4+ required for certain features). jQuery 1.4.4 is included in the distribution.
+To use jqPlot include jquery, the jqPlot jQuery plugin, jqPlot css file and optionally the excanvas
+script for IE support in your web page. Note, excanvas is required only for IE versions below 9. IE 9 includes
+native support for the canvas element and does not require excanvas:
+
+> <!--[if lt IE 9]><script language="javascript" type="text/javascript" src="excanvas.js"></script><![endif]-->
+> <script language="javascript" type="text/javascript" src="jquery-1.4.4.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" />
+
+Add a plot container:
+
+Add a container (target) to your web page where you want your plot to show up.
+Be sure to give your target a width and a height:
+
+> <div id="chartdiv" style="height:400px;width:300px; "></div>
+
+Create a plot:
+
+Then, create the actual plot by calling the
+$.jqplot plugin with the id of your target and some data:
+
+> $.jqplot('chartdiv', [[[1, 2],[3,5.12],[5,13.1],[7,33.6],[9,85.9],[11,219.9]]]);
+
+Which will produce a
+chart like:
+
+(see images/basicline.png)
+
+Plot Options:
+
+You can customize the plot by passing options to the $.jqplot function. Options are described in
+<jqPlot Options> in the jqPlotOptions.txt file. An example of options usage:
+
+> $.jqplot('chartdiv', [[[1, 2],[3,5.12],[5,13.1],[7,33.6],[9,85.9],[11,219.9]]],
+> { title:'Exponential Line',
+> axes:{yaxis:{min:-10, max:240}},
+> series:[{color:'#5FAB78'}]
+> });
+
+Which will produce
+a plot like:
+
+(see images/basicoptions.png)
+
+Using Plugins:
+
+You can use jqPlot plugins (that is, plugins to the jqPlot plugin) by including them in your html
+after you include the jqPlot plugin. Here is how to include the log axis plugin:
+
+> <link rel="stylesheet" type="text/css" href="jquery.jqplot.css" />
+> <!--[if IE]><script language="javascript" type="text/javascript" src="excanvas.js"></script><![endif]-->
+> <script language="javascript" type="text/javascript" src="jquery-1.4.4.min.js"></script>
+> <script language="javascript" type="text/javascript" src="jquery.jqplot.min.js"></script>
+> <script language="javascript" type="text/javascript" src="jqplot.logAxisRenderer.js"></script>
+
+Important note: For jqplot builds r529 and above (0.9.7r529 and higher), you must explicitly
+enable plugins via either the { show: true } plugin option to the plot or by using
+the $.jqplot.config.enablePlugins = true; config options set on the page before plot creation.
+Only plugins that can be immediately active upon loading are affected. This includes
+non-renderer plugins like cursor, dragable, highlighter, and trendline.
+
+Here is a the same $.jqplot call
+but with a log y axis:
+
+> $.jqplot('chartdiv', [[[1, 2],[3,5.12],[5,13.1],[7,33.6],[9,85.9],[11,219.9]]],
+> { title:'Exponential Line',
+> axes:{yaxis:{renderer: $.jqplot.LogAxisRenderer}},
+> series:[{color:'#5FAB78'}]
+> });
+
+Which produces
+a plot like:
+
+(see images/basiclogaxis.png)
+
+You can further customize with options specific
+to the log axis plugin:
+
+> $.jqplot('chartdiv', [[[1, 2],[3,5.12],[5,13.1],[7,33.6],[9,85.9],[11,219.9]]],
+> { title:'Exponential Line',
+> axes:{yaxis:{renderer: $.jqplot.LogAxisRenderer, tickDistribution:'power'}},
+> series:[{color:'#5FAB78'}]
+> });
+
+Which makes a
+plot like:
+
+(see images/basiclogoptions.png)
+
+For a full list of options, see <jqPlot Options> in the jqPlotOptions.txt file.
+
+You can add as many plugins as you wish. Order is generally not important.
+Some plugins, like the highlighter plugin which highlights data points near the
+mouse, don't need any extra options or setup to function. Highlighter does have
+additional options which the user can set.
+
+Other plugins, the barRenderer for example, provide functionality the must be specified
+in the chart options object. To render a series as a bar graph with the bar renderer,
+you would first include the plugin after jqPlot:
+
+> <script language="javascript" type="text/javascript" src="plugins/jqplot.barRenderer.min.js"></script>
+
+Then you would create
+a chart like:
+
+> $.jqplot('chartdiv', [[34.53, 56.32, 25.1, 18.6]], {series:[{renderer:$.jqplot.BarRenderer}]});
+
+Here the default LineRenderer is replaced by a BarRenderer to generate a bar graph for the first (an only) series. \ No newline at end of file