/*
Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
Available via Academic Free License >= 2.1 OR the modified BSD license.
see: http://dojotoolkit.org/license for details
*/
if(!dojo._hasResource["dojox.widget.DataPresentation"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.widget.DataPresentation"] = true;
dojo.provide("dojox.widget.DataPresentation");
dojo.experimental("dojox.widget.DataPresentation");
dojo.require("dojox.grid.DataGrid");
dojo.require("dojox.charting.Chart2D");
dojo.require("dojox.charting.widget.Legend");
dojo.require("dojox.charting.action2d.Tooltip");
dojo.require("dojox.charting.action2d.Highlight");
dojo.require("dojo.colors");
dojo.require("dojo.data.ItemFileWriteStore");
(function(){
// sort out the labels for the independent axis of the chart
var getLabels = function(range, labelMod, charttype, domNode){
// prepare labels for the independent axis
var labels = [];
// add empty label, hack
labels[0] = {value: 0, text: ''};
var nlabels = range.length;
// auto-set labelMod for horizontal charts if the labels will otherwise collide
if((charttype !== "ClusteredBars") && (charttype !== "StackedBars")){
var cwid = domNode.offsetWidth;
var tmp = ("" + range[0]).length * range.length * 7; // *assume* 7 pixels width per character ( was 9 )
if(labelMod == 1){
for(var z = 1; z < 500; ++z){
if((tmp / z) < cwid){
break;
}
++labelMod;
}
}
}
// now set the labels
for(var i = 0; i < nlabels; i++){
//sparse labels
labels.push({
value: i + 1,
text: (!labelMod || i % labelMod) ? "" : range[i]
});
}
// add empty label again, hack
labels.push({value: nlabels + 1, text:''});
return labels;
};
// get the configuration of an independent axis for the chart
var getIndependentAxisArgs = function(charttype, labels){
var args = { vertical: false, labels: labels, min: 0, max: labels.length-1, majorTickStep: 1, minorTickStep: 1 };
// clustered or stacked bars have a vertical independent axis
if((charttype === "ClusteredBars") || (charttype === "StackedBars")){
args.vertical = true;
}
// lines, areas and stacked areas don't need the extra slots at each end
if((charttype === "Lines") || (charttype === "Areas") || (charttype === "StackedAreas")){
args.min++;
args.max--;
}
return args;
};
// get the configuration of a dependent axis for the chart
var getDependentAxisArgs = function(charttype, axistype, minval, maxval){
var args = { vertical: true, fixLower: "major", fixUpper: "major", natural: true };
// secondary dependent axis is not left-bottom
if(axistype === "secondary"){
args.leftBottom = false;
}
// clustered or stacked bars have horizontal dependent axes
if((charttype === "ClusteredBars") || (charttype === "StackedBars")){
args.vertical = false;
}
// ensure axis does not "collapse" for flat series
if(minval == maxval){
args.min = minval - 1;
args.max = maxval + 1;
}
return args;
};
// get the configuration of a plot for the chart
var getPlotArgs = function(charttype, axistype, animate){
var args = { type: charttype, hAxis: "independent", vAxis: "dependent-" + axistype, gap: 4, lines: false, areas: false, markers: false };
// clustered or stacked bars have horizontal dependent axes
if((charttype === "ClusteredBars") || (charttype === "StackedBars")){
args.hAxis = args.vAxis;
args.vAxis = "independent";
}
// turn on lines for Lines, Areas and StackedAreas
if((charttype === "Lines") || (charttype === "Hybrid-Lines") || (charttype === "Areas") || (charttype === "StackedAreas")){
args.lines = true;
}
// turn on areas for Areas and StackedAreas
if((charttype === "Areas") || (charttype === "StackedAreas")){
args.areas = true;
}
// turn on markers and shadow for Lines
if(charttype === "Lines"){
args.markers = true;
}
// turn on shadow for Hybrid-Lines
// also, Hybrid-Lines is not a true chart type: use Lines for the actual plot
if(charttype === "Hybrid-Lines"){
args.shadows = {dx: 2, dy: 2, dw: 2};
args.type = "Lines";
}
// also, Hybrid-ClusteredColumns is not a true chart type: use ClusteredColumns for the actual plot
if(charttype === "Hybrid-ClusteredColumns"){
args.type = "ClusteredColumns";
}
// enable animation on the plot if animation is requested
if(animate){
args.animate = animate;
}
return args;
};
// set up a chart presentation
var setupChart = function(/*DomNode*/domNode, /*Object?*/chart, /*String*/type, /*Boolean*/reverse, /*Object*/animate, /*Integer*/labelMod, /*String*/theme, /*String*/tooltip, /*Object?*/store, /*String?*/query, /*String?*/queryOptions){
var _chart = chart;
if(!_chart){
domNode.innerHTML = ""; // any other content in the node disrupts the chart rendering
_chart = new dojox.charting.Chart2D(domNode);
}
// set the theme
if(theme){
// workaround for a theme bug: its _clone method
// does not transfer the markers, so we repair
// that omission here
// FIXME this should be removed once the theme bug is fixed
theme._clone = function(){
var result = new dojox.charting.Theme({
chart: this.chart,
plotarea: this.plotarea,
axis: this.axis,
series: this.series,
marker: this.marker,
antiAlias: this.antiAlias,
assignColors: this.assignColors,
assignMarkers: this.assigneMarkers,
colors: dojo.delegate(this.colors)
});
result.markers = this.markers;
result._buildMarkerArray();
return result;
};
_chart.setTheme(theme);
}
var range = store.series_data[0].slice(0);
// reverse the labels if requested
if(reverse){
range.reverse();
}
var labels = getLabels(range, labelMod, type, domNode);
// collect details of whether primary and/or secondary axes are required
// and what plots we have instantiated using each type of axis
var plots = {};
// collect maximum and minimum data values
var maxval = null;
var minval = null;
var seriestoremove = {};
for(var sname in _chart.runs){
seriestoremove[sname] = true;
}
// set x values & max data value
var nseries = store.series_name.length;
for(var i = 0; i < nseries; i++){
// only include series with chart=true and with some data values in
if(store.series_chart[i] && (store.series_data[i].length > 0)){
var charttype = type;
var axistype = store.series_axis[i];
if(charttype == "Hybrid"){
if(store.series_charttype[i] == 'line'){
charttype = "Hybrid-Lines";
}else{
charttype = "Hybrid-ClusteredColumns";
}
}
// ensure we have recorded that we are using this axis type
if(!plots[axistype]){
plots[axistype] = {};
}
// ensure we have the correct type of plot for this series
if(!plots[axistype][charttype]){
var axisname = axistype + "-" + charttype;
// create the plot and enable tooltips
_chart.addPlot(axisname, getPlotArgs(charttype, axistype, animate));
var tooltipArgs = {};
if(typeof tooltip == 'string'){
tooltipArgs.text = function(o){
var substitutions = [o.element, o.run.name, range[o.index], ((charttype === "ClusteredBars") || (charttype === "StackedBars")) ? o.x : o.y];
return dojo.replace(tooltip, substitutions); // from Dojo 1.4 onward
//return tooltip.replace(/\{([^\}]+)\}/g, function(_, token){ return dojo.getObject(token, false, substitutions); }); // prior to Dojo 1.4
}
}else if(typeof tooltip == 'function'){
tooltipArgs.text = tooltip;
}
new dojox.charting.action2d.Tooltip(_chart, axisname, tooltipArgs);
// add highlighting, except for lines
if(charttype !== "Lines" && charttype !== "Hybrid-Lines"){
new dojox.charting.action2d.Highlight(_chart, axisname);
}
// record that this plot type is now created
plots[axistype][charttype] = true;
}
// extract the series values
var xvals = [];
var valen = store.series_data[i].length;
for(var j = 0; j < valen; j++){
var val = store.series_data[i][j];
xvals.push(val);
if(maxval === null || val > maxval){
maxval = val;
}
if(minval === null || val < minval){
minval = val;
}
}
// reverse the values if requested
if(reverse){
xvals.reverse();
}
var seriesargs = { plot: axistype + "-" + charttype };
if(store.series_linestyle[i]){
seriesargs.stroke = { style: store.series_linestyle[i] };
}
_chart.addSeries(store.series_name[i], xvals, seriesargs);
delete seriestoremove[store.series_name[i]];
}
}
// remove any series that are no longer needed
for(sname in seriestoremove){
_chart.removeSeries(sname);
}
// create axes
_chart.addAxis("independent", getIndependentAxisArgs(type, labels));
_chart.addAxis("dependent-primary", getDependentAxisArgs(type, "primary", minval, maxval));
_chart.addAxis("dependent-secondary", getDependentAxisArgs(type, "secondary", minval, maxval));
return _chart;
};
// set up a legend presentation
var setupLegend = function(/*DomNode*/domNode, /*Legend*/legend, /*Chart2D*/chart, /*Boolean*/horizontal){
// destroy any existing legend and recreate
var _legend = legend;
if(!_legend){
_legend = new dojox.charting.widget.Legend({ chart: chart, horizontal: horizontal }, domNode);
}else{
_legend.refresh();
}
return _legend;
};
// set up a grid presentation
var setupGrid = function(/*DomNode*/domNode, /*Object?*/grid, /*Object?*/store, /*String?*/query, /*String?*/queryOptions){
var _grid = grid || new dojox.grid.DataGrid({}, domNode);
_grid.startup();
_grid.setStore(store, query, queryOptions);
var structure = [];
for(var ser = 0; ser < store.series_name.length; ser++){
// only include series with grid=true and with some data values in
if(store.series_grid[ser] && (store.series_data[ser].length > 0)){
structure.push({ field: "data." + ser, name: store.series_name[ser], width: "auto", formatter: store.series_gridformatter[ser] });
}
}
_grid.setStructure(structure);
return _grid;
};
// set up a title presentation
var setupTitle = function(/*DomNode*/domNode, /*object*/store){
if(store.title){
domNode.innerHTML = store.title;
}
};
// set up a footer presentation
var setupFooter = function(/*DomNode*/domNode, /*object*/store){
if(store.footer){
domNode.innerHTML = store.footer;
}
};
// obtain a subfield from a field specifier which may contain
// multiple levels (eg, "child.foo[36].manacle")
var getSubfield = function(/*Object*/object, /*String*/field){
var result = object;
if(field){
var fragments = field.split(/[.\[\]]+/);
for(var frag = 0, l = fragments.length; frag < l; frag++){
if(result){
result = result[fragments[frag]];
}
}
}
return result;
};
dojo.declare("dojox.widget.DataPresentation", null, {
// summary:
//
// DataPresentation
//
// A widget that connects to a data store in a simple manner,
// and also provides some additional convenience mechanisms
// for connecting to common data sources without needing to
// explicitly construct a Dojo data store. The widget can then
// present the data in several forms: as a graphical chart,
// as a tabular grid, or as display panels presenting meta-data
// (title, creation information, etc) from the data. The
// widget can also create and manage several of these forms
// in one simple construction.
//
// Note: this is a first experimental draft and any/all details
// are subject to substantial change in later drafts.
//
// example:
//
// var pres = new dojox.data.DataPresentation("myChartNode", {
// type: "chart",
// url: "/data/mydata",
// gridNode: "myGridNode"
// });
//
// properties:
//
// store: Object
// Dojo data store used to supply data to be presented. This may
// be supplied on construction or created implicitly based on
// other construction parameters ('data', 'url').
//
// query: String
// Query to apply to the Dojo data store used to supply data to
// be presented.
//
// queryOptions: String
// Query options to apply to the Dojo data store used to supply
// data to be presented.
//
// data: Object
// Data to be presented. If supplied on construction this property
// will override any value supplied for the 'store' property.
//
// url: String
// URL to fetch data from in JSON format. If supplied on
// construction this property will override any values supplied
// for the 'store' and/or 'data' properties. Note that the data
// can also be comment-filtered JSON, although this will trigger
// a warning message in the console unless djConfig.useCommentedJson
// has been set to true.
//
// urlContent: Object
// Content to be passed to the URL when fetching data. If a URL has
// not been supplied, this value is ignored.
//
// urlError: function
// A function to be called if an error is encountered when fetching
// data from the supplied URL. This function will be supplied with
// two parameters exactly as the error function supplied to the
// dojo.xhrGet function. This function may be called multiple times
// if a refresh interval has been supplied.
//
// refreshInterval: Number
// the time interval in milliseconds after which the data supplied
// via the 'data' property or fetched from a URL via the 'url'
// property should be regularly refreshed. This property is
// ignored if neither the 'data' nor 'url' property has been
// supplied. If the refresh interval is zero, no regular refresh is done.
//
// refreshIntervalPending:
// the JavaScript set interval currently in progress, if any
//
// series: Array
// an array of objects describing the data series to be included
// in the data presentation. Each object may contain the
// following fields:
// datapoints: the name of the field from the source data which
// contains an array of the data points for this data series.
// If not supplied, the source data is assumed to be an array
// of data points to be used.
// field: the name of the field within each data point which
// contains the data for this data series. If not supplied,
// each data point is assumed to be the value for the series.
// name: a name for the series, used in the legend and grid headings
// namefield: the name of the field from the source data which
// contains the name the series, used in the legend and grid
// headings. If both name and namefield are supplied, name takes
// precedence. If neither are supplied, a default name is used.
// chart: true if the series should be included in a chart presentation (default: true)
// charttype: the type of presentation of the series in the chart, which can be
// "range", "line", "bar" (default: "bar")
// linestyle: the stroke style for lines (if applicable) (default: "Solid")
// axis: the dependant axis to which the series will be attached in the chart,
// which can be "primary" or "secondary"
// grid: true if the series should be included in a data grid presentation (default: true)
// gridformatter: an optional formatter to use for this series in the data grid
//
// a call-back function may alternatively be supplied. The function takes
// a single parameter, which will be the data (from the 'data' field or
// loaded from the value in the 'url' field), and should return the array
// of objects describing the data series to be included in the data
// presentation. This enables the series structures to be built dynamically
// after data load, and rebuilt if necessary on data refresh. The call-back
// function will be called each time new data is set, loaded or refreshed.
// A call-back function cannot be used if the data is supplied directly
// from a Dojo data store.
//
// type: String
// the type of presentation to be applied at the DOM attach point.
// This can be 'chart', 'legend', 'grid', 'title', 'footer'. The
// default type is 'chart'.
type: "chart",
//
// chartType: String
// the type of chart to display. This can be 'clusteredbars',
// 'areas', 'stackedcolumns', 'stackedbars', 'stackedareas',
// 'lines', 'hybrid'. The default type is 'bar'.
chartType: "clusteredBars",
//
// reverse: Boolean
// true if the chart independant axis should be reversed.
reverse: false,
//
// animate: Object
// if an object is supplied, then the chart bars or columns will animate
// into place. If the object contains a field 'duration' then the value
// supplied is the duration of the animation in milliseconds, otherwise
// a default duration is used. A boolean value true can alternatively be
// supplied to enable animation with the default duration.
// The default is null (no animation).
animate: null,
//
// labelMod: Integer
// the frequency of label annotations to be included on the
// independent axis. 1=every label. 0=no labels. The default is 1.
labelMod: 1,
//
// tooltip: String | Function
// a string pattern defining the tooltip text to be applied to chart
// data points, or a function which takes a single parameter and returns
// the tooltip text to be applied to chart data points. The string pattern
// will have the following substitutions applied:
// {0} - the type of chart element ('bar', 'surface', etc)
// {1} - the name of the data series
// {2} - the independent axis value at the tooltip data point
// {3} - the series value at the tooltip data point point
// The function, if supplied, will receive a single parameter exactly
// as per the dojox.charting.action2D.Tooltip class. The default value
// is to apply the default tooltip as defined by the
// dojox.charting.action2D.Tooltip class.
//
// legendHorizontal: Boolean | Number
// true if the legend should be rendered horizontally, or a number if
// the legend should be rendered as horizontal rows with that number of
// items in each row, or false if the legend should be rendered
// vertically (same as specifying 1). The default is true (legend
// rendered horizontally).
legendHorizontal: true,
//
// theme: String|Theme
// a theme to use for the chart, or the name of a theme.
//
// chartNode: String|DomNode
// an optional DOM node or the id of a DOM node to receive a
// chart presentation of the data. Supply only when a chart is
// required and the type is not 'chart'; when the type is
// 'chart' this property will be set to the widget attach point.
//
// legendNode: String|DomNode
// an optional DOM node or the id of a DOM node to receive a
// chart legend for the data. Supply only when a legend is
// required and the type is not 'legend'; when the type is
// 'legend' this property will be set to the widget attach point.
//
// gridNode: String|DomNode
// an optional DOM node or the id of a DOM node to receive a
// grid presentation of the data. Supply only when a grid is
// required and the type is not 'grid'; when the type is
// 'grid' this property will be set to the widget attach point.
//
// titleNode: String|DomNode
// an optional DOM node or the id of a DOM node to receive a
// title for the data. Supply only when a title is
// required and the type is not 'title'; when the type is
// 'title' this property will be set to the widget attach point.
//
// footerNode: String|DomNode
// an optional DOM node or the id of a DOM node to receive a
// footer presentation of the data. Supply only when a footer is
// required and the type is not 'footer'; when the type is
// 'footer' this property will be set to the widget attach point.
//
// chartWidget: Object
// the chart widget, if any
//
// legendWidget: Object
// the legend widget, if any
//
// gridWidget: Object
// the grid widget, if any
constructor: function(node, args){
// summary:
// Set up properties and initialize.
//
// arguments:
// node: DomNode
// The node to attach the data presentation to.
// kwArgs: Object (see above)
// apply arguments directly
dojo.mixin(this, args);
// store our DOM attach point
this.domNode = dojo.byId(node);
// also apply the DOM attach point as the node for the presentation type
this[this.type + "Node"] = this.domNode;
// load the theme if provided by name
if(typeof this.theme == 'string'){
this.theme = dojo.getObject(this.theme);
}
// resolve any the nodes that were supplied as ids
this.chartNode = dojo.byId(this.chartNode);
this.legendNode = dojo.byId(this.legendNode);
this.gridNode = dojo.byId(this.gridNode);
this.titleNode = dojo.byId(this.titleNode);
this.footerNode = dojo.byId(this.footerNode);
// we used to support a 'legendVertical' so for now
// at least maintain backward compatibility
if(this.legendVertical){
this.legendHorizontal = !this.legendVertical;
}
if(this.url){
this.setURL(null, null, this.refreshInterval);
}
else{
if(this.data){
this.setData(null, this.refreshInterval);
}
else{
this.setStore();
}
}
},
setURL: function(/*String?*/url, /*Object?*/ urlContent, /*Number?*/refreshInterval){
// summary:
// Sets the URL to fetch data from, with optional content
// supplied with the request, and an optional
// refresh interval in milliseconds (0=no refresh)
// if a refresh interval is supplied we will start a fresh
// refresh after storing the supplied url
if(refreshInterval){
this.cancelRefresh();
}
this.url = url || this.url;
this.urlContent = urlContent || this.urlContent;
this.refreshInterval = refreshInterval || this.refreshInterval;
var me = this;
dojo.xhrGet({
url: this.url,
content: this.urlContent,
handleAs: 'json-comment-optional',
load: function(response, ioArgs){
me.setData(response);
},
error: function(xhr, ioArgs){
if(me.urlError && (typeof me.urlError == "function")){
me.urlError(xhr, ioArgs);
}
}
});
if(refreshInterval && (this.refreshInterval > 0)){
this.refreshIntervalPending = setInterval(function(){
me.setURL();
}, this.refreshInterval);
}
},
setData: function(/*Object?*/data, /*Number?*/refreshInterval){
// summary:
// Sets the data to be presented, and an optional
// refresh interval in milliseconds (0=no refresh)
// if a refresh interval is supplied we will start a fresh
// refresh after storing the supplied data reference
if(refreshInterval){
this.cancelRefresh();
}
this.data = data || this.data;
this.refreshInterval = refreshInterval || this.refreshInterval;
// TODO if no 'series' property was provided, build one intelligently here
// (until that is done, a 'series' property must be supplied)
var _series = (typeof this.series == 'function') ? this.series(this.data) : this.series;
var datasets = [],
series_data = [],
series_name = [],
series_chart = [],
series_charttype = [],
series_linestyle = [],
series_axis = [],
series_grid = [],
series_gridformatter = [],
maxlen = 0;
// identify the dataset arrays in which series values can be found
for(var ser = 0; ser < _series.length; ser++){
datasets[ser] = getSubfield(this.data, _series[ser].datapoints);
if(datasets[ser] && (datasets[ser].length > maxlen)){
maxlen = datasets[ser].length;
}
series_data[ser] = [];
// name can be specified in series structure, or by field in series structure, otherwise use a default
series_name[ser] = _series[ser].name || (_series[ser].namefield ? getSubfield(this.data, _series[ser].namefield) : null) || ("series " + ser);
series_chart[ser] = (_series[ser].chart !== false);
series_charttype[ser] = _series[ser].charttype || "bar";
series_linestyle[ser] = _series[ser].linestyle;
series_axis[ser] = _series[ser].axis || "primary";
series_grid[ser] = (_series[ser].grid !== false);
series_gridformatter[ser] = _series[ser].gridformatter;
}
// create an array of data points by sampling the series
// and an array of series arrays by collecting the series
// each data point has an 'index' item containing a sequence number
// and items named "data.0", "data.1", ... containing the series samples
// and the first data point also has items named "name.0", "name.1", ... containing the series names
// and items named "series.0", "series.1", ... containing arrays with the complete series in
var point, datapoint, datavalue, fdatavalue;
var datapoints = [];
for(point = 0; point < maxlen; point++){
datapoint = { index: point };
for(ser = 0; ser < _series.length; ser++){
if(datasets[ser] && (datasets[ser].length > point)){
datavalue = getSubfield(datasets[ser][point], _series[ser].field);
if(series_chart[ser]){
// convert the data value to a float if possible
fdatavalue = parseFloat(datavalue);
if(!isNaN(fdatavalue)){
datavalue = fdatavalue;
}
}
datapoint["data." + ser] = datavalue;
series_data[ser].push(datavalue);
}
}
datapoints.push(datapoint);
}
if(maxlen <= 0){
datapoints.push({index: 0});
}
// now build a prepared store from the data points we've constructed
var store = new dojo.data.ItemFileWriteStore({ data: { identifier: 'index', items: datapoints }});
if(this.data.title){
store.title = this.data.title;
}
if(this.data.footer){
store.footer = this.data.footer;
}
store.series_data = series_data;
store.series_name = series_name;
store.series_chart = series_chart;
store.series_charttype = series_charttype;
store.series_linestyle = series_linestyle;
store.series_axis = series_axis;
store.series_grid = series_grid;
store.series_gridformatter = series_gridformatter;
this.setPreparedStore(store);
if(refreshInterval && (this.refreshInterval > 0)){
var me = this;
this.refreshIntervalPending = setInterval(function(){
me.setData();
}, this.refreshInterval);
}
},
refresh: function(){
// summary:
// If a URL or data has been supplied, refreshes the
// presented data from the URL or data. If a refresh
// interval is also set, the periodic refresh is
// restarted. If a URL or data was not supplied, this
// method has no effect.
if(this.url){
this.setURL(this.url, this.urlContent, this.refreshInterval);
}else if(this.data){
this.setData(this.data, this.refreshInterval);
}
},
cancelRefresh: function(){
// summary:
// Cancels any and all outstanding data refreshes
if(this.refreshIntervalPending){
// cancel existing refresh
clearInterval(this.refreshIntervalPending);
this.refreshIntervalPending = undefined;
}
},
setStore: function(/*Object?*/store, /*String?*/query, /*Object?*/queryOptions){
// FIXME build a prepared store properly -- this requires too tight a convention to be followed to be useful
this.setPreparedStore(store, query, queryOptions);
},
setPreparedStore: function(/*Object?*/store, /*String?*/query, /*Object?*/queryOptions){
// summary:
// Sets the store and query.
//
this.preparedstore = store || this.store;
this.query = query || this.query;
this.queryOptions = queryOptions || this.queryOptions;
if(this.preparedstore){
if(this.chartNode){
this.chartWidget = setupChart(this.chartNode, this.chartWidget, this.chartType, this.reverse, this.animate, this.labelMod, this.theme, this.tooltip, this.preparedstore, this.query, this,queryOptions);
this.renderChartWidget();
}
if(this.legendNode){
this.legendWidget = setupLegend(this.legendNode, this.legendWidget, this.chartWidget, this.legendHorizontal);
}
if(this.gridNode){
this.gridWidget = setupGrid(this.gridNode, this.gridWidget, this.preparedstore, this.query, this.queryOptions);
this.renderGridWidget();
}
if(this.titleNode){
setupTitle(this.titleNode, this.preparedstore);
}
if(this.footerNode){
setupFooter(this.footerNode, this.preparedstore);
}
}
},
renderChartWidget: function(){
// summary:
// Renders the chart widget (if any). This method is
// called whenever a chart widget is created or
// configured, and may be connected to.
if(this.chartWidget){
this.chartWidget.render();
}
},
renderGridWidget: function(){
// summary:
// Renders the grid widget (if any). This method is
// called whenever a grid widget is created or
// configured, and may be connected to.
if(this.gridWidget){
this.gridWidget.render();
}
},
getChartWidget: function(){
// summary:
// Returns the chart widget (if any) created if the type
// is "chart" or the "chartNode" property was supplied.
return this.chartWidget;
},
getGridWidget: function(){
// summary:
// Returns the grid widget (if any) created if the type
// is "grid" or the "gridNode" property was supplied.
return this.gridWidget;
},
destroy: function(){
// summary:
// Destroys the widget and all components and resources.
// cancel any outstanding refresh requests
this.cancelRefresh();
if(this.chartWidget){
this.chartWidget.destroy();
delete this.chartWidget;
}
if(this.legendWidget){
// no legend.destroy()
delete this.legendWidget;
}
if(this.gridWidget){
// no grid.destroy()
delete this.gridWidget;
}
if(this.chartNode){
this.chartNode.innerHTML = "";
}
if(this.legendNode){
this.legendNode.innerHTML = "";
}
if(this.gridNode){
this.gridNode.innerHTML = "";
}
if(this.titleNode){
this.titleNode.innerHTML = "";
}
if(this.footerNode){
this.footerNode.innerHTML = "";
}
}
});
})();
}