/**
* jqPlot
* Pure JavaScript plotting plugin using jQuery
*
* Version: @VERSION
* Revision: @REVISION
*
* Copyright (c) 2009-2013 Chris Leonello
* jqPlot is currently available for use in all personal or commercial projects
* under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
* version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
* choose the license that best suits your project and use it accordingly.
*
* Although not required, the author would appreciate an email letting him
* know of any substantial use of jqPlot. You can reach the author at:
* chris at jqplot dot com or see http://www.jqplot.com/info.php .
*
* If you are feeling kind and generous, consider supporting the project by
* making a donation at: http://www.jqplot.com/donate.php .
*
* sprintf functions contained in jqplot.sprintf.js by Ash Searle:
*
* version 2007.04.27
* author Ash Searle
* http://hexmen.com/blog/2007/03/printf-sprintf/
* http://hexmen.com/js/sprintf.js
* The author (Ash Searle) has placed this code in the public domain:
* "This code is unrestricted: you are free to use it however you like."
*
*/
(function($) {
/**
* Class: $.jqplot.ThemeEngine
* Theme Engine provides a programatic way to change some of the more
* common jqplot styling options such as fonts, colors and grid options.
* A theme engine instance is created with each plot. The theme engine
* manages a collection of themes which can be modified, added to, or
* applied to the plot.
*
* The themeEngine class is not instantiated directly.
* When a plot is initialized, the current plot options are scanned
* an a default theme named "Default" is created. This theme is
* used as the basis for other themes added to the theme engine and
* is always available.
*
* A theme is a simple javascript object with styling parameters for
* various entities of the plot. A theme has the form:
*
*
* > {
* > _name:f "Default",
* > target: {
* > backgroundColor: "transparent"
* > },
* > legend: {
* > textColor: null,
* > fontFamily: null,
* > fontSize: null,
* > border: null,
* > background: null
* > },
* > title: {
* > textColor: "rgb(102, 102, 102)",
* > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif",
* > fontSize: "19.2px",
* > textAlign: "center"
* > },
* > seriesStyles: {},
* > series: [{
* > color: "#4bb2c5",
* > lineWidth: 2.5,
* > linePattern: "solid",
* > shadow: true,
* > fillColor: "#4bb2c5",
* > showMarker: true,
* > markerOptions: {
* > color: "#4bb2c5",
* > show: true,
* > style: 'filledCircle',
* > lineWidth: 1.5,
* > size: 4,
* > shadow: true
* > }
* > }],
* > grid: {
* > drawGridlines: true,
* > gridLineColor: "#cccccc",
* > gridLineWidth: 1,
* > backgroundColor: "#fffdf6",
* > borderColor: "#999999",
* > borderWidth: 2,
* > shadow: true
* > },
* > axesStyles: {
* > label: {},
* > ticks: {}
* > },
* > axes: {
* > xaxis: {
* > borderColor: "#999999",
* > borderWidth: 2,
* > ticks: {
* > show: true,
* > showGridline: true,
* > showLabel: true,
* > showMark: true,
* > size: 4,
* > textColor: "",
* > whiteSpace: "nowrap",
* > fontSize: "12px",
* > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif"
* > },
* > label: {
* > textColor: "rgb(102, 102, 102)",
* > whiteSpace: "normal",
* > fontSize: "14.6667px",
* > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif",
* > fontWeight: "400"
* > }
* > },
* > yaxis: {
* > borderColor: "#999999",
* > borderWidth: 2,
* > ticks: {
* > show: true,
* > showGridline: true,
* > showLabel: true,
* > showMark: true,
* > size: 4,
* > textColor: "",
* > whiteSpace: "nowrap",
* > fontSize: "12px",
* > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif"
* > },
* > label: {
* > textColor: null,
* > whiteSpace: null,
* > fontSize: null,
* > fontFamily: null,
* > fontWeight: null
* > }
* > },
* > x2axis: {...
* > },
* > ...
* > y9axis: {...
* > }
* > }
* > }
*
* "seriesStyles" is a style object that will be applied to all series in the plot.
* It will forcibly override any styles applied on the individual series. "axesStyles" is
* a style object that will be applied to all axes in the plot. It will also forcibly
* override any styles on the individual axes.
*
* The example shown above has series options for a line series. Options for other
* series types are shown below:
*
* Bar Series:
*
* > {
* > color: "#4bb2c5",
* > seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
* > lineWidth: 2.5,
* > shadow: true,
* > barPadding: 2,
* > barMargin: 10,
* > barWidth: 15.09375,
* > highlightColors: ["rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)"]
* > }
*
* Pie Series:
*
* > {
* > seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
* > padding: 20,
* > sliceMargin: 0,
* > fill: true,
* > shadow: true,
* > startAngle: 0,
* > lineWidth: 2.5,
* > highlightColors: ["rgb(129,201,214)", "rgb(240,189,104)", "rgb(214,202,165)", "rgb(137,180,158)", "rgb(168,180,137)", "rgb(180,174,89)", "rgb(180,113,161)", "rgb(129,141,236)", "rgb(227,205,120)", "rgb(255,138,76)", "rgb(76,169,219)", "rgb(215,126,190)", "rgb(220,232,135)", "rgb(200,167,96)", "rgb(103,202,235)", "rgb(208,154,215)"]
* > }
*
* Funnel Series:
*
* > {
* > color: "#4bb2c5",
* > lineWidth: 2,
* > shadow: true,
* > padding: {
* > top: 20,
* > right: 20,
* > bottom: 20,
* > left: 20
* > },
* > sectionMargin: 6,
* > seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"],
* > highlightColors: ["rgb(147,208,220)", "rgb(242,199,126)", "rgb(220,210,178)", "rgb(154,191,172)", "rgb(180,191,154)", "rgb(191,186,112)", "rgb(191,133,174)", "rgb(147,157,238)", "rgb(231,212,139)", "rgb(255,154,102)", "rgb(102,181,224)", "rgb(221,144,199)", "rgb(225,235,152)", "rgb(200,167,96)", "rgb(124,210,238)", "rgb(215,169,221)"]
* > }
*
*/
$.jqplot.ThemeEngine = function(){
// Group: Properties
//
// prop: themes
// hash of themes managed by the theme engine.
// Indexed by theme name.
this.themes = {};
// prop: activeTheme
// Pointer to currently active theme
this.activeTheme=null;
};
// called with scope of plot
$.jqplot.ThemeEngine.prototype.init = function() {
// get the Default theme from the current plot settings.
var th = new $.jqplot.Theme({_name:'Default'});
var n, i, nn;
for (n in th.target) {
if (n == "textColor") {
th.target[n] = this.target.css('color');
}
else {
th.target[n] = this.target.css(n);
}
}
if (this.title.show && this.title._elem) {
for (n in th.title) {
if (n == "textColor") {
th.title[n] = this.title._elem.css('color');
}
else {
th.title[n] = this.title._elem.css(n);
}
}
}
for (n in th.grid) {
th.grid[n] = this.grid[n];
}
if (th.grid.backgroundColor == null && this.grid.background != null) {
th.grid.backgroundColor = this.grid.background;
}
if (this.legend.show && this.legend._elem) {
for (n in th.legend) {
if (n == 'textColor') {
th.legend[n] = this.legend._elem.css('color');
}
else {
th.legend[n] = this.legend._elem.css(n);
}
}
}
var s;
for (i=0; i<this.series.length; i++) {
s = this.series[i];
if (s.renderer.constructor == $.jqplot.LineRenderer) {
th.series.push(new LineSeriesProperties());
}
else if (s.renderer.constructor == $.jqplot.BarRenderer) {
th.series.push(new BarSeriesProperties());
}
else if (s.renderer.constructor == $.jqplot.PieRenderer) {
th.series.push(new PieSeriesProperties());
}
else if (s.renderer.constructor == $.jqplot.DonutRenderer) {
th.series.push(new DonutSeriesProperties());
}
else if (s.renderer.constructor == $.jqplot.FunnelRenderer) {
th.series.push(new FunnelSeriesProperties());
}
else if (s.renderer.constructor == $.jqplot.MeterGaugeRenderer) {
th.series.push(new MeterSeriesProperties());
}
else {
th.series.push({});
}
for (n in th.series[i]) {
th.series[i][n] = s[n];
}
}
var a, ax;
for (n in this.axes) {
ax = this.axes[n];
a = th.axes[n] = new AxisProperties();
a.borderColor = ax.borderColor;
a.borderWidth = ax.borderWidth;
if (ax._ticks && ax._ticks[0]) {
for (nn in a.ticks) {
if (ax._ticks[0].hasOwnProperty(nn)) {
a.ticks[nn] = ax._ticks[0][nn];
}
else if (ax._ticks[0]._elem){
a.ticks[nn] = ax._ticks[0]._elem.css(nn);
}
}
}
if (ax._label && ax._label.show) {
for (nn in a.label) {
// a.label[nn] = ax._label._elem.css(nn);
if (ax._label[nn]) {
a.label[nn] = ax._label[nn];
}
else if (ax._label._elem){
if (nn == 'textColor') {
a.label[nn] = ax._label._elem.css('color');
}
else {
a.label[nn] = ax._label._elem.css(nn);
}
}
}
}
}
this.themeEngine._add(th);
this.themeEngine.activeTheme = this.themeEngine.themes[th._name];
};
/**
* Group: methods
*
* method: get
*
* Get and return the named theme or the active theme if no name given.
*
* parameter:
*
* name - name of theme to get.
*
* returns:
*
* Theme instance of given name.
*/
$.jqplot.ThemeEngine.prototype.get = function(name) {
if (!name) {
// return the active theme
return this.activeTheme;
}
else {
return this.themes[name];
}
};
function numericalOrder(a,b) { return a-b; }
/**
* method: getThemeNames
*
* Return the list of theme names in this manager in alpha-numerical order.
*
* parameter:
*
* None
*
* returns:
*
* A the list of theme names in this manager in alpha-numerical order.
*/
$.jqplot.ThemeEngine.prototype.getThemeNames = function() {
var tn = [];
for (var n in this.themes) {
tn.push(n);
}
return tn.sort(numericalOrder);
};
/**
* method: getThemes
*
* Return a list of themes in alpha-numerical order by name.
*
* parameter:
*
* None
*
* returns:
*
* A list of themes in alpha-numerical order by name.
*/
$.jqplot.ThemeEngine.prototype.getThemes = function() {
var tn = [];
var themes = [];
for (var n in this.themes) {
tn.push(n);
}
tn.sort(numericalOrder);
for (var i=0; i<tn.length; i++) {
themes.push(this.themes[tn[i]]);
}
return themes;
};
$.jqplot.ThemeEngine.prototype.activate = function(plot, name) {
// sometimes need to redraw whole plot.
var redrawPlot = false;
if (!name && this.activeTheme && this.activeTheme._name) {
name = this.activeTheme._name;
}
if (!this.themes.hasOwnProperty(name)) {
throw new Error("No theme of that name");
}
else {
var th = this.themes[name];
this.activeTheme = th;
var val, checkBorderColor = false, checkBorderWidth = false;
var arr = ['xaxis', 'x2axis', 'yaxis', 'y2axis'];
for (i=0; i<arr.length; i++) {
var ax = arr[i];
if (th.axesStyles.borderColor != null) {
plot.axes[ax].borderColor = th.axesStyles.borderColor;
}
if (th.axesStyles.borderWidth != null) {
plot.axes[ax].borderWidth = th.axesStyles.borderWidth;
}
}
for (var axname in plot.axes) {
var axis = plot.axes[axname];
if (axis.show) {
var thaxis = th.axes[axname] || {};
var thaxstyle = th.axesStyles;
var thax = $.jqplot.extend(true, {}, thaxis, thaxstyle);
val = (th.axesStyles.borderColor != null) ? th.axesStyles.borderColor : thax.borderColor;
if (thax.borderColor != null) {
axis.borderColor = thax.borderColor;
redrawPlot = true;
}
val = (th.axesStyles.borderWidth != null) ? th.axesStyles.borderWidth : thax.borderWidth;
if (thax.borderWidth != null) {
axis.borderWidth = thax.borderWidth;
redrawPlot = true;
}
if (axis._ticks && axis._ticks[0]) {
for (var nn in thax.ticks) {
// val = null;
// if (th.axesStyles.ticks && th.axesStyles.ticks[nn] != null) {
// val = th.axesStyles.ticks[nn];
// }
// else if (thax.ticks[nn] != null){
// val = thax.ticks[nn]
// }
val = thax.ticks[nn];
if (val != null) {
axis.tickOptions[nn] = val;
axis._ticks = [];
redrawPlot = true;
}
}
}
if (axis._label && axis._label.show) {
for (var nn in thax.label) {
// val = null;
// if (th.axesStyles.label && th.axesStyles.label[nn] != null) {
// val = th.axesStyles.label[nn];
// }
// else if (thax.label && thax.label[nn] != null){
// val = thax.label[nn]
// }
val = thax.label[nn];
if (val != null) {
axis.labelOptions[nn] = val;
redrawPlot = true;
}
}
}
}
}
for (var n in th.grid) {
if (th.grid[n] != null) {
plot.grid[n] = th.grid[n];
}
}
if (!redrawPlot) {
plot.grid.draw();
}
if (plot.legend.show) {
for (n in th.legend) {
if (th.legend[n] != null) {
plot.legend[n] = th.legend[n];
}
}
}
if (plot.title.show) {
for (n in th.title) {
if (th.title[n] != null) {
plot.title[n] = th.title[n];
}
}
}
var i;
for (i=0; i<th.series.length; i++) {
var opts = {};
var redrawSeries = false;
for (n in th.series[i]) {
val = (th.seriesStyles[n] != null) ? th.seriesStyles[n] : th.series[i][n];
if (val != null) {
opts[n] = val;
if (n == 'color') {
plot.series[i].renderer.shapeRenderer.fillStyle = val;
plot.series[i].renderer.shapeRenderer.strokeStyle = val;
plot.series[i][n] = val;
}
else if ((n == 'lineWidth') || (n == 'linePattern')) {
plot.series[i].renderer.shapeRenderer[n] = val;
plot.series[i][n] = val;
}
else if (n == 'markerOptions') {
merge (plot.series[i].markerOptions, val);
merge (plot.series[i].markerRenderer, val);
}
else {
plot.series[i][n] = val;
}
redrawPlot = true;
}
}
}
if (redrawPlot) {
plot.target.empty();
plot.draw();
}
for (n in th.target) {
if (th.target[n] != null) {
plot.target.css(n, th.target[n]);
}
}
}
};
$.jqplot.ThemeEngine.prototype._add = function(theme, name) {
if (name) {
theme._name = name;
}
if (!theme._name) {
theme._name = Date.parse(new Date());
}
if (!this.themes.hasOwnProperty(theme._name)) {
this.themes[theme._name] = theme;
}
else {
throw new Error("jqplot.ThemeEngine Error: Theme already in use");
}
};
// method remove
// Delete the named theme, return true on success, false on failure.
/**
* method: remove
*
* Remove the given theme from the themeEngine.
*
* parameters:
*
* name - name of the theme to remove.
*
* returns:
*
* true on success, false on failure.
*/
$.jqplot.ThemeEngine.prototype.remove = function(name) {
if (name == 'Default') {
return false;
}
return delete this.themes[name];
};
/**
* method: newTheme
*
* Create a new theme based on the default theme, adding it the themeEngine.
*
* parameters:
*
* name - name of the new theme.
* obj - optional object of styles to be applied to this new theme.
*
* returns:
*
* new Theme object.
*/
$.jqplot.ThemeEngine.prototype.newTheme = function(name, obj) {
if (typeof(name) == 'object') {
obj = obj || name;
name = null;
}
if (obj && obj._name) {
name = obj._name;
}
else {
name = name || Date.parse(new Date());
}
// var th = new $.jqplot.Theme(name);
var th = this.copy(this.themes['Default']._name, name);
$.jqplot.extend(th, obj);
return th;
};
// function clone(obj) {
// return eval(obj.toSource());
// }
function clone(obj){
if(obj == null || typeof(obj) != 'object'){
return obj;
}
var temp = new obj.constructor();
for(var key in obj){
temp[key] = clone(obj[key]);
}
return temp;
}
$.jqplot.clone = clone;
function merge(obj1, obj2) {
if (obj2 == null || typeof(obj2) != 'object') {
return;
}
for (var key in obj2) {
if (key == 'highlightColors') {
obj1[key] = clone(obj2[key]);
}
if (obj2[key] != null && typeof(obj2[key]) == 'object') {
if (!obj1.hasOwnProperty(key)) {
obj1[key] = {};
}
merge(obj1[key], obj2[key]);
}
else {
obj1[key] = obj2[key];
}
}
}
$.jqplot.merge = merge;
// Use the jQuery 1.3.2 extend function since behaviour in jQuery 1.4 seems problematic
$.jqplot.extend = function() {
// copy reference to target object
var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;
target = arguments[1] || {};
// skip the boolean and the target
i = 2;
}
// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !toString.call(target) === "[object Function]" ) {
target = {};
}
for ( ; i < length; i++ ){
// Only deal with non-null/undefined values
if ( (options = arguments[ i ]) != null ) {
// Extend the base object
for ( var name in options ) {
var src = target[ name ], copy = options[ name ];
// Prevent never-ending loop
if ( target === copy ) {
continue;
}
// Recurse if we're merging object values
if ( deep && copy && typeof copy === "object" && !copy.nodeType ) {
target[ name ] = $.jqplot.extend( deep,
// Never move original objects, clone them
src || ( copy.length != null ? [ ] : { } )
, copy );
}
// Don't bring in undefined values
else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}
// Return the modified object
return target;
};
/**
* method: rename
*
* Rename a theme.
*
* parameters:
*
* oldName - current name of the theme.
* newName - desired name of the theme.
*
* returns:
*
* new Theme object.
*/
$.jqplot.ThemeEngine.prototype.rename = function (oldName, newName) {
if (oldName == 'Default' || newName == 'Default') {
throw new Error ("jqplot.ThemeEngine Error: Cannot rename from/to Default");
}
if (this.themes.hasOwnProperty(newName)) {
throw new Error ("jqplot.ThemeEngine Error: New name already in use.");
}
else if (this.themes.hasOwnProperty(oldName)) {
var th = this.copy (oldName, newName);
this.remove(oldName);
return th;
}
throw new Error("jqplot.ThemeEngine Error: Old name or new name invalid");
};
/**
* method: copy
*
* Create a copy of an existing theme in the themeEngine, adding it the themeEngine.
*
* parameters:
*
* sourceName - name of the existing theme.
* targetName - name of the copy.
* obj - optional object of style parameter to apply to the new theme.
*
* returns:
*
* new Theme object.
*/
$.jqplot.ThemeEngine.prototype.copy = function (sourceName, targetName, obj) {
if (targetName == 'Default') {
throw new Error ("jqplot.ThemeEngine Error: Cannot copy over Default theme");
}
if (!this.themes.hasOwnProperty(sourceName)) {
var s = "jqplot.ThemeEngine Error: Source name invalid";
throw new Error(s);
}
if (this.themes.hasOwnProperty(targetName)) {
var s = "jqplot.ThemeEngine Error: Target name invalid";
throw new Error(s);
}
else {
var th = clone(this.themes[sourceName]);
th._name = targetName;
$.jqplot.extend(true, th, obj);
this._add(th);
return th;
}
};
$.jqplot.Theme = function(name, obj) {
if (typeof(name) == 'object') {
obj = obj || name;
name = null;
}
name = name || Date.parse(new Date());
this._name = name;
this.target = {
backgroundColor: null
};
this.legend = {
textColor: null,
fontFamily: null,
fontSize: null,
border: null,
background: null
};
this.title = {
textColor: null,
fontFamily: null,
fontSize: null,
textAlign: null
};
this.seriesStyles = {};
this.series = [];
this.grid = {
drawGridlines: null,
gridLineColor: null,
gridLineWidth: null,
backgroundColor: null,
borderColor: null,
borderWidth: null,
shadow: null
};
this.axesStyles = {label:{}, ticks:{}};
this.axes = {};
if (typeof(obj) == 'string') {
this._name = obj;
}
else if(typeof(obj) == 'object') {
$.jqplot.extend(true, this, obj);
}
};
var AxisProperties = function() {
this.borderColor = null;
this.borderWidth = null;
this.ticks = new AxisTicks();
this.label = new AxisLabel();
};
var AxisTicks = function() {
this.show = null;
this.showGridline = null;
this.showLabel = null;
this.showMark = null;
this.size = null;
this.textColor = null;
this.whiteSpace = null;
this.fontSize = null;
this.fontFamily = null;
};
var AxisLabel = function() {
this.textColor = null;
this.whiteSpace = null;
this.fontSize = null;
this.fontFamily = null;
this.fontWeight = null;
};
var LineSeriesProperties = function() {
this.color=null;
this.lineWidth=null;
this.linePattern=null;
this.shadow=null;
this.fillColor=null;
this.showMarker=null;
this.markerOptions = new MarkerOptions();
};
var MarkerOptions = function() {
this.show = null;
this.style = null;
this.lineWidth = null;
this.size = null;
this.color = null;
this.shadow = null;
};
var BarSeriesProperties = function() {
this.color=null;
this.seriesColors=null;
this.lineWidth=null;
this.shadow=null;
this.barPadding=null;
this.barMargin=null;
this.barWidth=null;
this.highlightColors=null;
};
var PieSeriesProperties = function() {
this.seriesColors=null;
this.padding=null;
this.sliceMargin=null;
this.fill=null;
this.shadow=null;
this.startAngle=null;
this.lineWidth=null;
this.highlightColors=null;
};
var DonutSeriesProperties = function() {
this.seriesColors=null;
this.padding=null;
this.sliceMargin=null;
this.fill=null;
this.shadow=null;
this.startAngle=null;
this.lineWidth=null;
this.innerDiameter=null;
this.thickness=null;
this.ringMargin=null;
this.highlightColors=null;
};
var FunnelSeriesProperties = function() {
this.color=null;
this.lineWidth=null;
this.shadow=null;
this.padding=null;
this.sectionMargin=null;
this.seriesColors=null;
this.highlightColors=null;
};
var MeterSeriesProperties = function() {
this.padding=null;
this.backgroundColor=null;
this.ringColor=null;
this.tickColor=null;
this.ringWidth=null;
this.intervalColors=null;
this.intervalInnerRadius=null;
this.intervalOuterRadius=null;
this.hubRadius=null;
this.needleThickness=null;
this.needlePad=null;
};
})(jQuery);