You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1277 lines
42 KiB
1277 lines
42 KiB
define(["../main", "dojo/_base/lang", "dojo/_base/array","dojo/_base/declare", "dojo/dom-style", |
|
"dojo/dom", "dojo/dom-geometry", "dojo/dom-construct","dojo/_base/Color", "dojo/sniff", |
|
"./Element", "./SimpleTheme", "./Series", "./axis2d/common", "./plot2d/common", "dojox/gfx/shape", |
|
"dojox/gfx", "dojo/has!dojo-bidi?./bidi/Chart", "dojox/lang/functional", "dojox/lang/functional/fold", "dojox/lang/functional/reversed"], |
|
function(dojox, lang, arr, declare, domStyle, |
|
dom, domGeom, domConstruct, Color, has, |
|
Element, SimpleTheme, Series, common, plot2dCommon, shape, |
|
g, BidiChart, func){ |
|
/*===== |
|
var __ChartCtorArgs = { |
|
// summary: |
|
// The keyword arguments that can be passed in a Chart constructor. |
|
// margins: Object? |
|
// Optional margins for the chart, in the form of { l, t, r, b}. |
|
// stroke: dojox.gfx.Stroke? |
|
// An optional outline/stroke for the chart. |
|
// fill: dojox.gfx.Fill? |
|
// An optional fill for the chart. |
|
// delayInMs: Number |
|
// Delay in ms for delayedRender(). Default: 200. |
|
}; |
|
=====*/ |
|
|
|
/*===== |
|
var __SeriesCtorArgs = { |
|
// summary: |
|
// An optional arguments object that can be used in the Series constructor. |
|
// plot: String? |
|
// The plot (by name) that this series belongs to. |
|
}; |
|
=====*/ |
|
|
|
/*===== |
|
var __BaseAxisCtorArgs = { |
|
// summary: |
|
// Optional arguments used in the definition of an invisible axis. |
|
// vertical: Boolean? |
|
// A flag that says whether an axis is vertical (i.e. y axis) or horizontal. Default is false (horizontal). |
|
// min: Number? |
|
// The smallest value on an axis. Default is 0. |
|
// max: Number? |
|
// The largest value on an axis. Default is 1. |
|
}; |
|
=====*/ |
|
|
|
var dc = lang.getObject("charting", true, dojox), |
|
defaultMargins = {l: 10, t: 10, r: 10, b: 10}; |
|
|
|
function clear (item) { |
|
return item.clear(); |
|
} |
|
|
|
function destroy (item) { |
|
return item.destroy(); |
|
} |
|
|
|
function makeClean (item) { |
|
item.dirty = false; |
|
return false; |
|
} |
|
|
|
function makeDirty (item) { |
|
item.dirty = true; |
|
return true; |
|
} |
|
|
|
function getName (item) { |
|
return item.name; |
|
} |
|
|
|
var Chart = declare(has("dojo-bidi")? "dojox.charting.NonBidiChart" : "dojox.charting.Chart", null, { |
|
// summary: |
|
// The main chart object in dojox.charting. This will create a two dimensional |
|
// chart based on dojox.gfx. |
|
// |
|
// description: |
|
// dojox.charting.Chart is the primary object used for any kind of charts. It |
|
// is simple to create--just pass it a node reference, which is used as the |
|
// container for the chart--and a set of optional keyword arguments and go. |
|
// |
|
// Note that like most of dojox.gfx, most of dojox.charting.Chart's methods are |
|
// designed to return a reference to the chart itself, to allow for functional |
|
// chaining. This makes defining everything on a Chart very easy to do. |
|
// |
|
// example: |
|
// Create an area chart, with smoothing. |
|
// | require(["dojox/charting/Chart", "dojox/charting/themes/Shrooms", "dojox/charting/plot2d/Areas", ...], |
|
// | function(Chart, Shrooms, Areas, ...){ |
|
// | new Chart(node) |
|
// | .addPlot("default", { type: Areas, tension: "X" }) |
|
// | .setTheme(Shrooms) |
|
// | .addSeries("Series A", [1, 2, 0.5, 1.5, 1, 2.8, 0.4]) |
|
// | .addSeries("Series B", [2.6, 1.8, 2, 1, 1.4, 0.7, 2]) |
|
// | .addSeries("Series C", [6.3, 1.8, 3, 0.5, 4.4, 2.7, 2]) |
|
// | .render(); |
|
// | }); |
|
// |
|
// example: |
|
// The form of data in a data series can take a number of forms: a simple array, |
|
// an array of objects {x,y}, or something custom (as determined by the plot). |
|
// Here's an example of a Candlestick chart, which expects an object of |
|
// { open, high, low, close }. |
|
// | require(["dojox/charting/Chart", "dojox/charting/plot2d/Candlesticks", ...], |
|
// | function(Chart, Candlesticks, ...){ |
|
// | new Chart(node) |
|
// | .addPlot("default", {type: Candlesticks, gap: 1}) |
|
// | .addAxis("x", {fixLower: "major", fixUpper: "major", includeZero: true}) |
|
// | .addAxis("y", {vertical: true, fixLower: "major", fixUpper: "major", natural: true}) |
|
// | .addSeries("Series A", [ |
|
// | { open: 20, close: 16, high: 22, low: 8 }, |
|
// | { open: 16, close: 22, high: 26, low: 6, mid: 18 }, |
|
// | { open: 22, close: 18, high: 22, low: 11, mid: 21 }, |
|
// | { open: 18, close: 29, high: 32, low: 14, mid: 27 }, |
|
// | { open: 29, close: 24, high: 29, low: 13, mid: 27 }, |
|
// | { open: 24, close: 8, high: 24, low: 5 }, |
|
// | { open: 8, close: 16, high: 22, low: 2 }, |
|
// | { open: 16, close: 12, high: 19, low: 7 }, |
|
// | { open: 12, close: 20, high: 22, low: 8 }, |
|
// | { open: 20, close: 16, high: 22, low: 8 }, |
|
// | { open: 16, close: 22, high: 26, low: 6, mid: 18 }, |
|
// | { open: 22, close: 18, high: 22, low: 11, mid: 21 }, |
|
// | { open: 18, close: 29, high: 32, low: 14, mid: 27 }, |
|
// | { open: 29, close: 24, high: 29, low: 13, mid: 27 }, |
|
// | { open: 24, close: 8, high: 24, low: 5 }, |
|
// | { open: 8, close: 16, high: 22, low: 2 }, |
|
// | { open: 16, close: 12, high: 19, low: 7 }, |
|
// | { open: 12, close: 20, high: 22, low: 8 }, |
|
// | { open: 20, close: 16, high: 22, low: 8 }, |
|
// | { open: 16, close: 22, high: 26, low: 6 }, |
|
// | { open: 22, close: 18, high: 22, low: 11 }, |
|
// | { open: 18, close: 29, high: 32, low: 14 }, |
|
// | { open: 29, close: 24, high: 29, low: 13 }, |
|
// | { open: 24, close: 8, high: 24, low: 5 }, |
|
// | { open: 8, close: 16, high: 22, low: 2 }, |
|
// | { open: 16, close: 12, high: 19, low: 7 }, |
|
// | { open: 12, close: 20, high: 22, low: 8 }, |
|
// | { open: 20, close: 16, high: 22, low: 8 } |
|
// | ], |
|
// | { stroke: { color: "green" }, fill: "lightgreen" } |
|
// | ) |
|
// | .render(); |
|
// | }); |
|
|
|
// theme: dojox/charting/SimpleTheme? |
|
// An optional theme to use for styling the chart. |
|
// axes: dojox/charting/axis2d/Base{}? |
|
// A map of axes for use in plotting a chart. |
|
// stack: dojox/charting/plot2d/Base[] |
|
// A stack of plotters. |
|
// plots: dojox/charting/plot2d/Base{} |
|
// A map of plotter indices |
|
// series: dojox/charting/Series[] |
|
// The stack of data runs used to create plots. |
|
// runs: dojox/charting/Series{} |
|
// A map of series indices |
|
// margins: Object? |
|
// The margins around the chart. Default is { l:10, t:10, r:10, b:10 }. |
|
// stroke: dojox.gfx.Stroke? |
|
// The outline of the chart (stroke in vector graphics terms). |
|
// fill: dojox.gfx.Fill? |
|
// The color for the chart. |
|
// node: DOMNode |
|
// The container node passed to the constructor. |
|
// surface: dojox/gfx/shape.Surface |
|
// The main graphics surface upon which a chart is drawn. |
|
// dirty: Boolean |
|
// A boolean flag indicating whether or not the chart needs to be updated/re-rendered. |
|
// htmlLabels: Boolean |
|
// A boolean flag indicating whether or not it should try to use HTML-based labels for the title or not. |
|
// The default is true. The only caveat is IE and Opera browsers will always use GFX-based labels. |
|
|
|
constructor: function(/* DOMNode */node, /* __ChartCtorArgs? */kwArgs){ |
|
// summary: |
|
// The constructor for a new Chart. Initializes all parameters used for a chart. |
|
// returns: dojox/charting/Chart |
|
// The newly created chart. |
|
|
|
// initialize parameters |
|
if(!kwArgs){ kwArgs = {}; } |
|
this.margins = kwArgs.margins || defaultMargins; |
|
this._customMargins = !!kwArgs.margins; |
|
this.stroke = kwArgs.stroke; |
|
this.fill = kwArgs.fill; |
|
this.delayInMs = kwArgs.delayInMs || 200; |
|
this.title = kwArgs.title; |
|
this.titleGap = kwArgs.titleGap; |
|
this.titlePos = kwArgs.titlePos; |
|
this.titleFont = kwArgs.titleFont; |
|
this.titleFontColor = kwArgs.titleFontColor; |
|
this.titleAlign = kwArgs.titleAlign; // This can be middle, left, right, or edge |
|
// edge is left or right aligned with chart plot edge depending on bidi. |
|
this.chartTitle = null; |
|
this.htmlLabels = true; |
|
if("htmlLabels" in kwArgs){ |
|
this.htmlLabels = kwArgs.htmlLabels; |
|
} |
|
|
|
// default initialization |
|
this.theme = null; |
|
this.axes = {}; // map of axes |
|
this.stack = []; // stack of plotters |
|
this.plots = {}; // map of plotter indices |
|
this.series = []; // stack of data runs |
|
this.runs = {}; // map of data run indices |
|
this.dirty = true; |
|
|
|
// create a surface |
|
this.node = dom.byId(node); |
|
var box = domGeom.getMarginBox(node); |
|
this.surface = g.createSurface(this.node, box.w || 400, box.h || 300); |
|
if(this.surface.declaredClass.indexOf("vml") == -1){ |
|
// except if vml use native clipping |
|
this._nativeClip = true; |
|
} |
|
}, |
|
destroy: function(){ |
|
// summary: |
|
// Cleanup when a chart is to be destroyed. |
|
// returns: void |
|
arr.forEach(this.series, destroy); |
|
arr.forEach(this.stack, destroy); |
|
func.forIn(this.axes, destroy); |
|
this.surface.destroy(); |
|
if(this.chartTitle && this.chartTitle.tagName){ |
|
// destroy title if it is a DOM node |
|
domConstruct.destroy(this.chartTitle); |
|
} |
|
}, |
|
getCoords: function(){ |
|
// summary: |
|
// Get the coordinates and dimensions of the containing DOMNode, as |
|
// returned by dojo.coords. |
|
// returns: Object |
|
// The resulting coordinates of the chart. See dojo.coords for details. |
|
var node = this.node; |
|
var s = domStyle.getComputedStyle(node), coords = domGeom.getMarginBox(node, s); |
|
var abs = domGeom.position(node, true); |
|
coords.x = abs.x; |
|
coords.y = abs.y; |
|
return coords; // Object |
|
}, |
|
setTheme: function(theme){ |
|
// summary: |
|
// Set a theme of the chart. |
|
// theme: dojox/charting/SimpleTheme |
|
// The theme to be used for visual rendering. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
this.theme = theme.clone(); |
|
if(!this._customMargins){ |
|
this.margins = this.theme.chart.margins || defaultMargins; |
|
} |
|
this.dirty = true; |
|
return this; // dojox/charting/Chart |
|
}, |
|
addAxis: function(name, kwArgs){ |
|
// summary: |
|
// Add an axis to the chart, for rendering. |
|
// name: String |
|
// The name of the axis. |
|
// kwArgs: __BaseAxisCtorArgs? |
|
// An optional keyword arguments object for use in defining details of an axis. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
var axis, axisType = kwArgs && kwArgs.type || "Default"; |
|
if(typeof axisType == "string"){ |
|
if(!dc.axis2d || !dc.axis2d[axisType]){ |
|
throw Error("Can't find axis: " + axisType + " - Check " + "require() dependencies."); |
|
} |
|
axis = new dc.axis2d[axisType](this, kwArgs); |
|
}else{ |
|
axis = new axisType(this, kwArgs); |
|
} |
|
axis.name = name; |
|
axis.dirty = true; |
|
if(name in this.axes){ |
|
this.axes[name].destroy(); |
|
} |
|
this.axes[name] = axis; |
|
this.dirty = true; |
|
return this; // dojox/charting/Chart |
|
}, |
|
getAxis: function(name){ |
|
// summary: |
|
// Get the given axis, by name. |
|
// name: String |
|
// The name the axis was defined by. |
|
// returns: dojox/charting/axis2d/Default |
|
// The axis as stored in the chart's axis map. |
|
return this.axes[name]; // dojox/charting/axis2d/Default |
|
}, |
|
removeAxis: function(name){ |
|
// summary: |
|
// Remove the axis that was defined using name. |
|
// name: String |
|
// The axis name, as defined in addAxis. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
if(name in this.axes){ |
|
// destroy the axis |
|
this.axes[name].destroy(); |
|
delete this.axes[name]; |
|
// mark the chart as dirty |
|
this.dirty = true; |
|
} |
|
return this; // dojox/charting/Chart |
|
}, |
|
addPlot: function(name, kwArgs){ |
|
// summary: |
|
// Add a new plot to the chart, defined by name and using the optional keyword arguments object. |
|
// Note that dojox.charting assumes the main plot to be called "default"; if you do not have |
|
// a plot called "default" and attempt to add data series to the chart without specifying the |
|
// plot to be rendered on, you WILL get errors. |
|
// name: String |
|
// The name of the plot to be added to the chart. If you only plan on using one plot, call it "default". |
|
// kwArgs: dojox.charting.plot2d.__PlotCtorArgs |
|
// An object with optional parameters for the plot in question. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
var plot, plotType = kwArgs && kwArgs.type || "Default"; |
|
if(typeof plotType == "string"){ |
|
if(!dc.plot2d || !dc.plot2d[plotType]){ |
|
throw Error("Can't find plot: " + plotType + " - didn't you forget to dojo" + ".require() it?"); |
|
} |
|
plot = new dc.plot2d[plotType](this, kwArgs); |
|
}else{ |
|
plot = new plotType(this, kwArgs); |
|
} |
|
plot.name = name; |
|
plot.dirty = true; |
|
if(name in this.plots){ |
|
this.stack[this.plots[name]].destroy(); |
|
this.stack[this.plots[name]] = plot; |
|
}else{ |
|
this.plots[name] = this.stack.length; |
|
this.stack.push(plot); |
|
} |
|
this.dirty = true; |
|
return this; // dojox/charting/Chart |
|
}, |
|
getPlot: function(name){ |
|
// summary: |
|
// Get the given plot, by name. |
|
// name: String |
|
// The name the plot was defined by. |
|
// returns: dojox/charting/plot2d/Base |
|
// The plot. |
|
return this.stack[this.plots[name]]; |
|
}, |
|
removePlot: function(name){ |
|
// summary: |
|
// Remove the plot defined using name from the chart's plot stack. |
|
// name: String |
|
// The name of the plot as defined using addPlot. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
if(name in this.plots){ |
|
// get the index and remove the name |
|
var index = this.plots[name]; |
|
delete this.plots[name]; |
|
// destroy the plot |
|
this.stack[index].destroy(); |
|
// remove the plot from the stack |
|
this.stack.splice(index, 1); |
|
// update indices to reflect the shift |
|
func.forIn(this.plots, function(idx, name, plots){ |
|
if(idx > index){ |
|
plots[name] = idx - 1; |
|
} |
|
}); |
|
// remove all related series |
|
var ns = arr.filter(this.series, function(run){ return run.plot != name; }); |
|
if(ns.length < this.series.length){ |
|
// kill all removed series |
|
arr.forEach(this.series, function(run){ |
|
if(run.plot == name){ |
|
run.destroy(); |
|
} |
|
}); |
|
// rebuild all necessary data structures |
|
this.runs = {}; |
|
arr.forEach(ns, function(run, index){ |
|
this.runs[run.plot] = index; |
|
}, this); |
|
this.series = ns; |
|
} |
|
// mark the chart as dirty |
|
this.dirty = true; |
|
} |
|
return this; // dojox/charting/Chart |
|
}, |
|
getPlotOrder: function(){ |
|
// summary: |
|
// Returns an array of plot names in the current order |
|
// (the top-most plot is the first). |
|
// returns: Array |
|
return func.map(this.stack, getName); // Array |
|
}, |
|
setPlotOrder: function(newOrder){ |
|
// summary: |
|
// Sets new order of plots. newOrder cannot add or remove |
|
// plots. Wrong names, or dups are ignored. |
|
// newOrder: Array |
|
// Array of plot names compatible with getPlotOrder(). |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
var names = {}, |
|
order = func.filter(newOrder, function(name){ |
|
if(!(name in this.plots) || (name in names)){ |
|
return false; |
|
} |
|
names[name] = 1; |
|
return true; |
|
}, this); |
|
if(order.length < this.stack.length){ |
|
func.forEach(this.stack, function(plot){ |
|
var name = plot.name; |
|
if(!(name in names)){ |
|
order.push(name); |
|
} |
|
}); |
|
} |
|
var newStack = func.map(order, function(name){ |
|
return this.stack[this.plots[name]]; |
|
}, this); |
|
func.forEach(newStack, function(plot, i){ |
|
this.plots[plot.name] = i; |
|
}, this); |
|
this.stack = newStack; |
|
this.dirty = true; |
|
return this; // dojox/charting/Chart |
|
}, |
|
movePlotToFront: function(name){ |
|
// summary: |
|
// Moves a given plot to front. |
|
// name: String |
|
// Plot's name to move. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
if(name in this.plots){ |
|
var index = this.plots[name]; |
|
if(index){ |
|
var newOrder = this.getPlotOrder(); |
|
newOrder.splice(index, 1); |
|
newOrder.unshift(name); |
|
return this.setPlotOrder(newOrder); // dojox/charting/Chart |
|
} |
|
} |
|
return this; // dojox/charting/Chart |
|
}, |
|
movePlotToBack: function(name){ |
|
// summary: |
|
// Moves a given plot to back. |
|
// name: String |
|
// Plot's name to move. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
if(name in this.plots){ |
|
var index = this.plots[name]; |
|
if(index < this.stack.length - 1){ |
|
var newOrder = this.getPlotOrder(); |
|
newOrder.splice(index, 1); |
|
newOrder.push(name); |
|
return this.setPlotOrder(newOrder); // dojox/charting/Chart |
|
} |
|
} |
|
return this; // dojox/charting/Chart |
|
}, |
|
addSeries: function(name, data, kwArgs){ |
|
// summary: |
|
// Add a data series to the chart for rendering. |
|
// name: String |
|
// The name of the data series to be plotted. |
|
// data: Array|Object |
|
// The array of data points (either numbers or objects) that |
|
// represents the data to be drawn. Or it can be an object. In |
|
// the latter case, it should have a property "data" (an array), |
|
// destroy(), and setSeriesObject(). |
|
// kwArgs: __SeriesCtorArgs? |
|
// An optional keyword arguments object that will be mixed into |
|
// the resultant series object. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
var run = new Series(this, data, kwArgs); |
|
run.name = name; |
|
if(name in this.runs){ |
|
this.series[this.runs[name]].destroy(); |
|
this.series[this.runs[name]] = run; |
|
}else{ |
|
this.runs[name] = this.series.length; |
|
this.series.push(run); |
|
} |
|
this.dirty = true; |
|
// fix min/max |
|
if(!("ymin" in run) && "min" in run){ run.ymin = run.min; } |
|
if(!("ymax" in run) && "max" in run){ run.ymax = run.max; } |
|
return this; // dojox/charting/Chart |
|
}, |
|
getSeries: function(name){ |
|
// summary: |
|
// Get the given series, by name. |
|
// name: String |
|
// The name the series was defined by. |
|
// returns: dojox/charting/Series |
|
// The series. |
|
return this.series[this.runs[name]]; |
|
}, |
|
removeSeries: function(name){ |
|
// summary: |
|
// Remove the series defined by name from the chart. |
|
// name: String |
|
// The name of the series as defined by addSeries. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
if(name in this.runs){ |
|
// get the index and remove the name |
|
var index = this.runs[name]; |
|
delete this.runs[name]; |
|
// destroy the run |
|
this.series[index].destroy(); |
|
// remove the run from the stack of series |
|
this.series.splice(index, 1); |
|
// update indices to reflect the shift |
|
func.forIn(this.runs, function(idx, name, runs){ |
|
if(idx > index){ |
|
runs[name] = idx - 1; |
|
} |
|
}); |
|
this.dirty = true; |
|
} |
|
return this; // dojox/charting/Chart |
|
}, |
|
updateSeries: function(name, data, offsets){ |
|
// summary: |
|
// Update the given series with a new set of data points. |
|
// name: String |
|
// The name of the series as defined in addSeries. |
|
// data: Array|Object |
|
// The array of data points (either numbers or objects) that |
|
// represents the data to be drawn. Or it can be an object. In |
|
// the latter case, it should have a property "data" (an array), |
|
// destroy(), and setSeriesObject(). |
|
// offsets: Boolean? |
|
// If true recomputes the offsets of the chart based on the new |
|
// data. This is useful if the range of data is drastically changing |
|
// and offsets need to be recomputed. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
if(name in this.runs){ |
|
var run = this.series[this.runs[name]]; |
|
run.update(data); |
|
if(offsets){ |
|
this.dirty = true; |
|
}else{ |
|
this._invalidateDependentPlots(run.plot, false); |
|
this._invalidateDependentPlots(run.plot, true); |
|
} |
|
} |
|
return this; // dojox/charting/Chart |
|
}, |
|
getSeriesOrder: function(plotName){ |
|
// summary: |
|
// Returns an array of series names in the current order |
|
// (the top-most series is the first) within a plot. |
|
// plotName: String |
|
// Plot's name. |
|
// returns: Array |
|
return func.map(func.filter(this.series, function(run){ |
|
return run.plot == plotName; |
|
}), getName); |
|
}, |
|
setSeriesOrder: function(newOrder){ |
|
// summary: |
|
// Sets new order of series within a plot. newOrder cannot add |
|
// or remove series. Wrong names, or dups are ignored. |
|
// newOrder: Array |
|
// Array of series names compatible with getPlotOrder(). All |
|
// series should belong to the same plot. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
var plotName, names = {}, |
|
order = func.filter(newOrder, function(name){ |
|
if(!(name in this.runs) || (name in names)){ |
|
return false; |
|
} |
|
var run = this.series[this.runs[name]]; |
|
if(plotName){ |
|
if(run.plot != plotName){ |
|
return false; |
|
} |
|
}else{ |
|
plotName = run.plot; |
|
} |
|
names[name] = 1; |
|
return true; |
|
}, this); |
|
func.forEach(this.series, function(run){ |
|
var name = run.name; |
|
if(!(name in names) && run.plot == plotName){ |
|
order.push(name); |
|
} |
|
}); |
|
var newSeries = func.map(order, function(name){ |
|
return this.series[this.runs[name]]; |
|
}, this); |
|
this.series = newSeries.concat(func.filter(this.series, function(run){ |
|
return run.plot != plotName; |
|
})); |
|
func.forEach(this.series, function(run, i){ |
|
this.runs[run.name] = i; |
|
}, this); |
|
this.dirty = true; |
|
return this; // dojox/charting/Chart |
|
}, |
|
moveSeriesToFront: function(name){ |
|
// summary: |
|
// Moves a given series to front of a plot. |
|
// name: String |
|
// Series' name to move. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
if(name in this.runs){ |
|
var index = this.runs[name], |
|
newOrder = this.getSeriesOrder(this.series[index].plot); |
|
if(name != newOrder[0]){ |
|
newOrder.splice(index, 1); |
|
newOrder.unshift(name); |
|
return this.setSeriesOrder(newOrder); // dojox/charting/Chart |
|
} |
|
} |
|
return this; // dojox/charting/Chart |
|
}, |
|
moveSeriesToBack: function(name){ |
|
// summary: |
|
// Moves a given series to back of a plot. |
|
// name: String |
|
// Series' name to move. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
if(name in this.runs){ |
|
var index = this.runs[name], |
|
newOrder = this.getSeriesOrder(this.series[index].plot); |
|
if(name != newOrder[newOrder.length - 1]){ |
|
newOrder.splice(index, 1); |
|
newOrder.push(name); |
|
return this.setSeriesOrder(newOrder); // dojox/charting/Chart |
|
} |
|
} |
|
return this; // dojox/charting/Chart |
|
}, |
|
resize: function(width, height){ |
|
// summary: |
|
// Resize the chart to the dimensions of width and height. |
|
// description: |
|
// Resize the chart and its surface to the width and height dimensions. |
|
// If a single argument of the form {w: value1, h: value2} is provided take that argument as the dimensions to use. |
|
// Finally if no argument is provided, resize the surface to the marginBox of the chart. |
|
// width: Number|Object? |
|
// The new width of the chart or the box definition. |
|
// height: Number? |
|
// The new height of the chart. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
switch(arguments.length){ |
|
// case 0, do not resize the div, just the surface |
|
case 1: |
|
// argument, override node box |
|
domGeom.setMarginBox(this.node, width); |
|
break; |
|
case 2: |
|
// argument, override node box |
|
domGeom.setMarginBox(this.node, {w: width, h: height}); |
|
break; |
|
} |
|
// in all cases take back the computed box |
|
var box = domGeom.getMarginBox(this.node); |
|
var d = this.surface.getDimensions(); |
|
if(d.width != box.w || d.height != box.h){ |
|
// and set it on the surface |
|
this.surface.setDimensions(box.w, box.h); |
|
this.dirty = true; |
|
return this.render(); // dojox/charting/Chart |
|
}else{ |
|
return this; |
|
} |
|
}, |
|
getGeometry: function(){ |
|
// summary: |
|
// Returns a map of information about all axes in a chart and what they represent |
|
// in terms of scaling (see dojox.charting.axis2d.Default.getScaler). |
|
// returns: Object |
|
// An map of geometry objects, a one-to-one mapping of axes. |
|
var ret = {}; |
|
func.forIn(this.axes, function(axis){ |
|
if(axis.initialized()){ |
|
ret[axis.name] = { |
|
name: axis.name, |
|
vertical: axis.vertical, |
|
scaler: axis.scaler, |
|
ticks: axis.ticks |
|
}; |
|
} |
|
}); |
|
return ret; // Object |
|
}, |
|
setAxisWindow: function(name, scale, offset, zoom){ |
|
// summary: |
|
// Zooms an axis and all dependent plots. Can be used to zoom in 1D. |
|
// name: String |
|
// The name of the axis as defined by addAxis. |
|
// scale: Number |
|
// The scale on the target axis. |
|
// offset: Number |
|
// Any offest, as measured by axis tick |
|
// zoom: Boolean|Object? |
|
// The chart zooming animation trigger. This is null by default, |
|
// e.g. {duration: 1200}, or just set true. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
var axis = this.axes[name]; |
|
if(axis){ |
|
axis.setWindow(scale, offset); |
|
arr.forEach(this.stack,function(plot){ |
|
if(plot.hAxis == name || plot.vAxis == name){ |
|
plot.zoom = zoom; |
|
} |
|
}); |
|
} |
|
return this; // dojox/charting/Chart |
|
}, |
|
setWindow: function(sx, sy, dx, dy, zoom){ |
|
// summary: |
|
// Zooms in or out any plots in two dimensions. |
|
// sx: Number |
|
// The scale for the x axis. |
|
// sy: Number |
|
// The scale for the y axis. |
|
// dx: Number |
|
// The pixel offset on the x axis. |
|
// dy: Number |
|
// The pixel offset on the y axis. |
|
// zoom: Boolean|Object? |
|
// The chart zooming animation trigger. This is null by default, |
|
// e.g. {duration: 1200}, or just set true. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
if(!("plotArea" in this)){ |
|
this.calculateGeometry(); |
|
} |
|
func.forIn(this.axes, function(axis){ |
|
var scale, offset, bounds = axis.getScaler().bounds, |
|
s = bounds.span / (bounds.upper - bounds.lower); |
|
if(axis.vertical){ |
|
scale = sy; |
|
offset = dy / s / scale; |
|
}else{ |
|
scale = sx; |
|
offset = dx / s / scale; |
|
} |
|
axis.setWindow(scale, offset); |
|
}); |
|
arr.forEach(this.stack, function(plot){ plot.zoom = zoom; }); |
|
return this; // dojox/charting/Chart |
|
}, |
|
zoomIn: function(name, range, delayed){ |
|
// summary: |
|
// Zoom the chart to a specific range on one axis. This calls render() |
|
// directly as a convenience method. |
|
// name: String |
|
// The name of the axis as defined by addAxis. |
|
// range: Array |
|
// The end points of the zoom range, measured in axis ticks. |
|
var axis = this.axes[name]; |
|
if(axis){ |
|
var scale, offset, bounds = axis.getScaler().bounds; |
|
var lower = Math.min(range[0],range[1]); |
|
var upper = Math.max(range[0],range[1]); |
|
lower = range[0] < bounds.lower ? bounds.lower : lower; |
|
upper = range[1] > bounds.upper ? bounds.upper : upper; |
|
scale = (bounds.upper - bounds.lower) / (upper - lower); |
|
offset = lower - bounds.lower; |
|
this.setAxisWindow(name, scale, offset); |
|
if(delayed){ |
|
this.delayedRender(); |
|
}else{ |
|
this.render(); |
|
} |
|
} |
|
}, |
|
calculateGeometry: function(){ |
|
// summary: |
|
// Calculate the geometry of the chart based on the defined axes of |
|
// a chart. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
if(this.dirty){ |
|
return this.fullGeometry(); |
|
} |
|
|
|
// calculate geometry |
|
var dirty = arr.filter(this.stack, function(plot){ |
|
return plot.dirty || |
|
(plot.hAxis && this.axes[plot.hAxis].dirty) || |
|
(plot.vAxis && this.axes[plot.vAxis].dirty); |
|
}, this); |
|
calculateAxes(dirty, this.plotArea); |
|
|
|
return this; // dojox/charting/Chart |
|
}, |
|
fullGeometry: function(){ |
|
// summary: |
|
// Calculate the full geometry of the chart. This includes passing |
|
// over all major elements of a chart (plots, axes, series, container) |
|
// in order to ensure proper rendering. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
this._makeDirty(); |
|
|
|
// clear old values |
|
arr.forEach(this.stack, clear); |
|
|
|
// rebuild new connections, and add defaults |
|
|
|
// set up a theme |
|
if(!this.theme){ |
|
this.setTheme(new SimpleTheme()); |
|
} |
|
|
|
// assign series |
|
arr.forEach(this.series, function(run){ |
|
if(!(run.plot in this.plots)){ |
|
// TODO remove auto-assignment |
|
if(!dc.plot2d || !dc.plot2d.Default){ |
|
throw Error("Can't find plot: Default - didn't you forget to dojo" + ".require() it?"); |
|
} |
|
var plot = new dc.plot2d.Default(this, {}); |
|
plot.name = run.plot; |
|
this.plots[run.plot] = this.stack.length; |
|
this.stack.push(plot); |
|
} |
|
this.stack[this.plots[run.plot]].addSeries(run); |
|
}, this); |
|
// assign axes |
|
arr.forEach(this.stack, function(plot){ |
|
if(plot.assignAxes){ |
|
plot.assignAxes(this.axes); |
|
} |
|
}, this); |
|
|
|
// calculate geometry |
|
|
|
// 1st pass |
|
var dim = this.dim = this.surface.getDimensions(); |
|
dim.width = g.normalizedLength(dim.width); |
|
dim.height = g.normalizedLength(dim.height); |
|
func.forIn(this.axes, clear); |
|
calculateAxes(this.stack, dim); |
|
|
|
// assumption: we don't have stacked axes yet |
|
var offsets = this.offsets = {l: 0, r: 0, t: 0, b: 0}; |
|
// chart mirroring starts |
|
var self = this; |
|
func.forIn(this.axes, function(axis){ |
|
if(has("dojo-bidi")){ |
|
self._resetLeftBottom(axis); |
|
} |
|
func.forIn(axis.getOffsets(), function(o, i){ offsets[i] = Math.max(o, offsets[i]); }); |
|
}); |
|
// chart mirroring ends |
|
// add title area |
|
if(this.title){ |
|
this.titleGap = (this.titleGap==0) ? 0 : this.titleGap || this.theme.chart.titleGap || 20; |
|
this.titlePos = this.titlePos || this.theme.chart.titlePos || "top"; |
|
this.titleFont = this.titleFont || this.theme.chart.titleFont; |
|
this.titleFontColor = this.titleFontColor || this.theme.chart.titleFontColor || "black"; |
|
this.titleAlign = this.titleAlign || this.theme && this.theme.chart && this.theme.chart.titleAlign || "middle"; |
|
var tsize = g.normalizedLength(g.splitFontString(this.titleFont).size); |
|
offsets[this.titlePos == "top" ? "t" : "b"] += (tsize + this.titleGap); |
|
} |
|
// add margins |
|
func.forIn(this.margins, function(o, i){ offsets[i] += o; }); |
|
|
|
// 2nd pass with realistic dimensions |
|
this.plotArea = { |
|
width: dim.width - offsets.l - offsets.r, |
|
height: dim.height - offsets.t - offsets.b |
|
}; |
|
func.forIn(this.axes, clear); |
|
calculateAxes(this.stack, this.plotArea); |
|
|
|
return this; // dojox/charting/Chart |
|
}, |
|
render: function(){ |
|
// summary: |
|
// Render the chart according to the current information defined. This should |
|
// be the last call made when defining/creating a chart, or if data within the |
|
// chart has been changed. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
|
|
// do we have a delayed renderer pending? If yes we need to clear it |
|
if(this._delayedRenderHandle){ |
|
clearTimeout(this._delayedRenderHandle); |
|
this._delayedRenderHandle = null; |
|
} |
|
|
|
if(this.theme){ |
|
this.theme.clear(); |
|
} |
|
|
|
if(this.dirty){ |
|
return this.fullRender(); |
|
} |
|
|
|
this.calculateGeometry(); |
|
|
|
// go over the stack backwards |
|
func.forEachRev(this.stack, function(plot){ plot.render(this.dim, this.offsets); }, this); |
|
|
|
// go over axes |
|
func.forIn(this.axes, function(axis){ axis.render(this.dim, this.offsets); }, this); |
|
|
|
this._makeClean(); |
|
|
|
return this; // dojox/charting/Chart |
|
}, |
|
fullRender: function(){ |
|
// summary: |
|
// Force a full rendering of the chart, including full resets on the chart itself. |
|
// You should not call this method directly unless absolutely necessary. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
|
|
// calculate geometry |
|
this.fullGeometry(); |
|
var offsets = this.offsets, dim = this.dim; |
|
var w = Math.max(0, dim.width - offsets.l - offsets.r), |
|
h = Math.max(0, dim.height - offsets.t - offsets.b); |
|
|
|
// get required colors |
|
//var requiredColors = func.foldl(this.stack, "z + plot.getRequiredColors()", 0); |
|
//this.theme.defineColors({num: requiredColors, cache: false}); |
|
|
|
// clear old shapes |
|
arr.forEach(this.series, plot2dCommon.purgeGroup); |
|
func.forIn(this.axes, plot2dCommon.purgeGroup); |
|
arr.forEach(this.stack, plot2dCommon.purgeGroup); |
|
var children = this.surface.children; |
|
// starting with 1.9 the registry is optional and thus dispose is |
|
if(shape.dispose){ |
|
for(var i = 0; i < children.length;++i){ |
|
shape.dispose(children[i]); |
|
} |
|
} |
|
if(this.chartTitle && this.chartTitle.tagName){ |
|
// destroy title if it is a DOM node |
|
domConstruct.destroy(this.chartTitle); |
|
} |
|
this.surface.clear(); |
|
this.chartTitle = null; |
|
|
|
this._renderChartBackground(dim, offsets); |
|
if(this._nativeClip){ |
|
this._renderPlotBackground(dim, offsets, w, h); |
|
}else{ |
|
// VML |
|
this._renderPlotBackground(dim, offsets, w, h); |
|
} |
|
|
|
// go over the stack backwards |
|
func.foldr(this.stack, function(z, plot){ return plot.render(dim, offsets), 0; }, 0); |
|
|
|
if(!this._nativeClip){ |
|
// VML, matting-clipping |
|
this._renderChartBackground(dim, offsets); |
|
} |
|
|
|
//create title: Whether to make chart title as a widget which extends dojox.charting.Element? |
|
if(this.title){ |
|
this._renderTitle(dim, offsets); |
|
} |
|
|
|
// go over axes |
|
func.forIn(this.axes, function(axis){ axis.render(dim, offsets); }); |
|
|
|
this._makeClean(); |
|
|
|
return this; // dojox/charting/Chart |
|
}, |
|
_renderTitle: function(dim, offsets){ |
|
// summary: |
|
// Internal function to render the chart title. |
|
// dim: |
|
// The dimension object of the chart |
|
// tags: |
|
// private |
|
var forceHtmlLabels = (g.renderer == "canvas") && this.htmlLabels, |
|
labelType = forceHtmlLabels || !has("ie") && !has("opera") && this.htmlLabels ? "html" : "gfx", |
|
tsize = g.normalizedLength(g.splitFontString(this.titleFont).size), |
|
tBox = g._base._getTextBox(this.title,{ font: this.titleFont }); |
|
|
|
var titleAlign = this.titleAlign; |
|
var isRtl = has("dojo-bidi") && this.isRightToLeft(); |
|
var posX = dim.width/2; // Default is middle. |
|
if(titleAlign === "edge"){ |
|
titleAlign = "left"; |
|
if(isRtl){ |
|
posX = dim.width - (offsets.r + tBox.w); |
|
}else { |
|
posX = offsets.l; |
|
} |
|
}else if(titleAlign != "middle"){ |
|
if(isRtl){ |
|
// We're in BIDI mode, reverse the alignment. |
|
titleAlign = titleAlign === "left" ? "right" : "left"; |
|
} |
|
if(titleAlign === "left"){ |
|
posX = this.margins.l; |
|
}else if(titleAlign === "right"){ |
|
titleAlign = "left"; |
|
posX = dim.width - (this.margins.l + tBox.w); |
|
} |
|
} |
|
this.chartTitle = common.createText[labelType]( |
|
this, |
|
this.surface, |
|
posX, |
|
this.titlePos=="top" ? tsize + this.margins.t : dim.height - this.margins.b, |
|
titleAlign, |
|
this.title, |
|
this.titleFont, |
|
this.titleFontColor |
|
); |
|
}, |
|
_renderChartBackground: function(dim, offsets){ |
|
var t = this.theme, rect; |
|
// chart background |
|
var fill = this.fill !== undefined ? this.fill : (t.chart && t.chart.fill); |
|
var stroke = this.stroke !== undefined ? this.stroke : (t.chart && t.chart.stroke); |
|
|
|
// TRT: support for "inherit" as a named value in a theme. |
|
if(fill == "inherit"){ |
|
// find the background color of the nearest ancestor node, and use that explicitly. |
|
var node = this.node; |
|
fill = new Color(domStyle.get(node, "backgroundColor")); |
|
while(fill.a==0 && node!=document.documentElement){ |
|
fill = new Color(domStyle.get(node, "backgroundColor")); |
|
node = node.parentNode; |
|
} |
|
} |
|
|
|
if(fill){ |
|
if(this._nativeClip){ |
|
fill = Element.prototype._shapeFill(Element.prototype._plotFill(fill, dim), |
|
{ x:0, y: 0, width: dim.width + 1, height: dim.height + 1 }); |
|
this.surface.createRect({ width: dim.width + 1, height: dim.height + 1 }).setFill(fill); |
|
}else{ |
|
// VML |
|
fill = Element.prototype._plotFill(fill, dim, offsets); |
|
if(offsets.l){ // left |
|
rect = { |
|
x: 0, |
|
y: 0, |
|
width: offsets.l, |
|
height: dim.height + 1 |
|
}; |
|
this.surface.createRect(rect).setFill(Element.prototype._shapeFill(fill, rect)); |
|
} |
|
if(offsets.r){ // right |
|
rect = { |
|
x: dim.width - offsets.r, |
|
y: 0, |
|
width: offsets.r + 1, |
|
height: dim.height + 2 |
|
}; |
|
this.surface.createRect(rect).setFill(Element.prototype._shapeFill(fill, rect)); |
|
} |
|
if(offsets.t){ // top |
|
rect = { |
|
x: 0, |
|
y: 0, |
|
width: dim.width + 1, |
|
height: offsets.t |
|
}; |
|
this.surface.createRect(rect).setFill(Element.prototype._shapeFill(fill, rect)); |
|
} |
|
if(offsets.b){ // bottom |
|
rect = { |
|
x: 0, |
|
y: dim.height - offsets.b, |
|
width: dim.width + 1, |
|
height: offsets.b + 2 |
|
}; |
|
this.surface.createRect(rect).setFill(Element.prototype._shapeFill(fill, rect)); |
|
} |
|
} |
|
} |
|
if(stroke){ |
|
this.surface.createRect({ |
|
width: dim.width - 1, |
|
height: dim.height - 1 |
|
}).setStroke(stroke); |
|
} |
|
}, |
|
_renderPlotBackground: function(dim, offsets, w, h){ |
|
var t = this.theme; |
|
|
|
// draw a plot background |
|
var fill = t.plotarea && t.plotarea.fill; |
|
var stroke = t.plotarea && t.plotarea.stroke; |
|
// size might be neg if offsets are bigger that chart size this happens quite often at |
|
// initialization time if the chart widget is used in a BorderContainer |
|
// this will fail on IE/VML |
|
var rect = { |
|
x: offsets.l - 1, y: offsets.t - 1, |
|
width: w + 2, |
|
height: h + 2 |
|
}; |
|
if(fill){ |
|
fill = Element.prototype._shapeFill(Element.prototype._plotFill(fill, dim, offsets), rect); |
|
this.surface.createRect(rect).setFill(fill); |
|
} |
|
if(stroke){ |
|
this.surface.createRect({ |
|
x: offsets.l, y: offsets.t, |
|
width: w + 1, |
|
height: h + 1 |
|
}).setStroke(stroke); |
|
} |
|
}, |
|
delayedRender: function(){ |
|
// summary: |
|
// Delayed render, which is used to collect multiple updates |
|
// within a delayInMs time window. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
|
|
if(!this._delayedRenderHandle){ |
|
this._delayedRenderHandle = setTimeout( |
|
lang.hitch(this, function(){ |
|
this.render(); |
|
}), |
|
this.delayInMs |
|
); |
|
} |
|
|
|
return this; // dojox/charting/Chart |
|
}, |
|
connectToPlot: function(name, object, method){ |
|
// summary: |
|
// A convenience method to connect a function to a plot. |
|
// name: String |
|
// The name of the plot as defined by addPlot. |
|
// object: Object |
|
// The object to be connected. |
|
// method: Function |
|
// The function to be executed. |
|
// returns: Array |
|
// A handle to the connection, as defined by dojo.connect (see dojo.connect). |
|
return name in this.plots ? this.stack[this.plots[name]].connect(object, method) : null; // Array |
|
}, |
|
fireEvent: function(seriesName, eventName, index){ |
|
// summary: |
|
// Fires a synthetic event for a series item. |
|
// seriesName: String |
|
// Series name. |
|
// eventName: String |
|
// Event name to simulate: onmouseover, onmouseout, onclick. |
|
// index: Number |
|
// Valid data value index for the event. |
|
// returns: dojox/charting/Chart |
|
// A reference to the current chart for functional chaining. |
|
if(seriesName in this.runs){ |
|
var plotName = this.series[this.runs[seriesName]].plot; |
|
if(plotName in this.plots){ |
|
var plot = this.stack[this.plots[plotName]]; |
|
if(plot){ |
|
plot.fireEvent(seriesName, eventName, index); |
|
} |
|
} |
|
} |
|
return this; // dojox/charting/Chart |
|
}, |
|
_makeClean: function(){ |
|
// reset dirty flags |
|
arr.forEach(this.axes, makeClean); |
|
arr.forEach(this.stack, makeClean); |
|
arr.forEach(this.series, makeClean); |
|
this.dirty = false; |
|
}, |
|
_makeDirty: function(){ |
|
// reset dirty flags |
|
arr.forEach(this.axes, makeDirty); |
|
arr.forEach(this.stack, makeDirty); |
|
arr.forEach(this.series, makeDirty); |
|
this.dirty = true; |
|
}, |
|
_invalidateDependentPlots: function(plotName, /* Boolean */ verticalAxis){ |
|
if(plotName in this.plots){ |
|
var plot = this.stack[this.plots[plotName]], axis, |
|
axisName = verticalAxis ? "vAxis" : "hAxis"; |
|
if(plot[axisName]){ |
|
axis = this.axes[plot[axisName]]; |
|
if(axis && axis.dependOnData()){ |
|
axis.dirty = true; |
|
// find all plots and mark them dirty |
|
arr.forEach(this.stack, function(p){ |
|
if(p[axisName] && p[axisName] == plot[axisName]){ |
|
p.dirty = true; |
|
} |
|
}); |
|
} |
|
}else{ |
|
plot.dirty = true; |
|
} |
|
} |
|
}, |
|
setDir : function(dir){ |
|
return this; |
|
}, |
|
_resetLeftBottom: function(axis){ |
|
}, |
|
formatTruncatedLabel: function(element, label, labelType){ |
|
} |
|
}); |
|
|
|
function hSection(stats){ |
|
return {min: stats.hmin, max: stats.hmax}; |
|
} |
|
|
|
function vSection(stats){ |
|
return {min: stats.vmin, max: stats.vmax}; |
|
} |
|
|
|
function hReplace(stats, h){ |
|
stats.hmin = h.min; |
|
stats.hmax = h.max; |
|
} |
|
|
|
function vReplace(stats, v){ |
|
stats.vmin = v.min; |
|
stats.vmax = v.max; |
|
} |
|
|
|
function combineStats(target, source){ |
|
if(target && source){ |
|
target.min = Math.min(target.min, source.min); |
|
target.max = Math.max(target.max, source.max); |
|
} |
|
return target || source; |
|
} |
|
|
|
function calculateAxes(stack, plotArea){ |
|
var plots = {}, axes = {}; |
|
arr.forEach(stack, function(plot){ |
|
var stats = plots[plot.name] = plot.getSeriesStats(); |
|
if(plot.hAxis){ |
|
axes[plot.hAxis] = combineStats(axes[plot.hAxis], hSection(stats)); |
|
} |
|
if(plot.vAxis){ |
|
axes[plot.vAxis] = combineStats(axes[plot.vAxis], vSection(stats)); |
|
} |
|
}); |
|
arr.forEach(stack, function(plot){ |
|
var stats = plots[plot.name]; |
|
if(plot.hAxis){ |
|
hReplace(stats, axes[plot.hAxis]); |
|
} |
|
if(plot.vAxis){ |
|
vReplace(stats, axes[plot.vAxis]); |
|
} |
|
plot.initializeScalers(plotArea, stats); |
|
}); |
|
} |
|
|
|
return has("dojo-bidi")? declare("dojox.charting.Chart", [Chart, BidiChart]) : Chart; |
|
});
|
|
|