/*jslint browser: true, plusplus: true, nomen: true, white: false, continue: true */
/*global jQuery, console, jqPlot */
/**
* Title: jqPlot Charts
*
* Pure JavaScript plotting plugin for jQuery.
*
* About: Version
*
* version: @VERSION
* revision: @REVISION
*
* About: Copyright & License
*
* Copyright (c) 2009-2013 Chris Leonello
* jqPlot is currently available for use in all personal or commercial projects
* under both the MIT and GPL version 2.0 licenses. This means that you can
* choose the license that best suits your project and use it accordingly.
*
* See <GPL Version 2> and <MIT License> contained within this distribution for further information.
*
* The author would appreciate an email letting him know of any substantial
* use of jqPlot. You can reach the author at: chris at jqplot dot com
* or see http://www.jqplot.com/info.php. This is, of course, not required.
*
* If you are feeling kind and generous, consider supporting the project by
* making a donation at: http://www.jqplot.com/donate.php.
*
* sprintf functions contained in jqplot.sprintf.js by Ash Searle:
*
* version 2007.04.27
* author Ash Searle
* http://hexmen.com/blog/2007/03/printf-sprintf/
* http://hexmen.com/js/sprintf.js
* The author (Ash Searle) has placed this code in the public domain:
* "This code is unrestricted: you are free to use it however you like."
*
*
* About: Introduction
*
* jqPlot requires jQuery (1.4+ required for certain features). jQuery 1.4.2 is included in the distribution.
* To use jqPlot include jQuery, the jqPlot jQuery plugin, the jqPlot css file and optionally
* the excanvas script for IE support in your web page:
*
* > <!--[if lt IE 9]><script language="javascript" type="text/javascript" src="excanvas.js"></script><![endif]-->
* > <script language="javascript" type="text/javascript" src="jquery-1.4.4.min.js"></script>
* > <script language="javascript" type="text/javascript" src="jquery.jqplot.min.js"></script>
* > <link rel="stylesheet" type="text/css" href="jquery.jqplot.css" />
*
* jqPlot can be customized by overriding the defaults of any of the objects which make
* up the plot. The general usage of jqplot is:
*
* > chart = $.jqplot('targetElemId', [dataArray,...], {optionsObject});
*
* The options available to jqplot are detailed in <jqPlot Options> in the jqPlotOptions.txt file.
*
* An actual call to $.jqplot() may look like the
* examples below:
*
* > chart = $.jqplot('chartdiv', [[[1, 2],[3,5.12],[5,13.1],[7,33.6],[9,85.9],[11,219.9]]]);
*
* or
*
* > dataArray = [34,12,43,55,77];
* > chart = $.jqplot('targetElemId', [dataArray, ...], {title:'My Plot', axes:{yaxis:{min:20, max:100}}});
*
* For more inforrmation, see <jqPlot Usage>.
*
* About: Usage
*
* See <jqPlot Usage>
*
* About: Available Options
*
* See <jqPlot Options> for a list of options available thorugh the options object (not complete yet!)
*
* About: Options Usage
*
* See <Options Tutorial>
*
* About: Changes
*
* See <Change Log>
*
*/
(function ($, window) {
"use strict";
var _axisNames = ['yMidAxis', 'xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis'],
/**
* Sorts the series data in increasing order.
* @param {array} series
*/
sortData = function (series) {
var d, sd, pd, ppd, ret,
i,
j,
l = series.length,
check,
bat,
n,
dlen,
/**
*
*/
simplesort1 = function (a, b) {
return a[1] - b[1];
},
/**
*
*/
simplesort0 = function (a, b) {
return a[0] - b[0];
};
for (i = 0; i < l; i++) {
bat = [series[i].data, series[i]._stackData, series[i]._plotData, series[i]._prevPlotData];
for (n = 0; n < 4; n++) {
check = true;
d = bat[n];
if (series[i]._stackAxis === 'x') {
for (j = 0, dlen = d.length; j < dlen; j++) {
if (typeof (d[j][1]) !== "number") {
check = false;
break;
}
}
if (check) {
d.sort(simplesort1);
}
} else {
for (j = 0, dlen = d.length; j < dlen; j++) {
if (typeof (d[j][0]) !== "number") {
check = false;
break;
}
}
if (check) {
d.sort(simplesort0);
}
}
}
}
},
/**
* @param {object} ev
* @return {object}
*/
getEventPosition = function (ev) {
var plot = ev.data.plot,
go = plot.eventCanvas._elem.offset(),
gridPos = {x: ev.pageX - go.left, y: ev.pageY - go.top},
dataPos = {xaxis: null, yaxis: null, x2axis: null, y2axis: null, y3axis: null, y4axis: null, y5axis: null, y6axis: null, y7axis: null, y8axis: null, y9axis: null, yMidAxis: null},
an = ['xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis'],
ax = plot.axes,
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
};
},
/**
* Checks if event location is over a area.
* @param {object} gridpos
* @param {object} plot
- */
checkIntersection = function (gridpos, plot) {
var series = plot.series,
i,
j,
k,
s,
hp,
output = null,
/**
* @param {object} gridpos
* @param {object} s
* @returns {object}
*/
rendererBar = function (gridpos, s) {
var j,
l,
x = gridpos.x,
y = gridpos.y,
points,
p;
for (j = 0, l = s._barPoints.length; j < l; j++) {
points = s._barPoints[j];
p = s.gridData[j];
if (x > points[0][0] && x < points[2][0] && y > points[2][1] && y < points[0][1]) {
return {
seriesIndex: s.index,
pointIndex: j,
gridData: p,
data: s.data[j],
points: s._barPoints[j]
};
}
}
},
/**
* @param {object} gridpos
* @param {object} s
* @returns {object}
*/
rendererPyramid = function (gridpos, s) {
var j,
l,
x = gridpos.x,
y = gridpos.y,
points,
p,
hp = s._highlightThreshold;
for (j = 0, l = s._barPoints.length; j < l; j++) {
points = s._barPoints[j];
p = s.gridData[j];
if (x > points[0][0] + hp[0][0] && x < points[2][0] + hp[2][0] && y > points[2][1] && y < points[0][1]) {
return {
seriesIndex: s.index,
pointIndex: j,
gridData: p,
data: s.data[j],
points: s._barPoints[j]
};
}
}
},
/**
* @param {object} gridpos grid position
* @param {object} s slice
* @returns {object}
*/
rendererDonut = function (gridpos, s) {
var sa,
x,
y,
r,
theta,
sm,
minang,
maxang,
j,
l;
sa = s.startAngle / 180 * Math.PI;
x = gridpos.x - s._center[0];
y = gridpos.y - s._center[1];
r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
if (x > 0 && -y >= 0) {
theta = 2 * Math.PI - Math.atan(-y / x);
} else if (x > 0 && -y < 0) {
theta = -Math.atan(-y / x);
} else if (x < 0) {
theta = Math.PI - Math.atan(-y / x);
} else if (x === 0 && -y > 0) {
theta = 3 * Math.PI / 2;
} else if (x === 0 && -y < 0) {
theta = Math.PI / 2;
} else if (x === 0 && y === 0) {
theta = 0;
}
if (sa) {
theta -= sa;
if (theta < 0) {
theta += 2 * Math.PI;
} else if (theta > 2 * Math.PI) {
theta -= 2 * Math.PI;
}
}
sm = s.sliceMargin / 180 * Math.PI;
if (r < s._radius && r > s._innerRadius) {
for (j = 0, l = s.gridData.length; j < l; j++) {
minang = (j > 0) ? s.gridData[j - 1][1] + sm : sm;
maxang = s.gridData[j][1];
if (theta > minang && theta < maxang) {
return {
seriesIndex: s.index,
pointIndex: j,
gridData: [gridpos.x, gridpos.y],
data: s.data[j]
};
}
}
}
},
/**
* @param {object} gridpos
* @param {object} s
* @returns {object}
*/
rendererPie = function (gridpos, s) {
var sa,
x,
y,
r,
theta,
sm,
minang,
maxang,
j,
l;
sa = s.startAngle / 180 * Math.PI;
x = gridpos.x - s._center[0];
y = gridpos.y - s._center[1];
r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
if (x > 0 && -y >= 0) {
theta = 2 * Math.PI - Math.atan(-y / x);
} else if (x > 0 && -y < 0) {
theta = -Math.atan(-y / x);
} else if (x < 0) {
theta = Math.PI - Math.atan(-y / x);
} else if (x === 0 && -y > 0) {
theta = 3 * Math.PI / 2;
} else if (x === 0 && -y < 0) {
theta = Math.PI / 2;
} else if (x === 0 && y === 0) {
theta = 0;
}
if (sa) {
theta -= sa;
if (theta < 0) {
theta += 2 * Math.PI;
} else if (theta > 2 * Math.PI) {
theta -= 2 * Math.PI;
}
}
sm = s.sliceMargin / 180 * Math.PI;
if (r < s._radius) {
for (j = 0, l = s.gridData.length; j < l; j++) {
minang = (j > 0) ? s.gridData[j - 1][1] + sm : sm;
maxang = s.gridData[j][1];
if (theta > minang && theta < maxang) {
return {
seriesIndex: s.index,
pointIndex: j,
gridData: [gridpos.x, gridpos.y],
data: s.data[j]
};
}
}
}
},
/**
* @param {object} gridpos
* @param {object} s
* @returns {object}
*/
rendererBubble = function (gridpos, s) {
var x,
y,
ret = null,
p,
d,
d0,
j,
l;
x = gridpos.x;
y = gridpos.y;
if (s.show) {
for (j = 0, l = s.gridData.length; j < l; j++) {
p = s.gridData[j];
d = Math.sqrt((x - p[0]) * (x - p[0]) + (y - p[1]) * (y - p[1]));
if (d <= p[2] && (d <= d0 || d0 === null)) {
d0 = d;
ret = {
seriesIndex: i,
pointIndex: j,
gridData: p,
data: s.data[j]
};
}
}
if (ret !== null) {
return ret;
}
}
},
/**
* @param {object} gridpos
* @param {object} s
* @returns {object}
*/
rendererDefault = function (gridpos, s) {
var x,
y,
r,
t,
threshold,
j,
l,
p,
yp,
d,
d0;
x = gridpos.x;
y = gridpos.y;
r = s.renderer;
if (s.show) {
t = s.markerRenderer.size / 2 + s.neighborThreshold;
threshold = (t > 0) ? t : 0;
for (j = 0, l = s.gridData.length; j < s.gridData.length; j++) {
p = s.gridData[j];
// neighbor looks different to OHLC chart.
if (r.constructor === $.jqplot.OHLCRenderer) {
if (r.candleStick) {
yp = s._yaxis.series_u2p;
if (x >= p[0] - r._bodyWidth / 2 && x <= p[0] + r._bodyWidth / 2 && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) {
return {
seriesIndex: i,
pointIndex: j,
gridData: p,
data: s.data[j]
};
}
// if an open hi low close chart
} else if (!r.hlc) {
yp = s._yaxis.series_u2p;
if (x >= p[0] - r._tickLength && x <= p[0] + r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) {
return {
seriesIndex: i,
pointIndex: j,
gridData: p,
data: s.data[j]
};
}
// a hi low close chart
} else {
yp = s._yaxis.series_u2p;
if (x >= p[0] - r._tickLength && x <= p[0] + r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) {
return {
seriesIndex: i,
pointIndex: j,
gridData: p,
data: s.data[j]
};
}
}
} else {
d = Math.sqrt((x - p[0]) * (x - p[0]) + (y - p[1]) * (y - p[1]));
if (d <= threshold && (d <= d0 || d0 === null)) {
d0 = d;
return {
seriesIndex: i,
pointIndex: j,
gridData: p,
data: s.data[j]
};
}
}
}
}
},
/**
* @param {object} gridpos
* @param {object} s
* @returns {object}
*/
rendererFunnel = function (gridpos, s) {
var x = gridpos.x,
y = gridpos.y,
v = s._vertices,
vfirst = v[0],
vlast = v[v.length - 1],
lex,
rex,
cv,
j,
l,
/**
* Equations of right and left sides, returns x, y values given height of section (y value and 2 points)
*/
findedge = function (l, p1, p2) {
var m = (p1[1] - p2[1]) / (p1[0] - p2[0]),
b = p1[1] - m * p1[0],
y = l + p1[1];
return [(y - b) / m, y];
};
// check each section
lex = findedge(y, vfirst[0], vlast[3]);
rex = findedge(y, vfirst[1], vlast[2]);
for (j = 0, l = v.length; j < l; j++) {
cv = v[j];
if (y >= cv[0][1] && y <= cv[3][1] && x >= lex[0] && x <= rex[0]) {
return {
seriesIndex: s.index,
pointIndex: j,
gridData: null,
data: s.data[j]
};
}
}
},
/**
* @param {object} gridpos
* @param {object} s
* @returns {object}
*/
rendererLine = function (gridpos, s) {
var x,
y,
r,
threshold,
t,
j,
l,
p,
d,
d0,
yp,
inside = false,
numPoints,
ii,
vertex1,
vertex2;
x = gridpos.x;
y = gridpos.y;
r = s.renderer;
if (s.show) {
if ((s.fill || (s.renderer.bands.show && s.renderer.bands.fill)) && (!plot.plugins.highlighter || !plot.plugins.highlighter.show)) {
// first check if it is in bounding box
inside = false;
if (x > s._boundingBox[0][0] && x < s._boundingBox[1][0] && y > s._boundingBox[1][1] && y < s._boundingBox[0][1]) {
// now check the crossing number
numPoints = s._areaPoints.length;
j = numPoints - 1;
for (ii = 0; ii < numPoints; ii++) {
vertex1 = [s._areaPoints[ii][0], s._areaPoints[ii][1]];
vertex2 = [s._areaPoints[j][0], s._areaPoints[j][1]];
if ((vertex1[1] < y && vertex2[1] >= y) || (vertex2[1] < y && vertex1[1] >= y)) {
if (vertex1[0] + (y - vertex1[1]) / (vertex2[1] - vertex1[1]) * (vertex2[0] - vertex1[0]) < x) {
inside = !inside;
}
}
j = ii;
}
}
if (inside) {
return {
seriesIndex: i,
pointIndex: null,
gridData: s.gridData,
data: s.data,
points: s._areaPoints
};
}
} else {
t = s.markerRenderer.size / 2 + s.neighborThreshold;
threshold = (t > 0) ? t : 0;
for (j = 0, l = s.gridData.length; j < l; j++) {
p = s.gridData[j];
// neighbor looks different to OHLC chart.
if (r.constructor === $.jqplot.OHLCRenderer) {
if (r.candleStick) {
yp = s._yaxis.series_u2p;
if (x >= p[0] - r._bodyWidth / 2 && x <= p[0] + r._bodyWidth / 2 && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) {
return {
seriesIndex: i,
pointIndex: j,
gridData: p,
data: s.data[j]
};
}
// if an open hi low close chart
} else if (!r.hlc) {
yp = s._yaxis.series_u2p;
if (x >= p[0] - r._tickLength && x <= p[0] + r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) {
return {
seriesIndex: i,
pointIndex: j,
gridData: p,
data: s.data[j]
};
}
// a hi low close chart
} else {
yp = s._yaxis.series_u2p;
if (x >= p[0] - r._tickLength && x <= p[0] + r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) {
return {
seriesIndex: i,
pointIndex: j,
gridData: p,
data: s.data[j]
};
}
}
} else if (p[0] !== null && p[1] !== null) {
d = Math.sqrt((x - p[0]) * (x - p[0]) + (y - p[1]) * (y - p[1]));
if (d <= threshold && (d <= d0 || typeof d0 === "undefined")) {
d0 = d;
return {
seriesIndex: i,
pointIndex: j,
gridData: p,
data: s.data[j]
};
}
}
}
}
}
};
for (k = plot.seriesStack.length - 1; k >= 0; k--) {
i = plot.seriesStack[k];
s = series[i];
switch (s.renderer.constructor) {
case $.jqplot.BarRenderer:
output = rendererBar(gridpos, s);
break;
case $.jqplot.PyramidRenderer:
output = rendererPyramid(gridpos, s);
break;
case $.jqplot.DonutRenderer:
output = rendererDonut(gridpos, s);
break;
case $.jqplot.PieRenderer:
output = rendererPie(gridpos, s);
break;
case $.jqplot.BubbleRenderer:
output = rendererBubble(gridpos, s);
break;
case $.jqplot.FunnelRenderer:
output = rendererFunnel(gridpos, s);
break;
case $.jqplot.LineRenderer:
output = rendererLine(gridpos, s);
break;
default:
output = rendererDefault(gridpos, s);
break;
}
if (output && output !== null){
return output;
}
}
return output;
};
/**
*
*/
$.fn.emptyForce = function () {
var i,
elem;
for (i = 0, elem; typeof (elem = $(this)[i]) !== "undefined"; i++) {
// Remove element nodes and prevent memory leaks
if (elem.nodeType === 1) {
$.cleanData(elem.getElementsByTagName("*"));
}
// Remove any remaining nodes
if ($.jqplot.use_excanvas) {
elem.outerHTML = "";
} else {
while (elem.firstChild) {
elem.removeChild(elem.firstChild);
}
}
elem = null;
}
return $(this);
};
/**
* @param {object} parent
*/
$.fn.removeChildForce = function (parent) {
while (parent.firstChild) {
this.removeChildForce(parent.firstChild);
parent.removeChild(parent.firstChild);
}
};
/**
*
*/
$.fn.jqplot = function () {
var datas = [],
options = [],
i,
l;
// see how many data arrays we have
for (i = 0, l = arguments.length; i < l; i++) {
if ($.isArray(arguments[i])) {
datas.push(arguments[i]);
} else if ($.isPlainObject(arguments[i])) {
options.push(arguments[i]);
}
}
return this.each(function (index) {
var tid,
plot,
$this = $(this),
dl = datas.length,
ol = options.length,
data,
opts;
if (index < dl) {
data = datas[index];
} else {
data = dl ? datas[dl - 1] : null;
}
if (index < ol) {
opts = options[index];
} else {
opts = ol ? options[ol - 1] : null;
}
// does el have an id?
// if not assign it one.
tid = $this.attr('id');
if (typeof tid === "undefined") {
tid = 'jqplot_target_' + $.jqplot.targetCounter++;
$this.attr('id', tid);
}
plot = $.jqplot(tid, data, opts);
$this.data('jqplot', plot);
});
};
$.fn.checkIntersection = checkIntersection;
/**
* Namespace: $.jqplot
* jQuery function called by the user to create a plot.
*
* Parameters:
* target - ID of target element to render the plot into.
* data - an array of data series.
* options - user defined options object. See the individual classes for available options.
*
* Properties:
* config - object to hold configuration information for jqPlot plot object.
*
* attributes:
* enablePlugins - False to disable plugins by default. Plugins must then be explicitly
* enabled in the individual plot options. Default: false.
* This property sets the "show" property of certain plugins to true or false.
* Only plugins that can be immediately active upon loading are affected. This includes
* non-renderer plugins like cursor, dragable, highlighter, and trendline.
* defaultHeight - Default height for plots where no css height specification exists. This
* is a jqplot wide default.
* defaultWidth - Default height for plots where no css height specification exists. This
* is a jqplot wide default.
*
* @namespace $.jqplot
*/
$.jqplot = function (target, data, options) {
var _data = null,
_options = null,
plot,
msg;
if (arguments.length === 3) {
_data = data;
_options = options;
} else if (arguments.length === 2) {
if ($.isArray(data)) {
_data = data;
} else if ($.isPlainObject(data)) {
_options = data;
}
}
if (_data === null && _options !== null && _options.data) {
_data = _options.data;
}
// @TODO Need to make sure we don't run into circular references...
plot = new JqPlot();
// remove any error class that may be stuck on target.
$('#' + target).removeClass('jqplot-error');
if ($.jqplot.config.catchErrors) {
try {
plot.init(target, _data, _options);
plot.draw();
plot.themeEngine.init.call(plot);
return plot;
} catch (e) {
msg = $.jqplot.config.errorMessage || e.message;
$('#' + target)
.append('<div class="jqplot-error-message">' + msg + '</div>')
.addClass('jqplot-error')
.css({
background: $.jqplot.config.errorBackground,
border: $.jqplot.config.errorBorder,
fontFamily: $.jqplot.config.errorFontFamily,
fontSize: $.jqplot.config.errorFontSize,
fontStyle: $.jqplot.config.errorFontStyle,
fontWeight: $.jqplot.config.errorFontWeight
});
}
} else {
plot.init(target, _data, _options);
plot.draw();
plot.themeEngine.init.call(plot);
return plot;
}
};
$.jqplot.version = "@VERSION";
$.jqplot.revision = "@REVISION";
$.jqplot.targetCounter = 1;
// canvas manager to reuse canvases on the plot.
// Should help solve problem of canvases not being freed and
// problem of waiting forever for firefox to decide to free memory.
/**
* @class
*/
$.jqplot.CanvasManager = function () {
// canvases are managed globally so that they can be reused
// across plots after they have been freed
if (typeof $.jqplot.CanvasManager.canvases === 'undefined') {
$.jqplot.CanvasManager.canvases = [];
$.jqplot.CanvasManager.free = [];
}
var myCanvases = [];
this.getCanvas = function () {
var canvas,
makeNew = true,
i,
l;
if (!$.jqplot.use_excanvas) {
for (i = 0, l = $.jqplot.CanvasManager.canvases.length; i < l; i++) {
if ($.jqplot.CanvasManager.free[i] === true) {
makeNew = false;
canvas = $.jqplot.CanvasManager.canvases[i];
// $(canvas).removeClass('jqplot-canvasManager-free').addClass('jqplot-canvasManager-inuse');
$.jqplot.CanvasManager.free[i] = false;
myCanvases.push(i);
break;
}
}
}
if (makeNew) {
canvas = document.createElement('canvas');
myCanvases.push($.jqplot.CanvasManager.canvases.length);
$.jqplot.CanvasManager.canvases.push(canvas);
$.jqplot.CanvasManager.free.push(false);
}
return canvas;
};
// this method has to be used after settings the dimesions
// on the element returned by getCanvas()
this.initCanvas = function (canvas) {
if ($.jqplot.use_excanvas) {
return window.G_vmlCanvasManager.initElement(canvas);
}
return canvas;
};
this.freeAllCanvases = function () {
var i, l;
for (i = 0, l = myCanvases.length; i < l; i++) {
this.freeCanvas(myCanvases[i]);
}
myCanvases = [];
};
this.freeCanvas = function (idx) {
if ($.jqplot.use_excanvas && typeof window.G_vmlCanvasManager.uninitElement !== "undefined") {
// excanvas can't be reused, but properly unset
window.G_vmlCanvasManager.uninitElement($.jqplot.CanvasManager.canvases[idx]);
$.jqplot.CanvasManager.canvases[idx] = null;
} else {
var canvas = $.jqplot.CanvasManager.canvases[idx];
canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
$(canvas).unbind().removeAttr('class').removeAttr('style');
// Style attributes seemed to be still hanging around. wierd. Some ticks
// still retained a left: 0px attribute after reusing a canvas.
$(canvas).css({left: '', top: '', position: ''});
// setting size to 0 may save memory of unused canvases?
canvas.width = 0;
canvas.height = 0;
$.jqplot.CanvasManager.free[idx] = true;
}
};
};
/**
* Convienence function that won't hang IE or FF without FireBug.
*/
$.jqplot.log = function () {
if (window.console) {
window.console.log.apply(window.console, arguments);
}
};
/**
* Settings and configuration
*/
$.jqplot.config = {
addDomReference: false,
enablePlugins: false,
defaultHeight: 300,
defaultWidth: 400,
UTCAdjust: false,
timezoneOffset: new Date(new Date().getTimezoneOffset() * 60000),
errorMessage: '',
errorBackground: '',
errorBorder: '',
errorFontFamily: '',
errorFontSize: '',
errorFontStyle: '',
errorFontWeight: '',
catchErrors: true, // debug mode
defaultTickFormatString: "%.1f",
defaultColors: [ "#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
defaultNegativeColors: [ "#498991", "#C08840", "#9F9274", "#546D61", "#646C4A", "#6F6621", "#6E3F5F", "#4F64B0", "#A89050", "#C45923", "#187399", "#945381", "#959E5C", "#C7AF7B", "#478396", "#907294"],
dashLength: 4,
gapLength: 4,
dotGapLength: 2.5,
srcLocation: 'jqplot/src/',
pluginLocation: 'jqplot/src/plugins/'
};
$.jqplot.arrayMax = function (array) {
return Math.max.apply(Math, array);
};
$.jqplot.arrayMin = function (array) {
return Math.min.apply(Math, array);
};
$.jqplot.enablePlugins = $.jqplot.config.enablePlugins;
/**
* canvas related tests taken from modernizer:
* Copyright (c) 2009 - 2010 Faruk Ates.
* http://www.modernizr.com
*/
$.jqplot.support_canvas = function () {
if (typeof $.jqplot.support_canvas.result === 'undefined') {
$.jqplot.support_canvas.result = !!document.createElement('canvas').getContext;
}
return $.jqplot.support_canvas.result;
};
/**
* canvas related tests taken from modernizer:
* Copyright (c) 2009 - 2010 Faruk Ates.
* http://www.modernizr.com
*/
$.jqplot.support_canvas_text = function () {
if (typeof $.jqplot.support_canvas_text.result === 'undefined') {
if (window.G_vmlCanvasManager && window.G_vmlCanvasManager._version > 887) {
$.jqplot.support_canvas_text.result = true;
} else {
$.jqplot.support_canvas_text.result = !!(document.createElement('canvas').getContext && typeof document.createElement('canvas').getContext('2d').fillText === 'function');
}
}
return $.jqplot.support_canvas_text.result;
};
$.jqplot.use_excanvas = ((!$.support.boxModel || !$.support.objectAll || !$.support.leadingWhitespace) && !$.jqplot.support_canvas()) ? true : false;
/**
*
* Hooks: jqPlot Pugin Hooks
*
* $.jqplot.preInitHooks - called before initialization.
* $.jqplot.postInitHooks - called after initialization.
* $.jqplot.preParseOptionsHooks - called before user options are parsed.
* $.jqplot.postParseOptionsHooks - called after user options are parsed.
* $.jqplot.preDrawHooks - called before plot draw.
* $.jqplot.postDrawHooks - called after plot draw.
* $.jqplot.preDrawSeriesHooks - called before each series is drawn.
* $.jqplot.postDrawSeriesHooks - called after each series is drawn.
* $.jqplot.preDrawLegendHooks - called before the legend is drawn.
* $.jqplot.addLegendRowHooks - called at the end of legend draw, so plugins
* can add rows to the legend table.
* $.jqplot.preSeriesInitHooks - called before series is initialized.
* $.jqplot.postSeriesInitHooks - called after series is initialized.
* $.jqplot.preParseSeriesOptionsHooks - called before series related options
* are parsed.
* $.jqplot.postParseSeriesOptionsHooks - called after series related options
* are parsed.
* $.jqplot.eventListenerHooks - called at the end of plot drawing, binds
* listeners to the event canvas which lays on top of the grid area.
* $.jqplot.preDrawAllSeriesHooks - called before the first serie drawn and after grid drawn.
* $.jqplot.preDrawSeriesShadowHooks - called before series shadows are drawn.
* $.jqplot.postDrawSeriesShadowHooks - called after series shadows are drawn.
*
*/
$.jqplot.preInitHooks = [];
$.jqplot.postInitHooks = [];
$.jqplot.preParseOptionsHooks = [];
$.jqplot.postParseOptionsHooks = [];
$.jqplot.preDrawHooks = [];
$.jqplot.postDrawHooks = [];
$.jqplot.preDrawSeriesHooks = [];
$.jqplot.postDrawSeriesHooks = [];
$.jqplot.preDrawLegendHooks = [];
$.jqplot.addLegendRowHooks = [];
$.jqplot.preSeriesInitHooks = [];
$.jqplot.postSeriesInitHooks = [];
$.jqplot.preParseSeriesOptionsHooks = [];
$.jqplot.postParseSeriesOptionsHooks = [];
$.jqplot.eventListenerHooks = [];
$.jqplot.preDrawSeriesShadowHooks = [];
$.jqplot.preDrawAllSeriesHooks = [];
$.jqplot.postDrawSeriesShadowHooks = [];
// A superclass holding some common properties and methods.
$.jqplot.ElemContainer = function () {
this._elem = null;
this._plotWidth = 0;
this._plotHeight = 0;
this._plotDimensions = {height: null, width: null};
};
$.jqplot.ElemContainer.prototype.createElement = function (el, offsets, clss, cssopts, attrib) {
this._offsets = offsets;
var klass = clss || 'jqplot',
elem = document.createElement(el);
this._elem = $(elem);
this._elem.addClass(klass);
this._elem.css(cssopts);
this._elem.attr(attrib);
// avoid memory leak;
elem = null;
return this._elem;
};
$.jqplot.ElemContainer.prototype.getWidth = function () {
if (this._elem) {
return this._elem.outerWidth(true);
} else {
return null;
}
};
$.jqplot.ElemContainer.prototype.getHeight = function () {
if (this._elem) {
return this._elem.outerHeight(true);
} else {
return null;
}
};
$.jqplot.ElemContainer.prototype.getPosition = function () {
if (this._elem) {
return this._elem.position();
} else {
return {top: null, left: null, bottom: null, right: null};
}
};
$.jqplot.ElemContainer.prototype.getTop = function () {
return this.getPosition().top;
};
$.jqplot.ElemContainer.prototype.getLeft = function () {
return this.getPosition().left;
};
$.jqplot.ElemContainer.prototype.getBottom = function () {
return this._elem.css('bottom');
};
$.jqplot.ElemContainer.prototype.getRight = function () {
return this._elem.css('right');
};
/**
* @memberof $.jqplot
* @class
* Class: Axis
* An individual axis object. Cannot be instantiated directly, but created
* by the Plot object. Axis properties can be set or overridden by the
* options passed in from the user.
*
*/
function Axis(name) {
$.jqplot.ElemContainer.call(this);
// Group: Properties
//
// Axes options are specified within an axes object at the top level of the
// plot options like so:
// > {
// > axes: {
// > xaxis: {min: 5},
// > yaxis: {min: 2, max: 8, numberTicks:4},
// > x2axis: {pad: 1.5},
// > y2axis: {ticks:[22, 44, 66, 88]}
// > }
// > }
// There are 2 x axes, 'xaxis' and 'x2axis', and
// 9 yaxes, 'yaxis', 'y2axis'. 'y3axis', ... Any or all of which may be specified.
this.name = name;
this._series = [];
// prop: show
// Wether to display the axis on the graph.
this.show = false;
// prop: tickRenderer
// A class of a rendering engine for creating the ticks labels displayed on the plot,
// See <$.jqplot.AxisTickRenderer>.
this.tickRenderer = $.jqplot.AxisTickRenderer;
// prop: tickOptions
// Options that will be passed to the tickRenderer, see <$.jqplot.AxisTickRenderer> options.
this.tickOptions = {};
// prop: labelRenderer
// A class of a rendering engine for creating an axis label.
this.labelRenderer = $.jqplot.AxisLabelRenderer;
// prop: labelOptions
// Options passed to the label renderer.
this.labelOptions = {};
// prop: label
// Label for the axis
this.label = null;
// prop: showLabel
// true to show the axis label.
this.showLabel = true;
// prop: min
// minimum value of the axis (in data units, not pixels).
this.min = null;
// prop: max
// maximum value of the axis (in data units, not pixels).
this.max = null;
// prop: autoscale
// DEPRECATED
// the default scaling algorithm produces superior results.
this.autoscale = false;
// prop: pad
// Padding to extend the range above and below the data bounds.
// The data range is multiplied by this factor to determine minimum and maximum axis bounds.
// A value of 0 will be interpreted to mean no padding, and pad will be set to 1.0.
this.pad = 1.2;
// prop: padMax
// Padding to extend the range above data bounds.
// The top of the data range is multiplied by this factor to determine maximum axis bounds.
// A value of 0 will be interpreted to mean no padding, and padMax will be set to 1.0.
this.padMax = null;
// prop: padMin
// Padding to extend the range below data bounds.
// The bottom of the data range is multiplied by this factor to determine minimum axis bounds.
// A value of 0 will be interpreted to mean no padding, and padMin will be set to 1.0.
this.padMin = null;
// prop: ticks
// 1D [val, val, ...] or 2D [[val, label], [val, label], ...] array of ticks for the axis.
// If no label is specified, the value is formatted into an appropriate label.
this.ticks = [];
// prop: numberTicks
// Desired number of ticks. Default is to compute automatically.
this.numberTicks = null;
// prop: tickInterval
// number of units between ticks. Mutually exclusive with numberTicks.
this.tickInterval = null;
// prop: renderer
// A class of a rendering engine that handles tick generation,
// scaling input data to pixel grid units and drawing the axis element.
this.renderer = $.jqplot.LinearAxisRenderer;
// prop: rendererOptions
// renderer specific options. See <$.jqplot.LinearAxisRenderer> for options.
this.rendererOptions = {};
// prop: showTicks
// Wether to show the ticks (both marks and labels) or not.
// Will not override showMark and showLabel options if specified on the ticks themselves.
this.showTicks = true;
// prop: showTickMarks
// Wether to show the tick marks (line crossing grid) or not.
// Overridden by showTicks and showMark option of tick itself.
this.showTickMarks = true;
// prop: showMinorTicks
// Wether or not to show minor ticks. This is renderer dependent.
this.showMinorTicks = true;
// prop: drawMajorGridlines
// True to draw gridlines for major axis ticks.
this.drawMajorGridlines = true;
// prop: drawMinorGridlines
// True to draw gridlines for minor ticks.
this.drawMinorGridlines = false;
// prop: drawMajorTickMarks
// True to draw tick marks for major axis ticks.
this.drawMajorTickMarks = true;
// prop: drawMinorTickMarks
// True to draw tick marks for minor ticks. This is renderer dependent.
this.drawMinorTickMarks = true;
// prop: useSeriesColor
// Use the color of the first series associated with this axis for the
// tick marks and line bordering this axis.
this.useSeriesColor = false;
// prop: borderWidth
// width of line stroked at the border of the axis. Defaults
// to the width of the grid boarder.
this.borderWidth = null;
// prop: borderColor
// color of the border adjacent to the axis. Defaults to grid border color.
this.borderColor = null;
// prop: scaleToHiddenSeries
// True to include hidden series when computing axes bounds and scaling.
this.scaleToHiddenSeries = false;
// minimum and maximum values on the axis.
this._dataBounds = {min: null, max: null};
// statistics (min, max, mean) as well as actual data intervals for each series attached to axis.
// holds collection of {intervals:[], min:, max:, mean: } objects for each series on axis.
this._intervalStats = [];
// pixel position from the top left of the min value and max value on the axis.
this._offsets = {min: null, max: null};
this._ticks = [];
this._label = null;
// prop: syncTicks
// true to try and synchronize tick spacing across multiple axes so that ticks and
// grid lines line up. This has an impact on autoscaling algorithm, however.
// In general, autoscaling an individual axis will work better if it does not
// have to sync ticks.
this.syncTicks = null;
// prop: tickSpacing
// Approximate pixel spacing between ticks on graph. Used during autoscaling.
// This number will be an upper bound, actual spacing will be less.
this.tickSpacing = 75;
// Properties to hold the original values for min, max, ticks, tickInterval and numberTicks
// so they can be restored if altered by plugins.
this._min = null;
this._max = null;
this._tickInterval = null;
this._numberTicks = null;
this.__ticks = null;
// hold original user options.
this._options = {};
}
Axis.prototype = new $.jqplot.ElemContainer();
Axis.prototype.constructor = Axis;
Axis.prototype.init = function () {
if ($.isFunction(this.renderer)) {
this.renderer = new this.renderer();
}
// set the axis name
this.tickOptions.axis = this.name;
// if showMark or showLabel tick options not specified, use value of axis option.
// showTicks overrides showTickMarks.
if (typeof this.tickOptions.showMark === "undefined") {
this.tickOptions.showMark = this.showTicks;
}
if (typeof this.tickOptions.showTickMarks === "undefined") {
this.tickOptions.showTickMarks = this.showTickMarks;
}
if (typeof this.tickOptions.showLabel === "undefined") {
this.tickOptions.showLabel = this.showTicks;
}
if (typeof this.label === "undefined" || this.label === null || this.label === '') {
this.showLabel = false;
} else {
this.labelOptions.label = this.label;
}
if (this.showLabel === false) {
this.labelOptions.show = false;
}
// set the default padMax, padMin if not specified
// special check, if no padding desired, padding
// should be set to 1.0
if (this.pad === 0) {
this.pad = 1.0;
}
if (this.padMax === 0) {
this.padMax = 1.0;
}
if (this.padMin === 0) {
this.padMin = 1.0;
}
if (this.padMax === null) {
this.padMax = (this.pad - 1) / 2 + 1;
}
if (this.padMin === null) {
this.padMin = (this.pad - 1) / 2 + 1;
}
// now that padMin and padMax are correctly set, reset pad in case user has supplied
// padMin and/or padMax
this.pad = this.padMax + this.padMin - 1;
if (this.min !== null || this.max !== null) {
this.autoscale = false;
}
// if not set, sync ticks for y axes but not x by default.
if (this.syncTicks === null && this.name.indexOf('y') > -1) {
this.syncTicks = true;
} else if (this.syncTicks === null) {
this.syncTicks = false;
}
this.renderer.init.call(this, this.rendererOptions);
};
Axis.prototype.draw = function (ctx, plot) {
// Memory Leaks patch
if (this.__ticks) {
this.__ticks = null;
}
return this.renderer.draw.call(this, ctx, plot);
};
Axis.prototype.set = function () {
this.renderer.set.call(this);
};
Axis.prototype.pack = function (pos, offsets) {
if (this.show) {
this.renderer.pack.call(this, pos, offsets);
}
// these properties should all be available now.
if (this._min === null) {
this._min = this.min;
this._max = this.max;
this._tickInterval = this.tickInterval;
this._numberTicks = this.numberTicks;
this.__ticks = this._ticks;
}
};
// reset the axis back to original values if it has been scaled, zoomed, etc.
Axis.prototype.reset = function () {
this.renderer.reset.call(this);
};
Axis.prototype.resetScale = function (opts) {
$.extend(true, this, {min: null, max: null, numberTicks: null, tickInterval: null, _ticks: [], ticks: []}, opts);
this.resetDataBounds();
};
Axis.prototype.resetDataBounds = function () {
// Go through all the series attached to this axis and find
// the min/max bounds for this axis.
var db = this._dataBounds,
l,
s,
d,
// check for when to force min 0 on bar series plots.
doforce = (this.show) ? true : false,
i = 0,
j = 0,
minyidx,
maxyidx;
db.min = null;
db.max = null;
for (i = 0; i < this._series.length; i++) {
s = this._series[i];
if (s.show || this.scaleToHiddenSeries) {
d = s._plotData;
if (s._type === 'line' && s.renderer.bands.show && this.name.charAt(0) !== 'x') {
d = [[0, s.renderer.bands._min], [1, s.renderer.bands._max]];
}
minyidx = 1;
maxyidx = 1;
if (s._type !== null && s._type === 'ohlc') {
minyidx = 3;
maxyidx = 2;
}
for (j = 0, l = d.length; j < l; j++) {
if (this.name === 'xaxis' || this.name === 'x2axis') {
if ((d[j][0] !== null && d[j][0] < db.min) || db.min === null) {
db.min = d[j][0];
}
if ((d[j][0] !== null && d[j][0] > db.max) || db.max === null) {
db.max = d[j][0];
}
} else {
if ((d[j][minyidx] !== null && d[j][minyidx] < db.min) || db.min === null) {
db.min = d[j][minyidx];
}
if ((d[j][maxyidx] !== null && d[j][maxyidx] > db.max) || db.max === null) {
db.max = d[j][maxyidx];
}
}
}
// Hack to not pad out bottom of bar plots unless user has specified a padding.
// every series will have a chance to set doforce to false. once it is set to
// false, it cannot be reset to true.
// If any series attached to axis is not a bar, wont force 0.
if (doforce && s.renderer.constructor !== $.jqplot.BarRenderer) {
doforce = false;
} else if (doforce && this._options.hasOwnProperty('forceTickAt0') && this._options.forceTickAt0 === false) {
doforce = false;
} else if (doforce && s.renderer.constructor === $.jqplot.BarRenderer) {
if (s.barDirection === 'vertical' && this.name !== 'xaxis' && this.name !== 'x2axis') {
if (this._options.pad !== null || this._options.padMin !== null) {
doforce = false;
}
} else if (s.barDirection === 'horizontal' && (this.name === 'xaxis' || this.name === 'x2axis')) {
if (this._options.pad !== null || this._options.padMin !== null) {
doforce = false;
}
}
}
}
}
if (doforce && this.renderer.constructor === $.jqplot.LinearAxisRenderer && db.min >= 0) {
this.padMin = 1.0;
this.forceTickAt0 = true;
}
};
/**
* @memberof $.jqplot
* @class
* Class: Legend
* Legend object. Cannot be instantiated directly, but created
* by the Plot object. Legend properties can be set or overridden by the
* options passed in from the user.
*/
function Legend(options) {
$.jqplot.ElemContainer.call(this);
// Group: Properties
// prop: show
// Wether to display the legend on the graph.
this.show = false;
// prop: location
// Placement of the legend. one of the compass directions: nw, n, ne, e, se, s, sw, w
this.location = 'ne';
// prop: labels
// Array of labels to use. By default the renderer will look for labels on the series.
// Labels specified in this array will override labels specified on the series.
this.labels = [];
// prop: showLabels
// true to show the label text on the legend.
this.showLabels = true;
// prop: showSwatch
// true to show the color swatches on the legend.
this.showSwatches = true;
// prop: placement
// "insideGrid" places legend inside the grid area of the plot.
// "outsideGrid" places the legend outside the grid but inside the plot container,
// shrinking the grid to accomodate the legend.
// "inside" synonym for "insideGrid",
// "outside" places the legend ouside the grid area, but does not shrink the grid which
// can cause the legend to overflow the plot container.
this.placement = "insideGrid";
// prop: xoffset
// DEPRECATED. Set the margins on the legend using the marginTop, marginLeft, etc.
// properties or via CSS margin styling of the .jqplot-table-legend class.
this.xoffset = 0;
// prop: yoffset
// DEPRECATED. Set the margins on the legend using the marginTop, marginLeft, etc.
// properties or via CSS margin styling of the .jqplot-table-legend class.
this.yoffset = 0;
// prop: border
// css spec for the border around the legend box.
this.border = null;
// prop: background
// css spec for the background of the legend box.
this.background = null;
// prop: textColor
// css color spec for the legend text.
this.textColor = null;
// prop: fontFamily
// css font-family spec for the legend text.
this.fontFamily = null;
// prop: fontSize
// css font-size spec for the legend text.
this.fontSize = null;
// prop: rowSpacing
// css padding-top spec for the rows in the legend.
this.rowSpacing = '0.5em';
// renderer
// A class that will create a DOM object for the legend,
// see <$.jqplot.TableLegendRenderer>.
this.renderer = $.jqplot.TableLegendRenderer;
// prop: rendererOptions
// renderer specific options passed to the renderer.
this.rendererOptions = {};
// prop: predraw
// Wether to draw the legend before the series or not.
// Used with series specific legend renderers for pie, donut, mekko charts, etc.
this.preDraw = false;
// prop: marginTop
// CSS margin for the legend DOM element. This will set an element
// CSS style for the margin which will override any style sheet setting.
// The default will be taken from the stylesheet.
this.marginTop = null;
// prop: marginRight
// CSS margin for the legend DOM element. This will set an element
// CSS style for the margin which will override any style sheet setting.
// The default will be taken from the stylesheet.
this.marginRight = null;
// prop: marginBottom
// CSS margin for the legend DOM element. This will set an element
// CSS style for the margin which will override any style sheet setting.
// The default will be taken from the stylesheet.
this.marginBottom = null;
// prop: marginLeft
// CSS margin for the legend DOM element. This will set an element
// CSS style for the margin which will override any style sheet setting.
// The default will be taken from the stylesheet.
this.marginLeft = null;
// prop: escapeHtml
// True to escape special characters with their html entity equivalents
// in legend text. "<" becomes < and so on, so html tags are not rendered.
this.escapeHtml = false;
this._series = [];
$.extend(true, this, options);
}
Legend.prototype = new $.jqplot.ElemContainer();
Legend.prototype.constructor = Legend;
/**
* @param {object} options
*/
Legend.prototype.setOptions = function (options) {
$.extend(true, this, options);
// Try to emulate deprecated behaviour
// if user has specified xoffset or yoffset, copy these to
// the margin properties.
if (this.placement === 'inside') {
this.placement = 'insideGrid';
}
if (this.xoffset > 0) {
if (this.placement === 'insideGrid') {
switch (this.location) {
case 'nw':
case 'w':
case 'sw':
if (this.marginLeft === null) {
this.marginLeft = this.xoffset + 'px';
}
this.marginRight = '0px';
break;
//case 'ne':
//case 'e':
//case 'se':
default:
if (this.marginRight === null) {
this.marginRight = this.xoffset + 'px';
}
this.marginLeft = '0px';
break;
}
} else if (this.placement === 'outside') {
switch (this.location) {
case 'nw':
case 'w':
case 'sw':
if (this.marginRight === null) {
this.marginRight = this.xoffset + 'px';
}
this.marginLeft = '0px';
break;
//case 'ne':
//case 'e':
//case 'se':
default:
if (this.marginLeft === null) {
this.marginLeft = this.xoffset + 'px';
}
this.marginRight = '0px';
break;
}
}
this.xoffset = 0;
}
if (this.yoffset > 0) {
if (this.placement === 'outside') {
switch (this.location) {
case 'sw':
case 's':
case 'se':
if (this.marginTop === null) {
this.marginTop = this.yoffset + 'px';
}
this.marginBottom = '0px';
break;
//case 'ne':
//case 'n':
//case 'nw':
default:
if (this.marginBottom === null) {
this.marginBottom = this.yoffset + 'px';
}
this.marginTop = '0px';
break;
}
} else if (this.placement === 'insideGrid') {
switch (this.location) {
case 'sw':
case 's':
case 'se':
if (this.marginBottom === null) {
this.marginBottom = this.yoffset + 'px';
}
this.marginTop = '0px';
break;
//case 'ne':
//case 'n':
//case 'nw':
default:
if (this.marginTop === null) {
this.marginTop = this.yoffset + 'px';
}
this.marginBottom = '0px';
break;
}
}
this.yoffset = 0;
}
// TO-DO:
// Handle case where offsets are < 0.
//
};
Legend.prototype.init = function () {
if ($.isFunction(this.renderer)) {
this.renderer = new this.renderer();
}
this.renderer.init.call(this, this.rendererOptions);
};
Legend.prototype.draw = function (offsets, plot) {
var i, l;
for (i = 0, l = $.jqplot.preDrawLegendHooks.length; i < l; i++) {
$.jqplot.preDrawLegendHooks[i].call(this, offsets);
}
return this.renderer.draw.call(this, offsets, plot);
};
Legend.prototype.pack = function (offsets) {
this.renderer.pack.call(this, offsets);
};
/**
* @memberof $.jqplot
* @class
* Class: Title
* Plot Title object. Cannot be instantiated directly, but created
* by the Plot object. Title properties can be set or overridden by the
* options passed in from the user.
*
* Parameters:
* text - text of the title.
*/
function Title(text) {
$.jqplot.ElemContainer.call(this);
// Group: Properties
// prop: text
// text of the title;
this.text = text;
// prop: show
// whether or not to show the title
this.show = true;
// prop: fontFamily
// css font-family spec for the text.
this.fontFamily = null;
// prop: fontSize
// css font-size spec for the text.
this.fontSize = null;
// prop: textAlign
// css text-align spec for the text.
this.textAlign = null;
// prop: textColor
// css color spec for the text.
this.textColor = null;
// prop: renderer
// A class for creating a DOM element for the title,
// see <$.jqplot.DivTitleRenderer>.
this.renderer = $.jqplot.DivTitleRenderer;
// prop: rendererOptions
// renderer specific options passed to the renderer.
this.rendererOptions = {};
// prop: escapeHtml
// True to escape special characters with their html entity equivalents
// in title text. "<" becomes < and so on, so html tags are not rendered.
this.escapeHtml = false;
}
Title.prototype = new $.jqplot.ElemContainer();
Title.prototype.constructor = Title;
Title.prototype.init = function () {
if ($.isFunction(this.renderer)) {
this.renderer = new this.renderer();
}
this.renderer.init.call(this, this.rendererOptions);
};
Title.prototype.draw = function (width) {
return this.renderer.draw.call(this, width);
};
Title.prototype.pack = function () {
this.renderer.pack.call(this);
};
/**
* @memberof $.jqplot
* @class
* Class: Series
* An individual data series object. Cannot be instantiated directly, but created
* by the Plot object. Series properties can be set or overridden by the
* options passed in from the user.
* @param {object} options series' optons.
*/
function Series(options) {
options = options || {};
$.jqplot.ElemContainer.call(this);
// Group: Properties
// Properties will be assigned from a series array at the top level of the
// options. If you had two series and wanted to change the color and line
// width of the first and set the second to use the secondary y axis with
// no shadow and supply custom labels for each:
// > {
// > series:[
// > {color: '#ff4466', lineWidth: 5, label:'good line'},
// > {yaxis: 'y2axis', shadow: false, label:'bad line'}
// > ]
// > }
// prop: show
// whether or not to draw the series.
this.show = true;
// prop: xaxis
// which x axis to use with this series, either 'xaxis' or 'x2axis'.
this.xaxis = 'xaxis';
this._xaxis = null;
// prop: yaxis
// which y axis to use with this series, either 'yaxis' or 'y2axis'.
this.yaxis = 'yaxis';
this._yaxis = null;
this.gridBorderWidth = 2.0;
// prop: renderer
// A class of a renderer which will draw the series,
// see <$.jqplot.LineRenderer>.
this.renderer = $.jqplot.LineRenderer;
// prop: rendererOptions
// Options to pass on to the renderer.
this.rendererOptions = {};
this.data = [];
this.gridData = [];
// prop: label
// Line label to use in the legend.
this.label = '';
// prop: showLabel
// true to show label for this series in the legend.
this.showLabel = true;
// prop: color
// css color spec for the series
this.color = null;
// prop: negativeColor
// css color spec used for filled (area) plots that are filled to zero and
// the "useNegativeColors" option is true.
this.negativeColor = null;
// prop: lineWidth
// width of the line in pixels. May have different meanings depending on renderer.
this.lineWidth = 2.5;
// prop: lineJoin
// Canvas lineJoin style between segments of series.
this.lineJoin = 'round';
// prop: lineCap
// Canvas lineCap style at ends of line.
this.lineCap = 'round';
// prop: linePattern
// line pattern 'dashed', 'dotted', 'solid', some combination
// of '-' and '.' characters such as '.-.' or a numerical array like
// [draw, skip, draw, skip, ...] such as [1, 10] to draw a dotted line,
// [1, 10, 20, 10] to draw a dot-dash line, and so on.
this.linePattern = 'solid';
this.shadow = true;
// prop: shadowAngle
// Shadow angle in degrees
this.shadowAngle = 45;
// prop: shadowOffset
// Shadow offset from line in pixels
this.shadowOffset = 1.25;
// prop: shadowDepth
// Number of times shadow is stroked, each stroke offset shadowOffset from the last.
this.shadowDepth = 3;
// prop: shadowAlpha
// Alpha channel transparency of shadow. 0 = transparent.
this.shadowAlpha = '0.1';
// prop: breakOnNull
// Wether line segments should be be broken at null value.
// False will join point on either side of line.
this.breakOnNull = false;
// prop: markerRenderer
// A class of a renderer which will draw marker (e.g. circle, square, ...) at the data points,
// see <$.jqplot.MarkerRenderer>.
this.markerRenderer = $.jqplot.MarkerRenderer;
// prop: markerOptions
// renderer specific options to pass to the markerRenderer,
// see <$.jqplot.MarkerRenderer>.
this.markerOptions = {};
// prop: showLine
// whether to actually draw the line or not. Series will still be renderered, even if no line is drawn.
this.showLine = true;
// prop: showMarker
// whether or not to show the markers at the data points.
this.showMarker = true;
// prop: index
// 0 based index of this series in the plot series array.
this.index = null;
// prop: fill
// true or false, whether to fill under lines or in bars.
// May not be implemented in all renderers.
this.fill = false;
// prop: fillColor
// CSS color spec to use for fill under line. Defaults to line color.
this.fillColor = null;
// prop: fillGradient
// if true :enable gradient rendering for fill.
this.fillGradient = false;
// prop: fillGradientColor
// css colorif true :gradient Color combinated with Color.
this.fillGradientColor = null;
// prop: fillGradientDelay
// 0 for no delay,1 for no gradient.
this.fillGradientDelay = null;
// prop: fillAlpha
// Alpha transparency to apply to the fill under the line.
// Use this to adjust alpha separate from fill color.
this.fillAlpha = null;
// prop: fillAndStroke
// If true will stroke the line (with color this.color) as well as fill under it.
// Applies only when fill is true.
this.fillAndStroke = false;
// prop: disableStack
// true to not stack this series with other series in the plot.
// To render properly, non-stacked series must come after any stacked series
// in the plot's data series array. So, the plot's data series array would look like:
// > [stackedSeries1, stackedSeries2, ..., nonStackedSeries1, nonStackedSeries2, ...]
// disableStack will put a gap in the stacking order of series, and subsequent
// stacked series will not fill down through the non-stacked series and will
// most likely not stack properly on top of the non-stacked series.
this.disableStack = false;
// _stack is set by the Plot if the plot is a stacked chart.
// will stack lines or bars on top of one another to build a "mountain" style chart.
// May not be implemented in all renderers.
this._stack = false;
// prop: neighborThreshold
// how close or far (in pixels) the cursor must be from a point marker to detect the point.
this.neighborThreshold = 4;
// prop: fillToZero
// true will force bar and filled series to fill toward zero on the fill Axis.
this.fillToZero = false;
// prop: fillToValue
// fill a filled series to this value on the fill axis.
// Works in conjunction with fillToZero, so that must be true.
this.fillToValue = 0;
// prop: fillAxis
// Either 'x' or 'y'. Which axis to fill the line toward if fillToZero is true.
// 'y' means fill up/down to 0 on the y axis for this series.
this.fillAxis = 'y';
// prop: useNegativeColors
// true to color negative values differently in filled and bar charts.
this.useNegativeColors = true;
this._stackData = [];
// _plotData accounts for stacking. If plots not stacked, _plotData and data are same. If
// stacked, _plotData is accumulation of stacking data.
this._plotData = [];
// _plotValues hold the individual x and y values that will be plotted for this series.
this._plotValues = {x: [], y: []};
// statistics about the intervals between data points. Used for auto scaling.
this._intervals = {x: {}, y: {}};
// data from the previous series, for stacked charts.
this._prevPlotData = [];
this._prevGridData = [];
this._stackAxis = 'y';
this._primaryAxis = '_xaxis';
// give each series a canvas to draw on. This should allow for redrawing speedups.
this.canvas = new $.jqplot.GenericCanvas();
this.shadowCanvas = new $.jqplot.GenericCanvas();
this.plugins = {};
// sum of y values in this series.
this._sumy = 0;
this._sumx = 0;
this._type = '';
}
Series.prototype = new $.jqplot.ElemContainer();
Series.prototype.constructor = Series;
Series.prototype.init = function (index, gridbw, plot) {
var d = this.data,
temp = [],
i,
l,
comp,
comp2;
// weed out any null values in the data.
this.index = index;
this.gridBorderWidth = gridbw;
for (i = 0, l = d.length; i < l; i++) {
if (!this.breakOnNull) {
if (d[i] === null || d[i][0] === null || d[i][1] === null) {
// svandecappelle modification to implements null values in canvas. TODO check non-regression.
temp.push(d[i]);
continue;
} else {
temp.push(d[i]);
}
} else {
// TODO: figure out what to do with null values
// probably involve keeping nulls in data array
// and then updating renderers to break line
// when it hits null value.
// For now, just keep value.
temp.push(d[i]);
}
}
this.data = temp;
// parse the renderer options and apply default colors if not provided
// Set color even if not shown, so series don't change colors when other
// series on plot shown/hidden.
if (!this.color) {
this.color = plot.colorGenerator.get(this.index);
}
if (!this.negativeColor) {
this.negativeColor = plot.negativeColorGenerator.get(this.index);
}
if (!this.fillColor) {
this.fillColor = this.color;
}
if (this.fillAlpha) {
comp = $.jqplot.normalize2rgb(this.fillColor);
comp = $.jqplot.getColorComponents(comp);
this.fillColor = 'rgba(' + comp[0] + ',' + comp[1] + ',' + comp[2] + ',' + this.fillAlpha + ')';
if (this.fillGradient && this.fillGradientColor) {
comp2 = $.jqplot.normalize2rgb(this.fillGradientColor);
comp2 = $.jqplot.getColorComponents(comp2);
this.fillGradientColor = 'rgba(' + comp2[0] + ',' + comp2[1] + ',' + comp2[2] + ',' + this.fillAlpha + ')';
}
}
if ($.isFunction(this.renderer)) {
this.renderer = new this.renderer();
}
this.renderer.init.call(this, this.rendererOptions, plot);
this.markerRenderer = new this.markerRenderer();
if (!this.markerOptions.color) {
this.markerOptions.color = this.color;
}
if (this.markerOptions.show === null) {
this.markerOptions.show = this.showMarker;
}
this.showMarker = this.markerOptions.show;
// the markerRenderer is called within its own scope, don't want to overwrite series options!!
this.markerRenderer.init(this.markerOptions);
};
// data - optional data point array to draw using this series renderer
// gridData - optional grid data point array to draw using this series renderer
// stackData - array of cumulative data for stacked plots.
Series.prototype.draw = function (sctx, opts, plot) {
var options = (typeof opts === "undefined") ? {} : opts,
j,
l,
data,
gridData;
sctx = (typeof sctx === "undefined") ? this.canvas._ctx : sctx;
// hooks get called even if series not shown
// we don't clear canvas here, it would wipe out all other series as well.
for (j = 0, l = $.jqplot.preDrawSeriesHooks.length; j < l; j++) {
$.jqplot.preDrawSeriesHooks[j].call(this, sctx, options);
}
if (this.show) {
this.renderer.setGridData.call(this, plot);
if (!options.preventJqPlotSeriesDrawTrigger) {
$(sctx.canvas).trigger('jqplotSeriesDraw', [this.data, this.gridData]);
}
data = [];
if (options.data) {
data = options.data;
} else if (!this._stack) {
data = this.data;
} else {
data = this._plotData;
}
gridData = options.gridData || this.renderer.makeGridData.call(this, data, plot);
if (this._type === 'line' && this.renderer.smooth && this.renderer._smoothedData.length) {
gridData = this.renderer._smoothedData;
}
this.renderer.draw.call(this, sctx, gridData, options, plot);
}
for (j = 0, l = $.jqplot.postDrawSeriesHooks.length; j < l; j++) {
$.jqplot.postDrawSeriesHooks[j].call(this, sctx, options, plot);
}
sctx = opts = plot = j = data = gridData = null;
};
Series.prototype.drawShadow = function (sctx, opts, plot) {
var options = (typeof opts === "undefined") ? {} : opts,
j,
l,
data,
gridData;
sctx = (typeof sctx === "undefined") ? this.shadowCanvas._ctx : sctx;
// hooks get called even if series not shown
// we don't clear canvas here, it would wipe out all other series as well.
for (j = 0, l = $.jqplot.preDrawSeriesShadowHooks.length; j < l; j++) {
$.jqplot.preDrawSeriesShadowHooks[j].call(this, sctx, options);
}
if (this.shadow) {
this.renderer.setGridData.call(this, plot);
data = [];
if (options.data) {
data = options.data;
} else if (!this._stack) {
data = this.data;
} else {
data = this._plotData;
}
gridData = options.gridData || this.renderer.makeGridData.call(this, data, plot);
this.renderer.drawShadow.call(this, sctx, gridData, options, plot);
}
for (j = 0, l = $.jqplot.postDrawSeriesShadowHooks.length; j < l; j++) {
$.jqplot.postDrawSeriesShadowHooks[j].call(this, sctx, options);
}
sctx = opts = plot = j = data = gridData = null;
};
/**
* toggles series display on plot, e.g. show/hide series
* @param {object} ev - event.
* @param {function} callback - callback function called when seres had been toggled.
*/
Series.prototype.toggleDisplay = function (ev, callback) {
var s,
speed;
if (ev.data.series) {
s = ev.data.series;
} else {
s = this;
}
if (ev.data.speed) {
speed = ev.data.speed;
}
if (speed) {
// this can be tricky because series may not have a canvas element if replotting.
if (s.canvas._elem.is(':hidden') || !s.show) {
s.show = true;
s.canvas._elem.removeClass('jqplot-series-hidden');
if (s.shadowCanvas._elem) {
s.shadowCanvas._elem.fadeIn(speed);
}
s.canvas._elem.fadeIn(speed, callback);
s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-' + s.index).fadeIn(speed);
} else {
s.show = false;
s.canvas._elem.addClass('jqplot-series-hidden');
if (s.shadowCanvas._elem) {
s.shadowCanvas._elem.fadeOut(speed);
}
s.canvas._elem.fadeOut(speed, callback);
s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-' + s.index).fadeOut(speed);
}
} else {
// this can be tricky because series may not have a canvas element if replotting.
if (s.canvas._elem.is(':hidden') || !s.show) {
s.show = true;
s.canvas._elem.removeClass('jqplot-series-hidden');
if (s.shadowCanvas._elem) {
s.shadowCanvas._elem.show();
}
s.canvas._elem.show(0, callback);
s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-' + s.index).show();
} else {
s.show = false;
s.canvas._elem.addClass('jqplot-series-hidden');
if (s.shadowCanvas._elem) {
s.shadowCanvas._elem.hide();
}
s.canvas._elem.hide(0, callback);
s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-' + s.index).hide();
}
}
};
/**
* @memberof $.jqplot
* @class
*
* Object representing the grid on which the plot is drawn. The grid in this
* context is the area bounded by the axes, the area which will contain the series.
* Note, the series are drawn on their own canvas.
* The Grid object cannot be instantiated directly, but is created by the Plot object.
* Grid properties can be set or overridden by the options passed in from the user.
*/
function Grid() {
$.jqplot.ElemContainer.call(this);
// Group: Properties
// prop: drawGridlines
// whether to draw the gridlines on the plot.
this.drawGridlines = true;
// prop: gridLineColor
// color of the grid lines.
this.gridLineColor = '#cccccc';
// prop: gridLineWidth
// width of the grid lines.
this.gridLineWidth = 1.0;
// prop: background
// css spec for the background color.
this.background = 'transparent';
// prop: borderColor
// css spec for the color of the grid border.
this.borderColor = '#BABABA';
// prop: borderWidth
// width of the border in pixels.
this.borderWidth = 2.0;
// prop: drawBorder
// True to draw border around grid.
this.drawBorder = true;
// prop: shadow
// whether to show a shadow behind the grid.
this.shadow = true;
// prop: shadowAngle
// shadow angle in degrees
this.shadowAngle = 45;
// prop: shadowOffset
// Offset of each shadow stroke from the border in pixels
this.shadowOffset = 1.5;
// prop: shadowWidth
// width of the stoke for the shadow
this.shadowWidth = 3;
// prop: shadowDepth
// Number of times shadow is stroked, each stroke offset shadowOffset from the last.
this.shadowDepth = 3;
// prop: shadowColor
// an optional css color spec for the shadow in 'rgba(n, n, n, n)' form
this.shadowColor = null;
// prop: shadowAlpha
// Alpha channel transparency of shadow. 0 = transparent.
this.shadowAlpha = '0.07';
this._left = null;
this._top = null;
this._right = null;
this._bottom = null;
this._width = null;
this._height = null;
this._axes = [];
// prop: renderer
// Instance of a renderer which will actually render the grid,
// see <$.jqplot.CanvasGridRenderer>.
this.renderer = $.jqplot.CanvasGridRenderer;
// prop: rendererOptions
// Options to pass on to the renderer,
// see <$.jqplot.CanvasGridRenderer>.
this.rendererOptions = {};
this._offsets = {top: null, bottom: null, left: null, right: null};
}
Grid.prototype = new $.jqplot.ElemContainer();
Grid.prototype.constructor = Grid;
Grid.prototype.init = function () {
if ($.isFunction(this.renderer)) {
this.renderer = new this.renderer();
}
this.renderer.init.call(this, this.rendererOptions);
};
Grid.prototype.createElement = function (offsets, plot) {
this._offsets = offsets;
return this.renderer.createElement.call(this, plot);
};
Grid.prototype.draw = function () {
this.renderer.draw.call(this);
};
/**
* @class
*/
$.jqplot.GenericCanvas = function () {
$.jqplot.ElemContainer.call(this);
this._ctx = null;
};
$.jqplot.GenericCanvas.prototype = new $.jqplot.ElemContainer();
$.jqplot.GenericCanvas.prototype.constructor = $.jqplot.GenericCanvas;
$.jqplot.GenericCanvas.prototype.createElement = function (offsets, clss, plotDimensions, plot) {
this._offsets = offsets;
var klass = 'jqplot',
elem;
if (typeof clss !== "undefined") {
klass = clss;
}
elem = plot.canvasManager.getCanvas();
// if new plotDimensions supplied, use them.
if (plotDimensions !== null) {
this._plotDimensions = plotDimensions;
}
elem.width = this._plotDimensions.width - this._offsets.left - this._offsets.right;
elem.height = this._plotDimensions.height - this._offsets.top - this._offsets.bottom;
this._elem = $(elem);
this._elem.css({ position: 'absolute', left: this._offsets.left, top: this._offsets.top });
this._elem.addClass(klass);
elem = plot.canvasManager.initCanvas(elem);
elem = null;
return this._elem;
};
$.jqplot.GenericCanvas.prototype.setContext = function () {
this._ctx = this._elem.get(0).getContext("2d");
return this._ctx;
};
/**
* Memory Leaks patch
*/
$.jqplot.GenericCanvas.prototype.resetCanvas = function () {
if (this._elem) {
if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement) {
window.G_vmlCanvasManager.uninitElement(this._elem.get(0));
}
//this._elem.remove();
this._elem.emptyForce();
}
this._ctx = null;
};
/**
* @class
* @constructor
*/
$.jqplot.HooksManager = function () {
this.hooks = [];
this.args = [];
};
$.jqplot.HooksManager.prototype.addOnce = function (fn, args) {
args = args || [];
var havehook = false,
i,
l;
for (i = 0, l = this.hooks.length; i < l; i++) {
if (this.hooks[i] === fn) {
havehook = true;
}
}
if (!havehook) {
this.hooks.push(fn);
this.args.push(args);
}
};
$.jqplot.HooksManager.prototype.add = function (fn, args) {
args = args || [];
this.hooks.push(fn);
this.args.push(args);
};
/**
* @class
* @constructor
*/
$.jqplot.EventListenerManager = function () {
this.hooks = [];
};
$.jqplot.EventListenerManager.prototype.addOnce = function (ev, fn) {
var havehook = false,
h,
i,
l;
for (i = 0, l = this.hooks.length; i < l; i++) {
h = this.hooks[i];
if (h[0] === ev && h[1] === fn) {
havehook = true;
}
}
if (!havehook) {
this.hooks.push([ev, fn]);
}
};
$.jqplot.EventListenerManager.prototype.add = function (ev, fn) {
this.hooks.push([ev, fn]);
};
/**
* @memberof $.jqplot
* @class
* @contructor
*
* Plot object returned by call to $.jqplot. Handles parsing user options,
* creating sub objects (Axes, legend, title, series) and rendering the plot.
*
*/
function JqPlot() {
var seriesColorsIndex = 0;
// Group: Properties
// These properties are specified at the top of the options object
// like so:
// > {
// > axesDefaults:{min:0},
// > series:[{color:'#6633dd'}],
// > title: 'A Plot'
// > }
//
// prop: animate
// True to animate the series on initial plot draw (renderer dependent).
// Actual animation functionality must be supported in the renderer.
this.animate = false;
// prop: animateReplot
// True to animate series after a call to the replot() method.
// Use with caution! Replots can happen very frequently under
// certain circumstances (e.g. resizing, dragging points) and
// animation in these situations can cause problems.
this.animateReplot = false;
// prop: axes
// up to 4 axes are supported, each with its own options,
// See <Axis> for axis specific options.
this.axes = {
xaxis: new Axis('xaxis'),
yaxis: new Axis('yaxis'),
x2axis: new Axis('x2axis'),
y2axis: new Axis('y2axis'),
y3axis: new Axis('y3axis'),
y4axis: new Axis('y4axis'),
y5axis: new Axis('y5axis'),
y6axis: new Axis('y6axis'),
y7axis: new Axis('y7axis'),
y8axis: new Axis('y8axis'),
y9axis: new Axis('y9axis'),
yMidAxis: new Axis('yMidAxis')
};
// jqplot base canvas. Lowest canvas.
this.baseCanvas = new $.jqplot.GenericCanvas();
// true to intercept right click events and fire a 'jqplotRightClick' event.
// this will also block the context menu.
this.captureRightClick = false;
// prop: data
// user's data. Data should *NOT* be specified in the options object,
// but be passed in as the second argument to the $.jqplot() function.
// The data property is described here soley for reference.
// The data should be in the form of an array of 2D or 1D arrays like
// > [ [[x1, y1], [x2, y2],...], [y1, y2, ...] ].
this.data = [];
// prop: dataRenderer
// A callable which can be used to preprocess data passed into the plot.
// Will be called with 3 arguments: the plot data, a reference to the plot,
// and the value of dataRendererOptions.
this.dataRenderer = null;
// prop: dataRendererOptions
// Options that will be passed to the dataRenderer.
// Can be of any type.
this.dataRendererOptions = null;
this.defaults = {
// prop: axesDefaults
// default options that will be applied to all axes.
// see <Axis> for axes options.
axesDefaults: {},
axes: {xaxis: {}, yaxis: {}, x2axis: {}, y2axis: {}, y3axis: {}, y4axis: {}, y5axis: {}, y6axis: {}, y7axis: {}, y8axis: {}, y9axis: {}, yMidAxis: {}},
// prop: seriesDefaults
// default options that will be applied to all series.
// see <Series> for series options.
seriesDefaults: {},
series: []
};
// prop: defaultAxisStart
// 1-D data series are internally converted into 2-D [x,y] data point arrays
// by jqPlot. This is the default starting value for the missing x or y value.
// The added data will be a monotonically increasing series (e.g. [1, 2, 3, ...])
// starting at this value.
this.defaultAxisStart = 1;
// this.doCustomEventBinding = true;
// prop: drawIfHidden
// True to execute the draw method even if the plot target is hidden.
// Generally, this should be false. Most plot elements will not be sized/
// positioned correclty if renderered into a hidden container. To render into
// a hidden container, call the replot method when the container is shown.
this.drawIfHidden = false;
this.eventCanvas = new $.jqplot.GenericCanvas();
this.bellowSeriesCanvas = new $.jqplot.GenericCanvas();
// prop: fillBetween
// Fill between 2 line series in a plot.
// Options object:
// {
// series1: first index (0 based) of series in fill
// series2: second index (0 based) of series in fill
// color: color of fill [default fillColor of series1]
// baseSeries: fill will be drawn below this series (0 based index)
// fill: false to turn off fill [default true].
// }
this.fillBetween = {
series1: null,
series2: null,
color: null,
baseSeries: 0,
fill: true
};
// prop; fontFamily
// css spec for the font-family attribute. Default for the entire plot.
this.fontFamily = null;
// prop: fontSize
// css spec for the font-size attribute. Default for the entire plot.
this.fontSize = null;
// prop: grid
// See <Grid> for grid specific options.
this.grid = new Grid();
// prop: legend
// see <$.jqplot.TableLegendRenderer>
this.legend = new Legend();
// prop: noDataIndicator
// Options to set up a mock plot with a data loading indicator if no data is specified.
this.noDataIndicator = {
show: false,
indicator: 'Loading Data...',
axes: {
xaxis: {
min: 0,
max: 10,
tickInterval: 2,
show: true
},
yaxis: {
min: 0,
max: 12,
tickInterval: 3,
show: true
}
}
};
// prop: negativeSeriesColors
// colors to use for portions of the line below zero.
this.negativeSeriesColors = $.jqplot.config.defaultNegativeColors;
// container to hold all of the merged options. Convienence for plugins.
this.options = {};
this.previousSeriesStack = [];
// Namespace to hold plugins. Generally non-renderer plugins add themselves to here.
this.plugins = {};
// prop: series
// Array of series object options.
// see <Series> for series specific options.
this.series = [];
// array of series indices. Keep track of order
// which series canvases are displayed, lowest
// to highest, back to front.
this.seriesStack = [];
// prop: seriesColors
// Ann array of CSS color specifications that will be applied, in order,
// to the series in the plot. Colors will wrap around so, if their
// are more series than colors, colors will be reused starting at the
// beginning. For pie charts, this specifies the colors of the slices.
this.seriesColors = $.jqplot.config.defaultColors;
// prop: sortData
// false to not sort the data passed in by the user.
// Many bar, stacked and other graphs as well as many plugins depend on
// having sorted data.
this.sortData = true;
// prop: stackSeries
// true or false, creates a stack or "mountain" plot.
// Not all series renderers may implement this option.
this.stackSeries = false;
// a shortcut for axis syncTicks options. Not implemented yet.
this.syncXTicks = true;
// a shortcut for axis syncTicks options. Not implemented yet.
this.syncYTicks = true;
// the jquery object for the dom target.
this.target = null;
// The id of the dom element to render the plot into
this.targetId = null;
// prop textColor
// css spec for the css color attribute. Default for the entire plot.
this.textColor = null;
// prop: title
// Title object. See <Title> for specific options. As a shortcut, you
// can specify the title option as just a string like: title: 'My Plot'
// and this will create a new title object with the specified text.
this.title = new Title();
// Count how many times the draw method has been called while the plot is visible.
// Mostly used to test if plot has never been dran (=0), has been successfully drawn
// into a visible container once (=1) or draw more than once into a visible container.
// Can use this in tests to see if plot has been visibly drawn at least one time.
// After plot has been visibly drawn once, it generally doesn't need redrawing if its
// container is hidden and shown.
this._drawCount = 0;
// sum of y values for all series in plot.
// used in mekko chart.
this._sumy = 0;
this._sumx = 0;
// array to hold the cumulative stacked series data.
// used to ajust the individual series data, which won't have access to other
// series data.
this._stackData = [];
// array that holds the data to be plotted. This will be the series data
// merged with the the appropriate data from _stackData according to the stackAxis.
this._plotData = [];
this._width = null;
this._height = null;
this._plotDimensions = {height: null, width: null};
this._gridPadding = {top: null, right: null, bottom: null, left: null};
this._defaultGridPadding = {top: 10, right: 10, bottom: 23, left: 10};
// Setting up $.jqplot plugins
this._addDomReference = $.jqplot.config.addDomReference;
this.preInitHooks = new $.jqplot.HooksManager();
this.postInitHooks = new $.jqplot.HooksManager();
this.preParseOptionsHooks = new $.jqplot.HooksManager();
this.postParseOptionsHooks = new $.jqplot.HooksManager();
this.preDrawHooks = new $.jqplot.HooksManager();
this.postDrawHooks = new $.jqplot.HooksManager();
this.preDrawSeriesHooks = new $.jqplot.HooksManager();
this.postDrawSeriesHooks = new $.jqplot.HooksManager();
this.preDrawLegendHooks = new $.jqplot.HooksManager();
this.addLegendRowHooks = new $.jqplot.HooksManager();
this.preSeriesInitHooks = new $.jqplot.HooksManager();
this.postSeriesInitHooks = new $.jqplot.HooksManager();
this.preParseSeriesOptionsHooks = new $.jqplot.HooksManager();
this.postParseSeriesOptionsHooks = new $.jqplot.HooksManager();
this.eventListenerHooks = new $.jqplot.EventListenerManager();
this.preDrawAllSeriesHooks = new $.jqplot.HooksManager();
this.preDrawSeriesShadowHooks = new $.jqplot.HooksManager();
this.postDrawSeriesShadowHooks = new $.jqplot.HooksManager();
this.colorGenerator = new $.jqplot.ColorGenerator();
this.negativeColorGenerator = new $.jqplot.ColorGenerator();
this.canvasManager = new $.jqplot.CanvasManager();
this.themeEngine = new $.jqplot.ThemeEngine();
/**
* Function to safely return colors from the color array and wrap around at the end.
* @param object {t}
* @returns String serie color
* @TODO: Figure out why this function is set to run immediately?
*/
this.getNextSeriesColor = (function (t) {
var idx = 0,
sc = t.seriesColors;
return function () {
if (idx < sc.length) {
return sc[idx++];
} else {
idx = 0;
return sc[idx++];
}
};
}(this));
}
/**
* Group: methods
*
* method: init
* sets the plot target, checks data and applies user
* options to plot.
*
* @param {object} target - html id of the html element which chart is inserted into.
* @param {number[]} data data - chart data
* @param {object} options - options of the chart.
*/
JqPlot.prototype.init = function (target, data, options) {
var i,
j,
l,
seriesLen,
h,
w,
ax,
prop,
name,
axis;
options = options || {};
for (i = 0, l = $.jqplot.preInitHooks.length; i < l; i++) {
$.jqplot.preInitHooks[i].call(this, target, data, options);
}
for (i = 0, l = this.preInitHooks.hooks.length; i < l; i++) {
this.preInitHooks.hooks[i].call(this, target, data, options);
}
this.targetId = '#' + target;
this.target = $('#' + target);
//////
// Add a reference to plot
//////
if (this._addDomReference) {
this.target.data('jqplot', this);
}
// remove any error class that may be stuck on target.
this.target.removeClass('jqplot-error');
if (!this.target.get(0)) {
throw new Error("No plot target specified");
}
// make sure the target is positioned by some means and set css
if (this.target.css('position') === 'static') {
this.target.css('position', 'relative');
}
if (!this.target.hasClass('jqplot-target')) {
this.target.addClass('jqplot-target');
}
// if no height or width specified, use a default.
if (!this.target.height()) {
if (options && options.height) {
h = parseInt(options.height, 10);
} else if (this.target.attr('data-height')) {
h = parseInt(this.target.attr('data-height'), 10);
} else {
h = parseInt($.jqplot.config.defaultHeight, 10);
}
this._height = h;
this.target.css('height', h + 'px');
} else {
this._height = h = this.target.height();
}
if (!this.target.width()) {
if (options && options.width) {
w = parseInt(options.width, 10);
} else if (this.target.attr('data-width')) {
w = parseInt(this.target.attr('data-width'), 10);
} else {
w = parseInt($.jqplot.config.defaultWidth, 10);
}
this._width = w;
this.target.css('width', w + 'px');
} else {
this._width = w = this.target.width();
}
for (i = 0, l = _axisNames.length; i < l; i++) {
this.axes[_axisNames[i]] = new Axis(_axisNames[i]);
}
this._plotDimensions.height = this._height;
this._plotDimensions.width = this._width;
this.grid._plotDimensions = this._plotDimensions;
this.title._plotDimensions = this._plotDimensions;
this.baseCanvas._plotDimensions = this._plotDimensions;
this.eventCanvas._plotDimensions = this._plotDimensions;
this.legend._plotDimensions = this._plotDimensions;
if (this._height <= 0 || this._width <= 0 || !this._height || !this._width) {
throw new Error("Canvas dimension not set");
}
if (options.dataRenderer && $.isFunction(options.dataRenderer)) {
if (options.dataRendererOptions) {
this.dataRendererOptions = options.dataRendererOptions;
}
this.dataRenderer = options.dataRenderer;
data = this.dataRenderer(data, this, this.dataRendererOptions);
}
if (options.noDataIndicator && $.isPlainObject(options.noDataIndicator)) {
$.extend(true, this.noDataIndicator, options.noDataIndicator);
}
if (data === null || $.isArray(data) === false || data.length === 0 || $.isArray(data[0]) === false || data[0].length === 0) {
if (this.noDataIndicator.show === false) {
throw new Error("No data specified. (Tips: Use noDataIndicator to show a noDataIndicator on chart)");
} else {
// have to be descructive here in order for plot to not try and render series.
// This means that $.jqplot() will have to be called again when there is data.
//delete options.series;
for (ax in this.noDataIndicator.axes) {
if (this.noDataIndicator.axes.hasOwnProperty(ax)) {
for (prop in this.noDataIndicator.axes[ax]) {
if (this.noDataIndicator.axes[ax].hasOwnProperty(prop)) {
this.axes[ax][prop] = this.noDataIndicator.axes[ax][prop];
}
}
}
}
this.postDrawHooks.add(function () {
var eh = this.eventCanvas.getHeight(),
ew = this.eventCanvas.getWidth(),
$temp = $('<div class="jqplot-noData-container" style="position:absolute;"></div>'),
$temp2 = $('<div class="jqplot-noData-contents" style="text-align:center; position:relative;"><div class="outer-center"><div class="inner-center"></div></div><div class="clear"></div></div>'),
th,
tw;
this.target.append($temp);
$temp
.height(eh)
.width(ew)
.css({'top': this.eventCanvas._offsets.top, 'left': this.eventCanvas._offsets.left })
.append($temp2);
$temp2.find(".inner-center").html(this.noDataIndicator.indicator);
th = $temp2.height();
tw = $temp2.width();
$temp2
.height(th)
.width(tw)
.css('top', (eh - th) / 2 + 'px');
});
}
}
// make a copy of the data
this.data = $.extend(true, [], data);
this.parseOptions(options);
if (this.textColor) {
this.target.css('color', this.textColor);
}
if (this.fontFamily) {
this.target.css('font-family', this.fontFamily);
}
if (this.fontSize) {
this.target.css('font-size', this.fontSize);
}
this.title.init();
this.legend.init();
this._sumy = 0;
this._sumx = 0;
this.computePlotData();
for (i = 0, seriesLen = this.series.length; i < seriesLen; i++) {
// set default stacking order for series canvases
this.seriesStack.push(i);
this.previousSeriesStack.push(i);
this.series[i].shadowCanvas._plotDimensions = this._plotDimensions;
this.series[i].canvas._plotDimensions = this._plotDimensions;
for (j = 0, l = $.jqplot.preSeriesInitHooks.length; j < l; j++) {
$.jqplot.preSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
}
for (j = 0, l = this.preSeriesInitHooks.hooks.length; j < l; j++) {
this.preSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
}
// this.populatePlotData(this.series[i], i);
this.series[i]._plotDimensions = this._plotDimensions;
this.series[i].init(i, this.grid.borderWidth, this);
for (j = 0, l = $.jqplot.postSeriesInitHooks.length; j < l; j++) {
$.jqplot.postSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
}
for (j = 0, l = this.postSeriesInitHooks.hooks.length; j < l; j++) {
this.postSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
}
this._sumy += this.series[i]._sumy;
this._sumx += this.series[i]._sumx;
}
for (i = 0, l = _axisNames.length; i < l; i++) {
name = _axisNames[i];
axis = this.axes[name];
axis._plotDimensions = this._plotDimensions;
axis.init();
if (this.axes[name].borderColor === null) {
if (name.charAt(0) !== 'x' && axis.useSeriesColor === true && axis.show) {
axis.borderColor = axis._series[0].color;
} else {
axis.borderColor = this.grid.borderColor;
}
}
}
if (this.sortData) {
sortData(this.series);
}
this.grid.init();
this.grid._axes = this.axes;
this.legend._series = this.series;
for (i = 0, l = $.jqplot.postInitHooks.length; i < l; i++) {
$.jqplot.postInitHooks[i].call(this, target, this.data, options);
}
for (i = 0, l = this.postInitHooks.hooks.length; i < l; i++) {
this.postInitHooks.hooks[i].call(this, target, this.data, options);
}
};
/**
* method: resetAxesScale
* Reset the specified axes min, max, numberTicks and tickInterval properties to null
* or reset these properties on all axes if no list of axes is provided.
*
* Parameters:
* @param {boolean} axes - Boolean to reset or not reset all axes or an array or object of axis names to reset.
* @param {object} options - Options to reset.
*/
JqPlot.prototype.resetAxesScale = function (axes, options) {
var opts = options || {},
ax = axes || this.axes,
i,
l,
name;
if (ax === true) {
ax = this.axes;
}
if ($.isArray(ax)) {
for (i = 0, l = ax.length; i < l; i++) {
this.axes[ax[i]].resetScale(opts[ax[i]]);
}
} else if (typeof (ax) === 'object') {
for (name in ax) {
if (ax.hasOwnProperty(name)) {
this.axes[name].resetScale(opts[name]);
}
}
}
};
/**
* method: reInitialize
* reinitialize plot for replotting.
* not called directly.
*
* @param {number[]} data - data of plot.
* @param {object} opts - options of plot.
*/
JqPlot.prototype.reInitialize = function (data, opts) {
// Plot should be visible and have a height and width.
// If plot doesn't have height and width for some
// reason, set it by other means. Plot must not have
// a display:none attribute, however.
var options = $.extend(true, {}, this.options, opts),
target = this.targetId.substr(1),
tdata = (data === null) ? this.data : data,
i,
l,
seriesLen,
name,
t,
j,
axis,
el,
tlen;
for (i = 0, l = $.jqplot.preInitHooks.length; i < l; i++) {
$.jqplot.preInitHooks[i].call(this, target, tdata, options);
}
for (i = 0, l = this.preInitHooks.hooks.length; i < l; i++) {
this.preInitHooks.hooks[i].call(this, target, tdata, options);
}
this._height = this.target.height();
this._width = this.target.width();
if (this._height <= 0 || this._width <= 0 || !this._height || !this._width) {
throw new Error("Target dimension not set");
}
this._plotDimensions.height = this._height;
this._plotDimensions.width = this._width;
this.grid._plotDimensions = this._plotDimensions;
this.title._plotDimensions = this._plotDimensions;
this.baseCanvas._plotDimensions = this._plotDimensions;
this.eventCanvas._plotDimensions = this._plotDimensions;
this.legend._plotDimensions = this._plotDimensions;
for (i = 0, l = _axisNames.length; i < l; i++) {
name = _axisNames[i];
axis = this.axes[name];
// Memory Leaks patch : clear ticks elements
t = axis._ticks;
for (j = 0, tlen = t.length; j < tlen; j++) {
el = t[j]._elem;
if (el) {
// if canvas renderer
if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement) {
window.G_vmlCanvasManager.uninitElement(el.get(0));
}
el.emptyForce();
el = null;
t._elem = null;
}
}
t = null;
delete axis.ticks;
delete axis._ticks;
this.axes[name] = new Axis(name);
this.axes[name]._plotWidth = this._width;
this.axes[name]._plotHeight = this._height;
}
if (data) {
if (options.dataRenderer && $.isFunction(options.dataRenderer)) {
if (options.dataRendererOptions) {
this.dataRendererOptions = options.dataRendererOptions;
}
this.dataRenderer = options.dataRenderer;
data = this.dataRenderer(data, this, this.dataRendererOptions);
}
// make a copy of the data
this.data = $.extend(true, [], data);
}
if (opts) {
this.parseOptions(options);
}
this.title._plotWidth = this._width;
if (this.textColor) {
this.target.css('color', this.textColor);
}
if (this.fontFamily) {
this.target.css('font-family', this.fontFamily);
}
if (this.fontSize) {
this.target.css('font-size', this.fontSize);
}
this.title.init();
this.legend.init();
this._sumy = 0;
this._sumx = 0;
this.seriesStack = [];
this.previousSeriesStack = [];
this.computePlotData();
for (i = 0, seriesLen = this.series.length; i < seriesLen; i++) {
// set default stacking order for series canvases
this.seriesStack.push(i);
this.previousSeriesStack.push(i);
this.series[i].shadowCanvas._plotDimensions = this._plotDimensions;
this.series[i].canvas._plotDimensions = this._plotDimensions;
for (j = 0, l = $.jqplot.preSeriesInitHooks.length; j < l; j++) {
$.jqplot.preSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
}
for (j = 0, l = this.preSeriesInitHooks.hooks.length; j < l; j++) {
this.preSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
}
// this.populatePlotData(this.series[i], i);
this.series[i]._plotDimensions = this._plotDimensions;
this.series[i].init(i, this.grid.borderWidth, this);
for (j = 0, l = $.jqplot.postSeriesInitHooks.length; j < l; j++) {
$.jqplot.postSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
}
for (j = 0, l = this.postSeriesInitHooks.hooks.length; j < l; j++) {
this.postSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this);
}
this._sumy += this.series[i]._sumy;
this._sumx += this.series[i]._sumx;
}
for (i = 0, l = _axisNames.length; i < l; i++) {
name = _axisNames[i];
axis = this.axes[name];
axis._plotDimensions = this._plotDimensions;
axis.init();
if (axis.borderColor === null) {
if (name.charAt(0) !== 'x' && axis.useSeriesColor === true && axis.show) {
axis.borderColor = axis._series[0].color;
} else {
axis.borderColor = this.grid.borderColor;
}
}
}
if (this.sortData) {
sortData(this.series);
}
this.grid.init();
this.grid._axes = this.axes;
this.legend._series = this.series;
for (i = 0, l = $.jqplot.postInitHooks.length; i < l; i++) {
$.jqplot.postInitHooks[i].call(this, target, this.data, options);
}
for (i = 0, l = this.postInitHooks.hooks.length; i < l; i++) {
this.postInitHooks.hooks[i].call(this, target, this.data, options);
}
};
/**
* method: quickInit
*
* Quick reinitialization plot for replotting.
* Does not parse options ore recreate axes and series.
* not called directly.
*/
JqPlot.prototype.quickInit = function () {
var ax,
name,
i,
j,
l,
t,
el;
// Plot should be visible and have a height and width.
// If plot doesn't have height and width for some
// reason, set it by other means. Plot must not have
// a display:none attribute, however.
this._height = this.target.height();
this._width = this.target.width();
if (this._height <= 0 || this._width <= 0 || !this._height || !this._width) {
throw new Error("Target dimension not set");
}
this._plotDimensions.height = this._height;
this._plotDimensions.width = this._width;
this.grid._plotDimensions = this._plotDimensions;
this.title._plotDimensions = this._plotDimensions;
this.baseCanvas._plotDimensions = this._plotDimensions;
this.eventCanvas._plotDimensions = this._plotDimensions;
this.legend._plotDimensions = this._plotDimensions;
for (ax in this.axes) {
if (this.axes.hasOwnProperty(ax)) {
this.axes[ax]._plotWidth = this._width;
this.axes[ax]._plotHeight = this._height;
}
}
this.title._plotWidth = this._width;
if (this.textColor) {
this.target.css('color', this.textColor);
}
if (this.fontFamily) {
this.target.css('font-family', this.fontFamily);
}
if (this.fontSize) {
this.target.css('font-size', this.fontSize);
}
this._sumy = 0;
this._sumx = 0;
this.computePlotData();
for (i = 0, l = this.series.length; i < l; i++) {
// this.populatePlotData(this.series[i], i);
if (this.series[i]._type === 'line' && this.series[i].renderer.bands.show) {
this.series[i].renderer.initBands.call(this.series[i], this.series[i].renderer.options, this);
}
this.series[i]._plotDimensions = this._plotDimensions;
this.series[i].canvas._plotDimensions = this._plotDimensions;
//this.series[i].init(i, this.grid.borderWidth);
this._sumy += this.series[i]._sumy;
this._sumx += this.series[i]._sumx;
}
for (j = 0; j < 12; j++) {
name = _axisNames[j];
// Memory Leaks patch : clear ticks elements
t = this.axes[name]._ticks;
for (i = 0, l = t.length; i < l; i++) {
el = t[i]._elem;
if (el) {
// if canvas renderer
if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement) {
window.G_vmlCanvasManager.uninitElement(el.get(0));
}
el.emptyForce();
el = null;
t._elem = null;
}
}
t = null;
this.axes[name]._plotDimensions = this._plotDimensions;
this.axes[name]._ticks = [];
// this.axes[name].renderer.init.call(this.axes[name], {});
}
if (this.sortData) {
sortData(this.series);
}
this.grid._axes = this.axes;
this.legend._series = this.series;
};
/**
*
*/
JqPlot.prototype.computePlotData = function () {
var series,
index,
i,
l,
j,
cd = [],
plotValues = {},
sidx,
k,
cdl,
temp,
prevval,
dtlen;
this._plotData = [];
this._stackData = [];
for (index = 0, l = this.series.length; index < l; index++) {
series = this.series[index];
this._plotData.push([]);
this._stackData.push([]);
cd = series.data;
this._plotData[index] = $.extend(true, [], cd);
this._stackData[index] = $.extend(true, [], cd);
series._plotData = this._plotData[index];
series._stackData = this._stackData[index];
plotValues = {x: [], y: []};
if (this.stackSeries && !series.disableStack) {
series._stack = true;
///////////////////////////
// have to check for nulls
///////////////////////////
sidx = (series._stackAxis === 'x') ? 0 : 1;
for (k = 0, cdl = cd.length; k < cdl; k++) {
temp = cd[k][sidx];
if (temp === null) {
temp = 0;
}
this._plotData[index][k][sidx] = temp;
this._stackData[index][k][sidx] = temp;
if (index > 0) {
j = index;
while (j--) {
prevval = this._plotData[j][k][sidx];
// only need to sum up the stack axis column of data
// and only sum if it is of same sign.
// if previous series isn't same sign, keep looking
// at earlier series untill we find one of same sign.
if (temp * prevval >= 0) {
this._plotData[index][k][sidx] += prevval;
this._stackData[index][k][sidx] += prevval;
break;
}
}
}
}
} else {
for (i = 0, dtlen = series.data.length; i < dtlen; i++) {
plotValues.x.push(series.data[i][0]);
plotValues.y.push(series.data[i][1]);
}
this._stackData.push(series.data);
this.series[index]._stackData = series.data;
this._plotData.push(series.data);
series._plotData = series.data;
series._plotValues = plotValues;
}
if (index > 0) {
series._prevPlotData = this.series[index - 1]._plotData;
}
series._sumy = 0;
series._sumx = 0;
for (i = series.data.length - 1; i > -1; i--) {
series._sumy += series.data[i][1];
series._sumx += series.data[i][0];
}
}
};
/**
* Populates the _stackData and _plotData arrays for the plot and the series.
* @param {object[]} series - array object.
* @param {number} index - index of the serie.
*/
JqPlot.prototype.populatePlotData = function (series, index) {
var plotValues = {x: [], y: []},
sidx,
temp,
plotdata,
tempx,
tempy,
dval,
stackval,
comparator,
i,
j,
cd,
l,
k;
// if a stacked chart, compute the stacked data
this._plotData = [];
this._stackData = [];
series._stackData = [];
series._plotData = [];
if (this.stackSeries && !series.disableStack) {
series._stack = true;
sidx = (series._stackAxis === 'x') ? 0 : 1;
// var idx = sidx ? 0 : 1;
// push the current data into stackData
//this._stackData.push(this.series[i].data);
temp = $.extend(true, [], series.data);
// create the data that will be plotted for this series
plotdata = $.extend(true, [], series.data);
// for first series, nothing to add to stackData.
for (j = 0; j < index; j++) {
cd = this.series[j].data;
for (k = 0, l = cd.length; k < l; k++) {
dval = cd[k];
tempx = (dval[0] !== null) ? dval[0] : 0;
tempy = (dval[1] !== null) ? dval[1] : 0;
temp[k][0] += tempx;
temp[k][1] += tempy;
stackval = (sidx) ? tempy : tempx;
// only need to sum up the stack axis column of data
// and only sum if it is of same sign.
if (series.data[k][sidx] * stackval >= 0) {
plotdata[k][sidx] += stackval;
}
}
}
for (i = 0, l = plotdata.length; i < l; i++) {
plotValues.x.push(plotdata[i][0]);
plotValues.y.push(plotdata[i][1]);
}
this._plotData.push(plotdata);
this._stackData.push(temp);
series._stackData = temp;
series._plotData = plotdata;
series._plotValues = plotValues;
} else {
for (i = 0, l = series.data.length; i < l; i++) {
plotValues.x.push(series.data[i][0]);
plotValues.y.push(series.data[i][1]);
}
this._stackData.push(series.data);
this.series[index]._stackData = series.data;
this._plotData.push(series.data);
series._plotData = series.data;
series._plotValues = plotValues;
}
if (index > 0) {
series._prevPlotData = this.series[index - 1]._plotData;
}
series._sumy = 0;
series._sumx = 0;
for (i = series.data.length - 1; i > -1; i--) {
series._sumy += series.data[i][1];
series._sumx += series.data[i][0];
}
};
/**
* @param {object} options options object to parse.
*/
JqPlot.prototype.parseOptions = function (options) {
var i = 0,
j = 0,
l = 0,
dataLen = 0,
opts,
temp = ['series1', 'series2', 'color', 'baseSeries', 'fill'],
tempi,
n,
axis,
normalizeData = function (data, dir, start) {
// return data as an array of point arrays,
// in form [[x1,y1...], [x2,y2...], ...]
var temp = [],
i,
l,
j;
dir = dir || 'vertical';
if (!$.isArray(data[0])) {
// we have a series of scalars. One line with just y values.
// turn the scalar list of data into a data array of form:
// [[1, data[0]], [2, data[1]], ...]
for (i = 0, l = data.length; i < l; i++) {
if (dir === 'vertical') {
temp.push([start + i, data[i]]);
} else {
temp.push([data[i], start + i]);
}
}
} else {
// we have a properly formatted data series, copy it.
$.extend(true, temp, data);
}
return temp;
},
colorIndex = 0,
sopts,
dir = 'vertical';
for (i = 0, l = this.preParseOptionsHooks.hooks.length; i < l; i++) {
this.preParseOptionsHooks.hooks[i].call(this, options);
}
for (i = 0, l = $.jqplot.preParseOptionsHooks.length; i < l; i++) {
$.jqplot.preParseOptionsHooks[i].call(this, options);
}
this.options = $.extend(true, {}, this.defaults, options);
opts = this.options;
this.animate = opts.animate;
this.animateReplot = opts.animateReplot;
this.stackSeries = opts.stackSeries;
if ($.isPlainObject(opts.fillBetween)) {
for (i = 0, l = temp.length; i < l; i++) {
tempi = temp[i];
if (opts.fillBetween[tempi] !== null) {
this.fillBetween[tempi] = opts.fillBetween[tempi];
}
}
}
if (opts.seriesColors) {
this.seriesColors = opts.seriesColors;
}
if (opts.negativeSeriesColors) {
this.negativeSeriesColors = opts.negativeSeriesColors;
}
if (opts.captureRightClick) {
this.captureRightClick = opts.captureRightClick;
}
this.defaultAxisStart = (options && typeof options.defaultAxisStart !== "undefined") ? options.defaultAxisStart : this.defaultAxisStart;
this.colorGenerator.setColors(this.seriesColors);
this.negativeColorGenerator.setColors(this.negativeSeriesColors);
// var cg = new this.colorGenerator(this.seriesColors);
// var ncg = new this.colorGenerator(this.negativeSeriesColors);
// this._gridPadding = this.options.gridPadding;
$.extend(true, this._gridPadding, opts.gridPadding);
this.sortData = (typeof opts.sortData !== "undefined") ? opts.sortData : this.sortData;
for (i = 0; i < 12; i++) {
n = _axisNames[i];
axis = this.axes[n];
axis._options = $.extend(true, {}, opts.axesDefaults, opts.axes[n]);
$.extend(true, axis, opts.axesDefaults, opts.axes[n]);
axis._plotWidth = this._width;
axis._plotHeight = this._height;
}
// if (this.data.length == 0) {
// this.data = [];
// for (var i=0; i<this.options.series.length; i++) {
// this.data.push(this.options.series.data);
// }
// }
this.series = [];
for (i = 0, dataLen = this.data.length; i < dataLen; i++) {
sopts = $.extend(true, {index: i}, { seriesColors: this.seriesColors, negativeSeriesColors: this.negativeSeriesColors}, this.options.seriesDefaults, this.options.series[i], {rendererOptions: {animation: {show: this.animate}}});
// pass in options in case something needs set prior to initialization.
temp = new Series(sopts);
for (j = 0, l = $.jqplot.preParseSeriesOptionsHooks.length; j < l; j++) {
$.jqplot.preParseSeriesOptionsHooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]);
}
for (j = 0, l = this.preParseSeriesOptionsHooks.hooks.length; j < l; j++) {
this.preParseSeriesOptionsHooks.hooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]);
}
// Now go back and apply the options to the series. Really should just do this during initializaiton, but don't want to
// mess up preParseSeriesOptionsHooks at this point.
$.extend(true, temp, sopts);
if (temp.renderer === $.jqplot.BarRenderer && temp.rendererOptions && temp.rendererOptions.barDirection === 'horizontal') {
dir = 'horizontal';
temp._stackAxis = 'x';
temp._primaryAxis = '_yaxis';
}
temp.data = normalizeData(this.data[i], dir, this.defaultAxisStart);
switch (temp.xaxis) {
case 'xaxis':
temp._xaxis = this.axes.xaxis;
break;
case 'x2axis':
temp._xaxis = this.axes.x2axis;
break;
default:
break;
}
temp._yaxis = this.axes[temp.yaxis];
temp._xaxis._series.push(temp);
temp._yaxis._series.push(temp);
if (temp.show) {
temp._xaxis.show = true;
temp._yaxis.show = true;
} else {
if (temp._xaxis.scaleToHiddenSeries) {
temp._xaxis.show = true;
}
if (temp._yaxis.scaleToHiddenSeries) {
temp._yaxis.show = true;
}
}
// // parse the renderer options and apply default colors if not provided
// if (!temp.color && temp.show != false) {
// temp.color = cg.next();
// colorIndex = cg.getIndex() - 1;;
// }
// if (!temp.negativeColor && temp.show != false) {
// temp.negativeColor = ncg.get(colorIndex);
// ncg.setIndex(colorIndex);
// }
if (!temp.label) {
temp.label = 'Series ' + (i + 1).toString();
}
// temp.rendererOptions.show = temp.show;
// $.extend(true, temp.renderer, {color:this.seriesColors[i]}, this.rendererOptions);
this.series.push(temp);
for (j = 0, l = $.jqplot.postParseSeriesOptionsHooks.length; j < l; j++) {
$.jqplot.postParseSeriesOptionsHooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]);
}
for (j = 0, l = this.postParseSeriesOptionsHooks.hooks.length; j < l; j++) {
this.postParseSeriesOptionsHooks.hooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]);
}
}
// copy the grid and title options into this object.
$.extend(true, this.grid, this.options.grid);
// if axis border properties aren't set, set default.
for (i = 0, l = _axisNames.length; i < l; i++) {
n = _axisNames[i];
axis = this.axes[n];
if (axis.borderWidth === null) {
axis.borderWidth = this.grid.borderWidth;
}
}
if (typeof this.options.title === 'string') {
this.title.text = this.options.title;
} else if (typeof this.options.title === 'object') {
$.extend(true, this.title, this.options.title);
}
this.title._plotWidth = this._width;
this.legend.setOptions(this.options.legend);
for (i = 0, l = $.jqplot.postParseOptionsHooks.length; i < l; i++) {
$.jqplot.postParseOptionsHooks[i].call(this, options);
}
for (i = 0, l = this.postParseOptionsHooks.hooks.length; i < l; i++) {
this.postParseOptionsHooks.hooks[i].call(this, options);
}
};
/**
* method: destroy
* Releases all resources occupied by the plot
*/
JqPlot.prototype.destroy = function () {
var e,
events;
this.canvasManager.freeAllCanvases();
if (this.eventCanvas && this.eventCanvas._elem) {
this.eventCanvas._elem.unbind();
}
events = $._data(this.target[0], "events");
// Unbind events
// For example, Cursor.zoomProxy bind jqplotZoom and jqplotResetZoom to element
for (e in events) {
if (events.hasOwnProperty(e)) {
this.target.unbind(e);
}
}
// Couple of posts on Stack Overflow indicate that empty() doesn't
// always cear up the dom and release memory. Sometimes setting
// innerHTML property to null is needed. Particularly on IE, may
// have to directly set it to null, bypassing $.
this.target.empty();
this.target[0].innerHTML = '';
};
/**
* method: replot
* Does a reinitialization of the plot followed by
* a redraw. Method could be used to interactively
* change plot characteristics and then replot.
*
* Parameters:
* options - Options used for replotting.
*
* Properties:
* @param {boolean} clear - false to not clear (empty) the plot container before replotting (default: true).
* @param {boolean} resetAxes - true to reset all axes min, max, numberTicks and tickInterval setting so axes will rescale themselves.
* optionally pass in list of axes to reset (e.g. ['xaxis', 'y2axis']) (default: false).
*/
JqPlot.prototype.replot = function (options) {
var opts = options || {},
data = opts.data || null,
clear = (opts.clear === false) ? false : true,
resetAxes = opts.resetAxes || false;
delete opts.data;
delete opts.clear;
delete opts.resetAxes;
this.target.trigger('jqplotPreReplot');
if (clear) {
this.destroy();
}
// if have data or other options, full reinit.
// otherwise, quickinit.
if (data || !$.isEmptyObject(opts)) {
this.reInitialize(data, opts);
} else {
this.quickInit();
}
if (resetAxes) {
this.resetAxesScale(resetAxes, opts.axes);
}
this.draw();
this.target.trigger('jqplotPostReplot');
};
/**
* method: redraw
* Empties the plot target div and redraws the plot.
* This enables plot data and properties to be changed
* and then to comletely clear the plot and redraw.
* redraw *will not* reinitialize any plot elements.
* That is, axes will not be autoscaled and defaults
* will not be reapplied to any plot elements. redraw
* is used primarily with zooming.
*
* @param {boolean} clear false to not clear (empty) the plot container before redrawing (default: true).
*/
JqPlot.prototype.redraw = function (clear) {
var i,
ax,
tsl;
clear = (clear !== null) ? clear : true;
this.target.trigger('jqplotPreRedraw');
if (clear) {
this.canvasManager.freeAllCanvases();
this.eventCanvas._elem.unbind();
// Dont think I bind any events to the target, this shouldn't be necessary.
// It will remove user's events.
// this.target.unbind();
this.target.empty();
}
for (ax in this.axes) {
if (this.axes.hasOwnProperty(ax)) {
this.axes[ax]._ticks = [];
}
}
this.computePlotData();
// for (var i=0; i<this.series.length; i++) {
// this.populatePlotData(this.series[i], i);
// }
this._sumy = 0;
this._sumx = 0;
for (i = 0, tsl = this.series.length; i < tsl; i++) {
this._sumy += this.series[i]._sumy;
this._sumx += this.series[i]._sumx;
}
this.draw();
this.target.trigger('jqplotPostRedraw');
};
/**
* method: draw
* Draws all elements of the plot into the container.
* Does not clear the container before drawing.
*/
JqPlot.prototype.draw = function () {
var i,
j,
l,
tempseries,
legendElem,
gridPadding,
ax,
name,
ra = ['y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis'],
rapad = [0, 0, 0, 0, 0, 0, 0, 0],
gpr = 0,
n,
gdw,
gdh,
widthAdj,
heightAdj,
positions = ['top', 'bottom', 'left', 'right'],
legendPadding,
ltemp,
series,
seriesLength,
fb,
temps,
tempr,
sel,
_els,
title;
if (this.drawIfHidden || this.target.is(':visible')) {
this.target.trigger('jqplotPreDraw');
for (i = 0, l = $.jqplot.preDrawHooks.length; i < l; i++) {
$.jqplot.preDrawHooks[i].call(this);
}
for (i = 0, l = this.preDrawHooks.hooks.length; i < l; i++) {
this.preDrawHooks.hooks[i].apply(this, this.preDrawSeriesHooks.args[i]);
}
// create an underlying canvas to be used for special features.
this.target.append(this.baseCanvas.createElement({left: 0, right: 0, top: 0, bottom: 0}, 'jqplot-base-canvas', null, this));
this.baseCanvas.setContext();
if (this.options.background) {
this.baseCanvas._ctx.fillStyle = this.options.background;
this.baseCanvas._ctx.fillRect(0, 0, this.baseCanvas._ctx.canvas.width, this.baseCanvas._ctx.canvas.height);
}
title = this.title.draw();
this.target.append(title);
this.title.pack({top: 0, left: 0});
// make room for the legend between the grid and the edge.
// pass a dummy offsets object and a reference to the plot.
legendElem = this.legend.draw({}, this);
gridPadding = {top: 0, left: 0, bottom: 0, right: 0};
if (legendElem && this.legend.placement === "outsideGrid") {
// temporarily append the legend to get dimensions
this.target.append(legendElem);
switch (this.legend.location) {
case 'n':
gridPadding.top += this.legend.getHeight();
break;
case 's':
gridPadding.bottom += this.legend.getHeight();
break;
case 'ne':
case 'e':
case 'se':
gridPadding.right += this.legend.getWidth();
break;
case 'nw':
case 'w':
case 'sw':
gridPadding.left += this.legend.getWidth();
break;
default: // same as 'ne'
gridPadding.right += this.legend.getWidth();
break;
}
legendElem = legendElem.detach();
}
ax = this.axes;
// draw the yMidAxis first, so xaxis of pyramid chart can adjust itself if needed.
for (i = 0; i < 12; i++) {
name = _axisNames[i];
this.target.append(ax[name].draw(this.baseCanvas._ctx, this));
ax[name].set();
}
if (ax.yaxis.show) {
gridPadding.left += ax.yaxis.getWidth();
}
for (n = 0; n < 8; n++) {
if (ax[ra[n]].show) {
gpr += ax[ra[n]].getWidth();
rapad[n] = gpr;
}
}
gridPadding.right += gpr;
if (ax.x2axis.show) {
gridPadding.top += ax.x2axis.getHeight();
}
if (this.title.show) {
gridPadding.top += this.title.getHeight();
}
if (ax.xaxis.show) {
gridPadding.bottom += ax.xaxis.getHeight();
}
// end of gridPadding adjustments.
// if user passed in gridDimensions option, check against calculated gridPadding
if (this.options.gridDimensions && $.isPlainObject(this.options.gridDimensions)) {
gdw = parseInt(this.options.gridDimensions.width, 10) || 0;
gdh = parseInt(this.options.gridDimensions.height, 10) || 0;
widthAdj = (this._width - gridPadding.left - gridPadding.right - gdw) / 2;
heightAdj = (this._height - gridPadding.top - gridPadding.bottom - gdh) / 2;
if (heightAdj >= 0 && widthAdj >= 0) {
gridPadding.top += heightAdj;
gridPadding.bottom += heightAdj;
gridPadding.left += widthAdj;
gridPadding.right += widthAdj;
}
}
for (i = 0, l = positions.length; i < l; i++) {
if (this._gridPadding[positions[i]] === null && gridPadding[positions[i]] > 0) {
this._gridPadding[positions[i]] = gridPadding[positions[i]];
} else if (this._gridPadding[positions[i]] === null) {
this._gridPadding[positions[i]] = this._defaultGridPadding[positions[i]];
}
}
legendPadding = this._gridPadding;
if (this.legend.placement === 'outsideGrid') {
legendPadding = {
top: this.title.getHeight(),
left: 0,
right: 0,
bottom: 0
};
if (this.legend.location === 's') {
legendPadding.left = this._gridPadding.left;
legendPadding.right = this._gridPadding.right;
}
}
ax.xaxis.pack({position: 'absolute', bottom: this._gridPadding.bottom - ax.xaxis.getHeight(), left: 0, width: this._width}, {min: this._gridPadding.left, max: this._width - this._gridPadding.right});
ax.yaxis.pack({position: 'absolute', top: 0, left: this._gridPadding.left - ax.yaxis.getWidth(), height: this._height}, {min: this._height - this._gridPadding.bottom, max: this._gridPadding.top});
ax.x2axis.pack({position: 'absolute', top: this._gridPadding.top - ax.x2axis.getHeight(), left: 0, width: this._width}, {min: this._gridPadding.left, max: this._width - this._gridPadding.right});
for (i = 8; i > 0; i--) {
ax[ra[i - 1]].pack({position: 'absolute', top: 0, right: this._gridPadding.right - rapad[i - 1]}, {min: this._height - this._gridPadding.bottom, max: this._gridPadding.top});
}
ltemp = (this._width - this._gridPadding.left - this._gridPadding.right) / 2.0 + this._gridPadding.left - ax.yMidAxis.getWidth() / 2.0;
ax.yMidAxis.pack({position: 'absolute', top: 0, left: ltemp, zIndex: 9, textAlign: 'center'}, {min: this._height - this._gridPadding.bottom, max: this._gridPadding.top});
this.target.append(this.grid.createElement(this._gridPadding, this));
this.grid.draw();
for (i = 0, l = $.jqplot.preDrawAllSeriesHooks.length; i < l; i++) {
$.jqplot.preDrawAllSeriesHooks[i].call(this);
}
for (i = 0, l = this.preDrawAllSeriesHooks.hooks.length; i < l; i++) {
this.preDrawAllSeriesHooks.hooks[i].apply(this, this.preDrawAllSeriesHooks.args[i]);
}
// Need to use filled canvas to capture events in IE.
// Also, canvas seems to block selection of other elements in document on FF.
this.target.append(this.bellowSeriesCanvas.createElement(this._gridPadding, 'jqplot-bellow-series-canvas', null, this));
this.bellowSeriesCanvas.setContext();
this.bellowSeriesCanvas._ctx.fillStyle = 'rgba(0, 0, 0 ,0)';
this.bellowSeriesCanvas._ctx.fillRect(0, 0, this.bellowSeriesCanvas._ctx.canvas.width, this.bellowSeriesCanvas._ctx.canvas.height);
series = this.series;
seriesLength = series.length;
// put the shadow canvases behind the series canvases so shadows don't overlap on stacked bars.
for (i = 0; i < seriesLength; i++) {
// draw series in order of stacking. This affects only
// order in which canvases are added to dom.
j = this.seriesStack[i];
this.target.append(series[j].shadowCanvas.createElement(this._gridPadding, 'jqplot-series-shadowCanvas', null, this));
series[j].shadowCanvas.setContext();
series[j].shadowCanvas._elem.data('seriesIndex', j);
}
for (i = 0; i < seriesLength; i++) {
// draw series in order of stacking. This affects only
// order in which canvases are added to dom.
j = this.seriesStack[i];
this.target.append(series[j].canvas.createElement(this._gridPadding, 'jqplot-series-canvas', null, this));
series[j].canvas.setContext();
series[j].canvas._elem.data('seriesIndex', j);
}
// Need to use filled canvas to capture events in IE.
// Also, canvas seems to block selection of other elements in document on FF.
this.target.append(this.eventCanvas.createElement(this._gridPadding, 'jqplot-event-canvas', null, this));
this.eventCanvas.setContext();
this.eventCanvas._ctx.fillStyle = 'rgba(0, 0, 0, 0)';
this.eventCanvas._ctx.fillRect(0, 0, this.eventCanvas._ctx.canvas.width, this.eventCanvas._ctx.canvas.height);
// bind custom event handlers to regular events.
this.bindCustomEvents();
// draw legend before series if the series needs to know the legend dimensions.
if (this.legend.preDraw) {
this.eventCanvas._elem.before(legendElem);
this.legend.pack(legendPadding);
if (this.legend._elem) {
this.drawSeries({legendInfo: { location: this.legend.location, placement: this.legend.placement, width: this.legend.getWidth(), height: this.legend.getHeight(), xoffset: this.legend.xoffset, yoffset: this.legend.yoffset}});
} else {
this.drawSeries();
}
} else { // draw series before legend
this.drawSeries();
if (seriesLength) {
$(series[seriesLength - 1].canvas._elem).after(legendElem);
}
this.legend.pack(legendPadding);
}
// register event listeners on the overlay canvas
for (i = 0, l = $.jqplot.eventListenerHooks.length; i < l; i++) {
// in the handler, this will refer to the eventCanvas dom element.
// make sure there are references back into plot objects.
this.eventCanvas._elem.bind($.jqplot.eventListenerHooks[i][0], {plot: this}, $.jqplot.eventListenerHooks[i][1]);
}
// register event listeners on the overlay canvas
for (i = 0, l = this.eventListenerHooks.hooks.length; i < l; i++) {
// in the handler, this will refer to the eventCanvas dom element.
// make sure there are references back into plot objects.
this.eventCanvas._elem.bind(this.eventListenerHooks.hooks[i][0], {plot: this}, this.eventListenerHooks.hooks[i][1]);
}
fb = this.fillBetween;
if (fb.fill && fb.series1 !== fb.series2 && fb.series1 < seriesLength && fb.series2 < seriesLength && series[fb.series1]._type === 'line' && series[fb.series2]._type === 'line') {
this.doFillBetweenLines();
}
for (i = 0, l = $.jqplot.postDrawHooks.length; i < l; i++) {
$.jqplot.postDrawHooks[i].call(this);
}
for (i = 0, l = this.postDrawHooks.hooks.length; i < l; i++) {
this.postDrawHooks.hooks[i].apply(this, this.postDrawHooks.args[i]);
}
if (this.target.is(':visible')) {
this._drawCount += 1;
}
// ughh. ideally would hide all series then show them.
for (i = 0; i < seriesLength; i++) {
temps = series[i];
tempr = temps.renderer;
sel = '.jqplot-point-label.jqplot-series-' + i;
if (tempr.animation && tempr.animation._supported && tempr.animation.show && (this._drawCount < 2 || this.animateReplot)) {
_els = this.target.find(sel);
_els.stop(true, true).hide();
temps.canvas._elem.stop(true, true).hide();
temps.shadowCanvas._elem.stop(true, true).hide();
temps.canvas._elem.jqplotEffect('blind', { mode: 'show', direction: tempr.animation.direction}, tempr.animation.speed);
temps.shadowCanvas._elem.jqplotEffect('blind', { mode: 'show', direction: tempr.animation.direction}, tempr.animation.speed);
_els.fadeIn(tempr.animation.speed * 0.8);
}
}
_els = null;
this.target.trigger('jqplotPostDraw', [this]);
}
};
/**
*
*/
JqPlot.prototype.doFillBetweenLines = function () {
var fb = this.fillBetween,
sid1 = fb.series1,
sid2 = fb.series2,
// first series should always be lowest index
id1 = (sid1 < sid2) ? sid1 : sid2,
id2 = (sid2 > sid1) ? sid2 : sid1,
series1 = this.series[id1],
series2 = this.series[id2],
tempgd,
gd,
color,
baseSeries,
sr,
opts;
if (series2.renderer.smooth) {
tempgd = series2.renderer._smoothedData.slice(0).reverse();
} else {
tempgd = series2.gridData.slice(0).reverse();
}
if (series1.renderer.smooth) {
gd = series1.renderer._smoothedData.concat(tempgd);
} else {
gd = series1.gridData.concat(tempgd);
}
color = (fb.color !== null) ? fb.color : this.series[sid1].fillColor;
baseSeries = (fb.baseSeries !== null) ? fb.baseSeries : id1;
// now apply a fill to the shape on the lower series shadow canvas,
// so it is behind both series.
sr = this.series[baseSeries].renderer.shapeRenderer;
opts = {fillStyle: color, fill: true, closePath: true};
sr.draw(series1.shadowCanvas._ctx, gd, opts);
};
/**
* Set the theme name on the plot.
* @param {string} name theme name.
*/
JqPlot.prototype.activateTheme = function (name) {
this.themeEngine.activate(this, name);
};
/**
* method: restoreOriginalSeriesOrder
* This method requires jQuery 1.4+
* Restore the series canvas order to its original order
* when the plot was created.
*/
JqPlot.prototype.restoreOriginalSeriesOrder = function () {
var i,
j,
l,
arr = [],
serelem,
shadelem;
for (i = 0, l = this.series.length; i < l; i++) {
arr.push(i);
}
if (this.seriesStack === arr) {
return;
}
this.previousSeriesStack = this.seriesStack.slice(0);
this.seriesStack = arr;
for (i = 1; i < this.seriesStack.length; i++) {
serelem = this.series[i].canvas._elem.detach();
shadelem = this.series[i].shadowCanvas._elem.detach();
this.series[i - 1].shadowCanvas._elem.after(shadelem);
this.series[i - 1].canvas._elem.after(serelem);
}
};
/**
* method: restorePreviousSeriesOrder
* This method requires jQuery 1.4+
* Restore the series canvas order to its previous state.
* Useful to put a series back where it belongs after moving
* it to the front.
*/
JqPlot.prototype.restorePreviousSeriesOrder = function () {
var i,
j,
l,
serelem,
shadelem,
temp,
move,
keep;
// if no change, return.
if (this.seriesStack === this.previousSeriesStack) {
return;
}
for (i = 1, l = this.previousSeriesStack.length; i < l; i++) {
move = this.previousSeriesStack[i];
keep = this.previousSeriesStack[i - 1];
serelem = this.series[move].canvas._elem.detach();
shadelem = this.series[move].shadowCanvas._elem.detach();
this.series[keep].shadowCanvas._elem.after(shadelem);
this.series[keep].canvas._elem.after(serelem);
}
temp = this.seriesStack.slice(0);
this.seriesStack = this.previousSeriesStack.slice(0);
this.previousSeriesStack = temp;
};
/**
* method: moveSeriesToBack
* This method requires jQuery 1.4+
* Moves the specified series canvas behind all other series canvases.
*
* @param {string} idx index of the serie to move to back. 0 based index of the series to move. This will be the index of the series
* as it was first passed into the jqplot function.
*/
JqPlot.prototype.moveSeriesToBack = function (idx) {
idx = parseInt(idx, 10);
var stackIndex = $.inArray(idx, this.seriesStack),
opidx,
serelem,
shadelem;
// if already in back, return
if (stackIndex === 0 || stackIndex === -1) {
return;
}
opidx = this.seriesStack[0];
serelem = this.series[idx].canvas._elem.detach();
shadelem = this.series[idx].shadowCanvas._elem.detach();
this.series[opidx].shadowCanvas._elem.before(shadelem);
this.series[opidx].canvas._elem.before(serelem);
this.previousSeriesStack = this.seriesStack.slice(0);
this.seriesStack.splice(stackIndex, 1);
this.seriesStack.unshift(idx);
};
/**
* method: moveSeriesToFront
* This method requires jQuery 1.4+
* Moves the specified series canvas in front of all other series canvases.
* This effectively "draws" the specified series on top of all other series,
* although it is performed through DOM manipulation, no redrawing is performed.
*
* @param {string} idx index of the serie to move to front. 0 based index of the series to move. This will be the index of the series
* as it was first passed into the jqplot function.
*/
JqPlot.prototype.moveSeriesToFront = function (idx) {
var stackIndex,
opidx,
serelem,
shadelem;
idx = parseInt(idx, 10);
stackIndex = $.inArray(idx, this.seriesStack);
// if already in front, return
if (stackIndex === -1) {
return;
}
if (stackIndex === this.seriesStack.length - 1) {
this.previousSeriesStack = this.seriesStack.slice(0);
return;
}
opidx = this.seriesStack[this.seriesStack.length - 1];
serelem = this.series[idx].canvas._elem.detach();
shadelem = this.series[idx].shadowCanvas._elem.detach();
this.series[opidx].shadowCanvas._elem.after(shadelem);
this.series[opidx].canvas._elem.after(serelem);
this.previousSeriesStack = this.seriesStack.slice(0);
this.seriesStack.splice(stackIndex, 1);
this.seriesStack.push(idx);
};
/**
* method: drawSeries
* Redraws all or just one series on the plot. No axis scaling
* is performed and no other elements on the plot are redrawn.
* options is an options object to pass on to the series renderers.
* It can be an empty object {}. idx is the series index
* to redraw if only one series is to be redrawn.
* @param {object} options options' series
* @param {number} idx Optional index of the serie to redraw.
*/
JqPlot.prototype.drawSeries = function (options, idx) {
var i,
l,
series,
ctx;
// if only one argument passed in and it is a number, use it ad idx.
idx = (typeof (options) === "number" && idx === null) ? options : idx;
options = (typeof (options) === "object") ? options : {};
// draw specified series
if (typeof idx !== "undefined") {
series = this.series[idx];
ctx = series.shadowCanvas._ctx;
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
series.drawShadow(ctx, options, this);
ctx = series.canvas._ctx;
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
series.draw(ctx, options, this);
if (series.renderer.constructor === $.jqplot.BezierCurveRenderer) {
if (idx < this.series.length - 1) {
this.drawSeries(idx + 1);
}
}
} else {
// if call series drawShadow method first, in case all series shadows
// should be drawn before any series. This will ensure, like for
// stacked bar plots, that shadows don't overlap series.
for (i = 0; i < this.series.length; i++) {
// first clear the canvas
series = this.series[i];
ctx = series.shadowCanvas._ctx;
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
series.drawShadow(ctx, options, this);
ctx = series.canvas._ctx;
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
series.draw(ctx, options, this);
}
}
options = idx = i = series = ctx = l = null;
};
/**
* Event handler onMouseLeave on event canvas.
* @param {object} ev event
*/
JqPlot.prototype.onMouseLeave = function (ev) {
var positions = getEventPosition(ev),
p = ev.data.plot,
evt = $.Event('jqplotMouseLeave');
evt.pageX = ev.pageX;
evt.pageY = ev.pageY;
evt.relatedTarget = ev.relatedTarget;
$(this).trigger(evt, [positions.gridPos, positions.dataPos, null, p]);
};
/**
* Event handler onClick on event canvas.
* @param {object} ev event
*/
JqPlot.prototype.onClick = function (ev) {
// Event passed in is normalized and will have data attribute.
// Event passed out is unnormalized.
var positions = getEventPosition(ev),
p = ev.data.plot,
neighbor = checkIntersection(positions.gridPos, p),
evt = $.Event('jqplotClick');
evt.pageX = ev.pageX;
evt.pageY = ev.pageY;
$(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
};
/**
* Event handler onDblClick on event canvas.
* @param {object} ev event
*/
JqPlot.prototype.onDblClick = function (ev) {
// Event passed in is normalized and will have data attribute.
// Event passed out is unnormalized.
var positions = getEventPosition(ev),
p = ev.data.plot,
neighbor = checkIntersection(positions.gridPos, p),
evt = $.Event('jqplotDblClick');
evt.pageX = ev.pageX;
evt.pageY = ev.pageY;
$(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
};
/**
* Event handler onMouseDown on event canvas.
* @param {object} ev event
*/
JqPlot.prototype.onMouseDown = function (ev) {
var positions = getEventPosition(ev),
p = ev.data.plot,
neighbor = checkIntersection(positions.gridPos, p),
evt = $.Event('jqplotMouseDown');
evt.pageX = ev.pageX;
evt.pageY = ev.pageY;
$(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
};
/**
* Event handler onMouseUp on event canvas.
* @param {object} ev event
*/
JqPlot.prototype.onMouseUp = function (ev) {
var positions = getEventPosition(ev),
evt = $.Event('jqplotMouseUp');
evt.pageX = ev.pageX;
evt.pageY = ev.pageY;
$(this).trigger(evt, [positions.gridPos, positions.dataPos, null, ev.data.plot]);
};
/**
* Event handler onRightClick on event canvas.
* @param {object} ev event
*/
JqPlot.prototype.onRightClick = function (ev) {
var positions = getEventPosition(ev),
p = ev.data.plot,
neighbor = checkIntersection(positions.gridPos, p),
evt;
if (p.captureRightClick) {
if (ev.which === 3) {
evt = $.Event('jqplotRightClick');
evt.pageX = ev.pageX;
evt.pageY = ev.pageY;
$(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
} else {
evt = $.Event('jqplotMouseUp');
evt.pageX = ev.pageX;
evt.pageY = ev.pageY;
$(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
}
}
};
/**
* Event handler onMouseMove on event canvas.
* @param {object} ev event
*/
JqPlot.prototype.onMouseMove = function (ev) {
var positions = getEventPosition(ev),
p = ev.data.plot,
neighbor = checkIntersection(positions.gridPos, p),
evt = $.Event('jqplotMouseMove');
evt.pageX = ev.pageX;
evt.pageY = ev.pageY;
$(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]);
};
/**
* Event handler onMouseEnter on event canvas.
* @param {object} ev event.
*/
JqPlot.prototype.onMouseEnter = function (ev) {
var positions = getEventPosition(ev),
p = ev.data.plot,
evt = $.Event('jqplotMouseEnter');
evt.pageX = ev.pageX;
evt.pageY = ev.pageY;
evt.relatedTarget = ev.relatedTarget;
$(this).trigger(evt, [positions.gridPos, positions.dataPos, null, p]);
};
/**
* Bind events attached to canvas.
*/
JqPlot.prototype.bindCustomEvents = function () {
this.eventCanvas._elem.bind('click', {plot: this}, this.onClick);
this.eventCanvas._elem.bind('dblclick', {plot: this}, this.onDblClick);
this.eventCanvas._elem.bind('mousedown', {plot: this}, this.onMouseDown);
this.eventCanvas._elem.bind('mousemove', {plot: this}, this.onMouseMove);
this.eventCanvas._elem.bind('mouseenter', {plot: this}, this.onMouseEnter);
this.eventCanvas._elem.bind('mouseleave', {plot: this}, this.onMouseLeave);
if (this.captureRightClick) {
this.eventCanvas._elem.bind('mouseup', {plot: this}, this.onRightClick);
this.eventCanvas._elem.get(0).oncontextmenu = function () {
return false;
};
} else {
this.eventCanvas._elem.bind('mouseup', {plot: this}, this.onMouseUp);
}
};
/**
* Computes a highlight color or array of highlight colors from given colors.
* @param {array} colors
*/
$.jqplot.computeHighlightColors = function (colors) {
var ret,
i,
rgba,
newrgb,
sum,
j,
l;
if ($.isArray(colors)) {
ret = [];
for (i = 0, l = colors.length; i < l; i++) {
rgba = $.jqplot.getColorComponents(colors[i]);
newrgb = [rgba[0], rgba[1], rgba[2]];
sum = newrgb[0] + newrgb[1] + newrgb[2];
for (j = 0; j < 3; j++) {
// when darkening, lowest color component can be is 60.
newrgb[j] = (sum > 660) ? newrgb[j] * 0.85 : 0.73 * newrgb[j] + 90;
newrgb[j] = parseInt(newrgb[j], 10);
newrgb[j] = (newrgb[j] > 255) ? 255 : newrgb[j];
}
// newrgb[3] = (rgba[3] > 0.4) ? rgba[3] * 0.4 : rgba[3] * 1.5;
// newrgb[3] = (rgba[3] > 0.5) ? 0.8 * rgba[3] - .1 : rgba[3] + 0.2;
newrgb[3] = 0.3 + 0.35 * rgba[3];
ret.push('rgba(' + newrgb[0] + ',' + newrgb[1] + ',' + newrgb[2] + ',' + newrgb[3] + ')');
}
} else {
rgba = $.jqplot.getColorComponents(colors);
newrgb = [rgba[0], rgba[1], rgba[2]];
sum = newrgb[0] + newrgb[1] + newrgb[2];
for (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);
newrgb[j] = (sum > 660) ? newrgb[j] * 0.85 : 0.73 * newrgb[j] + 90;
newrgb[j] = parseInt(newrgb[j], 10);
newrgb[j] = (newrgb[j] > 255) ? 255 : newrgb[j];
}
// newrgb[3] = (rgba[3] > 0.4) ? rgba[3] * 0.4 : rgba[3] * 1.5;
// newrgb[3] = (rgba[3] > 0.5) ? 0.8 * rgba[3] - .1 : rgba[3] + 0.2;
newrgb[3] = 0.3 + 0.35 * rgba[3];
ret = 'rgba(' + newrgb[0] + ',' + newrgb[1] + ',' + newrgb[2] + ',' + newrgb[3] + ')';
}
return ret;
};
/**
* @class
* @contructor
* @param {array} colors
*/
$.jqplot.ColorGenerator = function (colors) {
colors = colors || $.jqplot.config.defaultColors;
var idx = 0;
this.next = function () {
if (idx < colors.length) {
return colors[idx++];
} else {
idx = 0;
return colors[idx++];
}
};
this.previous = function () {
if (idx > 0) {
return colors[idx--];
} else {
idx = colors.length - 1;
return colors[idx];
}
};
// get a color by index without advancing pointer.
this.get = function (i) {
var idx = i - colors.length * Math.floor(i / colors.length);
return colors[idx];
};
this.setColors = function (c) {
colors = c;
};
this.reset = function () {
idx = 0;
};
this.getIndex = function () {
return idx;
};
this.setIndex = function (index) {
idx = index;
};
};
/** convert a hex color string to rgb string.
* @param {string] h 3 or 6 character hex string, with or without leading #
* @param {number} a optional alpha
*/
$.jqplot.hex2rgb = function (h, a) {
var rgb;
h = h.replace('#', '');
if (h.length === 3) {
h = h.charAt(0) + h.charAt(0) + h.charAt(1) + h.charAt(1) + h.charAt(2) + h.charAt(2);
}
rgb = 'rgba(' + parseInt(h.slice(0, 2), 16) + ', ' + parseInt(h.slice(2, 4), 16) + ', ' + parseInt(h.slice(4, 6), 16);
if (a) {
rgb += ', ' + a;
}
rgb += ')';
return rgb;
};
/**
* convert an rgb color spec to a hex spec. ignore any alpha specification.
* @param {string} s string css spe
*/
$.jqplot.rgb2hex = function (s) {
var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *(?:, *[0-9.]*)?\)/,
m = s.match(pat),
h = '#',
i,
temp;
for (i = 1; i < 4; i++) {
if (m[i].search(/%/) !== -1) {
temp = parseInt(255 * m[i] / 100, 10).toString(16);
if (temp.length === 1) {
temp = '0' + temp;
}
} else {
temp = parseInt(m[i], 10).toString(16);
if (temp.length === 1) {
temp = '0' + temp;
}
}
h += temp;
}
return h;
};
/**
* given a css color spec, return an rgb css color spec.
* @param {string} s css spec
* @param {number} a alpha opacity
*/
$.jqplot.normalize2rgb = function (s, a) {
if (s.search(/^ *rgba?\(/) !== -1) {
return s;
} else if (s.search(/^ *#?[0-9a-fA-F]?[0-9a-fA-F]/) !== -1) {
return $.jqplot.hex2rgb(s, a);
} else {
throw new Error('Invalid color spec');
}
};
/**
* Extracts the r, g, b, a color components out of a css color spec.
* @param {string} s match the string color name for the rgba color.
* @returns {string} the rgba color specified @see {@link $.jqplot.colorKeywordMap}
*/
$.jqplot.getColorComponents = function (s) {
// check to see if a color keyword.
s = $.jqplot.colorKeywordMap[s] || s;
var rgb = $.jqplot.normalize2rgb(s),
pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *,? *([0-9.]* *)?\)/,
m = rgb.match(pat),
ret = [],
i;
for (i = 1; i < 4; i++) {
if (m[i].search(/%/) !== -1) {
ret[i - 1] = parseInt(255 * m[i] / 100, 10);
} else {
ret[i - 1] = parseInt(m[i], 10);
}
}
ret[3] = parseFloat(m[4]) || 1.0;
return ret;
};
/**
* know if a color is dark or not
* @param {string} color string color css representation
* @returns {boolean} true if the color is dark.
*/
$.jqplot.isDarkColor = function (color) {
var L = 0,
rgba = this.getColorRGB(color);
if (rgba !== null) {
color = rgba;
} else {
color = $.jqplot.getColorComponents(color);
}
if (color !== null) {
L = 0.300 * (color[0] / 255) + 0.590 * (color[1] / 255) + 0.110 * (color[2] / 255);
}
return (L < 0.45);
};
/**
* Gets the RGB color value for a given color
* @param {String} color css string color representation.
* @returns [Number] rgb array color.
*/
$.jqplot.getColorRGB = function (color) {
var rgbError = [0, 0, 0],
red,
green,
blue,
rgbString,
rgb,
strLen = color.length;
if (strLen && color.lastIndexOf("rgb") !== -1) {
color = color.replace("rgba", "");
color = color.replace("rgb", "");
color = color.replace("\\(", "");
color = color.replace("\\)", "");
color = color.replace(" ", "");
rgbString = color.split(",");
red = parseInt(rgbString[0], 10);
green = parseInt(rgbString[1], 10);
blue = parseInt(rgbString[2], 10);
rgb = [red, green, blue];
return rgb;
} else if (strLen && color.indexOf("#") === 0) {
// eg. #F90
if (strLen === 4) {
red = color.substr(1, 1);
green = color.substr(2, 1);
blue = color.substr(3, 1);
// eg. #FF9900
} else {
red = color.substr(1, 2);
green = color.substr(3, 2);
blue = color.substr(5, 2);
}
red = parseInt(red, 16);
green = parseInt(green, 16);
blue = parseInt(blue, 16);
rgb = [red, green, blue];
return rgb;
} else {
return null;
}
};
/**
* {@link}
* @property {map} jqplot themes commons colors map.
*/
$.jqplot.colorKeywordMap = {
aliceblue: 'rgb(240, 248, 255)',
antiquewhite: 'rgb(250, 235, 215)',
aqua: 'rgb( 0, 255, 255)',
aquamarine: 'rgb(127, 255, 212)',
azure: 'rgb(240, 255, 255)',
beige: 'rgb(245, 245, 220)',
bisque: 'rgb(255, 228, 196)',
black: 'rgb( 0, 0, 0)',
blanchedalmond: 'rgb(255, 235, 205)',
blue: 'rgb( 0, 0, 255)',
blueviolet: 'rgb(138, 43, 226)',
brown: 'rgb(165, 42, 42)',
burlywood: 'rgb(222, 184, 135)',
cadetblue: 'rgb( 95, 158, 160)',
chartreuse: 'rgb(127, 255, 0)',
chocolate: 'rgb(210, 105, 30)',
coral: 'rgb(255, 127, 80)',
cornflowerblue: 'rgb(100, 149, 237)',
cornsilk: 'rgb(255, 248, 220)',
crimson: 'rgb(220, 20, 60)',
cyan: 'rgb( 0, 255, 255)',
darkblue: 'rgb( 0, 0, 139)',
darkcyan: 'rgb( 0, 139, 139)',
darkgoldenrod: 'rgb(184, 134, 11)',
darkgray: 'rgb(169, 169, 169)',
darkgreen: 'rgb( 0, 100, 0)',
darkgrey: 'rgb(169, 169, 169)',
darkkhaki: 'rgb(189, 183, 107)',
darkmagenta: 'rgb(139, 0, 139)',
darkolivegreen: 'rgb( 85, 107, 47)',
darkorange: 'rgb(255, 140, 0)',
darkorchid: 'rgb(153, 50, 204)',
darkred: 'rgb(139, 0, 0)',
darksalmon: 'rgb(233, 150, 122)',
darkseagreen: 'rgb(143, 188, 143)',
darkslateblue: 'rgb( 72, 61, 139)',
darkslategray: 'rgb( 47, 79, 79)',
darkslategrey: 'rgb( 47, 79, 79)',
darkturquoise: 'rgb( 0, 206, 209)',
darkviolet: 'rgb(148, 0, 211)',
deeppink: 'rgb(255, 20, 147)',
deepskyblue: 'rgb( 0, 191, 255)',
dimgray: 'rgb(105, 105, 105)',
dimgrey: 'rgb(105, 105, 105)',
dodgerblue: 'rgb( 30, 144, 255)',
firebrick: 'rgb(178, 34, 34)',
floralwhite: 'rgb(255, 250, 240)',
forestgreen: 'rgb( 34, 139, 34)',
fuchsia: 'rgb(255, 0, 255)',
gainsboro: 'rgb(220, 220, 220)',
ghostwhite: 'rgb(248, 248, 255)',
gold: 'rgb(255, 215, 0)',
goldenrod: 'rgb(218, 165, 32)',
gray: 'rgb(128, 128, 128)',
grey: 'rgb(128, 128, 128)',
green: 'rgb( 0, 128, 0)',
greenyellow: 'rgb(173, 255, 47)',
honeydew: 'rgb(240, 255, 240)',
hotpink: 'rgb(255, 105, 180)',
indianred: 'rgb(205, 92, 92)',
indigo: 'rgb( 75, 0, 130)',
ivory: 'rgb(255, 255, 240)',
khaki: 'rgb(240, 230, 140)',
lavender: 'rgb(230, 230, 250)',
lavenderblush: 'rgb(255, 240, 245)',
lawngreen: 'rgb(124, 252, 0)',
lemonchiffon: 'rgb(255, 250, 205)',
lightblue: 'rgb(173, 216, 230)',
lightcoral: 'rgb(240, 128, 128)',
lightcyan: 'rgb(224, 255, 255)',
lightgoldenrodyellow: 'rgb(250, 250, 210)',
lightgray: 'rgb(211, 211, 211)',
lightgreen: 'rgb(144, 238, 144)',
lightgrey: 'rgb(211, 211, 211)',
lightpink: 'rgb(255, 182, 193)',
lightsalmon: 'rgb(255, 160, 122)',
lightseagreen: 'rgb( 32, 178, 170)',
lightskyblue: 'rgb(135, 206, 250)',
lightslategray: 'rgb(119, 136, 153)',
lightslategrey: 'rgb(119, 136, 153)',
lightsteelblue: 'rgb(176, 196, 222)',
lightyellow: 'rgb(255, 255, 224)',
lime: 'rgb( 0, 255, 0)',
limegreen: 'rgb( 50, 205, 50)',
linen: 'rgb(250, 240, 230)',
magenta: 'rgb(255, 0, 255)',
maroon: 'rgb(128, 0, 0)',
mediumaquamarine: 'rgb(102, 205, 170)',
mediumblue: 'rgb( 0, 0, 205)',
mediumorchid: 'rgb(186, 85, 211)',
mediumpurple: 'rgb(147, 112, 219)',
mediumseagreen: 'rgb( 60, 179, 113)',
mediumslateblue: 'rgb(123, 104, 238)',
mediumspringgreen: 'rgb( 0, 250, 154)',
mediumturquoise: 'rgb( 72, 209, 204)',
mediumvioletred: 'rgb(199, 21, 133)',
midnightblue: 'rgb( 25, 25, 112)',
mintcream: 'rgb(245, 255, 250)',
mistyrose: 'rgb(255, 228, 225)',
moccasin: 'rgb(255, 228, 181)',
navajowhite: 'rgb(255, 222, 173)',
navy: 'rgb( 0, 0, 128)',
oldlace: 'rgb(253, 245, 230)',
olive: 'rgb(128, 128, 0)',
olivedrab: 'rgb(107, 142, 35)',
orange: 'rgb(255, 165, 0)',
orangered: 'rgb(255, 69, 0)',
orchid: 'rgb(218, 112, 214)',
palegoldenrod: 'rgb(238, 232, 170)',
palegreen: 'rgb(152, 251, 152)',
paleturquoise: 'rgb(175, 238, 238)',
palevioletred: 'rgb(219, 112, 147)',
papayawhip: 'rgb(255, 239, 213)',
peachpuff: 'rgb(255, 218, 185)',
peru: 'rgb(205, 133, 63)',
pink: 'rgb(255, 192, 203)',
plum: 'rgb(221, 160, 221)',
powderblue: 'rgb(176, 224, 230)',
purple: 'rgb(128, 0, 128)',
red: 'rgb(255, 0, 0)',
rosybrown: 'rgb(188, 143, 143)',
royalblue: 'rgb( 65, 105, 225)',
saddlebrown: 'rgb(139, 69, 19)',
salmon: 'rgb(250, 128, 114)',
sandybrown: 'rgb(244, 164, 96)',
seagreen: 'rgb( 46, 139, 87)',
seashell: 'rgb(255, 245, 238)',
sienna: 'rgb(160, 82, 45)',
silver: 'rgb(192, 192, 192)',
skyblue: 'rgb(135, 206, 235)',
slateblue: 'rgb(106, 90, 205)',
slategray: 'rgb(112, 128, 144)',
slategrey: 'rgb(112, 128, 144)',
snow: 'rgb(255, 250, 250)',
springgreen: 'rgb( 0, 255, 127)',
steelblue: 'rgb( 70, 130, 180)',
tan: 'rgb(210, 180, 140)',
teal: 'rgb( 0, 128, 128)',
thistle: 'rgb(216, 191, 216)',
tomato: 'rgb(255, 99, 71)',
turquoise: 'rgb( 64, 224, 208)',
violet: 'rgb(238, 130, 238)',
wheat: 'rgb(245, 222, 179)',
white: 'rgb(255, 255, 255)',
whitesmoke: 'rgb(245, 245, 245)',
yellow: 'rgb(255, 255, 0)',
yellowgreen: 'rgb(154, 205, 50)'
};
}(jQuery, window));