summaryrefslogtreecommitdiff
path: root/js/dojo-1.6/dojox/charting/DataChart.xd.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/dojo-1.6/dojox/charting/DataChart.xd.js534
1 files changed, 534 insertions, 0 deletions
diff --git a/js/dojo-1.6/dojox/charting/DataChart.xd.js b/js/dojo-1.6/dojox/charting/DataChart.xd.js
new file mode 100644
index 0000000..a78ee3f
--- /dev/null
+++ b/js/dojo-1.6/dojox/charting/DataChart.xd.js
@@ -0,0 +1,534 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.charting.DataChart"],
+["require", "dojox.charting.Chart2D"],
+["require", "dojox.charting.themes.PlotKit.blue"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.charting.DataChart"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.charting.DataChart"] = true;
+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);
+ },
+
+ onDataReceived: function(/*Array*/items){
+ // summary:
+ // stub. Fires after data is received but
+ // before data is parsed and rendered
+ },
+
+ getProperty: function(/*storeObject*/item, prop){
+ // summary:
+ // The main use of this function is to determine
+ // between a single value and an array of values.
+ // Other property types included for convenience.
+ //
+ if(prop==this.label){
+ return this.store.getLabel(item);
+ }
+ if(prop=="id"){
+ return this.store.getIdentity(item);
+ }
+ var value = this.store.getValues(item, prop);
+ if(value.length < 2){
+ value = this.store.getValue(item, prop);
+ }
+ return value;
+ },
+ onData: function(/*Array*/items){
+ // summary:
+ // Called after a completed fetch
+ // or when store items change.
+ // On first run, sets the chart data,
+ // then updates chart and legends.
+ //
+ //console.log("Store:", store);console.log("items: (", items.length+")", items);console.log("Chart:", this);
+ if(!items || !items.length){ return; }
+
+ if(this.items && this.items.length != items.length){
+ dojo.forEach(items, function(m){
+ var id = this.getProperty(m, "id");
+ dojo.forEach(this.items, function(m2, i){
+ if(this.getProperty(m2, "id") == id){
+ this.items[i] = m2;
+ }
+ },this);
+ }, this);
+ items = this.items;
+ }
+ if(this.stretchToFit){
+ this.displayRange = items.length;
+ }
+ this.onDataReceived(items);
+ this.items = items;
+
+
+ if(this.comparative){
+ // all items are gathered together and used as one
+ // series so their properties can be compared.
+ var nm = "default";
+
+ this.seriesData[nm] = [];
+ this.seriesDataBk[nm] = [];
+ dojo.forEach(items, function(m, i){
+ var field = this.getProperty(m, this.fieldName);
+ this.seriesData[nm].push(field);
+ }, this);
+
+ }else{
+
+ // each item is a seperate series.
+ dojo.forEach(items, function(m, i){
+ var nm = this.store.getLabel(m);
+ if(!this.seriesData[nm]){
+ this.seriesData[nm] = [];
+ this.seriesDataBk[nm] = [];
+ }
+
+ // the property in the item we are using
+ var field = this.getProperty(m, this.fieldName);
+ if(dojo.isArray(field)){
+ // Data is an array, so it's a snapshot, and not
+ // live, updating data
+ //
+ this.seriesData[nm] = field;
+
+ }else{
+ if(!this.scroll){
+ // Data updates, and "moves in place". Columns and
+ // line markers go up and down
+ //
+ // create empty chart elements by starting an array
+ // with zeros until we reach our relevant data
+ var ar = dojo.map(new Array(i+1), function(){ return 0; });
+ ar.push(Number(field));
+ this.seriesData[nm] = ar;
+
+ }else{
+ // Data updates and scrolls to the left
+ if(this.seriesDataBk[nm].length > this.seriesData[nm].length){
+ this.seriesData[nm] = this.seriesDataBk[nm];
+ }
+ // Collecting and storing series data. The items come in
+ // only one at a time, but we need to display historical
+ // data, so it is kept in memory.
+ this.seriesData[nm].push(Number(field));
+ }
+ this.seriesDataBk[nm].push(Number(field));
+ }
+ }, this);
+ }
+
+ // displayData is the segment of the data array that is within
+ // the chart boundaries
+ var displayData;
+ if(this.firstRun){
+ // First time around we need to add the series (chart lines)
+ // to the chart.
+ this.firstRun = false;
+ for(nm in this.seriesData){
+ this.addSeries(nm, this.seriesData[nm]);
+ displayData = this.seriesData[nm];
+ }
+
+ }else{
+
+ // update existing series
+ for(nm in this.seriesData){
+ displayData = this.seriesData[nm];
+
+ if(this.scroll && displayData.length > this.displayRange){
+ // chart lines have gone beyond the right boundary.
+ this.dataOffset = displayData.length-this.displayRange - 1;
+ displayData = displayData.slice(displayData.length-this.displayRange, displayData.length);
+ }
+ this.updateSeries(nm, displayData);
+ }
+ }
+ this.dataLength = displayData.length;
+
+ if(this.showing){
+ this.render();
+ }
+
+ },
+
+ fetch: function(){
+ // summary:
+ // Fetches initial data. Subsequent changes
+ // are received via onSet in data store.
+ //
+ if(!this.store){ return; }
+ this.store.fetch({query:this.query, queryOptions:this.queryOptions, start:this.start, count:this.count, sort:this.sort,
+ onComplete:dojo.hitch(this, function(data){
+ setTimeout(dojo.hitch(this, function(){
+ this.onData(data)
+ }),0);
+ }),
+ onError:dojo.hitch(this, "onError")
+ });
+ },
+
+ convertLabels: function(axis){
+ // summary:
+ // Convenience method to convert a label array of strings
+ // into an array of objects
+ //
+ if(!axis.labels || dojo.isObject(axis.labels[0])){ return null; }
+
+ axis.labels = dojo.map(axis.labels, function(ele, i){
+ return {value:i, text:ele};
+ });
+ return null; // null
+ },
+
+ seriesLabels: function(/*Number*/val){
+ // summary:
+ // Convenience method that sets series labels based on item labels.
+ val--;
+ if(this.series.length<1 || (!this.comparative && val>this.series.length)){ return "-"; }
+ if(this.comparative){
+ return this.store.getLabel(this.items[val]);// String
+
+ }else{
+ // FIXME:
+ // Here we are setting the label base on if there is data in the array slot.
+ // A typical series may look like: [0,0,3.1,0,0,0] which mean the data is populated in the
+ // 3rd row or column. This works well and keeps the labels aligned but has a side effect
+ // of not showing the label is the data is zero. Work around is to not go lower than
+ // 0.01 or something.
+ for(var i=0;i<this.series.length; i++){
+ if(this.series[i].data[val]>0){
+ return this.series[i].name; // String
+ }
+ }
+ }
+ return "-"; // String
+
+ },
+
+ resizeChart: function(/*Object*/dim){
+ // summary:
+ // Call this function to change the chart size.
+ // Can be connected to a layout widget that calls
+ // resize.
+ //
+ var w = Math.max(dim.w, this.minWidth);
+ var h = Math.max(dim.h, this.minHeight);
+ this.resize(w, h);
+ }
+ });
+})();
+
+}
+
+}};});