dojox/charting/DataChart.js

  • Provides:

    • dojox.charting.DataChart
  • Requires:

    • dojox.charting.Chart2D in common
    • dojox.charting.themes.PlotKit.blue in common
  • dojox.charting.DataChart

    • type
      Function
    • chains:
      • dojox.charting.Chart2D: (prototype)
      • dojox.charting.Chart2D: (call)
    • summary
      Set up properties and initialize chart build.
      
      arguments:
    • 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:
    • parameters:
      • node: (typeof DomNode)
        The node to attach the chart to.
      • kwArgs: (typeof Object)
    • source: [view]
         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);
         }
  • dojox.charting.DataChart.scroll

    • type
      Boolean
    • summary
      Whether live data updates and changes display, like columns moving
      up and down, or whether it scrolls to the left as data is added
  • dojox.charting.DataChart.comparative

    • type
      Boolean
    • summary
      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.
  • dojox.charting.DataChart.query

    • type
      String
    • summary
      Used for fetching items. Will vary depending upon store.
  • dojox.charting.DataChart.queryOptions

    • type
      String
    • summary
      Option used for fetching items
      
      
      
      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
  • dojox.charting.DataChart.fieldName

    • type
      String
    • summary
      The field in the store item that is getting charted
  • dojox.charting.DataChart.chartTheme

    • type
      dojox.charting.themes.
    • summary
      The theme to style the chart. Defaults to PlotKit.blue.
  • dojox.charting.DataChart.displayRange

    • type
      Number
    • summary
      The number of major ticks to show on the xaxis
  • dojox.charting.DataChart.stretchToFit

    • type
      Boolean
    • summary
      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?
  • dojox.charting.DataChart.minWidth

    • type
      Number
    • summary
      The the smallest the chart width can be
  • dojox.charting.DataChart.minHeight

    • type
      Number
    • summary
      The the smallest the chart height can be
  • dojox.charting.DataChart.showing

    • type
      Boolean
    • summary
      Whether the chart is showing (default) on
      initialization or hidden.
  • dojox.charting.DataChart.label

    • type
      String
    • summary
      The name field of the store item
      DO NOT SET: Set from store.labelAttribute
  • dojox.charting.DataChart.destroy

    • type
      Function
    • source: [view]
         dojo.forEach(this._events, dojo.disconnect);
         this.inherited(arguments);
    • summary
  • dojox.charting.DataChart.setStore

    • type
      Function
    • parameters:
      • store: (typeof Object)
      • query: (typeof ? String)
      • fieldName: (typeof ? String)
      • queryOptions: (typeof ? Object)
    • source: [view]
         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();
    • summary
      Sets the chart store and query
      then does the first fetch and
      connects to subsequent changes.
      
      TODO: Not handling resetting store
  • dojox.charting.DataChart.show

    • type
      Function
    • source: [view]
         if(!this.showing){
          dojo.style(this.domNode, "display", "");
          this.showing = true;
          this.render();
         }
    • summary
      If chart is hidden, show it
  • dojox.charting.DataChart.hide

    • type
      Function
    • source: [view]
         if(this.showing){
          dojo.style(this.domNode, "display", "none");
          this.showing = false;
         }
    • summary
      If chart is showing, hide it
      Prevents rendering while hidden
  • dojox.charting.DataChart.onSet

    • type
      Function
    • parameters:
      • item: (typeof storeObject)
    • source: [view]
         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);
         }
    • 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.
  • dojox.charting.DataChart.onError

    • type
      Function
    • parameters:
      • err: (typeof Error)
    • 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);
    • summary
  • dojox.charting.DataChart.onDataReceived

    • type
      Function
    • parameters:
      • items: (typeof Array)
    • source: [view]
         // summary:
         //  stub. Fires after data is received but
         //  before data is parsed and rendered
    • summary
      stub. Fires after data is received but
      before data is parsed and rendered
  • dojox.charting.DataChart.getProperty

    • type
      Function
    • parameters:
      • item: (typeof storeObject)
      • prop: (typeof )
    • source: [view]
         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;
    • 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.
  • dojox.charting.DataChart.onData

    • type
      Function
    • parameters:
      • items: (typeof Array)
    • source: [view]
         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();
         }
    • 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(&quot;Store:&quot;, store);console.log(&quot;items: (&quot;, items.length+&quot;)&quot;, items);console.log(&quot;Chart:&quot;, this);
  • dojox.charting.DataChart.fetch

    • type
      Function
    • source: [view]
         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")
         });
    • summary
      Fetches initial data. Subsequent changes
      are received via onSet in data store.
  • dojox.charting.DataChart.convertLabels

    • type
      Function
    • parameters:
      • axis: (typeof )
    • source: [view]
         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
    • summary
      Convenience method to convert a label array of strings
      into an array of objects
    • returns
      null
  • dojox.charting.DataChart.seriesLabels

    • type
      Function
    • parameters:
      • val: (typeof Number)
    • source: [view]
         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     if(this.series[i].data[val]>0){
            return this.series[i].name; // String
           }
          }
         }
         return "-"; // String
    • summary
      Convenience method that sets series labels based on item labels.
    • returns
      String
  • dojox.charting.DataChart.resizeChart

    • type
      Function
    • parameters:
      • dim: (typeof Object)
    • source: [view]
         var w = Math.max(dim.w, this.minWidth);
         var h = Math.max(dim.h, this.minHeight);
         this.resize(w, h);
    • summary
      Call this function to change the chart size.
      Can be connected to a layout widget that calls
      resize.
  • dojox.charting.DataChart.firstRun

    • summary
  • dojox.charting.DataChart.store

    • summary
  • dojox.charting.DataChart._events

    • summary
  • dojox.charting.DataChart.onSetInterval

    • summary
  • dojox.charting.DataChart.onSetItems

    • summary
  • dojox.charting.DataChart.items

    • summary
  • dojox.charting.DataChart.dataOffset

    • summary
  • dojox.charting.DataChart.dataLength

    • summary
  • dojox.charting.DataChart.domNode

    • summary
  • dojox.charting.DataChart.xaxis

    • type
      Object
    • summary
      First query for store
      grid: Object
      Options for the grid plot
      chartPlot: Object
      Options for chart elements (lines, bars, etc)
  • dojox.charting.DataChart.xaxis.labelFunc

    • summary
  • dojox.charting.DataChart.yaxis

    • type
      Object
    • summary
      optional parameters for yaxis (see above)
      store: Object
      dojo.data store (currently nly supports Persevere)
  • dojox.charting.DataChart.yaxis.labelFunc

    • summary
  • dojox.charting.DataChart.seriesData

    • summary
  • dojox.charting.DataChart.seriesDataBk

    • summary
  • dojox.charting.DataChart.chartTheme.plotarea.stroke

    • summary
  • dojox.charting.DataChart.xaxis.to

    • summary
  • kwArgs.grid || {}.type

    • summary
  • kwArgs.grid || {}.hMinorLines

    • summary
  • dojox.charting

    • type
      Object
    • summary
  • dojox

    • type
      Object
    • summary