diff options
author | mattpiwik <matthieu.aubry@gmail.com> | 2011-05-20 05:48:42 +0400 |
---|---|---|
committer | mattpiwik <matthieu.aubry@gmail.com> | 2011-05-20 05:48:42 +0400 |
commit | 01b6104ad5522b56cf72a7a86ecf0f2ae47fcd59 (patch) | |
tree | faae86d53e152227b25fed4c37952bbc4918e976 /libs/jqplot | |
parent | cf8fb7a3b537f0affa37417cdb4355a1150f9763 (diff) |
Unix end of lines is the way to go, build was passing but I didn't realize...
git-svn-id: http://dev.piwik.org/svn/trunk@4730 59fd770c-687e-43c8-a1e3-f5a4ff64c105
Diffstat (limited to 'libs/jqplot')
-rwxr-xr-x | libs/jqplot/plugins/jqplot.cursor.js | 2038 | ||||
-rw-r--r-- | libs/jqplot/plugins/jqplot.highlighter.js | 824 | ||||
-rwxr-xr-x | libs/jqplot/plugins/jqplot.pieRenderer.js | 1606 |
3 files changed, 2234 insertions, 2234 deletions
diff --git a/libs/jqplot/plugins/jqplot.cursor.js b/libs/jqplot/plugins/jqplot.cursor.js index a8deb9fb83..6251531fbd 100755 --- a/libs/jqplot/plugins/jqplot.cursor.js +++ b/libs/jqplot/plugins/jqplot.cursor.js @@ -1,1020 +1,1020 @@ -/**
- * 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();
- }
- 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));
- c.zoomCanvas.setContext();
- // }
- c._tooltipElem = $('<div class="jqplot-cursor-tooltip" style="position:absolute;display:none"></div>');
- c.zoomCanvas._elem.before(c._tooltipElem);
- if (c.showVerticalLine || c.showHorizontalLine) {
- c.cursorCanvas = new $.jqplot.GenericCanvas();
- this.eventCanvas._elem.before(c.cursorCanvas.createElement(this._gridPadding, 'jqplot-cursor-canvas', this._plotDimensions));
- c.cursorCanvas.setContext();
- }
-
- // if we are showing the positions in unit coordinates, and no axes groups
- // were specified, create a default set.
- if (c.showTooltipUnitPosition){
- if (c.tooltipAxisGroups.length === 0) {
- var series = this.series;
- var s;
- var temp = [];
- for (var i=0; i<series.length; i++) {
- s = series[i];
- var ax = s.xaxis+','+s.yaxis;
- if ($.inArray(ax, temp) == -1) {
- temp.push(ax);
- }
- }
- for (var i=0; i<temp.length; i++) {
- c.tooltipAxisGroups.push(temp[i].split(','));
- }
- }
- }
- };
-
- // Group: methods
- //
- // method: $.jqplot.Cursor.zoomProxy
- // links targetPlot to controllerPlot so that plot zooming of
- // targetPlot will be controlled by zooming on the controllerPlot.
- // controllerPlot will not actually zoom, but acts as an
- // overview plot. Note, the zoom options must be set to true for
- // zoomProxy to work.
- $.jqplot.Cursor.zoomProxy = function(targetPlot, controllerPlot) {
- var tc = targetPlot.plugins.cursor;
- var cc = controllerPlot.plugins.cursor;
- tc.zoomTarget = true;
- tc.zoom = true;
- tc.style = 'auto';
- tc.dblClickReset = false;
- cc.zoom = true;
- cc.zoomProxy = true;
-
- controllerPlot.target.bind('jqplotZoom', plotZoom);
- controllerPlot.target.bind('jqplotResetZoom', plotReset);
-
- function plotZoom(ev, gridpos, datapos, plot, cursor) {
- tc.doZoom(gridpos, datapos, targetPlot, cursor);
- }
-
- function plotReset(ev, plot, cursor) {
- targetPlot.resetZoom();
- }
- };
-
- $.jqplot.Cursor.prototype.resetZoom = function(plot, cursor) {
- var axes = plot.axes;
- var cax = cursor._zoom.axes;
- if (!plot.plugins.cursor.zoomProxy && cursor._zoom.isZoomed) {
- for (var ax in axes) {
- axes[ax]._ticks = [];
- axes[ax].min = cax[ax].min;
- axes[ax].max = cax[ax].max;
- axes[ax].numberTicks = cax[ax].numberTicks;
- axes[ax].tickInterval = cax[ax].tickInterval;
- // for date axes
- axes[ax].daTickInterval = cax[ax].daTickInterval;
- }
- plot.redraw();
- cursor._zoom.isZoomed = false;
- }
- else {
- var ctx = cursor.zoomCanvas._ctx;
- ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
- 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;
- }
-
-
- 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].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;
- var ctx = c.zoomCanvas._ctx;
- if (c.show) {
- if (c.showTooltip) {
- updateTooltip(gridpos, datapos, plot);
- if (c.followMouse) {
- moveTooltip(gridpos, plot);
- }
- }
- if (c.showVerticalLine || c.showHorizontalLine) {
- moveLine(gridpos, plot);
- }
- }
- ctx = null;
- }
-
- 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.show) {
- var series = this._series, s;
- // make a table. one line label per row.
- this._elem = $('<table class="jqplot-legend jqplot-cursor-legend" style="position:absolute"></table>');
-
- var pad = false;
- for (var i = 0; i< series.length; i++) {
- s = series[i];
- if (s.show && 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;
- };
-
+/** + * 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(); + } + 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)); + c.zoomCanvas.setContext(); + // } + c._tooltipElem = $('<div class="jqplot-cursor-tooltip" style="position:absolute;display:none"></div>'); + c.zoomCanvas._elem.before(c._tooltipElem); + if (c.showVerticalLine || c.showHorizontalLine) { + c.cursorCanvas = new $.jqplot.GenericCanvas(); + this.eventCanvas._elem.before(c.cursorCanvas.createElement(this._gridPadding, 'jqplot-cursor-canvas', this._plotDimensions)); + c.cursorCanvas.setContext(); + } + + // if we are showing the positions in unit coordinates, and no axes groups + // were specified, create a default set. + if (c.showTooltipUnitPosition){ + if (c.tooltipAxisGroups.length === 0) { + var series = this.series; + var s; + var temp = []; + for (var i=0; i<series.length; i++) { + s = series[i]; + var ax = s.xaxis+','+s.yaxis; + if ($.inArray(ax, temp) == -1) { + temp.push(ax); + } + } + for (var i=0; i<temp.length; i++) { + c.tooltipAxisGroups.push(temp[i].split(',')); + } + } + } + }; + + // Group: methods + // + // method: $.jqplot.Cursor.zoomProxy + // links targetPlot to controllerPlot so that plot zooming of + // targetPlot will be controlled by zooming on the controllerPlot. + // controllerPlot will not actually zoom, but acts as an + // overview plot. Note, the zoom options must be set to true for + // zoomProxy to work. + $.jqplot.Cursor.zoomProxy = function(targetPlot, controllerPlot) { + var tc = targetPlot.plugins.cursor; + var cc = controllerPlot.plugins.cursor; + tc.zoomTarget = true; + tc.zoom = true; + tc.style = 'auto'; + tc.dblClickReset = false; + cc.zoom = true; + cc.zoomProxy = true; + + controllerPlot.target.bind('jqplotZoom', plotZoom); + controllerPlot.target.bind('jqplotResetZoom', plotReset); + + function plotZoom(ev, gridpos, datapos, plot, cursor) { + tc.doZoom(gridpos, datapos, targetPlot, cursor); + } + + function plotReset(ev, plot, cursor) { + targetPlot.resetZoom(); + } + }; + + $.jqplot.Cursor.prototype.resetZoom = function(plot, cursor) { + var axes = plot.axes; + var cax = cursor._zoom.axes; + if (!plot.plugins.cursor.zoomProxy && cursor._zoom.isZoomed) { + for (var ax in axes) { + axes[ax]._ticks = []; + axes[ax].min = cax[ax].min; + axes[ax].max = cax[ax].max; + axes[ax].numberTicks = cax[ax].numberTicks; + axes[ax].tickInterval = cax[ax].tickInterval; + // for date axes + axes[ax].daTickInterval = cax[ax].daTickInterval; + } + plot.redraw(); + cursor._zoom.isZoomed = false; + } + else { + var ctx = cursor.zoomCanvas._ctx; + ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); + 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; + } + + + 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].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; + var ctx = c.zoomCanvas._ctx; + if (c.show) { + if (c.showTooltip) { + updateTooltip(gridpos, datapos, plot); + if (c.followMouse) { + moveTooltip(gridpos, plot); + } + } + if (c.showVerticalLine || c.showHorizontalLine) { + moveLine(gridpos, plot); + } + } + ctx = null; + } + + 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.show) { + var series = this._series, s; + // make a table. one line label per row. + this._elem = $('<table class="jqplot-legend jqplot-cursor-legend" style="position:absolute"></table>'); + + var pad = false; + for (var i = 0; i< series.length; i++) { + s = series[i]; + if (s.show && 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.highlighter.js b/libs/jqplot/plugins/jqplot.highlighter.js index 26e3a2f6bc..59a5b8faee 100644 --- a/libs/jqplot/plugins/jqplot.highlighter.js +++ b/libs/jqplot/plugins/jqplot.highlighter.js @@ -1,413 +1,413 @@ -/**
- * 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;
- }
-
- this.plugins.highlighter.highlightCanvas = new $.jqplot.GenericCanvas();
-
- this.eventCanvas._elem.before(this.plugins.highlighter.highlightCanvas.createElement(this._gridPadding, 'jqplot-highlight-canvas', this._plotDimensions));
- this.plugins.highlighter.highlightCanvas.setContext();
-
- var p = this.plugins.highlighter;
- p._tooltipElem = $('<div class="jqplot-highlighter-tooltip" style="position:absolute;display:none"></div>');
- this.eventCanvas._elem.before(p._tooltipElem);
- };
-
- $.jqplot.preInitHooks.push($.jqplot.Highlighter.init);
- $.jqplot.preParseSeriesOptionsHooks.push($.jqplot.Highlighter.parseOptions);
- $.jqplot.postDrawHooks.push($.jqplot.Highlighter.postPlotDraw);
-
- function draw(plot, neighbor) {
- var hl = plot.plugins.highlighter;
- var s = plot.series[neighbor.seriesIndex];
- var smr = s.markerRenderer;
- var mr = hl.markerRenderer;
- mr.style = smr.style;
- mr.lineWidth = smr.lineWidth + hl.lineWidthAdjust;
- mr.size = smr.size + hl.sizeAdjust;
- var rgba = $.jqplot.getColorComponents(smr.color);
- var newrgb = [rgba[0], rgba[1], rgba[2]];
- var alpha = (rgba[3] >= 0.6) ? rgba[3]*0.6 : rgba[3]*(2-rgba[3]);
- mr.color = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+alpha+')';
- mr.init();
- mr.draw(s.gridData[neighbor.pointIndex][0], s.gridData[neighbor.pointIndex][1], hl.highlightCanvas._ctx);
- }
-
- function showTooltip(plot, series, neighbor) {
- // neighbor looks like: {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}
- // gridData should be x,y pixel coords on the grid.
- // add the plot._gridPadding to that to get x,y in the target.
- var hl = plot.plugins.highlighter;
- var elem = hl._tooltipElem;
- if (hl.useAxesFormatters) {
- var xf = series._xaxis._ticks[0].formatter;
- var yf = series._yaxis._ticks[0].formatter;
- var xfstr = series._xaxis._ticks[0].formatString;
- var yfstr = series._yaxis._ticks[0].formatString;
- var str;
- var xstr = xf(xfstr, neighbor.data[0]);
- var ystrs = [];
- for (var i=1; i<hl.yvalues+1; i++) {
- ystrs.push(yf(yfstr, neighbor.data[i]));
- }
- if (hl.formatString) {
- switch (hl.tooltipAxes) {
- case 'both':
- case 'xy':
- ystrs.unshift(xstr);
- ystrs.unshift(hl.formatString);
- str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
- break;
- case 'yx':
- ystrs.push(xstr);
- ystrs.unshift(hl.formatString);
- str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
- break;
- case 'x':
- str = $.jqplot.sprintf.apply($.jqplot.sprintf, [hl.formatString, xstr]);
- break;
- case 'y':
- ystrs.unshift(hl.formatString);
- str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
- break;
- default: // same as xy
- ystrs.unshift(xstr);
- ystrs.unshift(hl.formatString);
- str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
- break;
- }
- }
- else {
- switch (hl.tooltipAxes) {
- case 'both':
- case 'xy':
- str = xstr;
- for (var i=0; i<ystrs.length; i++) {
- str += hl.tooltipSeparator + ystrs[i];
- }
- break;
- case 'yx':
- str = '';
- for (var i=0; i<ystrs.length; i++) {
- str += ystrs[i] + hl.tooltipSeparator;
- }
- str += xstr;
- break;
- case 'x':
- str = xstr;
- break;
- case 'y':
- str = 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);
- }
- }
- }
- }
+/** + * 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; + } + + this.plugins.highlighter.highlightCanvas = new $.jqplot.GenericCanvas(); + + this.eventCanvas._elem.before(this.plugins.highlighter.highlightCanvas.createElement(this._gridPadding, 'jqplot-highlight-canvas', this._plotDimensions)); + this.plugins.highlighter.highlightCanvas.setContext(); + + var p = this.plugins.highlighter; + p._tooltipElem = $('<div class="jqplot-highlighter-tooltip" style="position:absolute;display:none"></div>'); + this.eventCanvas._elem.before(p._tooltipElem); + }; + + $.jqplot.preInitHooks.push($.jqplot.Highlighter.init); + $.jqplot.preParseSeriesOptionsHooks.push($.jqplot.Highlighter.parseOptions); + $.jqplot.postDrawHooks.push($.jqplot.Highlighter.postPlotDraw); + + function draw(plot, neighbor) { + var hl = plot.plugins.highlighter; + var s = plot.series[neighbor.seriesIndex]; + var smr = s.markerRenderer; + var mr = hl.markerRenderer; + mr.style = smr.style; + mr.lineWidth = smr.lineWidth + hl.lineWidthAdjust; + mr.size = smr.size + hl.sizeAdjust; + var rgba = $.jqplot.getColorComponents(smr.color); + var newrgb = [rgba[0], rgba[1], rgba[2]]; + var alpha = (rgba[3] >= 0.6) ? rgba[3]*0.6 : rgba[3]*(2-rgba[3]); + mr.color = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+alpha+')'; + mr.init(); + mr.draw(s.gridData[neighbor.pointIndex][0], s.gridData[neighbor.pointIndex][1], hl.highlightCanvas._ctx); + } + + function showTooltip(plot, series, neighbor) { + // neighbor looks like: {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]} + // gridData should be x,y pixel coords on the grid. + // add the plot._gridPadding to that to get x,y in the target. + var hl = plot.plugins.highlighter; + var elem = hl._tooltipElem; + if (hl.useAxesFormatters) { + var xf = series._xaxis._ticks[0].formatter; + var yf = series._yaxis._ticks[0].formatter; + var xfstr = series._xaxis._ticks[0].formatString; + var yfstr = series._yaxis._ticks[0].formatString; + var str; + var xstr = xf(xfstr, neighbor.data[0]); + var ystrs = []; + for (var i=1; i<hl.yvalues+1; i++) { + ystrs.push(yf(yfstr, neighbor.data[i])); + } + if (hl.formatString) { + switch (hl.tooltipAxes) { + case 'both': + case 'xy': + ystrs.unshift(xstr); + ystrs.unshift(hl.formatString); + str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs); + break; + case 'yx': + ystrs.push(xstr); + ystrs.unshift(hl.formatString); + str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs); + break; + case 'x': + str = $.jqplot.sprintf.apply($.jqplot.sprintf, [hl.formatString, xstr]); + break; + case 'y': + ystrs.unshift(hl.formatString); + str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs); + break; + default: // same as xy + ystrs.unshift(xstr); + ystrs.unshift(hl.formatString); + str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs); + break; + } + } + else { + switch (hl.tooltipAxes) { + case 'both': + case 'xy': + str = xstr; + for (var i=0; i<ystrs.length; i++) { + str += hl.tooltipSeparator + ystrs[i]; + } + break; + case 'yx': + str = ''; + for (var i=0; i<ystrs.length; i++) { + str += ystrs[i] + hl.tooltipSeparator; + } + str += xstr; + break; + case 'x': + str = xstr; + break; + case 'y': + str = 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.pieRenderer.js b/libs/jqplot/plugins/jqplot.pieRenderer.js index 1094395d3b..499ae88ebb 100755 --- a/libs/jqplot/plugins/jqplot.pieRenderer.js +++ b/libs/jqplot/plugins/jqplot.pieRenderer.js @@ -1,804 +1,804 @@ -/**
- * 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));
- }
- // else put highlight canvas before event canvas.
- else {
- this.eventCanvas._elem.before(this.plugins.pieRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pieRenderer-highlight-canvas', this._plotDimensions));
- }
-
- var hctx = this.plugins.pieRenderer.highlightCanvas.setContext();
- }
-
- $.jqplot.preInitHooks.push(preInit);
-
- $.jqplot.PieTickRenderer = function() {
- $.jqplot.AxisTickRenderer.call(this);
- };
-
- $.jqplot.PieTickRenderer.prototype = new $.jqplot.AxisTickRenderer();
- $.jqplot.PieTickRenderer.prototype.constructor = $.jqplot.PieTickRenderer;
-
-})(jQuery);
-
+/** + * 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)); + } + // else put highlight canvas before event canvas. + else { + this.eventCanvas._elem.before(this.plugins.pieRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pieRenderer-highlight-canvas', this._plotDimensions)); + } + + var hctx = this.plugins.pieRenderer.highlightCanvas.setContext(); + } + + $.jqplot.preInitHooks.push(preInit); + + $.jqplot.PieTickRenderer = function() { + $.jqplot.AxisTickRenderer.call(this); + }; + + $.jqplot.PieTickRenderer.prototype = new $.jqplot.AxisTickRenderer(); + $.jqplot.PieTickRenderer.prototype.constructor = $.jqplot.PieTickRenderer; + +})(jQuery); +
\ No newline at end of file |