blob: 14e1666d6200e82e56b81347f26be98b4edba84a [file] [log] [blame]
/*
Copyright (c) 2004-2006, The Dojo Foundation
All Rights Reserved.
Licensed under the Academic Free License version 2.1 or above OR the
modified BSD license. For more information on Dojo licensing, see:
http://dojotoolkit.org/community/licensing.shtml
*/
dojo.provide("dojo.widget.Chart");
dojo.require("dojo.widget.*");
dojo.require("dojo.gfx.color");
dojo.require("dojo.gfx.color.hsl");
dojo.declare(
"dojo.widget.Chart",
null,
function(){
// summary: Base class for svg and vml implementations of Chart
this.series = [];
},
{
isContainer: false,
assignColors: function(){
// summary
// Assigns/generates a color for a data series.
var hue=30;
var sat=120;
var lum=120;
var steps = Math.round(330/this.series.length);
for(var i=0; i<this.series.length; i++){
var c=dojo.gfx.color.hsl2rgb(hue,sat,lum);
if(!this.series[i].color){
this.series[i].color = dojo.gfx.color.rgb2hex(c[0],c[1],c[2]);
}
hue += steps;
}
},
parseData: function(table){
var thead=table.getElementsByTagName("thead")[0];
var tbody=table.getElementsByTagName("tbody")[0];
if(!(thead&&tbody)) dojo.raise("dojo.widget.Chart: supplied table must define a head and a body.");
// set up the series.
var columns=thead.getElementsByTagName("tr")[0].getElementsByTagName("th"); // should be <tr><..>
// assume column 0 == X
for (var i=1; i<columns.length; i++){
var key="column"+i;
var label=columns[i].innerHTML;
var plotType=columns[i].getAttribute("plotType")||"line";
var color=columns[i].getAttribute("color");
var ds=new dojo.widget.Chart.DataSeries(key,label,plotType,color);
this.series.push(ds);
}
// ok, get the values.
var rows=tbody.rows;
var xMin=Number.MAX_VALUE,xMax=Number.MIN_VALUE;
var yMin=Number.MAX_VALUE,yMax=Number.MIN_VALUE;
var ignore = [
"accesskey","align","bgcolor","class",
"colspan","height","id","nowrap",
"rowspan","style","tabindex","title",
"valign","width"
];
for(var i=0; i<rows.length; i++){
var row=rows[i];
var cells=row.cells;
var x=Number.MIN_VALUE;
for (var j=0; j<cells.length; j++){
if (j==0){
x=parseFloat(cells[j].innerHTML);
xMin=Math.min(xMin, x);
xMax=Math.max(xMax, x);
} else {
var ds=this.series[j-1];
var y=parseFloat(cells[j].innerHTML);
yMin=Math.min(yMin,y);
yMax=Math.max(yMax,y);
var o={x:x, value:y};
var attrs=cells[j].attributes;
for(var k=0; k<attrs.length; k++){
var attr=attrs.item(k);
var bIgnore=false;
for (var l=0; l<ignore.length; l++){
if (attr.nodeName.toLowerCase()==ignore[l]){
bIgnore=true;
break;
}
}
if(!bIgnore) o[attr.nodeName]=attr.nodeValue;
}
ds.add(o);
}
}
}
return { x:{ min:xMin, max:xMax}, y:{ min:yMin, max:yMax} };
}
});
dojo.declare(
"dojo.widget.Chart.DataSeries",
null,
function(key, label, plotType, color){
// summary:
// Every chart has a set of data series; this is the series. Note that each
// member of value is an object and in the minimum has 2 properties: .x and
// .value.
//
this.id = "DataSeries"+dojo.widget.Chart.DataSeries.count++;
this.key = key;
this.label = label||this.id;
this.plotType = plotType||"line"; // let line be the default.
this.color = color;
this.values = [];
},
{
add: function(v){
if(v.x==null||v.value==null){
dojo.raise("dojo.widget.Chart.DataSeries.add: v must have both an 'x' and 'value' property.");
}
this.values.push(v);
},
clear: function(){
this.values=[];
},
createRange: function(len){
var idx = this.values.length-1;
var length = (len||this.values.length);
return { "index": idx, "length": length, "start":Math.max(idx-length,0) };
},
// trend values
getMean: function(len){
var range = this.createRange(len);
if(range.index<0){ return 0; }
var t = 0;
var c = 0;
for(var i=range.index; i>=range.start; i--){
var n = parseFloat(this.values[i].value);
if(!isNaN(n)){ t += n; c++; }
}
t /= Math.max(c,1);
return t;
},
getMovingAverage: function(len){
var range = this.createRange(len);
if(range.index<0){ return 0; }
var t = 0;
var c = 0;
for(var i=range.index; i>=range.start; i--){
var n = parseFloat(this.values[i].value);
if(!isNaN(n)){ t += n; c++; }
}
t /= Math.max(c,1);
return t;
},
getVariance: function(len){
var range = this.createRange(len);
if(range.index < 0){ return 0; }
var t = 0; // FIXME: for tom: wtf are t, c, and s?
var s = 0;
var c = 0;
for(var i=range.index; i>=range.start; i--){
var n = parseFloat(this.values[i].value);
if(!isNaN(n)){
t += n;
s += Math.pow(n,2);
c++;
}
}
return (s/c)-Math.pow(t/c,2);
},
getStandardDeviation: function(len){
return Math.sqrt(this.getVariance(len));
},
getMax: function(len){
var range = this.createRange(len);
if(range.index < 0){ return 0; }
var t = 0;
for (var i=range.index; i>=range.start; i--){
var n=parseFloat(this.values[i].value);
if (!isNaN(n)){
t=Math.max(n,t);
}
}
return t;
},
getMin: function(len){
var range=this.createRange(len);
if(range.index < 0){ return 0; }
var t = 0;
for(var i=range.index; i>=range.start; i--){
var n = parseFloat(this.values[i].value);
if(!isNaN(n)){
t=Math.min(n,t);
}
}
return t;
},
getMedian: function(len){
var range = this.createRange(len);
if(range.index<0){ return 0; }
var a = [];
for (var i=range.index; i>=range.start; i--){
var n=parseFloat(this.values[i].value);
if (!isNaN(n)){
var b=false;
for(var j=0; j<a.length&&!b; j++){
if (n==a[j]) b=true;
}
if(!b){ a.push(n); }
}
}
a.sort();
if(a.length>0){ return a[Math.ceil(a.length/2)]; }
return 0;
},
getMode: function(len){
var range=this.createRange(len);
if(range.index<0){ return 0; }
var o = {};
var ret = 0
var m = 0;
for(var i=range.index; i>=range.start; i--){
var n=parseFloat(this.values[i].value);
if(!isNaN(n)){
if (!o[this.values[i].value]) o[this.values[i].value] = 1;
else o[this.values[i].value]++;
}
}
for(var p in o){
if(m<o[p]){ m=o[p]; ret=p; }
}
return parseFloat(ret);
}
});
dojo["requireIf"](dojo.render.svg.capable, "dojo.widget.svg.Chart");
dojo["requireIf"](!dojo.render.svg.capable && dojo.render.vml.capable, "dojo.widget.vml.Chart");