source: [view]
dojo.provide("dojox.charting.DataChart");
dojo.require("dojox.charting.Chart2D");
dojo.require("dojox.charting.themes.PlotKit.blue");
dojo.experimental("dojox.charting.DataChart");
(function(){
// Defaults for axes
// to be mixed in with xaxis/yaxis custom properties
// see dojox.charting.axis2d.Default for details.
var _yaxis = {
vertical: true,
min: 0,
max: 10,
majorTickStep: 5,
minorTickStep: 1,
natural:false,
stroke: "black",
majorTick: {stroke: "black", length: 8},
minorTick: {stroke: "gray", length: 2},
majorLabels:true
};
var _xaxis = {
natural: true, // true - no fractions
majorLabels: true, //show labels on major ticks
includeZero: false, // do not change on upating chart
majorTickStep: 1,
majorTick: {stroke: "black", length: 8},
fixUpper:"major",
stroke: "black",
htmlLabels: true,
from:1
};
// default for chart elements
var chartPlot = {
markers: true,
tension:2,
gap:2
};
dojo.declare("dojox.charting.DataChart", [dojox.charting.Chart2D], {
// summary:
// DataChart
// Extension to the 2D chart that connects to a data store in
// a simple manner. Convenience methods have been added for
// connecting store item labels to the chart labels.
//
// description:
// This code should be considered very experimental and the APIs subject
// to change. This is currently an alpha version and will need some testing
// and review.
//
// The main reason for this extension is to create animated charts, generally
// available with scroll=true, and a property field that gets continually updated.
// The previous property settings are kept in memory and displayed until scrolled
// off the chart.
//
// Although great effort was made to maintain the integrity of the current
// charting APIs, some things have been added or modified in order to get
// the store to connect and also to get the data to scroll/animate.
// "displayRange" in particular is used to force the xaxis to a specific
// size and keep the chart from stretching or squashing to fit the data.
//
// Currently, plot lines can only be set at initialization. Setting
// a new store query will have no effect (although using setStore
// may work but its untested).
//
// example:
//
// | var chart = new dojox.charting.DataChart("myNode", {
// | displayRange:8,
// | store:dataStore,
// | query:{symbol:"*"},
// | fieldName:"price"
// | type: dojox.charting.plot2d.Columns
// | });
//
// properties:
//
// scroll: Boolean
// Whether live data updates and changes display, like columns moving
// up and down, or whether it scrolls to the left as data is added
scroll:true,
//
// comparative: Boolean
// If false, all items are each their own series.
// If true, the items are combined into one series
// so that their charted properties can be compared.
comparative:false,
//
// query: String
// Used for fetching items. Will vary depending upon store.
query: "*",
//
// queryOptions: String
// Option used for fetching items
queryOptions: "",
//
// start:Number
// first item to fetch from store
// count:Number
// Total amount of items to fetch from store
// sort:Object
// Paramaters to sort the fetched items from store
//
// fieldName: String
// The field in the store item that is getting charted
fieldName: "value",
//
// chartTheme: dojox.charting.themes.*
// The theme to style the chart. Defaults to PlotKit.blue.
chartTheme: dojox.charting.themes.PlotKit.blue,
//
// displayRange: Number
// The number of major ticks to show on the xaxis
displayRange:0,
//
// stretchToFit: Boolean
// If true, chart is sized to data. If false, chart is a
// fixed size. Note, is overridden by displayRange.
// TODO: Stretch for the y-axis?
stretchToFit:true,
//
// minWidth: Number
// The the smallest the chart width can be
minWidth:200,
//
// minHeight: Number
// The the smallest the chart height can be
minHeight:100,
//
// showing: Boolean
// Whether the chart is showing (default) on
// initialization or hidden.
showing: true,
//
// label: String
// The name field of the store item
// DO NOT SET: Set from store.labelAttribute
label: "name",
constructor: function(node, kwArgs){
// summary:
// Set up properties and initialize chart build.
//
// arguments:
// node: DomNode
// The node to attach the chart to.
// kwArgs: Object
// xaxis: Object
// optional parameters for xaxis (see above)
// yaxis: Object
// optional parameters for yaxis (see above)
// store: Object
// dojo.data store (currently nly supports Persevere)
// xaxis: Object
// First query for store
// grid: Object
// Options for the grid plot
// chartPlot: Object
// Options for chart elements (lines, bars, etc)
this.domNode = dojo.byId(node);
dojo.mixin(this, kwArgs);
this.xaxis = dojo.mixin(dojo.mixin({}, _xaxis), kwArgs.xaxis);
if(this.xaxis.labelFunc == "seriesLabels"){
this.xaxis.labelFunc = dojo.hitch(this, "seriesLabels");
}
this.yaxis = dojo.mixin(dojo.mixin({}, _yaxis), kwArgs.yaxis);
if(this.yaxis.labelFunc == "seriesLabels"){
this.yaxis.labelFunc = dojo.hitch(this, "seriesLabels");
}
// potential event's collector
this._events = [];
this.convertLabels(this.yaxis);
this.convertLabels(this.xaxis);
this.onSetItems = {};
this.onSetInterval = 0;
this.dataLength = 0;
this.seriesData = {};
this.seriesDataBk = {};
this.firstRun = true;
this.dataOffset = 0;
// FIXME: looks better with this, but it's custom
this.chartTheme.plotarea.stroke = {color: "gray", width: 3};
this.setTheme(this.chartTheme);
// displayRange overrides stretchToFit
if(this.displayRange){
this.stretchToFit = false;
}
if(!this.stretchToFit){
this.xaxis.to = this.displayRange;
}
this.addAxis("x", this.xaxis);
this.addAxis("y", this.yaxis);
chartPlot.type = kwArgs.type || "Markers"
this.addPlot("default", dojo.mixin(chartPlot, kwArgs.chartPlot));
this.addPlot("grid", dojo.mixin(kwArgs.grid || {}, {type: "Grid", hMinorLines: true}));
if(this.showing){
this.render();
}
if(kwArgs.store){
this.setStore(kwArgs.store, kwArgs.query, kwArgs.fieldName, kwArgs.queryOptions);
}
},
destroy: function(){
dojo.forEach(this._events, dojo.disconnect);
this.inherited(arguments);
},
setStore: function(/*Object*/store, /* ? String*/query, /* ? String*/fieldName, /* ? Object */queryOptions){
// summary:
// Sets the chart store and query
// then does the first fetch and
// connects to subsequent changes.
//
// TODO: Not handling resetting store
//
this.firstRun = true;
this.store = store || this.store;
this.query = query || this.query;
this.fieldName = fieldName || this.fieldName;
this.label = this.store.getLabelAttributes();
this.queryOptions = queryOptions || queryOptions;
dojo.forEach(this._events, dojo.disconnect);
this._events = [
dojo.connect(this.store, "onSet", this, "onSet"),
dojo.connect(this.store, "onError", this, "onError")
];
this.fetch();
},
show: function(){
// summary:
// If chart is hidden, show it
if(!this.showing){
dojo.style(this.domNode, "display", "");
this.showing = true;
this.render();
}
},
hide: function(){
// summary:
// If chart is showing, hide it
// Prevents rendering while hidden
if(this.showing){
dojo.style(this.domNode, "display", "none");
this.showing = false;
}
},
onSet: function(/*storeObject*/item){
// summary:
// Fired when a store item changes.
// Collects the item calls and when
// done (after 200ms), sends item
// array to onData().
//
// FIXME: Using labels instead of IDs for item
// identifiers here and in the chart series. This
// is obviously short sighted, but currently used
// for seriesLabels. Workaround for potential bugs
// is to assign a label for which all items are unique.
var nm = this.getProperty(item, this.label);
// FIXME: why the check for if-in-runs?
if(nm in this.runs || this.comparative){
clearTimeout(this.onSetInterval);
if(!this.onSetItems[nm]){
this.onSetItems[nm] = item;
}
this.onSetInterval = setTimeout(dojo.hitch(this, function(){
clearTimeout(this.onSetInterval);
var items = [];
for(var nm in this.onSetItems){
items.push(this.onSetItems[nm]);
}
this.onData(items);
this.onSetItems = {};
}),200);
}
},
onError: function(/*Error*/err){
// stub
// Fires on fetch error
console.error("DataChart Error:", err);