Permalink
Browse files

0.3.6: Grouped Bar Graphs

Started adding grouped bar graphs.
  • Loading branch information...
1 parent 5c22721 commit 793f9ebcd0153fd893d9447e0fb315f5f12c43d5 Alex Young committed Nov 28, 2011
Showing with 1,377 additions and 48 deletions.
  1. +3 −2 Makefile
  2. +1 −0 README.textile
  3. +8 −1 docs/examples.html
  4. +0 −1 docs/ico-min.js
  5. +1,126 −0 docs/ico.js
  6. +7 −0 docs/ico.min.js
  7. +31 −3 docs/index.html
  8. +29 −2 docs/index.md
  9. +1 −1 docs/layout/begin.html
  10. +0 −1 ico-min.js
  11. +85 −18 ico.js
  12. +1 −1 ico.min.js
  13. +1 −1 src/end.js
  14. +35 −3 src/graphs/bar.js
  15. +26 −12 src/graphs/base.js
  16. +23 −2 src/helpers.js
View
5 Makefile
@@ -7,7 +7,7 @@ build: $(SRC)
@cat $^ > ico.js
min: build
- ./node_modules/.bin/uglifyjs --no-mangle ico.js > ico.min.js
+ @./node_modules/.bin/uglifyjs --no-mangle ico.js > ico.min.js
test: build
./node_modules/.bin/expresso
@@ -16,7 +16,8 @@ docs: min
@markdown docs/index.md \
| cat docs/layout/begin.html - docs/layout/end.html \
> docs/index.html
- @cp ico-min.js docs/ico-min.js
+ @cp ico.min.js docs/ico.min.js
+ @cp ico.js docs/ico.js
@cp raphael.js docs/raphael.js
publishdocs:
View
1 README.textile
@@ -13,6 +13,7 @@ Donations to support future development are welcome and appreciated: "Donate":ht
h3. News
+* [2011-11-28] 0.3.6: Added grouped bar graphs, for documentation see "Grouped Bar Graphs":http://alexyoung.github.com/ico/
* [2011-11-26] Updated documentation to include details on each graph's options
* [2011-11-26] 0.3.5: Added <code>max_bar_width</code> option for controlling the maximum width a bar can be, <code>bar_padding</code> for controlling the padding between bars, and <code>bar_width</code> for forcing a given bar width. These options also apply to horizontal bar graphs
* [2011-11-08] 0.3.4.1: Added <code>bar_labels</code> option that displays values above the standard bar graphs
View
9 docs/examples.html
@@ -4,7 +4,7 @@
<head>
<title>Raphael Ico</title>
<script src="raphael.js" type="text/javascript" charset="utf-8"></script>
- <script src="ico-min.js" type="text/javascript" charset="utf-8"></script>
+ <script src="ico.min.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
function $(id) {
return document.getElementById(id);
@@ -89,6 +89,10 @@
<div id="bar_negative_and_positive" class="linegraph"></div>
<div id="horizontal_negative_and_positive" class="linegraph"></div>
+ <h2>Grouped Bars</h2>
+
+ <div class="linegraph" id="grouped_bars"></div>
+
<h2>Contributed Examples</h2>
<p>Min and max are the same:</p>
@@ -139,6 +143,9 @@
new Ico.LineGraph($('linegraph_negative_and_positive'), [-57,-31,-87,-66,-30,-77,-88,-75,-25,-48,-56,-91,-16,-41,-87,-69,-65,-62,-58,-15,-49,-75,-42,-78,-79]);
new Ico.BarGraph($('bar_negative_and_positive'), [-57,-31,-87,66,-30,-77,-88,-75,-25,-48,-56,-91,16,-41,-87,-69,-65,-62,58,-15,-49,-75,-42,-78,-79]);
new Ico.HorizontalBarGraph($('horizontal_negative_and_positive'), [-57,-31,-87,66,-30,-77,-88,-75,-20,-48,-56,-91,16,-41,-87,-69,-65,-62,58,-15,-49,-75,-42,-78,-79]);
+
+new Ico.BarGraph($('grouped_bars'), [[10, 15, 4], [18, 19, 12], [17, 23, 11], [11, 22, 14]], { grid: true, font_size: 10, colours: ['#ff0099', '#339933', '#333399'], labels: ['Winter', 'Spring', 'Summer', 'Autumn'] });
+
new Ico.BarGraph($('rics_poll'), [20, 20], { max_bar_size: 100, font_size: 10, colour: '#ff0099', labels: ['label one', 'label two'] });
new Ico.LineGraph($('issue4'), [20, 20, 20, 20, 20], { font_size: 10, colour: '#ff0099' });
new Ico.BarGraph($('rics_poll4'), [20, 19, 15, 10], { font_size: 10, colour: '#ff0099', labels: ['label one', 'label two'] });
View
1 docs/ico-min.js
@@ -1 +0,0 @@
-(function(a){function d(a,b){var c;a.currentStyle?c=a.currentStyle[b]:window.getComputedStyle&&(c=document.defaultView.getComputedStyle(a,null).getPropertyValue(b)),c&&c.length===0&&(c=null);return c}function c(a){return a>1?a-1:1}var b={VERSION:"0.3.0",round:function(a,b){var c=Math.round(a*Math.pow(10,b))/Math.pow(10,b);return c}};Array.prototype.sum=function(){for(var a=0,b=0;a<this.length;b+=this[a++]);return b},typeof Array.prototype.max=="undefined"&&(Array.prototype.max=function(){return Math.max.apply({},this)}),typeof Array.prototype.min=="undefined"&&(Array.prototype.min=function(){return Math.min.apply({},this)}),Array.prototype.mean=function(){return this.sum()/this.length},Array.prototype.variance=function(){var a=this.mean(),b=0;for(var c=0;c<this.length;c++)b+=Math.pow(this[c]-a,2);return b/(this.length-1)},Array.prototype.standard_deviation=function(){return Math.sqrt(this.variance())},typeof Object.extend=="undefined"&&(Object.extend=function(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}),b.Normaliser=function(a,b){this.options={start_value:null},typeof b!="undefined"&&(this.options=b),this.min=a.min(),this.max=a.max(),this.standard_deviation=a.standard_deviation(),this.range=0,this.step=this.labelStep(this.max-this.min),this.start_value=this.calculateStart(),this.process()},b.Normaliser.prototype={calculateStart:function(){var a=typeof this.options.start_value!="undefined"&&this.min>=0?this.options.start_value:this.min,b=this.round(a,1);if(this.min>0&&b>this.min)return 0;return b},round:function(a,b){b=b||1;var c=a;if(this.standard_deviation>.1){var d=Math.pow(10,-b);c=Math.round(a*d)/d;if(c>this.min)return this.round(a-this.step)}return c},process:function(){this.range=this.max-this.start_value,this.step=this.labelStep(this.range)},labelStep:function(a){return Math.pow(10,Math.round(Math.log(a)/Math.LN10)-1)}},b.Base={normaliseData:function(a){var b=[],c=0;for(c=0;c<a.length;c++)b.push(this.normalise(a[c]));return b},flatten:function(a){var b=[];if(typeof a.length=="undefined"){if(typeof a!="object")return[];for(var c in a)a.hasOwnProperty(c)&&(b=b.concat(this.flatten(a[c])))}for(var d=0;d<a.length;d++)typeof a[d].length=="number"?b=b.concat(this.flatten(a[d])):b.push(a[d]);return b},makeRange:function(a,b){var c=[],d;for(d=a;d<b;d++)c.push(d);return c}},b.BaseGraph=function(){this.initialize.apply(this,arguments)},Object.extend(b.BaseGraph.prototype,b.Base),Object.extend(b.BaseGraph.prototype,{initialize:function(a,c,e){this.element=a,this.data_sets=this.buildDataSets(c,e),this.flat_data=this.flatten(c),this.data_size=this.longestDataSetLength();if(e&&e.colour){e.colours={};for(var f in this.data_sets)this.data_sets.hasOwnProperty(f)&&(e.colours[f]=e.colour)}this.options={width:parseInt(d(a,"width"),10),height:parseInt(d(a,"height"),10),labels:this.makeRange(1,this.data_size+1),plot_padding:10,font_size:10,show_horizontal_labels:!0,show_vertical_labels:!0,background_colour:d(a,"backgroundColor")||"#ffffff",label_colour:"#666",markers:!1,marker_size:5,meanline:!1,grid:!1,grid_colour:"#ccc",y_padding_top:20,draw:!0},Object.extend(this.options,this.chartDefaults()||{}),Object.extend(this.options,e||{}),this.normaliser=new b.Normaliser(this.flat_data,this.normaliserOptions()),this.label_step=this.normaliser.step,this.range=this.normaliser.range,this.start_value=this.normaliser.start_value,this.x_padding_left=10+this.paddingLeftOffset(),this.x_padding_right=20,this.x_padding=this.x_padding_left+this.x_padding_right,this.y_padding_top=this.options.y_padding_top,this.y_padding_bottom=20+this.paddingBottomOffset(),this.y_padding=this.y_padding_top+this.y_padding_bottom,this.graph_width=this.options.width-this.x_padding,this.graph_height=this.options.height-this.y_padding,this.step=this.calculateStep(),this.y_label_count=Math.ceil(this.range/this.label_step),this.normaliser.min+this.y_label_count*this.normaliser.step<this.normaliser.max&&(this.y_label_count+=1),this.value_labels=this.makeValueLabels(this.y_label_count),this.top_value=this.value_labels[this.value_labels.length-1],this.grid_start_offset=-1,this.options.draw&&(typeof this.options.colours=="undefined"&&(this.options.colours=this.makeRandomColours()),this.paper=Raphael(this.element,this.options.width,this.options.height),this.background=this.paper.rect(0,0,this.options.width,this.options.height),this.background.attr({fill:this.options.background_colour,stroke:"none"}),this.options.meanline===!0&&(this.options.meanline={"stroke-width":"2px",stroke:"#BBBBBB"}),this.setChartSpecificOptions(),this.lastPoint={x:0,y:0},this.draw())},buildDataSets:function(a,b){return typeof a.length!="undefined"?{one:a}:a},normaliserOptions:function(){return{}},chartDefaults:function(){return{}},drawPlot:function(a,b,c,d,e){},calculateStep:function(){},makeRandomColours:function(){var a={};for(var b in this.data_sets)a.hasOwnProperty(b)||(a[b]=Raphael.hsb2rgb(Math.random(),1,.75).hex);return a},longestDataSetLength:function(){var a=0;for(var b in this.data_sets)this.data_sets.hasOwnProperty(b)&&(a=this.data_sets[b].length>a?this.data_sets[b].length:a);return a},roundValue:function(a,b){var c=Math.pow(10,b);a*=c,a=Math.round(a)/c;return a},roundValues:function(a,b){var c=[];for(var d=0;d<a.length;d++)c.push(this.roundValue(a[d],b));return c},longestLabel:function(a){var b=Array.prototype.slice.call(a||this.options.labels,0);return b.sort(function(a,b){return a.toString().length<b.toString().length})[0].toString().length},paddingLeftOffset:function(){var a=this.roundValues(this.flat_data,2),b=0;b=a.sort(function(a,b){return a.toString().length<b.toString().length})[0].toString().length,b=b>2?b-1:b;return 10+b*this.options.font_size},paddingBottomOffset:function(){return this.options.font_size},normalise:function(a){var b=this.start_value===0?this.top_value:this.range;return a/b*this.graph_height},draw:function(){this.options.grid&&this.drawGrid(),this.options.meanline&&this.drawMeanLine(this.normaliseData(this.flat_data)),this.drawAxis(),this.options.show_vertical_labels&&this.drawVerticalLabels(),this.options.show_horizontal_labels&&this.drawHorizontalLabels();for(var a in this.data_sets)if(this.data_sets.hasOwnProperty(a)){var b=this.data_sets[a];this.drawLines(a,this.options.colours[a],this.normaliseData(b))}this.start_value!==0&&this.drawFocusHint()},drawGrid:function(){var a="",b;if(this.options.show_vertical_labels){var c=this.graph_height+this.y_padding_top;for(b=0;b<this.y_label_count;b++)c=c-this.graph_height/this.y_label_count,a+="M"+this.x_padding_left+","+c,a+="L"+(this.x_padding_left+this.graph_width)+","+c}if(this.options.show_horizontal_labels){var d=this.x_padding_left+this.options.plot_padding+this.grid_start_offset,e=this.options.labels.length;for(b=0;b<e;b++)a+="M"+d+","+this.y_padding_top,a+="L"+d+","+(this.y_padding_top+this.graph_height),d=d+this.step;d=d-this.options.plot_padding-1,a+="M"+d+","+this.y_padding_top,a+="L"+d+","+(this.y_padding_top+this.graph_height)}this.paper.path(a).attr({stroke:this.options.grid_colour,"stroke-width":"1px"})},drawLines:function(a,b,c){var d=this.calculateCoords(c),e="";for(var f=0;f<d.length;f++){var g=d[f][0]||0,h=d[f][1]||0;e=this.drawPlot(f,e,g,h,b)}this.paper.path(e).attr({stroke:b,"stroke-width":"3px"})},calculateCoords:function(a){var b=this.x_padding_left+this.options.plot_padding-this.step,c=this.graph_height+this.y_padding_top+this.normalise(this.start_value),d=0,e=[];for(var f=0;f<a.length;f++)d=c-a[f],b=b+this.step,e.push([b,d]);return e},drawFocusHint:function(){var a=5,b=this.x_padding_left+a/2-1,c=this.options.height-this.y_padding_bottom,d="";d+="M"+b+","+c,d+="L"+(b-a)+","+(c-a),d+="M"+b+","+(c-a),d+="L"+(b-a)+","+(c-a*2),this.paper.path(d).attr({stroke:this.options.label_colour,"stroke-width":2})},drawMeanLine:function(a){var b=a.sum()/a.length,c="";c+="M"+(this.x_padding_left-1)+","+(this.options.height-this.y_padding_bottom-b),c+="L"+(this.graph_width+this.x_padding_left)+","+(this.options.height-this.y_padding_bottom-b),this.paper.path(c).attr(this.options.meanline)},drawAxis:function(){var a="";a+="M"+(this.x_padding_left-1)+","+(this.options.height-this.y_padding_bottom),a+="L"+(this.graph_width+this.x_padding_left)+","+(this.options.height-this.y_padding_bottom),a+="M"+(this.x_padding_left-1)+","+(this.options.height-this.y_padding_bottom),a+="L"+(this.x_padding_left-1)+","+this.y_padding_top,this.paper.path(a).attr({stroke:this.options.label_colour})},makeValueLabels:function(a){var b=this.label_step,c=this.start_value,d=[];for(var e=0;e<a;e++)c=this.roundValue(c+b,2),d.push(c);return d},drawMarkers:function(a,b,c,d,e,f){function h(a){return a*b[1]}function g(a){return a*b[0]}var i=this.x_padding_left-1+g(d),j=this.options.height-this.y_padding_bottom+h(d),k="",l={font:this.options.font_size+'px "Arial"',stroke:"none",fill:"#000"};Object.extend(l,f||{});for(var m=0;m<a.length;m++)k+="M"+i+","+j,typeof a[m]!="undefined"&&(a[m]+"").length>0&&(k+="L"+(i+h(5))+","+(j+g(5)),this.paper.text(i+e[0],j-e[1],a[m]).attr(l).toFront()),i=i+g(c),j=j+h(c);this.paper.path(k).attr({stroke:this.options.label_colour})},drawVerticalLabels:function(){var a=this.graph_height/this.y_label_count;this.drawMarkers(this.value_labels,[0,-1],a,a,[-8,-2],{"text-anchor":"end"})},drawHorizontalLabels:function(){this.drawMarkers(this.options.labels,[1,0],this.step,this.options.plot_padding,[0,(this.options.font_size+7)*-1])}}),b.BarGraph=function(){this.initialize.apply(this,arguments)},Object.extend(b.BarGraph.prototype,b.BaseGraph.prototype),Object.extend(b.BarGraph.prototype,{chartDefaults:function(){return{plot_padding:0}},normaliserOptions:function(){return{start_value:0}},setChartSpecificOptions:function(){this.bar_padding=5,this.bar_width=this.calculateBarWidth(),this.options.plot_padding=this.bar_width/2,this.step=this.calculateStep(),this.grid_start_offset=this.bar_padding-1,this.start_y=this.options.height-this.y_padding_bottom},calculateBarWidth:function(){return this.graph_width/this.data_size-this.bar_padding},calculateStep:function(){return(this.graph_width-this.options.plot_padding*2-this.bar_padding*2)/c(this.data_size)},drawPlot:function(a,b,c,d,e){c=c+this.bar_padding,b+="M"+c+","+this.start_y,b+="L"+c+","+d,this.paper.path(b).attr({stroke:e,"stroke-width":this.bar_width+"px"}),b="",c=c+this.step,b+="M"+c+","+this.start_y;return b},drawHorizontalLabels:function(){var a=this.bar_padding+this.options.plot_padding;this.drawMarkers(this.options.labels,[1,0],this.step,a,[0,(this.options.font_size+7)*-1])}}),b.HorizontalBarGraph=function(){this.initialize.apply(this,arguments)},Object.extend(b.HorizontalBarGraph.prototype,b.BaseGraph.prototype),Object.extend(b.HorizontalBarGraph.prototype,{setChartSpecificOptions:function(){this.y_padding_top=0,this.x_padding_left=20+this.longestLabel()*(this.options.font_size/2),this.bar_padding=5,this.bar_width=this.calculateBarHeight(),this.options.plot_padding=0,this.step=this.calculateStep()},normalise:function(a){var b=this.x_padding_left;return a/this.range*(this.graph_width-b)},calculateBarHeight:function(){return this.graph_height/this.data_size-this.bar_padding},calculateStep:function(){return(this.options.height-this.y_padding_bottom)/c(this.data_size)},drawLines:function(a,b,c){var d=this.x_padding_left+this.options.plot_padding*2,e=this.options.height-this.y_padding_bottom-this.step/2,f="M"+d+","+e,g;for(g=0;g<c.length;g++)f+="L"+(d+c[g]-this.normalise(this.start_value))+","+e,e=e-this.step,f+="M"+d+","+e;this.paper.path(f).attr({stroke:b,"stroke-width":this.bar_width+"px"})},drawFocusHint:function(){var a=5,b=this.x_padding_left+this.step*2,c=this.options.height-this.y_padding_bottom,d="";d+="M"+b+","+c,d+="L"+(b-a)+","+(c+a),d+="M"+(b-a)+","+c,d+="L"+(b-a*2)+","+(c+a),this.paper.path(d).attr({stroke:this.options.label_colour,"stroke-width":2})},drawVerticalLabels:function(){var a=this.step/2-this.options.plot_padding*2;this.drawMarkers(this.options.labels,[0,-1],this.step,a,[-8,this.options.font_size/8],{"text-anchor":"end"})},drawHorizontalLabels:function(){var a=this.graph_width/this.y_label_count,b=this.makeValueLabels(this.y_label_count);this.drawMarkers(b,[1,0],a,a,[0,(this.options.font_size+7)*-1])}}),b.LineGraph=function(){this.initialize.apply(this,arguments)},Object.extend(b.LineGraph.prototype,b.BaseGraph.prototype),Object.extend(b.LineGraph.prototype,{normalise:function(a){var b=this.start_value===0?this.top_value:this.top_value-this.start_value;return a/b*this.graph_height},chartDefaults:function(){return{plot_padding:10}},setChartSpecificOptions:function(){this.x_padding_left=30+this.longestLabel(this.value_labels)*(this.options.font_size/2),typeof this.options.curve_amount=="undefined"&&(this.options.curve_amount=10)},normaliserOptions:function(){return{start_value:this.options.start_value}},calculateStep:function(){return(this.graph_width-this.options.plot_padding*2)/c(this.data_size)},startPlot:function(a,b,c,d){this.lastPoint={x:b,y:c};return a+"M"+b+","+c},drawPlot:function(a,b,c,d,e){var f=this.options.curve_amount;if(this.options.markers==="circle"){var g=this.paper.circle(c,d,this.options.marker_size);g.attr({"stroke-width":"1px",stroke:this.options.background_colour,fill:e})}if(a===0)return this.startPlot(b,c,d,e);f?b+=["C",this.lastPoint.x+f,this.lastPoint.y,c-f,d,c,d]:b+="L"+c+","+d,this.lastPoint={x:c,y:d};return b}}),b.SparkLine=function(){this.initialize.apply(this,arguments)},b.SparkLine.prototype={initialize:function(a,b,c){this.element=a,this.data=b,this.options={width:parseInt(d(a,"width"),10),height:parseInt(d(a,"height"),10),highlight:!1,background_colour:d(a,"backgroundColor")||"#ffffff",colour:"#036"},Object.extend(this.options,c||{}),this.step=this.calculateStep(),this.paper=Raphael(this.element,this.options.width,this.options.height),this.options.acceptable_range?this.background=this.paper.rect(0,this.options.height-this.normalise(this.options.acceptable_range[1]),this.options.width,this.options.height-this.normalise(this.options.acceptable_range[0])):this.background=this.paper.rect(0,0,this.options.width,this.options.height),this.background.attr({fill:this.options.background_colour,stroke:"none"}),this.draw()},calculateStep:function(){return this.options.width/c(this.data.length)},normalise:function(a){return this.options.height/this.data.max()*a},draw:function(){var a=this.normaliseData(this.data);this.drawLines("",this.options.colour,a),this.options.highlight&&this.showHighlight(a)},drawLines:function(a,c,d){var e="",f=0,g=d.slice(1),h=0;e="M0,"+(this.options.height-d[0]);for(h=1;h<d.length;h++)f=f+this.step,e+="L"+f+","+b.round(this.options.height-d[h],2);this.paper.path(e).attr({stroke:c}),this.lastPoint={x:0,y:this.options.height-d[0]}},showHighlight:function(a){var b=2,c=this.options.width-b,d=this.options.highlight.index||a.length-1,e=a[d]+Math.round(b/2);typeof this.options.highlight.index!="undefined"&&(c=this.step*this.options.highlight.index);var f=this.paper.circle(c,this.options.height-e,b);f.attr({stroke:!1,fill:this.options.highlight.colour})}},Object.extend(b.SparkLine.prototype,b.Base),b.SparkBar=function(){this.initialize.apply(this,arguments)},Object.extend(b.SparkBar.prototype,b.SparkLine.prototype),Object.extend(b.SparkBar.prototype,{calculateStep:function(){return this.options.width/c(this.data.length)},drawLines:function(a,b,c){var d=this.step>2?this.step-1:this.step,e=d,f="",g=0;for(g=0;g<c.length;g++)f+="M"+e+","+(this.options.height-c[g]),f+="L"+e+","+this.options.height,e=e+this.step;this.paper.path(f).attr({stroke:b,"stroke-width":d})}}),a.Ico=b})(typeof window=="undefined"?this:window)
View
1,126 docs/ico.js
@@ -0,0 +1,1126 @@
+/*!
+ * Ico
+ * Copyright (C) 2009-2011 Alex R. Young
+ * MIT Licensed
+ */
+
+/**
+ * The Ico object.
+ */
+(function(global) {
+ var Ico = {
+ VERSION: '0.3.5',
+
+ /**
+ * Rounds a float to the specified number of decimal places.
+ *
+ * @param {Float} num A number to round
+ * @param {Integer} dec The number of decimal places
+ * @returns {Float} The rounded result
+ */
+ round: function(num, dec) {
+ var result = Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec);
+ return result;
+ }
+ };
+
+/**
+ * Determines if a value is valid as a 'step' value.
+ * Steps are the increments between each bar or line.
+ *
+ * @param {Integer} value A number to test
+ * @returns {Integer} A valid step value
+ */
+function validStepDivider(value) {
+ return value > 1 ? value - 1 : 1;
+}
+
+/**
+ * Gets a CSS style property.
+ *
+ * @param {Object} el A DOM element
+ * @param {String} styleProp The name of a style property
+ * @returns {Object} The style value
+ */
+function getStyle(el, styleProp) {
+ if (typeof window === 'undefined') {
+ return;
+ }
+
+ var style;
+ if (el.currentStyle) {
+ style = el.currentStyle[styleProp];
+ } else if (window.getComputedStyle) {
+ style = document.defaultView.getComputedStyle(el, null).getPropertyValue(styleProp);
+ }
+ if (style && style.length === 0) {
+ style = null;
+ }
+ return style;
+}
+
+var Helpers = {};
+
+Helpers.sum = function(a) {
+ var i, sum;
+ for (i = 0, sum = 0; i < a.length; sum += a[i++]) {}
+ return sum;
+};
+
+if (typeof Array.prototype.max === 'undefined') {
+ Helpers.max = function(a) {
+ return Math.max.apply({}, a);
+ };
+} else {
+ Helpers.max = function(a) {
+ return a.max();
+ };
+}
+
+if (typeof Array.prototype.min === 'undefined') {
+ Helpers.min = function(a) {
+ return Math.min.apply({}, a);
+ };
+} else {
+ Helpers.min = function(a) {
+ return a.min();
+ };
+}
+
+Helpers.mean = function(a) {
+ return Helpers.sum(a) / a.length;
+};
+
+Helpers.variance = function(a) {
+ var mean = Helpers.mean(a),
+ variance = 0;
+ for (var i = 0; i < a.length; i++) {
+ variance += Math.pow(a[i] - mean, 2);
+ }
+ return variance / (a.length - 1);
+};
+
+Helpers.standard_deviation = function(a) {
+ return Math.sqrt(Helpers.variance(a));
+};
+
+if (typeof Object.extend === 'undefined') {
+ Helpers.extend = function(destination, source) {
+ for (var property in source) {
+ if (source.hasOwnProperty(property)) {
+ destination[property] = source[property];
+ }
+ }
+ return destination;
+ };
+} else {
+ Helpers.extend = Object.extend;
+}
+
+if (Object.keys) {
+ Helpers.keys = Object.keys;
+} else {
+ Helpers.keys = function(o) {
+ if (o !== Object(o)) {
+ throw new TypeError('Object.keys called on non-object');
+ }
+
+ var ret = [], p;
+ for (p in o) {
+ if (Object.prototype.hasOwnProperty.call(o,p)) {
+ ret.push(p);
+ }
+ }
+ return ret;
+ }
+}
+/**
+ * Normalises lists of values to fit inside a graph.
+ *
+ * @param {Array} data A list of values
+ * @param {Object} options Can be used to set the `start_value`
+ */
+Ico.Normaliser = function(data, options) {
+ this.options = {
+ start_value: null
+ };
+
+ if (typeof options !== 'undefined') {
+ this.options = options;
+ }
+
+ this.min = Helpers.min(data);
+ this.max = Helpers.max(data);
+ this.standard_deviation = Helpers.standard_deviation(data);
+ this.range = 0;
+ this.step = this.labelStep(this.max - this.min);
+ this.start_value = this.calculateStart();
+ this.process();
+};
+
+Ico.Normaliser.prototype = {
+ /**
+ * Calculates the start value. This is often 0.
+ * @returns {Float} The start value
+ */
+ calculateStart: function() {
+ var min = typeof this.options.start_value !== 'undefined' && this.min >= 0 ? this.options.start_value : this.min,
+ start_value = this.round(min, 1);
+
+ /* This is a boundary condition */
+ if (this.min > 0 && start_value > this.min) {
+ return 0;
+ }
+
+ if (this.min === this.max) {
+ return 0;
+ }
+
+ return start_value;
+ },
+
+ /* Given a value, this method rounds it to the nearest good value for an origin */
+ round: function(value, offset) {
+ offset = offset || 1;
+ var roundedValue = value;
+
+ if (this.standard_deviation > 0.1) {
+ var multiplier = Math.pow(10, -offset);
+ roundedValue = Math.round(value * multiplier) / multiplier;
+
+ if (roundedValue > this.min) {
+ return this.round(value - this.step);
+ }
+ }
+ return roundedValue;
+ },
+
+ /**
+ * Calculates the range and step values.
+ */
+ process: function() {
+ this.range = this.max - this.start_value;
+ this.step = this.labelStep(this.range);
+ },
+
+ /**
+ * Calculates the label step value.
+ *
+ * @param {Float} value A value to convert to a label position
+ * @returns {Float} The rounded label step result
+ */
+ labelStep: function(value) {
+ return Math.pow(10, Math.round((Math.log(value) / Math.LN10)) - 1);
+ }
+};
+
+
+/*!
+ * Ico
+ * Copyright (C) 2009-2011 Alex R. Young
+ * MIT Licensed
+ */
+
+/**
+ * The Ico.Base object which contains useful generic functions.
+ */
+Ico.Base = {
+ /**
+ * Runs this.normalise on each value.
+ *
+ * @param {Array} data Values to normalise
+ * @returns {Array} Normalised values
+ */
+ normaliseData: function(data) {
+ var values = [],
+ i = 0;
+ for (i = 0; i < data.length; i++) {
+ values.push(this.normalise(data[i]));
+ }
+ return values;
+ },
+
+ /**
+ * Flattens objects into an array.
+ *
+ * @param {Object} data Values to flatten
+ * @returns {Array} Flattened values
+ */
+ flatten: function(data) {
+ var flat_data = [];
+
+ if (typeof data.length === 'undefined') {
+ if (typeof data === 'object') {
+ for (var key in data) {
+ if (data.hasOwnProperty(key))
+ flat_data = flat_data.concat(this.flatten(data[key]));
+ }
+ } else {
+ return [];
+ }
+ }
+
+ for (var i = 0; i < data.length; i++) {
+ if (typeof data[i].length === 'number') {
+ flat_data = flat_data.concat(this.flatten(data[i]));
+ } else {
+ flat_data.push(data[i]);
+ }
+ }
+ return flat_data;
+ },
+
+ /**
+ * Handy method to produce an array of numbers.
+ *
+ * @param {Integer} start A number to start at
+ * @param {Integer} end A number to end at
+ * @returns {Array} An array of values
+ */
+ makeRange: function(start, end) {
+ var values = [], i;
+ for (i = start; i < end; i++) {
+ values.push(i);
+ }
+ return values;
+ }
+};
+/**
+ * Ico.BaseGraph is extended by most of the other graphs. It
+ * uses a simple pattern with methods that can be overridden.
+ */
+Ico.BaseGraph = function() { this.initialize.apply(this, arguments); };
+Helpers.extend(Ico.BaseGraph.prototype, Ico.Base);
+Helpers.extend(Ico.BaseGraph.prototype, {
+ /**
+ * This base class is used by the other graphs in Ico.
+ *
+ * Options:
+ *
+ * `width`: The width of the container element
+ * `height`: The height of the container element
+ * `labels`: The textual labels
+ * `label_count`: The number of numerical labels to display
+ * `label_step`: The value to increment each numerical label
+ * `start_value`: The value to start plotting from (generally 0)
+ * `bar_size`: Set the size for a bar in a bar chart
+ * `max_bar_size`: Set the maximum size for a bar in a bar chart
+ *
+ * @param {Object} A DOM element
+ * @param {Array|Object} Data to display
+ * @param {Object} Options
+ *
+ */
+ initialize: function(element, data, options) {
+ options = options || {};
+ this.element = element;
+ this.data_sets = this.buildDataSets(data, options);
+ this.flat_data = this.flatten(data);
+ this.data_size = this.longestDataSetLength();
+ this.plottedCoords = [];
+
+ /* If one colour is specified, map it to a compatible set */
+ if (options && options.colour) {
+ options.colours = {};
+ for (var key in this.data_sets) {
+ if (this.data_sets.hasOwnProperty(key))
+ options.colours[key] = options.colour;
+ }
+ }
+
+ this.options = {
+ width: parseInt(getStyle(element, 'width'), 10),
+ height: parseInt(getStyle(element, 'height'), 10),
+ labels: this.makeRange(1, this.data_size + 1), // Label data
+ plot_padding: 10, // Padding for the graph line/bar plots
+ font_size: 10, // Label font size
+ show_horizontal_labels: true,
+ show_vertical_labels: true,
+ background_colour: getStyle(element, 'backgroundColor') || '#ffffff',
+ label_colour: '#666', // Label text colour
+ markers: false, // false, circle
+ marker_size: 5,
+ meanline: false,
+ grid: false,
+ grid_colour: '#ccc',
+ y_padding_top: 20,
+ draw: true,
+ bar_labels: false // Display values on the top of bars
+ };
+
+ Helpers.extend(this.options, this.chartDefaults() || {});
+ Helpers.extend(this.options, options);
+
+ this.font_options = { 'font': this.options.font_size + 'px "Arial"', stroke: 'none', fill: '#000' };
+ this.normaliser = new Ico.Normaliser(this.flat_data, this.normaliserOptions());
+ this.label_step = options.label_step || this.normaliser.step;
+
+ this.range = this.normaliser.range;
+ this.start_value = options.start_value || this.normaliser.start_value;
+
+ /* Padding around the graph area to make room for labels */
+ this.x_padding_left = 10 + this.paddingLeftOffset();
+ this.x_padding_right = 20;
+ this.x_padding = this.x_padding_left + this.x_padding_right;
+ this.y_padding_top = this.options.y_padding_top;
+
+ this.y_padding_bottom = 20 + this.paddingBottomOffset();
+ this.y_padding = this.y_padding_top + this.y_padding_bottom;
+
+ this.graph_width = this.options.width - (this.x_padding);
+ this.graph_height = this.options.height - (this.y_padding);
+
+ this.step = this.calculateStep();
+
+ if (this.options.bar_labels) {
+ // TODO: Improve this so extra padding is used instead
+ this.range += this.normaliser.step;
+ }
+
+ /* Calculate how many labels are required */
+ if (options.label_count) {
+ this.y_label_count = options.label_count;
+ } else {
+ this.y_label_count = Math.ceil(this.range / this.label_step);
+ if ((this.normaliser.min + (this.y_label_count * this.normaliser.step)) < this.normaliser.max) {
+ this.y_label_count += 1;
+ }
+ }
+ this.value_labels = this.makeValueLabels(this.y_label_count);
+ this.top_value = this.value_labels[this.value_labels.length - 1];
+
+ /* Grid control options */
+ this.grid_start_offset = -1;
+
+ /* Drawing */
+ if (this.options.draw) {
+ if (typeof this.options.colours === 'undefined') {
+ this.options.colours = this.makeRandomColours();
+ }
+
+ this.paper = Raphael(this.element, this.options.width, this.options.height);
+ this.background = this.paper.rect(0, 0, this.options.width, this.options.height);
+ this.background.attr({fill: this.options.background_colour, stroke: 'none' });
+
+ if (this.options.meanline === true) {
+ this.options.meanline = { 'stroke-width': '2px', stroke: '#BBBBBB' };
+ }
+
+ this.setChartSpecificOptions();
+ this.lastPoint = { x: 0, y: 0 };
+ this.draw();
+ }
+ },
+
+ buildDataSets: function(data, options) {
+ return (typeof data.length !== 'undefined') ? { 'one': data } : data;
+ },
+
+ normaliserOptions: function() {
+ return {};
+ },
+
+ chartDefaults: function() {
+ /* Define in child class */
+ return {};
+ },
+
+ drawPlot: function(index, pathString, x, y, colour) {
+ /* Define in child class */
+ },
+
+ calculateStep: function() {
+ /* Define in child classes */
+ },
+
+ makeRandomColours: function() {
+ var colours = {};
+ for (var key in this.data_sets) {
+ if (!colours.hasOwnProperty(key))
+ colours[key] = Raphael.hsb2rgb(Math.random(), 1, 0.75).hex;
+ }
+ return colours;
+ },
+
+ longestDataSetLength: function() {
+ if (this.grouped) {
+ // Return the total number of grouped values rather than
+ // the longest array of data
+ return this.flat_data.length;
+ }
+
+ var length = 0;
+ for (var key in this.data_sets) {
+ if (this.data_sets.hasOwnProperty(key)) {
+ length = this.data_sets[key].length > length ? this.data_sets[key].length : length;
+ }
+ }
+ return length;
+ },
+
+ roundValue: function(value, length) {
+ var multiplier = Math.pow(10, length);
+ value *= multiplier;
+ value = Math.round(value) / multiplier;
+ return value;
+ },
+
+ roundValues: function(data, length) {
+ var roundedData = [], i;
+ for (i = 0; i < data.length; i++) {
+ roundedData.push(this.roundValue(data[i], length));
+ }
+ return roundedData;
+ },
+
+ longestLabel: function(values) {
+ var labels = Array.prototype.slice.call(values || this.options.labels, 0);
+ if (labels.length) {
+ return labels.sort(function(a, b) { return a.toString().length < b.toString().length; })[0].toString().length;
+ }
+ return 0;
+ },
+
+ paddingLeftOffset: function() {
+ /* Find the longest label and multiply it by the font size */
+ var data = this.roundValues(this.flat_data, 2),
+ longest_label_length = 0;
+
+ longest_label_length = data.sort(function(a, b) {
+ return a.toString().length < b.toString().length;
+ })[0].toString().length;
+
+ longest_label_length = longest_label_length > 2 ? longest_label_length - 1 : longest_label_length;
+ return 10 + (longest_label_length * this.options.font_size);
+ },
+
+ paddingBottomOffset: function() {
+ /* Find the longest label and multiply it by the font size */
+ return this.options.font_size;
+ },
+
+ normalise: function(value) {
+ var total = this.start_value === 0 ? this.top_value : this.range;
+ return ((value / total) * (this.graph_height));
+ },
+
+ draw: function() {
+ if (this.options.grid) {
+ this.drawGrid();
+ }
+
+ if (this.options.meanline) {
+ this.drawMeanLine(this.normaliseData(this.flat_data));
+ }
+
+ this.drawAxis();
+
+ if (this.options.show_vertical_labels) {
+ this.drawVerticalLabels();
+ }
+
+ if (this.options.show_horizontal_labels) {
+ this.drawHorizontalLabels();
+ }
+
+ if (this.grouped) {
+ this.drawLines(key, null, this.normaliseData(this.flat_data));
+ } else {
+ for (var key in this.data_sets) {
+ if (this.data_sets.hasOwnProperty(key)) {
+ var data = this.data_sets[key];
+ this.drawLines(key, this.options.colours[key], this.normaliseData(data));
+ }
+ }
+ }
+
+ if (this.start_value !== 0) {
+ this.drawFocusHint();
+ }
+ },
+
+ drawGrid: function() {
+ var pathString = '', i, y;
+
+ if (this.options.show_vertical_labels) {
+ y = this.graph_height + this.y_padding_top;
+ for (i = 0; i < this.y_label_count; i++) {
+ y = y - (this.graph_height / this.y_label_count);
+ pathString += 'M' + this.x_padding_left + ',' + y;
+ pathString += 'L' + (this.x_padding_left + this.graph_width) + ',' + y;
+ }
+ }
+
+ if (this.options.show_horizontal_labels) {
+ var x = this.x_padding_left + this.options.plot_padding + this.grid_start_offset,
+ x_labels = this.grouped ? this.flat_data.length : this.options.labels.length,
+ i;
+
+ for (i = 0; i < x_labels; i++) {
+ pathString += 'M' + x + ',' + this.y_padding_top;
+ pathString += 'L' + x +',' + (this.y_padding_top + this.graph_height);
+ x = x + this.step;
+ }
+
+ x = x - this.options.plot_padding - 1;
+ pathString += 'M' + x + ',' + this.y_padding_top;
+ pathString += 'L' + x + ',' + (this.y_padding_top + this.graph_height);
+ }
+
+ this.paper.path(pathString).attr({ stroke: this.options.grid_colour, 'stroke-width': '1px' });
+ },
+
+ drawLines: function(label, colour, data) {
+ var coords = this.calculateCoords(data),
+ pathString = '',
+ i, x, y;
+
+ for (i = 0; i < coords.length; i++) {
+ x = coords[i][0] || 0;
+ y = coords[i][1] || 0;
+
+ if (this.grouped && this.options.colours) {
+ colour = this.options.colours[i % this.group_size];
+ }
+
+ this.plottedCoords.push([x + this.bar_padding, y]);
+ pathString = this.drawPlot(i, pathString, x, y, colour);
+ }
+
+ this.paper.path(pathString).attr({ stroke: colour, 'stroke-width': '3px' });
+
+ if (this.options.bar_labels) {
+ this.drawBarMarkers();
+ }
+ },
+
+ calculateCoords: function(data) {
+ var x = this.x_padding_left + this.options.plot_padding - this.step,
+ y_offset = (this.graph_height + this.y_padding_top) + this.normalise(this.start_value),
+ y = 0,
+ coords = [],
+ i;
+
+ for (i = 0; i < data.length; i++) {
+ y = y_offset - data[i];
+ x = x + this.step;
+ coords.push([x, y]);
+ }
+ return coords;
+ },
+
+ drawFocusHint: function() {
+ var length = 5,
+ x = this.x_padding_left + (length / 2) - 1,
+ y = this.options.height - this.y_padding_bottom,
+ pathString = '';
+
+ pathString += 'M' + x + ',' + y;
+ pathString += 'L' + (x - length) + ',' + (y - length);
+ pathString += 'M' + x + ',' + (y - length);
+ pathString += 'L' + (x - length) + ',' + (y - (length * 2));
+ this.paper.path(pathString).attr({ stroke: this.options.label_colour, 'stroke-width': 2 });
+ },
+
+ drawMeanLine: function(data) {
+ var offset = Helpers.sum(data) / data.length,
+ pathString = '';
+
+ pathString += 'M' + (this.x_padding_left - 1) + ',' + (this.options.height - this.y_padding_bottom - offset);
+ pathString += 'L' + (this.graph_width + this.x_padding_left) + ',' + (this.options.height - this.y_padding_bottom - offset);
+ this.paper.path(pathString).attr(this.options.meanline);
+ },
+
+ drawAxis: function() {
+ var pathString = '';
+
+ pathString += 'M' + (this.x_padding_left - 1) + ',' + (this.options.height - this.y_padding_bottom);
+ pathString += 'L' + (this.graph_width + this.x_padding_left) + ',' + (this.options.height - this.y_padding_bottom);
+ pathString += 'M' + (this.x_padding_left - 1) + ',' + (this.options.height - this.y_padding_bottom);
+ pathString += 'L' + (this.x_padding_left - 1) + ',' + (this.y_padding_top);
+
+ this.paper.path(pathString).attr({ stroke: this.options.label_colour });
+ },
+
+ makeValueLabels: function(steps) {
+ var step = this.label_step,
+ label = this.start_value,
+ labels = [];
+
+ for (var i = 0; i < steps; i++) {
+ label = this.roundValue((label + step), 2);
+ labels.push(label);
+ }
+ return labels;
+ },
+
+ /* Axis label markers */
+ drawMarkers: function(labels, direction, step, start_offset, font_offsets, extra_font_options) {
+ function x_offset(value) {
+ return value * direction[0];
+ }
+
+ function y_offset(value) {
+ return value * direction[1];
+ }
+
+ /* Start at the origin */
+ var x = this.x_padding_left - 1 + x_offset(start_offset),
+ y = this.options.height - this.y_padding_bottom + y_offset(start_offset),
+ pathString = '',
+ i,
+ font_options = {};
+ Helpers.extend(font_options, this.font_options);
+ Helpers.extend(font_options, extra_font_options || {});
+
+ for (i = 0; i < labels.length; i++) {
+ pathString += 'M' + x + ',' + y;
+ if (typeof labels[i] !== 'undefined' && (labels[i] + '').length > 0) {
+ pathString += 'L' + (x + y_offset(5)) + ',' + (y + x_offset(5));
+ this.paper.text(x + font_offsets[0], y - font_offsets[1], labels[i]).attr(font_options).toFront();
+ }
+ x = x + x_offset(step);
+ y = y + y_offset(step);
+ }
+
+ this.paper.path(pathString).attr({ stroke: this.options.label_colour });
+ },
+
+ drawVerticalLabels: function() {
+ var y_step = this.graph_height / this.y_label_count;
+ this.drawMarkers(this.value_labels, [0, -1], y_step, y_step, [-8, -2], { 'text-anchor': 'end' });
+ },
+
+ drawHorizontalLabels: function() {
+ this.drawMarkers(this.options.labels, [1, 0], this.step, this.options.plot_padding, [0, (this.options.font_size + 7) * -1]);
+ }
+});
+/**
+ * The BarGraph class.
+ *
+ * Example:
+ *
+ * new Ico.BarGraph($('bargraph'), [100, 10, 90, 20, 80, 30]);
+ *
+ */
+Ico.BarGraph = function() { this.initialize.apply(this, arguments); };
+Helpers.extend(Ico.BarGraph.prototype, Ico.BaseGraph.prototype);
+Helpers.extend(Ico.BarGraph.prototype, {
+ // Overridden to handle grouped bar graphs
+ buildDataSets: function(data, options) {
+ if (typeof data.length !== 'undefined') {
+ if (typeof data[0].length !== 'undefined') {
+ this.grouped = true;
+ // TODO: Find longest?
+ this.group_size = data[0].length;
+ var o = {}, k, i = 0;
+ for (k in options.labels) {
+ k = options.labels[k];
+ o[k] = data[i];
+ i++;
+ }
+ return o;
+ } else {
+ return { 'one': data };
+ }
+ } else {
+ return data;
+ }
+ },
+
+ /**
+ * Sensible defaults for BarGraph.
+ */
+ chartDefaults: function() {
+ return { plot_padding: 0 };
+ },
+
+ /**
+ * Ensures the normalises is always 0.
+ */
+ normaliserOptions: function() {
+ return { start_value: 0 };
+ },
+
+ /**
+ * Options specific to BarGraph.
+ */
+ setChartSpecificOptions: function() {
+ this.bar_padding = this.options.bar_padding || 5;
+ this.bar_width = this.options.bar_size || this.calculateBarWidth();
+
+ if (this.options.bar_size && !this.options.bar_padding) {
+ this.bar_padding = this.graph_width / this.data_size;
+ }
+
+ this.options.plot_padding = (this.bar_width / 2) - (this.bar_padding / 2);
+ this.step = this.calculateStep();
+ this.grid_start_offset = this.bar_padding - 1;
+ this.start_y = this.options.height - this.y_padding_bottom;
+ },
+
+ /**
+ * Calculates the width of each bar.
+ *
+ * @returns {Integer} The bar width
+ */
+ calculateBarWidth: function() {
+ var width = (this.graph_width / this.data_size) - this.bar_padding;
+
+ if (this.grouped) {
+ //width = width / this.group_size - (this.bar_padding * this.group_size);
+ }
+
+ if (this.options.max_bar_size && width > this.options.max_bar_size) {
+ width = this.options.max_bar_size;
+ this.bar_padding = this.graph_width / this.data_size;
+ }
+
+ return width;
+ },
+
+ /**
+ * Calculates step used to move from one bar to another.
+ *
+ * @returns {Float} The start value
+ */
+ calculateStep: function() {
+ return (this.graph_width - (this.options.plot_padding * 2) - (this.bar_padding * 2)) / validStepDivider(this.data_size);
+ },
+
+ /**
+ * Generates paths for Raphael.
+ *
+ * @param {Integer} index The index of the data value to plot
+ * @param {String} pathString The pathString so far
+ * @param {Integer} x The x-coord to plot
+ * @param {Integer} y The y-coord to plot
+ * @param {String} colour A string that represents a colour
+ * @returns {String} The resulting path string
+ */
+ drawPlot: function(index, pathString, x, y, colour) {
+ x = x + this.bar_padding;
+ pathString += 'M' + x + ',' + this.start_y;
+ pathString += 'L' + x + ',' + y;
+ this.paper.path(pathString).attr({ stroke: colour, 'stroke-width': this.bar_width + 'px' });
+ pathString = '';
+ x = x + this.step;
+ pathString += 'M' + x + ',' + this.start_y;
+ return pathString;
+ },
+
+ /* Change the standard options to correctly offset against the bars */
+ drawHorizontalLabels: function() {
+ var x_start = this.bar_padding + this.options.plot_padding,
+ step = this.step;
+ if (this.grouped) {
+ step = step * this.group_size;
+ x_start = ((this.bar_width * this.group_size) + (this.bar_padding * this.group_size)) / 2
+ x_start = this.roundValue(x_start, 0);
+ }
+ this.drawMarkers(this.options.labels, [1, 0], step, x_start, [0, (this.options.font_size + 7) * -1]);
+ },
+
+ drawBarMarkers: function() {
+ if (this.plottedCoords.length === 0) {
+ return;
+ }
+
+ var i, length = this.flat_data.length, x, y, label, font_options = {};
+ Helpers.extend(font_options, this.font_options);
+ font_options['text-anchor'] = 'center';
+
+ for (i = 0; i < length; i++) {
+ label = this.roundValue(this.flat_data[i], 2).toString();
+ x = this.plottedCoords[i][0];
+ y = this.roundValue(this.plottedCoords[i][1], 0);
+ this.paper.text(x, y - this.options.font_size, label).attr(font_options).toFront();
+ }
+ }
+});
+
+/**
+ * Draws horizontal bar graphs.
+ *
+ * Example:
+ *
+ * new Ico.HorizontalBarGraph(element,
+ * [2, 5, 1, 10, 15, 33, 20, 25, 1],
+ * { font_size: 14 });
+ *
+ */
+Ico.HorizontalBarGraph = function() { this.initialize.apply(this, arguments); };
+Helpers.extend(Ico.HorizontalBarGraph.prototype, Ico.BaseGraph.prototype);
+Helpers.extend(Ico.HorizontalBarGraph.prototype, {
+ setChartSpecificOptions: function() {
+ // Approximate the width required by the labels
+ this.y_padding_top = 0;
+ this.x_padding_left = 20 + this.longestLabel() * (this.options.font_size / 2);
+ this.bar_padding = this.options.bar_padding || 5;
+ this.bar_width = this.options.bar_size || this.calculateBarHeight();
+
+ if (this.options.bar_size && !this.options.bar_padding) {
+ this.bar_padding = this.graph_height / this.data_size;
+ }
+
+ this.options.plot_padding = 0;
+ this.step = this.calculateStep();
+ },
+
+ normalise: function(value) {
+ var offset = this.x_padding_left;
+ return ((value / this.range) * (this.graph_width - offset));
+ },
+
+ /* Height */
+ calculateBarHeight: function() {
+ var height = (this.graph_height / this.data_size) - this.bar_padding;
+
+ if (this.options.max_bar_size && height > this.options.max_bar_size) {
+ height = this.options.max_bar_size;
+ this.bar_padding = this.graph_height / this.data_size;
+ }
+
+ return height;
+ },
+
+ calculateStep: function() {
+ return (this.options.height - this.y_padding_bottom) / validStepDivider(this.data_size);
+ },
+
+ drawLines: function(label, colour, data) {
+ var x = this.x_padding_left + (this.options.plot_padding * 2),
+ y = this.options.height - this.y_padding_bottom - (this.step / 2),
+ pathString = 'M' + x + ',' + y,
+ i;
+
+ for (i = 0; i < data.length; i++) {
+ pathString += 'L' + (x + data[i] - this.normalise(this.start_value)) + ',' + y;
+ y = y - this.step;
+ pathString += 'M' + x + ',' + y;
+ }
+ this.paper.path(pathString).attr({stroke: colour, 'stroke-width': this.bar_width + 'px'});
+ },
+
+ /* Horizontal version */
+ drawFocusHint: function() {
+ var length = 5,
+ x = this.x_padding_left + (this.step * 2),
+ y = this.options.height - this.y_padding_bottom,
+ pathString = '';
+
+ pathString += 'M' + x + ',' + y;
+ pathString += 'L' + (x - length) + ',' + (y + length);
+ pathString += 'M' + (x - length) + ',' + y;
+ pathString += 'L' + (x - (length * 2)) + ',' + (y + length);
+ this.paper.path(pathString).attr({stroke: this.options.label_colour, 'stroke-width': 2});
+ },
+
+ drawVerticalLabels: function() {
+ var y_start = (this.step / 2) - (this.options.plot_padding * 2);
+ this.drawMarkers(this.options.labels, [0, -1], this.step, y_start, [-8, (this.options.font_size / 8)], { 'text-anchor': 'end' });
+ },
+
+ drawHorizontalLabels: function() {
+ var x_step = this.graph_width / this.y_label_count,
+ x_labels = this.makeValueLabels(this.y_label_count);
+ this.drawMarkers(x_labels, [1, 0], x_step, x_step, [0, (this.options.font_size + 7) * -1]);
+ }
+});
+
+/**
+ * Draws line graphs.
+ *
+ * Example:
+ *
+ * new Ico.LineGraph(element, [10, 5, 22, 44, 4]);
+ *
+ */
+Ico.LineGraph = function() { this.initialize.apply(this, arguments); };
+Helpers.extend(Ico.LineGraph.prototype, Ico.BaseGraph.prototype);
+Helpers.extend(Ico.LineGraph.prototype, {
+ normalise: function(value) {
+ var total = this.start_value === 0 ? this.top_value : this.top_value - this.start_value;
+ return ((value / total) * (this.graph_height));
+ },
+
+ chartDefaults: function() {
+ return { plot_padding: 10 };
+ },
+
+ setChartSpecificOptions: function() {
+ // Approximate the width required by the labels
+ var longestLabel = this.longestLabel(this.value_labels);
+ this.x_padding_left = 30 + longestLabel * (this.options.font_size / 2);
+
+ if (typeof this.options.curve_amount === 'undefined') {
+ this.options.curve_amount = 10;
+ }
+ },
+
+ normaliserOptions: function() {
+ return { start_value: this.options.start_value };
+ },
+
+ calculateStep: function() {
+ return (this.graph_width - (this.options.plot_padding * 2)) / validStepDivider(this.data_size);
+ },
+
+ startPlot: function(pathString, x, y, colour) {
+ this.lastPoint = { x: x, y: y };
+ return pathString + 'M' + x + ',' + y;
+ },
+
+ drawPlot: function(index, pathString, x, y, colour) {
+ var w = this.options.curve_amount;
+
+ if (this.options.markers === 'circle') {
+ var circle = this.paper.circle(x, y, this.options.marker_size);
+ circle.attr({ 'stroke-width': '1px', stroke: this.options.background_colour, fill: colour });
+ }
+
+ if (index === 0) {
+ return this.startPlot(pathString, x, y, colour);
+ }
+
+ if (w) {
+ pathString += ['C', this.lastPoint.x + w, this.lastPoint.y, x - w, y, x, y];
+ } else {
+ pathString += 'L' + x + ',' + y;
+ }
+
+ this.lastPoint = { x: x, y: y };
+ return pathString;
+ }
+});
+
+/**
+ * Draws spark line graphs.
+ *
+ * Example:
+ *
+ * new Ico.SparkLine(element,
+ * [21, 41, 32, 1, 10, 5, 32, 10, 23],
+ * { width: 30, height: 14,
+ * background_colour: '#ccc' });
+ *
+ *
+ */
+Ico.SparkLine = function() { this.initialize.apply(this, arguments); };
+Ico.SparkLine.prototype = {
+ initialize: function(element, data, options) {
+ this.element = element;
+ this.data = data;
+ this.options = {
+ width: parseInt(getStyle(element, 'width'), 10),
+ height: parseInt(getStyle(element, 'height'), 10),
+ highlight: false,
+ background_colour: getStyle(element, 'backgroundColor') || '#ffffff',
+ colour: '#036'
+ };
+ Helpers.extend(this.options, options || { });
+
+ this.step = this.calculateStep();
+ this.paper = Raphael(this.element, this.options.width, this.options.height);
+
+ if (this.options.acceptable_range) {
+ this.background = this.paper.rect(0, this.options.height - this.normalise(this.options.acceptable_range[1]),
+ this.options.width,
+ this.options.height - this.normalise(this.options.acceptable_range[0]));
+ } else {
+ this.background = this.paper.rect(0, 0, this.options.width, this.options.height);
+ }
+
+ this.background.attr({fill: this.options.background_colour, stroke: 'none' });
+ this.draw();
+ },
+
+ calculateStep: function() {
+ return this.options.width / validStepDivider(this.data.length);
+ },
+
+ normalise: function(value) {
+ return (this.options.height / Helpers.max(this.data)) * value;
+ },
+
+ draw: function() {
+ var data = this.normaliseData(this.data);
+ this.drawLines('', this.options.colour, data);
+
+ if (this.options.highlight) {
+ this.showHighlight(data);
+ }
+ },
+
+ drawLines: function(label, colour, data) {
+ var pathString = '',
+ x = 0,
+ values = data.slice(1),
+ i = 0;
+
+ pathString = 'M0,' + (this.options.height - data[0]);
+ for (i = 1; i < data.length; i++) {
+ x = x + this.step;
+ pathString += 'L' + x +',' + Ico.round(this.options.height - data[i], 2);
+ }
+ this.paper.path(pathString).attr({stroke: colour});
+ this.lastPoint = { x: 0, y: this.options.height - data[0] };
+ },
+
+ showHighlight: function(data) {
+ var size = 2,
+ x = this.options.width - size,
+ i = this.options.highlight.index || data.length - 1,
+ y = data[i] + (Math.round(size / 2));
+
+ if (typeof(this.options.highlight.index) !== 'undefined') {
+ x = this.step * this.options.highlight.index;
+ }
+
+ var circle = this.paper.circle(x, this.options.height - y, size);
+ circle.attr({ stroke: false, fill: this.options.highlight.colour});
+ }
+};
+Helpers.extend(Ico.SparkLine.prototype, Ico.Base);
+
+/**
+ * Draws spark bar graphs.
+ *
+ * Example:
+ *
+ * new Ico.SparkBar($('sparkline_2'),
+ * [1, 5, 10, 15, 20, 15, 10, 15, 30, 15, 10],
+ * { width: 30, height: 14, background_colour: '#ccc' });
+ *
+ */
+Ico.SparkBar = function() { this.initialize.apply(this, arguments); };
+Helpers.extend(Ico.SparkBar.prototype, Ico.SparkLine.prototype);
+Helpers.extend(Ico.SparkBar.prototype, {
+ calculateStep: function() {
+ return this.options.width / validStepDivider(this.data.length);
+ },
+
+ drawLines: function(label, colour, data) {
+ var width = this.step > 2 ? this.step - 1 : this.step,
+ x = width,
+ pathString = '',
+ i = 0;
+ for (i = 0; i < data.length; i++) {
+ pathString += 'M' + x + ',' + (this.options.height - data[i]);
+ pathString += 'L' + x + ',' + this.options.height;
+ x = x + this.step;
+ }
+ this.paper.path(pathString).attr({ stroke: colour, 'stroke-width': width });
+ }
+});
+
+ /**
+ * Assign the Ico object as a global property.
+ */
+ global.Ico = Ico;
+
+ if (typeof exports !== 'undefined') {
+ module.exports = Ico;
+ }
+}(typeof window === 'undefined' ? this : window));
+
View
7 docs/ico.min.js
@@ -0,0 +1,7 @@
+/*!
+ * Ico
+ * Copyright (C) 2009-2011 Alex R. Young
+ * MIT Licensed
+ *//**
+ * The Ico object.
+ */(function(global){var Ico={VERSION:"0.3.5",round:function(num,dec){var result=Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);return result}};function validStepDivider(value){return value>1?value-1:1}function getStyle(el,styleProp){if(typeof window=="undefined")return;var style;return el.currentStyle?style=el.currentStyle[styleProp]:window.getComputedStyle&&(style=document.defaultView.getComputedStyle(el,null).getPropertyValue(styleProp)),style&&style.length===0&&(style=null),style}var Helpers={};Helpers.sum=function(a){var i,sum;for(i=0,sum=0;i<a.length;sum+=a[i++]);return sum},typeof Array.prototype.max=="undefined"?Helpers.max=function(a){return Math.max.apply({},a)}:Helpers.max=function(a){return a.max()},typeof Array.prototype.min=="undefined"?Helpers.min=function(a){return Math.min.apply({},a)}:Helpers.min=function(a){return a.min()},Helpers.mean=function(a){return Helpers.sum(a)/a.length},Helpers.variance=function(a){var mean=Helpers.mean(a),variance=0;for(var i=0;i<a.length;i++)variance+=Math.pow(a[i]-mean,2);return variance/(a.length-1)},Helpers.standard_deviation=function(a){return Math.sqrt(Helpers.variance(a))},typeof Object.extend=="undefined"?Helpers.extend=function(destination,source){for(var property in source)source.hasOwnProperty(property)&&(destination[property]=source[property]);return destination}:Helpers.extend=Object.extend,Object.keys?Helpers.keys=Object.keys:Helpers.keys=function(o){if(o!==Object(o))throw new TypeError("Object.keys called on non-object");var ret=[],p;for(p in o)Object.prototype.hasOwnProperty.call(o,p)&&ret.push(p);return ret},Ico.Normaliser=function(data,options){this.options={start_value:null},typeof options!="undefined"&&(this.options=options),this.min=Helpers.min(data),this.max=Helpers.max(data),this.standard_deviation=Helpers.standard_deviation(data),this.range=0,this.step=this.labelStep(this.max-this.min),this.start_value=this.calculateStart(),this.process()},Ico.Normaliser.prototype={calculateStart:function(){var min=typeof this.options.start_value!="undefined"&&this.min>=0?this.options.start_value:this.min,start_value=this.round(min,1);return this.min>0&&start_value>this.min?0:this.min===this.max?0:start_value},round:function(value,offset){offset=offset||1;var roundedValue=value;if(this.standard_deviation>.1){var multiplier=Math.pow(10,-offset);roundedValue=Math.round(value*multiplier)/multiplier;if(roundedValue>this.min)return this.round(value-this.step)}return roundedValue},process:function(){this.range=this.max-this.start_value,this.step=this.labelStep(this.range)},labelStep:function(value){return Math.pow(10,Math.round(Math.log(value)/Math.LN10)-1)}},Ico.Base={normaliseData:function(data){var values=[],i=0;for(i=0;i<data.length;i++)values.push(this.normalise(data[i]));return values},flatten:function(data){var flat_data=[];if(typeof data.length=="undefined"){if(typeof data!="object")return[];for(var key in data)data.hasOwnProperty(key)&&(flat_data=flat_data.concat(this.flatten(data[key])))}for(var i=0;i<data.length;i++)typeof data[i].length=="number"?flat_data=flat_data.concat(this.flatten(data[i])):flat_data.push(data[i]);return flat_data},makeRange:function(start,end){var values=[],i;for(i=start;i<end;i++)values.push(i);return values}},Ico.BaseGraph=function(){this.initialize.apply(this,arguments)},Helpers.extend(Ico.BaseGraph.prototype,Ico.Base),Helpers.extend(Ico.BaseGraph.prototype,{initialize:function(element,data,options){options=options||{},this.element=element,this.data_sets=this.buildDataSets(data,options),this.flat_data=this.flatten(data),this.data_size=this.longestDataSetLength(),this.plottedCoords=[];if(options&&options.colour){options.colours={};for(var key in this.data_sets)this.data_sets.hasOwnProperty(key)&&(options.colours[key]=options.colour)}this.options={width:parseInt(getStyle(element,"width"),10),height:parseInt(getStyle(element,"height"),10),labels:this.makeRange(1,this.data_size+1),plot_padding:10,font_size:10,show_horizontal_labels:!0,show_vertical_labels:!0,background_colour:getStyle(element,"backgroundColor")||"#ffffff",label_colour:"#666",markers:!1,marker_size:5,meanline:!1,grid:!1,grid_colour:"#ccc",y_padding_top:20,draw:!0,bar_labels:!1},Helpers.extend(this.options,this.chartDefaults()||{}),Helpers.extend(this.options,options),this.font_options={font:this.options.font_size+'px "Arial"',stroke:"none",fill:"#000"},this.normaliser=new Ico.Normaliser(this.flat_data,this.normaliserOptions()),this.label_step=options.label_step||this.normaliser.step,this.range=this.normaliser.range,this.start_value=options.start_value||this.normaliser.start_value,this.x_padding_left=10+this.paddingLeftOffset(),this.x_padding_right=20,this.x_padding=this.x_padding_left+this.x_padding_right,this.y_padding_top=this.options.y_padding_top,this.y_padding_bottom=20+this.paddingBottomOffset(),this.y_padding=this.y_padding_top+this.y_padding_bottom,this.graph_width=this.options.width-this.x_padding,this.graph_height=this.options.height-this.y_padding,this.step=this.calculateStep(),this.options.bar_labels&&(this.range+=this.normaliser.step),options.label_count?this.y_label_count=options.label_count:(this.y_label_count=Math.ceil(this.range/this.label_step),this.normaliser.min+this.y_label_count*this.normaliser.step<this.normaliser.max&&(this.y_label_count+=1)),this.value_labels=this.makeValueLabels(this.y_label_count),this.top_value=this.value_labels[this.value_labels.length-1],this.grid_start_offset=-1,this.options.draw&&(typeof this.options.colours=="undefined"&&(this.options.colours=this.makeRandomColours()),this.paper=Raphael(this.element,this.options.width,this.options.height),this.background=this.paper.rect(0,0,this.options.width,this.options.height),this.background.attr({fill:this.options.background_colour,stroke:"none"}),this.options.meanline===!0&&(this.options.meanline={"stroke-width":"2px",stroke:"#BBBBBB"}),this.setChartSpecificOptions(),this.lastPoint={x:0,y:0},this.draw())},buildDataSets:function(data,options){return typeof data.length!="undefined"?{one:data}:data},normaliserOptions:function(){return{}},chartDefaults:function(){return{}},drawPlot:function(index,pathString,x,y,colour){},calculateStep:function(){},makeRandomColours:function(){var colours={};for(var key in this.data_sets)colours.hasOwnProperty(key)||(colours[key]=Raphael.hsb2rgb(Math.random(),1,.75).hex);return colours},longestDataSetLength:function(){if(this.grouped)return this.flat_data.length;var length=0;for(var key in this.data_sets)this.data_sets.hasOwnProperty(key)&&(length=this.data_sets[key].length>length?this.data_sets[key].length:length);return length},roundValue:function(value,length){var multiplier=Math.pow(10,length);return value*=multiplier,value=Math.round(value)/multiplier,value},roundValues:function(data,length){var roundedData=[],i;for(i=0;i<data.length;i++)roundedData.push(this.roundValue(data[i],length));return roundedData},longestLabel:function(values){var labels=Array.prototype.slice.call(values||this.options.labels,0);return labels.length?labels.sort(function(a,b){return a.toString().length<b.toString().length})[0].toString().length:0},paddingLeftOffset:function(){var data=this.roundValues(this.flat_data,2),longest_label_length=0;return longest_label_length=data.sort(function(a,b){return a.toString().length<b.toString().length})[0].toString().length,longest_label_length=longest_label_length>2?longest_label_length-1:longest_label_length,10+longest_label_length*this.options.font_size},paddingBottomOffset:function(){return this.options.font_size},normalise:function(value){var total=this.start_value===0?this.top_value:this.range;return value/total*this.graph_height},draw:function(){this.options.grid&&this.drawGrid(),this.options.meanline&&this.drawMeanLine(this.normaliseData(this.flat_data)),this.drawAxis(),this.options.show_vertical_labels&&this.drawVerticalLabels(),this.options.show_horizontal_labels&&this.drawHorizontalLabels();if(this.grouped)this.drawLines(key,null,this.normaliseData(this.flat_data));else for(var key in this.data_sets)if(this.data_sets.hasOwnProperty(key)){var data=this.data_sets[key];this.drawLines(key,this.options.colours[key],this.normaliseData(data))}this.start_value!==0&&this.drawFocusHint()},drawGrid:function(){var pathString="",i,y;if(this.options.show_vertical_labels){y=this.graph_height+this.y_padding_top;for(i=0;i<this.y_label_count;i++)y-=this.graph_height/this.y_label_count,pathString+="M"+this.x_padding_left+","+y,pathString+="L"+(this.x_padding_left+this.graph_width)+","+y}if(this.options.show_horizontal_labels){var x=this.x_padding_left+this.options.plot_padding+this.grid_start_offset,x_labels=this.grouped?this.flat_data.length:this.options.labels.length,i;for(i=0;i<x_labels;i++)pathString+="M"+x+","+this.y_padding_top,pathString+="L"+x+","+(this.y_padding_top+this.graph_height),x+=this.step;x=x-this.options.plot_padding-1,pathString+="M"+x+","+this.y_padding_top,pathString+="L"+x+","+(this.y_padding_top+this.graph_height)}this.paper.path(pathString).attr({stroke:this.options.grid_colour,"stroke-width":"1px"})},drawLines:function(label,colour,data){var coords=this.calculateCoords(data),pathString="",i,x,y;for(i=0;i<coords.length;i++)x=coords[i][0]||0,y=coords[i][1]||0,this.grouped&&this.options.colours&&(colour=this.options.colours[i%this.group_size]),this.plottedCoords.push([x+this.bar_padding,y]),pathString=this.drawPlot(i,pathString,x,y,colour);this.paper.path(pathString).attr({stroke:colour,"stroke-width":"3px"}),this.options.bar_labels&&this.drawBarMarkers()},calculateCoords:function(data){var x=this.x_padding_left+this.options.plot_padding-this.step,y_offset=this.graph_height+this.y_padding_top+this.normalise(this.start_value),y=0,coords=[],i;for(i=0;i<data.length;i++)y=y_offset-data[i],x+=this.step,coords.push([x,y]);return coords},drawFocusHint:function(){var length=5,x=this.x_padding_left+length/2-1,y=this.options.height-this.y_padding_bottom,pathString="";pathString+="M"+x+","+y,pathString+="L"+(x-length)+","+(y-length),pathString+="M"+x+","+(y-length),pathString+="L"+(x-length)+","+(y-length*2),this.paper.path(pathString).attr({stroke:this.options.label_colour,"stroke-width":2})},drawMeanLine:function(data){var offset=Helpers.sum(data)/data.length,pathString="";pathString+="M"+(this.x_padding_left-1)+","+(this.options.height-this.y_padding_bottom-offset),pathString+="L"+(this.graph_width+this.x_padding_left)+","+(this.options.height-this.y_padding_bottom-offset),this.paper.path(pathString).attr(this.options.meanline)},drawAxis:function(){var pathString="";pathString+="M"+(this.x_padding_left-1)+","+(this.options.height-this.y_padding_bottom),pathString+="L"+(this.graph_width+this.x_padding_left)+","+(this.options.height-this.y_padding_bottom),pathString+="M"+(this.x_padding_left-1)+","+(this.options.height-this.y_padding_bottom),pathString+="L"+(this.x_padding_left-1)+","+this.y_padding_top,this.paper.path(pathString).attr({stroke:this.options.label_colour})},makeValueLabels:function(steps){var step=this.label_step,label=this.start_value,labels=[];for(var i=0;i<steps;i++)label=this.roundValue(label+step,2),labels.push(label);return labels},drawMarkers:function(labels,direction,step,start_offset,font_offsets,extra_font_options){function x_offset(value){return value*direction[0]}function y_offset(value){return value*direction[1]}var x=this.x_padding_left-1+x_offset(start_offset),y=this.options.height-this.y_padding_bottom+y_offset(start_offset),pathString="",i,font_options={};Helpers.extend(font_options,this.font_options),Helpers.extend(font_options,extra_font_options||{});for(i=0;i<labels.length;i++)pathString+="M"+x+","+y,typeof labels[i]!="undefined"&&(labels[i]+"").length>0&&(pathString+="L"+(x+y_offset(5))+","+(y+x_offset(5)),this.paper.text(x+font_offsets[0],y-font_offsets[1],labels[i]).attr(font_options).toFront()),x+=x_offset(step),y+=y_offset(step);this.paper.path(pathString).attr({stroke:this.options.label_colour})},drawVerticalLabels:function(){var y_step=this.graph_height/this.y_label_count;this.drawMarkers(this.value_labels,[0,-1],y_step,y_step,[-8,-2],{"text-anchor":"end"})},drawHorizontalLabels:function(){this.drawMarkers(this.options.labels,[1,0],this.step,this.options.plot_padding,[0,(this.options.font_size+7)*-1])}}),Ico.BarGraph=function(){this.initialize.apply(this,arguments)},Helpers.extend(Ico.BarGraph.prototype,Ico.BaseGraph.prototype),Helpers.extend(Ico.BarGraph.prototype,{buildDataSets:function(data,options){if(typeof data.length!="undefined"){if(typeof data[0].length!="undefined"){this.grouped=!0,this.group_size=data[0].length;var o={},k,i=0;for(k in options.labels)k=options.labels[k],o[k]=data[i],i++;return o}return{one:data}}return data},chartDefaults:function(){return{plot_padding:0}},normaliserOptions:function(){return{start_value:0}},setChartSpecificOptions:function(){this.bar_padding=this.options.bar_padding||5,this.bar_width=this.options.bar_size||this.calculateBarWidth(),this.options.bar_size&&!this.options.bar_padding&&(this.bar_padding=this.graph_width/this.data_size),this.options.plot_padding=this.bar_width/2-this.bar_padding/2,this.step=this.calculateStep(),this.grid_start_offset=this.bar_padding-1,this.start_y=this.options.height-this.y_padding_bottom},calculateBarWidth:function(){var width=this.graph_width/this.data_size-this.bar_padding;return!this.grouped,this.options.max_bar_size&&width>this.options.max_bar_size&&(width=this.options.max_bar_size,this.bar_padding=this.graph_width/this.data_size),width},calculateStep:function(){return(this.graph_width-this.options.plot_padding*2-this.bar_padding*2)/validStepDivider(this.data_size)},drawPlot:function(index,pathString,x,y,colour){return x+=this.bar_padding,pathString+="M"+x+","+this.start_y,pathString+="L"+x+","+y,this.paper.path(pathString).attr({stroke:colour,"stroke-width":this.bar_width+"px"}),pathString="",x+=this.step,pathString+="M"+x+","+this.start_y,pathString},drawHorizontalLabels:function(){var x_start=this.bar_padding+this.options.plot_padding,step=this.step;this.grouped&&(step*=this.group_size,x_start=(this.bar_width*this.group_size+this.bar_padding*this.group_size)/2,x_start=this.roundValue(x_start,0)),this.drawMarkers(this.options.labels,[1,0],step,x_start,[0,(this.options.font_size+7)*-1])},drawBarMarkers:function(){if(this.plottedCoords.length===0)return;var i,length=this.flat_data.length,x,y,label,font_options={};Helpers.extend(font_options,this.font_options),font_options["text-anchor"]="center";for(i=0;i<length;i++)label=this.roundValue(this.flat_data[i],2).toString(),x=this.plottedCoords[i][0],y=this.roundValue(this.plottedCoords[i][1],0),this.paper.text(x,y-this.options.font_size,label).attr(font_options).toFront()}}),Ico.HorizontalBarGraph=function(){this.initialize.apply(this,arguments)},Helpers.extend(Ico.HorizontalBarGraph.prototype,Ico.BaseGraph.prototype),Helpers.extend(Ico.HorizontalBarGraph.prototype,{setChartSpecificOptions:function(){this.y_padding_top=0,this.x_padding_left=20+this.longestLabel()*(this.options.font_size/2),this.bar_padding=this.options.bar_padding||5,this.bar_width=this.options.bar_size||this.calculateBarHeight(),this.options.bar_size&&!this.options.bar_padding&&(this.bar_padding=this.graph_height/this.data_size),this.options.plot_padding=0,this.step=this.calculateStep()},normalise:function(value){var offset=this.x_padding_left;return value/this.range*(this.graph_width-offset)},calculateBarHeight:function(){var height=this.graph_height/this.data_size-this.bar_padding;return this.options.max_bar_size&&height>this.options.max_bar_size&&(height=this.options.max_bar_size,this.bar_padding=this.graph_height/this.data_size),height},calculateStep:function(){return(this.options.height-this.y_padding_bottom)/validStepDivider(this.data_size)},drawLines:function(label,colour,data){var x=this.x_padding_left+this.options.plot_padding*2,y=this.options.height-this.y_padding_bottom-this.step/2,pathString="M"+x+","+y,i;for(i=0;i<data.length;i++)pathString+="L"+(x+data[i]-this.normalise(this.start_value))+","+y,y-=this.step,pathString+="M"+x+","+y;this.paper.path(pathString).attr({stroke:colour,"stroke-width":this.bar_width+"px"})},drawFocusHint:function(){var length=5,x=this.x_padding_left+this.step*2,y=this.options.height-this.y_padding_bottom,pathString="";pathString+="M"+x+","+y,pathString+="L"+(x-length)+","+(y+length),pathString+="M"+(x-length)+","+y,pathString+="L"+(x-length*2)+","+(y+length),this.paper.path(pathString).attr({stroke:this.options.label_colour,"stroke-width":2})},drawVerticalLabels:function(){var y_start=this.step/2-this.options.plot_padding*2;this.drawMarkers(this.options.labels,[0,-1],this.step,y_start,[-8,this.options.font_size/8],{"text-anchor":"end"})},drawHorizontalLabels:function(){var x_step=this.graph_width/this.y_label_count,x_labels=this.makeValueLabels(this.y_label_count);this.drawMarkers(x_labels,[1,0],x_step,x_step,[0,(this.options.font_size+7)*-1])}}),Ico.LineGraph=function(){this.initialize.apply(this,arguments)},Helpers.extend(Ico.LineGraph.prototype,Ico.BaseGraph.prototype),Helpers.extend(Ico.LineGraph.prototype,{normalise:function(value){var total=this.start_value===0?this.top_value:this.top_value-this.start_value;return value/total*this.graph_height},chartDefaults:function(){return{plot_padding:10}},setChartSpecificOptions:function(){var longestLabel=this.longestLabel(this.value_labels);this.x_padding_left=30+longestLabel*(this.options.font_size/2),typeof this.options.curve_amount=="undefined"&&(this.options.curve_amount=10)},normaliserOptions:function(){return{start_value:this.options.start_value}},calculateStep:function(){return(this.graph_width-this.options.plot_padding*2)/validStepDivider(this.data_size)},startPlot:function(pathString,x,y,colour){return this.lastPoint={x:x,y:y},pathString+"M"+x+","+y},drawPlot:function(index,pathString,x,y,colour){var w=this.options.curve_amount;if(this.options.markers==="circle"){var circle=this.paper.circle(x,y,this.options.marker_size);circle.attr({"stroke-width":"1px",stroke:this.options.background_colour,fill:colour})}return index===0?this.startPlot(pathString,x,y,colour):(w?pathString+=["C",this.lastPoint.x+w,this.lastPoint.y,x-w,y,x,y]:pathString+="L"+x+","+y,this.lastPoint={x:x,y:y},pathString)}}),Ico.SparkLine=function(){this.initialize.apply(this,arguments)},Ico.SparkLine.prototype={initialize:function(element,data,options){this.element=element,this.data=data,this.options={width:parseInt(getStyle(element,"width"),10),height:parseInt(getStyle(element,"height"),10),highlight:!1,background_colour:getStyle(element,"backgroundColor")||"#ffffff",colour:"#036"},Helpers.extend(this.options,options||{}),this.step=this.calculateStep(),this.paper=Raphael(this.element,this.options.width,this.options.height),this.options.acceptable_range?this.background=this.paper.rect(0,this.options.height-this.normalise(this.options.acceptable_range[1]),this.options.width,this.options.height-this.normalise(this.options.acceptable_range[0])):this.background=this.paper.rect(0,0,this.options.width,this.options.height),this.background.attr({fill:this.options.background_colour,stroke:"none"}),this.draw()},calculateStep:function(){return this.options.width/validStepDivider(this.data.length)},normalise:function(value){return this.options.height/Helpers.max(this.data)*value},draw:function(){var data=this.normaliseData(this.data);this.drawLines("",this.options.colour,data),this.options.highlight&&this.showHighlight(data)},drawLines:function(label,colour,data){var pathString="",x=0,values=data.slice(1),i=0;pathString="M0,"+(this.options.height-data[0]);for(i=1;i<data.length;i++)x+=this.step,pathString+="L"+x+","+Ico.round(this.options.height-data[i],2);this.paper.path(pathString).attr({stroke:colour}),this.lastPoint={x:0,y:this.options.height-data[0]}},showHighlight:function(data){var size=2,x=this.options.width-size,i=this.options.highlight.index||data.length-1,y=data[i]+Math.round(size/2);typeof this.options.highlight.index!="undefined"&&(x=this.step*this.options.highlight.index);var circle=this.paper.circle(x,this.options.height-y,size);circle.attr({stroke:!1,fill:this.options.highlight.colour})}},Helpers.extend(Ico.SparkLine.prototype,Ico.Base),Ico.SparkBar=function(){this.initialize.apply(this,arguments)},Helpers.extend(Ico.SparkBar.prototype,Ico.SparkLine.prototype),Helpers.extend(Ico.SparkBar.prototype,{calculateStep:function(){return this.options.width/validStepDivider(this.data.length)},drawLines:function(label,colour,data){var width=this.step>2?this.step-1:this.step,x=width,pathString="",i=0;for(i=0;i<data.length;i++)pathString+="M"+x+","+(this.options.height-data[i]),pathString+="L"+x+","+this.options.height,x+=this.step;this.paper.path(pathString).attr({stroke:colour,"stroke-width":width})}}),global.Ico=Ico,typeof exports!="undefined"&&(module.exports=Ico)})(typeof window=="undefined"?this:window);
View
34 docs/index.html
@@ -81,7 +81,7 @@
pre .atv, code .atv { color: #060; }
}
- #linegraph { width: 700px; height: 300px }
+ .graph { width: 700px; height: 300px }
</style>
</head>
@@ -154,8 +154,8 @@
</code></pre>
<script type="text/javascript" src="raphael.js"></script>
-<script type="text/javascript" src="ico-min.js"></script>
-<div id="linegraph"></div>
+<script type="text/javascript" src="ico.min.js"></script>
+<div id="linegraph" class="graph"></div>
<script type="text/javascript">
new Ico.LineGraph(document.getElementById('linegraph'), {
one: [30, 5, 1, 10, 15, 18, 20, 25, 1],
@@ -213,6 +213,34 @@
<li><code>bar_labels</code>: Display the actual value of each bar in a bar graph</li>
</ul>
+<h3>Grouped Bar Graphs</h3>
+
+<p>Multidimensional arrays will be rendered as 'grouped' bar graphs. Notice that two colours are specified, one for each bar in the group. This is still a work in progress and hasn't been tested thoroughly yet.</p>
+
+<pre class="prettyprint lang-js"><code>
+new Ico.BarGraph(
+ $('grouped_bars'),
+ [[10, 15], [18, 19], [17, 23], [11, 22]],
+ { grid: true, font_size: 10,
+ colours: ['#ff0099', '#339933'],
+ labels: ['Winter', 'Spring', 'Summer', 'Autumn']
+ }
+);
+</code></pre>
+
+<div id="grouped_bars" class="graph"></div>
+
+<script type="text/javascript">
+new Ico.BarGraph(
+ document.getElementById('grouped_bars'),
+ [[10, 15], [18, 19], [17, 23], [11, 22]],
+ { grid: true, font_size: 10,
+ colours: ['#ff0099', '#339933'],
+ labels: ['Winter', 'Spring', 'Summer', 'Autumn']
+ }
+);
+</script>
+
<h3>Options for <code>Ico.HorizontalBarGraph</code></h3>
<ul>
View
31 docs/index.md
@@ -50,8 +50,8 @@ new Ico.LineGraph($('linegraph'), {
</code></pre>
<script type="text/javascript" src="raphael.js"></script>
-<script type="text/javascript" src="ico-min.js"></script>
-<div id="linegraph"></div>
+<script type="text/javascript" src="ico.min.js"></script>
+<div id="linegraph" class="graph"></div>
<script type="text/javascript">
new Ico.LineGraph(document.getElementById('linegraph'), {
one: [30, 5, 1, 10, 15, 18, 20, 25, 1],
@@ -103,6 +103,33 @@ new Ico.LineGraph(document.getElementById('linegraph'), {
* `max_bar_size`: Set the maximum size for a bar in a bar graph
* `bar_labels`: Display the actual value of each bar in a bar graph
+### Grouped Bar Graphs
+
+Multidimensional arrays will be rendered as 'grouped' bar graphs. Notice that two colours are specified, one for each bar in the group. This is still a work in progress and hasn't been tested thoroughly yet.
+
+<pre class="prettyprint lang-js"><code>
+new Ico.BarGraph(
+ $('grouped_bars'),
+ [[10, 15], [18, 19], [17, 23], [11, 22]],
+ { grid: true, font_size: 10,
+ colours: ['#ff0099', '#339933'],
+ labels: ['Winter', 'Spring', 'Summer', 'Autumn']
+ }
+);
+</code></pre>
+
+<div id="grouped_bars" class="graph"></div>
+<script type="text/javascript">
+new Ico.BarGraph(
+ document.getElementById('grouped_bars'),
+ [[10, 15], [18, 19], [17, 23], [11, 22]],
+ { grid: true, font_size: 10,
+ colours: ['#ff0099', '#339933'],
+ labels: ['Winter', 'Spring', 'Summer', 'Autumn']
+ }
+);
+</script>
+
### Options for `Ico.HorizontalBarGraph`
* `bar_size`: Set the size for a bar in a bar graph
View
2 docs/layout/begin.html
@@ -81,7 +81,7 @@
pre .atv, code .atv { color: #060; }
}
- #linegraph { width: 700px; height: 300px }
+ .graph { width: 700px; height: 300px }
</style>
</head>
View
1 ico-min.js
@@ -1 +0,0 @@
-(function(a){function d(a,b){var c;a.currentStyle?c=a.currentStyle[b]:window.getComputedStyle&&(c=document.defaultView.getComputedStyle(a,null).getPropertyValue(b)),c&&c.length===0&&(c=null);return c}function c(a){return a>1?a-1:1}var b={VERSION:"0.3.0",round:function(a,b){var c=Math.round(a*Math.pow(10,b))/Math.pow(10,b);return c}};Array.prototype.sum=function(){for(var a=0,b=0;a<this.length;b+=this[a++]);return b},typeof Array.prototype.max=="undefined"&&(Array.prototype.max=function(){return Math.max.apply({},this)}),typeof Array.prototype.min=="undefined"&&(Array.prototype.min=function(){return Math.min.apply({},this)}),Array.prototype.mean=function(){return this.sum()/this.length},Array.prototype.variance=function(){var a=this.mean(),b=0;for(var c=0;c<this.length;c++)b+=Math.pow(this[c]-a,2);return b/(this.length-1)},Array.prototype.standard_deviation=function(){return Math.sqrt(this.variance())},typeof Object.extend=="undefined"&&(Object.extend=function(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}),b.Normaliser=function(a,b){this.options={start_value:null},typeof b!="undefined"&&(this.options=b),this.min=a.min(),this.max=a.max(),this.standard_deviation=a.standard_deviation(),this.range=0,this.step=this.labelStep(this.max-this.min),this.start_value=this.calculateStart(),this.process()},b.Normaliser.prototype={calculateStart:function(){var a=typeof this.options.start_value!="undefined"&&this.min>=0?this.options.start_value:this.min,b=this.round(a,1);if(this.min>0&&b>this.min)return 0;return b},round:function(a,b){b=b||1;var c=a;if(this.standard_deviation>.1){var d=Math.pow(10,-b);c=Math.round(a*d)/d;if(c>this.min)return this.round(a-this.step)}return c},process:function(){this.range=this.max-this.start_value,this.step=this.labelStep(this.range)},labelStep:function(a){return Math.pow(10,Math.round(Math.log(a)/Math.LN10)-1)}},b.Base={normaliseData:function(a){var b=[],c=0;for(c=0;c<a.length;c++)b.push(this.normalise(a[c]));return b},flatten:function(a){var b=[];if(typeof a.length=="undefined"){if(typeof a!="object")return[];for(var c in a)a.hasOwnProperty(c)&&(b=b.concat(this.flatten(a[c])))}for(var d=0;d<a.length;d++)typeof a[d].length=="number"?b=b.concat(this.flatten(a[d])):b.push(a[d]);return b},makeRange:function(a,b){var c=[],d;for(d=a;d<b;d++)c.push(d);return c}},b.BaseGraph=function(){this.initialize.apply(this,arguments)},Object.extend(b.BaseGraph.prototype,b.Base),Object.extend(b.BaseGraph.prototype,{initialize:function(a,c,e){this.element=a,this.data_sets=this.buildDataSets(c,e),this.flat_data=this.flatten(c),this.data_size=this.longestDataSetLength();if(e&&e.colour){e.colours={};for(var f in this.data_sets)this.data_sets.hasOwnProperty(f)&&(e.colours[f]=e.colour)}this.options={width:parseInt(d(a,"width"),10),height:parseInt(d(a,"height"),10),labels:this.makeRange(1,this.data_size+1),plot_padding:10,font_size:10,show_horizontal_labels:!0,show_vertical_labels:!0,background_colour:d(a,"backgroundColor")||"#ffffff",label_colour:"#666",markers:!1,marker_size:5,meanline:!1,grid:!1,grid_colour:"#ccc",y_padding_top:20,draw:!0},Object.extend(this.options,this.chartDefaults()||{}),Object.extend(this.options,e||{}),this.normaliser=new b.Normaliser(this.flat_data,this.normaliserOptions()),this.label_step=this.normaliser.step,this.range=this.normaliser.range,this.start_value=this.normaliser.start_value,this.x_padding_left=10+this.paddingLeftOffset(),this.x_padding_right=20,this.x_padding=this.x_padding_left+this.x_padding_right,this.y_padding_top=this.options.y_padding_top,this.y_padding_bottom=20+this.paddingBottomOffset(),this.y_padding=this.y_padding_top+this.y_padding_bottom,this.graph_width=this.options.width-this.x_padding,this.graph_height=this.options.height-this.y_padding,this.step=this.calculateStep(),this.y_label_count=Math.ceil(this.range/this.label_step),this.normaliser.min+this.y_label_count*this.normaliser.step<this.normaliser.max&&(this.y_label_count+=1),this.value_labels=this.makeValueLabels(this.y_label_count),this.top_value=this.value_labels[this.value_labels.length-1],this.grid_start_offset=-1,this.options.draw&&(typeof this.options.colours=="undefined"&&(this.options.colours=this.makeRandomColours()),this.paper=Raphael(this.element,this.options.width,this.options.height),this.background=this.paper.rect(0,0,this.options.width,this.options.height),this.background.attr({fill:this.options.background_colour,stroke:"none"}),this.options.meanline===!0&&(this.options.meanline={"stroke-width":"2px",stroke:"#BBBBBB"}),this.setChartSpecificOptions(),this.lastPoint={x:0,y:0},this.draw())},buildDataSets:function(a,b){return typeof a.length!="undefined"?{one:a}:a},normaliserOptions:function(){return{}},chartDefaults:function(){return{}},drawPlot:function(a,b,c,d,e){},calculateStep:function(){},makeRandomColours:function(){var a={};for(var b in this.data_sets)a.hasOwnProperty(b)||(a[b]=Raphael.hsb2rgb(Math.random(),1,.75).hex);return a},longestDataSetLength:function(){var a=0;for(var b in this.data_sets)this.data_sets.hasOwnProperty(b)&&(a=this.data_sets[b].length>a?this.data_sets[b].length:a);return a},roundValue:function(a,b){var c=Math.pow(10,b);a*=c,a=Math.round(a)/c;return a},roundValues:function(a,b){var c=[];for(var d=0;d<a.length;d++)c.push(this.roundValue(a[d],b));return c},longestLabel:function(a){var b=Array.prototype.slice.call(a||this.options.labels,0);return b.sort(function(a,b){return a.toString().length<b.toString().length})[0].toString().length},paddingLeftOffset:function(){var a=this.roundValues(this.flat_data,2),b=0;b=a.sort(function(a,b){return a.toString().length<b.toString().length})[0].toString().length,b=b>2?b-1:b;return 10+b*this.options.font_size},paddingBottomOffset:function(){return this.options.font_size},normalise:function(a){var b=this.start_value===0?this.top_value:this.range;return a/b*this.graph_height},draw:function(){this.options.grid&&this.drawGrid(),this.options.meanline&&this.drawMeanLine(this.normaliseData(this.flat_data)),this.drawAxis(),this.options.show_vertical_labels&&this.drawVerticalLabels(),this.options.show_horizontal_labels&&this.drawHorizontalLabels();for(var a in this.data_sets)if(this.data_sets.hasOwnProperty(a)){var b=this.data_sets[a];this.drawLines(a,this.options.colours[a],this.normaliseData(b))}this.start_value!==0&&this.drawFocusHint()},drawGrid:function(){var a="",b;if(this.options.show_vertical_labels){var c=this.graph_height+this.y_padding_top;for(b=0;b<this.y_label_count;b++)c=c-this.graph_height/this.y_label_count,a+="M"+this.x_padding_left+","+c,a+="L"+(this.x_padding_left+this.graph_width)+","+c}if(this.options.show_horizontal_labels){var d=this.x_padding_left+this.options.plot_padding+this.grid_start_offset,e=this.options.labels.length;for(b=0;b<e;b++)a+="M"+d+","+this.y_padding_top,a+="L"+d+","+(this.y_padding_top+this.graph_height),d=d+this.step;d=d-this.options.plot_padding-1,a+="M"+d+","+this.y_padding_top,a+="L"+d+","+(this.y_padding_top+this.graph_height)}this.paper.path(a).attr({stroke:this.options.grid_colour,"stroke-width":"1px"})},drawLines:function(a,b,c){var d=this.calculateCoords(c),e="";for(var f=0;f<d.length;f++){var g=d[f][0]||0,h=d[f][1]||0;e=this.drawPlot(f,e,g,h,b)}this.paper.path(e).attr({stroke:b,"stroke-width":"3px"})},calculateCoords:function(a){var b=this.x_padding_left+this.options.plot_padding-this.step,c=this.graph_height+this.y_padding_top+this.normalise(this.start_value),d=0,e=[];for(var f=0;f<a.length;f++)d=c-a[f],b=b+this.step,e.push([b,d]);return e},drawFocusHint:function(){var a=5,b=this.x_padding_left+a/2-1,c=this.options.height-this.y_padding_bottom,d="";d+="M"+b+","+c,d+="L"+(b-a)+","+(c-a),d+="M"+b+","+(c-a),d+="L"+(b-a)+","+(c-a*2),this.paper.path(d).attr({stroke:this.options.label_colour,"stroke-width":2})},drawMeanLine:function(a){var b=a.sum()/a.length,c="";c+="M"+(this.x_padding_left-1)+","+(this.options.height-this.y_padding_bottom-b),c+="L"+(this.graph_width+this.x_padding_left)+","+(this.options.height-this.y_padding_bottom-b),this.paper.path(c).attr(this.options.meanline)},drawAxis:function(){var a="";a+="M"+(this.x_padding_left-1)+","+(this.options.height-this.y_padding_bottom),a+="L"+(this.graph_width+this.x_padding_left)+","+(this.options.height-this.y_padding_bottom),a+="M"+(this.x_padding_left-1)+","+(this.options.height-this.y_padding_bottom),a+="L"+(this.x_padding_left-1)+","+this.y_padding_top,this.paper.path(a).attr({stroke:this.options.label_colour})},makeValueLabels:function(a){var b=this.label_step,c=this.start_value,d=[];for(var e=0;e<a;e++)c=this.roundValue(c+b,2),d.push(c);return d},drawMarkers:function(a,b,c,d,e,f){function h(a){return a*b[1]}function g(a){return a*b[0]}var i=this.x_padding_left-1+g(d),j=this.options.height-this.y_padding_bottom+h(d),k="",l={font:this.options.font_size+'px "Arial"',stroke:"none",fill:"#000"};Object.extend(l,f||{});for(var m=0;m<a.length;m++)k+="M"+i+","+j,typeof a[m]!="undefined"&&(a[m]+"").length>0&&(k+="L"+(i+h(5))+","+(j+g(5)),this.paper.text(i+e[0],j-e[1],a[m]).attr(l).toFront()),i=i+g(c),j=j+h(c);this.paper.path(k).attr({stroke:this.options.label_colour})},drawVerticalLabels:function(){var a=this.graph_height/this.y_label_count;this.drawMarkers(this.value_labels,[0,-1],a,a,[-8,-2],{"text-anchor":"end"})},drawHorizontalLabels:function(){this.drawMarkers(this.options.labels,[1,0],this.step,this.options.plot_padding,[0,(this.options.font_size+7)*-1])}}),b.BarGraph=function(){this.initialize.apply(this,arguments)},Object.extend(b.BarGraph.prototype,b.BaseGraph.prototype),Object.extend(b.BarGraph.prototype,{chartDefaults:function(){return{plot_padding:0}},normaliserOptions:function(){return{start_value:0}},setChartSpecificOptions:function(){this.bar_padding=5,this.bar_width=this.calculateBarWidth(),this.options.plot_padding=this.bar_width/2,this.step=this.calculateStep(),this.grid_start_offset=this.bar_padding-1,this.start_y=this.options.height-this.y_padding_bottom},calculateBarWidth:function(){return this.graph_width/this.data_size-this.bar_padding},calculateStep:function(){return(this.graph_width-this.options.plot_padding*2-this.bar_padding*2)/c(this.data_size)},drawPlot:function(a,b,c,d,e){c=c+this.bar_padding,b+="M"+c+","+this.start_y,b+="L"+c+","+d,this.paper.path(b).attr({stroke:e,"stroke-width":this.bar_width+"px"}),b="",c=c+this.step,b+="M"+c+","+this.start_y;return b},drawHorizontalLabels:function(){var a=this.bar_padding+this.options.plot_padding;this.drawMarkers(this.options.labels,[1,0],this.step,a,[0,(this.options.font_size+7)*-1])}}),b.HorizontalBarGraph=function(){this.initialize.apply(this,arguments)},Object.extend(b.HorizontalBarGraph.prototype,b.BaseGraph.prototype),Object.extend(b.HorizontalBarGraph.prototype,{setChartSpecificOptions:function(){this.y_padding_top=0,this.x_padding_left=20+this.longestLabel()*(this.options.font_size/2),this.bar_padding=5,this.bar_width=this.calculateBarHeight(),this.options.plot_padding=0,this.step=this.calculateStep()},normalise:function(a){var b=this.x_padding_left;return a/this.range*(this.graph_width-b)},calculateBarHeight:function(){return this.graph_height/this.data_size-this.bar_padding},calculateStep:function(){return(this.options.height-this.y_padding_bottom)/c(this.data_size)},drawLines:function(a,b,c){var d=this.x_padding_left+this.options.plot_padding*2,e=this.options.height-this.y_padding_bottom-this.step/2,f="M"+d+","+e,g;for(g=0;g<c.length;g++)f+="L"+(d+c[g]-this.normalise(this.start_value))+","+e,e=e-this.step,f+="M"+d+","+e;this.paper.path(f).attr({stroke:b,"stroke-width":this.bar_width+"px"})},drawFocusHint:function(){var a=5,b=this.x_padding_left+this.step*2,c=this.options.height-this.y_padding_bottom,d="";d+="M"+b+","+c,d+="L"+(b-a)+","+(c+a),d+="M"+(b-a)+","+c,d+="L"+(b-a*2)+","+(c+a),this.paper.path(d).attr({stroke:this.options.label_colour,"stroke-width":2})},drawVerticalLabels:function(){var a=this.step/2-this.options.plot_padding*2;this.drawMarkers(this.options.labels,[0,-1],this.step,a,[-8,this.options.font_size/8],{"text-anchor":"end"})},drawHorizontalLabels:function(){var a=this.graph_width/this.y_label_count,b=this.makeValueLabels(this.y_label_count);this.drawMarkers(b,[1,0],a,a,[0,(this.options.font_size+7)*-1])}}),b.LineGraph=function(){this.initialize.apply(this,arguments)},Object.extend(b.LineGraph.prototype,b.BaseGraph.prototype),Object.extend(b.LineGraph.prototype,{normalise:function(a){var b=this.start_value===0?this.top_value:this.top_value-this.start_value;return a/b*this.graph_height},chartDefaults:function(){return{plot_padding:10}},setChartSpecificOptions:function(){this.x_padding_left=30+this.longestLabel(this.value_labels)*(this.options.font_size/2),typeof this.options.curve_amount=="undefined"&&(this.options.curve_amount=10)},normaliserOptions:function(){return{start_value:this.options.start_value}},calculateStep:function(){return(this.graph_width-this.options.plot_padding*2)/c(this.data_size)},startPlot:function(a,b,c,d){this.lastPoint={x:b,y:c};return a+"M"+b+","+c},drawPlot:function(a,b,c,d,e){var f=this.options.curve_amount;if(this.options.markers==="circle"){var g=this.paper.circle(c,d,this.options.marker_size);g.attr({"stroke-width":"1px",stroke:this.options.background_colour,fill:e})}if(a===0)return this.startPlot(b,c,d,e);f?b+=["C",this.lastPoint.x+f,this.lastPoint.y,c-f,d,c,d]:b+="L"+c+","+d,this.lastPoint={x:c,y:d};return b}}),b.SparkLine=function(){this.initialize.apply(this,arguments)},b.SparkLine.prototype={initialize:function(a,b,c){this.element=a,this.data=b,this.options={width:parseInt(d(a,"width"),10),height:parseInt(d(a,"height"),10),highlight:!1,background_colour:d(a,"backgroundColor")||"#ffffff",colour:"#036"},Object.extend(this.options,c||{}),this.step=this.calculateStep(),this.paper=Raphael(this.element,this.options.width,this.options.height),this.options.acceptable_range?this.background=this.paper.rect(0,this.options.height-this.normalise(this.options.acceptable_range[1]),this.options.width,this.options.height-this.normalise(this.options.acceptable_range[0])):this.background=this.paper.rect(0,0,this.options.width,this.options.height),this.background.attr({fill:this.options.background_colour,stroke:"none"}),this.draw()},calculateStep:function(){return this.options.width/c(this.data.length)},normalise:function(a){return this.options.height/this.data.max()*a},draw:function(){var a=this.normaliseData(this.data);this.drawLines("",this.options.colour,a),this.options.highlight&&this.showHighlight(a)},drawLines:function(a,c,d){var e="",f=0,g=d.slice(1),h=0;e="M0,"+(this.options.height-d[0]);for(h=1;h<d.length;h++)f=f+this.step,e+="L"+f+","+b.round(this.options.height-d[h],2);this.paper.path(e).attr({stroke:c}),this.lastPoint={x:0,y:this.options.height-d[0]}},showHighlight:function(a){var b=2,c=this.options.width-b,d=this.options.highlight.index||a.length-1,e=a[d]+Math.round(b/2);typeof this.options.highlight.index!="undefined"&&(c=this.step*this.options.highlight.index);var f=this.paper.circle(c,this.options.height-e,b);f.attr({stroke:!1,fill:this.options.highlight.colour})}},Object.extend(b.SparkLine.prototype,b.Base),b.SparkBar=function(){this.initialize.apply(this,arguments)},Object.extend(b.SparkBar.prototype,b.SparkLine.prototype),Object.extend(b.SparkBar.prototype,{calculateStep:function(){return this.options.width/c(this.data.length)},drawLines:function(a,b,c){var d=this.step>2?this.step-1:this.step,e=d,f="",g=0;for(g=0;g<c.length;g++)f+="M"+e+","+(this.options.height-c[g]),f+="L"+e+","+this.options.height,e=e+this.step;this.paper.path(f).attr({stroke:b,"stroke-width":d})}}),a.Ico=b})(typeof window=="undefined"?this:window)
View
103 ico.js
@@ -43,7 +43,9 @@ function validStepDivider(value) {
* @returns {Object} The style value
*/
function getStyle(el, styleProp) {
- if (typeof window === 'undefined') return;
+ if (typeof window === 'undefined') {
+ return;
+ }
var style;
if (el.currentStyle) {
@@ -60,7 +62,8 @@ function getStyle(el, styleProp) {
var Helpers = {};
Helpers.sum = function(a) {
- for (var i = 0, sum = 0; i < a.length; sum += a[i++]) {}
+ var i, sum;
+ for (i = 0, sum = 0; i < a.length; sum += a[i++]) {}
return sum;
};
@@ -113,6 +116,24 @@ if (typeof Object.extend === 'undefined') {
} else {
Helpers.extend = Object.extend;
}
+
+if (Object.keys) {
+ Helpers.keys = Object.keys;
+} else {
+ Helpers.keys = function(o) {
+ if (o !== Object(o)) {
+ throw new TypeError('Object.keys called on non-object');
+ }
+
+ var ret = [], p;
+ for (p in o) {
+ if (Object.prototype.hasOwnProperty.call(o,p)) {
+ ret.push(p);
+ }
+ }
+ return ret;
+ }
+}
/**
* Normalises lists of values to fit inside a graph.
*
@@ -422,6 +443,12 @@ Helpers.extend(Ico.BaseGraph.prototype, {
},
longestDataSetLength: function() {
+ if (this.grouped) {
+ // Return the total number of grouped values rather than
+ // the longest array of data
+ return this.flat_data.length;
+ }
+
var length = 0;
for (var key in this.data_sets) {
if (this.data_sets.hasOwnProperty(key)) {
@@ -439,8 +466,8 @@ Helpers.extend(Ico.BaseGraph.prototype, {
},
roundValues: function(data, length) {
- var roundedData = [];
- for (var i = 0; i < data.length; i++) {
+ var roundedData = [], i;
+ for (i = 0; i < data.length; i++) {
roundedData.push(this.roundValue(data[i], length));
}
return roundedData;
@@ -496,10 +523,14 @@ Helpers.extend(Ico.BaseGraph.prototype, {
this.drawHorizontalLabels();
}
- for (var key in this.data_sets) {
- if (this.data_sets.hasOwnProperty(key)) {
- var data = this.data_sets[key];
- this.drawLines(key, this.options.colours[key], this.normaliseData(data));
+ if (this.grouped) {
+ this.drawLines(key, null, this.normaliseData(this.flat_data));
+ } else {
+ for (var key in this.data_sets) {
+ if (this.data_sets.hasOwnProperty(key)) {
+ var data = this.data_sets[key];
+ this.drawLines(key, this.options.colours[key], this.normaliseData(data));
+ }
}
}
@@ -519,10 +550,10 @@ Helpers.extend(Ico.BaseGraph.prototype, {
pathString += 'L' + (this.x_padding_left + this.graph_width) + ',' + y;
}
}
-
+
if (this.options.show_horizontal_labels) {
var x = this.x_padding_left + this.options.plot_padding + this.grid_start_offset,
- x_labels = this.options.labels.length,
+ x_labels = this.grouped ? this.flat_data.length : this.options.labels.length,
i;
for (i = 0; i < x_labels; i++) {
@@ -547,10 +578,15 @@ Helpers.extend(Ico.BaseGraph.prototype, {
for (i = 0; i < coords.length; i++) {
x = coords[i][0] || 0;
y = coords[i][1] || 0;
- this.plottedCoords.push([x + this.bar_padding, y]);
+
+ if (this.grouped && this.options.colours) {
+ colour = this.options.colours[i % this.group_size];
+ }
+
+ this.plottedCoords.push([x + this.bar_padding, y]);
pathString = this.drawPlot(i, pathString, x, y, colour);
}
-
+
this.paper.path(pathString).attr({ stroke: colour, 'stroke-width': '3px' });
if (this.options.bar_labels) {
@@ -636,7 +672,7 @@ Helpers.extend(Ico.BaseGraph.prototype, {
font_options = {};
Helpers.extend(font_options, this.font_options);
Helpers.extend(font_options, extra_font_options || {});
-
+
for (i = 0; i < labels.length; i++) {
pathString += 'M' + x + ',' + y;
if (typeof labels[i] !== 'undefined' && (labels[i] + '').length > 0) {
@@ -659,7 +695,6 @@ Helpers.extend(Ico.BaseGraph.prototype, {
this.drawMarkers(this.options.labels, [1, 0], this.step, this.options.plot_padding, [0, (this.options.font_size + 7) * -1]);
}
});
-
/**
* The BarGraph class.
*
@@ -671,6 +706,28 @@ Helpers.extend(Ico.BaseGraph.prototype, {
Ico.BarGraph = function() { this.initialize.apply(this, arguments); };
Helpers.extend(Ico.BarGraph.prototype, Ico.BaseGraph.prototype);
Helpers.extend(Ico.BarGraph.prototype, {
+ // Overridden to handle grouped bar graphs
+ buildDataSets: function(data, options) {
+ if (typeof data.length !== 'undefined') {
+ if (typeof data[0].length !== 'undefined') {
+ this.grouped = true;
+ // TODO: Find longest?
+ this.group_size = data[0].length;
+ var o = {}, k, i = 0;
+ for (k in options.labels) {
+ k = options.labels[k];
+ o[k] = data[i];
+ i++;
+ }
+ return o;
+ } else {
+ return { 'one': data };
+ }
+ } else {
+ return data;
+ }
+ },
+
/**
* Sensible defaults for BarGraph.
*/
@@ -710,6 +767,10 @@ Helpers.extend(Ico.BarGraph.prototype, {
calculateBarWidth: function() {
var width = (this.graph_width / this.data_size) - this.bar_padding;
+ if (this.grouped) {
+ //width = width / this.group_size - (this.bar_padding * this.group_size);
+ }
+
if (this.options.max_bar_size && width > this.options.max_bar_size) {
width = this.options.max_bar_size;
this.bar_padding = this.graph_width / this.data_size;
@@ -741,7 +802,7 @@ Helpers.extend(Ico.BarGraph.prototype, {
x = x + this.bar_padding;
pathString += 'M' + x + ',' + this.start_y;
pathString += 'L' + x + ',' + y;
- this.paper.path(pathString).attr({stroke: colour, 'stroke-width': this.bar_width + 'px'});
+ this.paper.path(pathString).attr({ stroke: colour, 'stroke-width': this.bar_width + 'px' });
pathString = '';
x = x + this.step;
pathString += 'M' + x + ',' + this.start_y;
@@ -750,8 +811,14 @@ Helpers.extend(Ico.BarGraph.prototype, {
/* Change the standard options to correctly offset against the bars */
drawHorizontalLabels: function() {
- var x_start = this.bar_padding + this.options.plot_padding;
- this.drawMarkers(this.options.labels, [1, 0], this.step, x_start, [0, (this.options.font_size + 7) * -1]);
+ var x_start = this.bar_padding + this.options.plot_padding,
+ step = this.step;
+ if (this.grouped) {
+ step = step * this.group_size;
+ x_start = ((this.bar_width * this.group_size) + (this.bar_padding * this.group_size)) / 2
+ x_start = this.roundValue(x_start, 0);
+ }
+ this.drawMarkers(this.options.labels, [1, 0], step, x_start, [0, (this.options.font_size + 7) * -1]);
},
drawBarMarkers: function() {
@@ -1055,5 +1122,5 @@ Helpers.extend(Ico.SparkBar.prototype, {
if (typeof exports !== 'undefined') {
module.exports = Ico;
}
-})(typeof window === 'undefined' ? this : window);
+}(typeof window === 'undefined' ? this : window));
View
2 ico.min.js
@@ -4,4 +4,4 @@
* MIT Licensed
*//**
* The Ico object.
- */(function(global){var Ico={VERSION:"0.3.5",round:function(num,dec){var result=Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);return result}};function validStepDivider(value){return value>1?value-1:1}function getStyle(el,styleProp){if(typeof window=="undefined")return;var style;return el.currentStyle?style=el.currentStyle[styleProp]:window.getComputedStyle&&(style=document.defaultView.getComputedStyle(el,null).getPropertyValue(styleProp)),style&&style.length===0&&(style=null),style}var Helpers={};Helpers.sum=function(a){for(var i=0,sum=0;i<a.length;sum+=a[i++]);return sum},typeof Array.prototype.max=="undefined"?Helpers.max=function(a){return Math.max.apply({},a)}:Helpers.max=function(a){return a.max()},typeof Array.prototype.min=="undefined"?Helpers.min=function(a){return Math.min.apply({},a)}:Helpers.min=function(a){return a.min()},Helpers.mean=function(a){return Helpers.sum(a)/a.length},Helpers.variance=function(a){var mean=Helpers.mean(a),variance=0;for(var i=0;i<a.length;i++)variance+=Math.pow(a[i]-mean,2);return variance/(a.length-1)},Helpers.standard_deviation=function(a){return Math.sqrt(Helpers.variance(a))},typeof Object.extend=="undefined"?Helpers.extend=function(destination,source){for(var property in source)source.hasOwnProperty(property)&&(destination[property]=source[property]);return destination}:Helpers.extend=Object.extend,Ico.Normaliser=function(data,options){this.options={start_value:null},typeof options!="undefined"&&(this.options=options),this.min=Helpers.min(data),this.max=Helpers.max(data),this.standard_deviation=Helpers.standard_deviation(data),this.range=0,this.step=this.labelStep(this.max-this.min),this.start_value=this.calculateStart(),this.process()},Ico.Normaliser.prototype={calculateStart:function(){var min=typeof this.options.start_value!="undefined"&&this.min>=0?this.options.start_value:this.min,start_value=this.round(min,1);return this.min>0&&start_value>this.min?0:this.min===this.max?0:start_value},round:function(value,offset){offset=offset||1;var roundedValue=value;if(this.standard_deviation>.1){var multiplier=Math.pow(10,-offset);roundedValue=Math.round(value*multiplier)/multiplier;if(roundedValue>this.min)return this.round(value-this.step)}return roundedValue},process:function(){this.range=this.max-this.start_value,this.step=this.labelStep(this.range)},labelStep:function(value){return Math.pow(10,Math.round(Math.log(value)/Math.LN10)-1)}},Ico.Base={normaliseData:function(data){var values=[],i=0;for(i=0;i<data.length;i++)values.push(this.normalise(data[i]));return values},flatten:function(data){var flat_data=[];if(typeof data.length=="undefined"){if(typeof data!="object")return[];for(var key in data)data.hasOwnProperty(key)&&(flat_data=flat_data.concat(this.flatten(data[key])))}for(var i=0;i<data.length;i++)typeof data[i].length=="number"?flat_data=flat_data.concat(this.flatten(data[i])):flat_data.push(data[i]);return flat_data},makeRange:function(start,end){var values=[],i;for(i=start;i<end;i++)values.push(i);return values}},Ico.BaseGraph=function(){this.initialize.apply(this,arguments)},Helpers.extend(Ico.BaseGraph.prototype,Ico.Base),Helpers.extend(Ico.BaseGraph.prototype,{initialize:function(element,data,options){options=options||{},this.element=element,this.data_sets=this.buildDataSets(data,options),this.flat_data=this.flatten(data),this.data_size=this.longestDataSetLength(),this.plottedCoords=[];if(options&&options.colour){options.colours={};for(var key in this.data_sets)this.data_sets.hasOwnProperty(key)&&(options.colours[key]=options.colour)}this.options={width:parseInt(getStyle(element,"width"),10),height:parseInt(getStyle(element,"height"),10),labels:this.makeRange(1,this.data_size+1),plot_padding:10,font_size:10,show_horizontal_labels:!0,show_vertical_labels:!0,background_colour:getStyle(element,"backgroundColor")||"#ffffff",label_colour:"#666",markers:!1,marker_size:5,meanline:!1,grid:!1,grid_colour:"#ccc",y_padding_top:20,draw:!0,bar_labels:!1},Helpers.extend(this.options,this.chartDefaults()||{}),Helpers.extend(this.options,options),this.font_options={font:this.options.font_size+'px "Arial"',stroke:"none",fill:"#000"},this.normaliser=new Ico.Normaliser(this.flat_data,this.normaliserOptions()),this.label_step=options.label_step||this.normaliser.step,this.range=this.normaliser.range,this.start_value=options.start_value||this.normaliser.start_value,this.x_padding_left=10+this.paddingLeftOffset(),this.x_padding_right=20,this.x_padding=this.x_padding_left+this.x_padding_right,this.y_padding_top=this.options.y_padding_top,this.y_padding_bottom=20+this.paddingBottomOffset(),this.y_padding=this.y_padding_top+this.y_padding_bottom,this.graph_width=this.options.width-this.x_padding,this.graph_height=this.options.height-this.y_padding,this.step=this.calculateStep(),this.options.bar_labels&&(this.range+=this.normaliser.step),options.label_count?this.y_label_count=options.label_count:(this.y_label_count=Math.ceil(this.range/this.label_step),this.normaliser.min+this.y_label_count*this.normaliser.step<this.normaliser.max&&(this.y_label_count+=1)),this.value_labels=this.makeValueLabels(this.y_label_count),this.top_value=this.value_labels[this.value_labels.length-1],this.grid_start_offset=-1,this.options.draw&&(typeof this.options.colours=="undefined"&&(this.options.colours=this.makeRandomColours()),this.paper=Raphael(this.element,this.options.width,this.options.height),this.background=this.paper.rect(0,0,this.options.width,this.options.height),this.background.attr({fill:this.options.background_colour,stroke:"none"}),this.options.meanline===!0&&(this.options.meanline={"stroke-width":"2px",stroke:"#BBBBBB"}),this.setChartSpecificOptions(),this.lastPoint={x:0,y:0},this.draw())},buildDataSets:function(data,options){return typeof data.length!="undefined"?{one:data}:data},normaliserOptions:function(){return{}},chartDefaults:function(){return{}},drawPlot:function(index,pathString,x,y,colour){},calculateStep:function(){},makeRandomColours:function(){var colours={};for(var key in this.data_sets)colours.hasOwnProperty(key)||(colours[key]=Raphael.hsb2rgb(Math.random(),1,.75).hex);return colours},longestDataSetLength:function(){var length=0;for(var key in this.data_sets)this.data_sets.hasOwnProperty(key)&&(length=this.data_sets[key].length>length?this.data_sets[key].length:length);return length},roundValue:function(value,length){var multiplier=Math.pow(10,length);return value*=multiplier,value=Math.round(value)/multiplier,value},roundValues:function(data,length){var roundedData=[];for(var i=0;i<data.length;i++)roundedData.push(this.roundValue(data[i],length));return roundedData},longestLabel:function(values){var labels=Array.prototype.slice.call(values||this.options.labels,0);return labels.length?labels.sort(function(a,b){return a.toString().length<b.toString().length})[0].toString().length:0},paddingLeftOffset:function(){var data=this.roundValues(this.flat_data,2),longest_label_length=0;return longest_label_length=data.sort(function(a,b){return a.toString().length<b.toString().length})[0].toString().length,longest_label_length=longest_label_length>2?longest_label_length-1:longest_label_length,10+longest_label_length*this.options.font_size},paddingBottomOffset:function(){return this.options.font_size},normalise:function(value){var total=this.start_value===0?this.top_value:this.range;return value/total*this.graph_height},draw:function(){this.options.grid&&this.drawGrid(),this.options.meanline&&this.drawMeanLine(this.normaliseData(this.flat_data)),this.drawAxis(),this.options.show_vertical_labels&&this.drawVerticalLabels(),this.options.show_horizontal_labels&&this.drawHorizontalLabels();for(var key in this.data_sets)if(this.data_sets.hasOwnProperty(key)){var data=this.data_sets[key];this.drawLines(key,this.options.colours[key],this.normaliseData(data))}this.start_value!==0&&this.drawFocusHint()},drawGrid:function(){var pathString="",i,y;if(this.options.show_vertical_labels){y=this.graph_height+this.y_padding_top;for(i=0;i<this.y_label_count;i++)y-=this.graph_height/this.y_label_count,pathString+="M"+this.x_padding_left+","+y,pathString+="L"+(this.x_padding_left+this.graph_width)+","+y}if(this.options.show_horizontal_labels){var x=this.x_padding_left+this.options.plot_padding+this.grid_start_offset,x_labels=this.options.labels.length,i;for(i=0;i<x_labels;i++)pathString+="M"+x+","+this.y_padding_top,pathString+="L"+x+","+(this.y_padding_top+this.graph_height),x+=this.step;x=x-this.options.plot_padding-1,pathString+="M"+x+","+this.y_padding_top,pathString+="L"+x+","+(this.y_padding_top+this.graph_height)}this.paper.path(pathString).attr({stroke:this.options.grid_colour,"stroke-width":"1px"})},drawLines:function(label,colour,data){var coords=this.calculateCoords(data),pathString="",i,x,y;for(i=0;i<coords.length;i++)x=coords[i][0]||0,y=coords[i][1]||0,this.plottedCoords.push([x+this.bar_padding,y]),pathString=this.drawPlot(i,pathString,x,y,colour);this.paper.path(pathString).attr({stroke:colour,"stroke-width":"3px"}),this.options.bar_labels&&this.drawBarMarkers()},calculateCoords:function(data){var x=this.x_padding_left+this.options.plot_padding-this.step,y_offset=this.graph_height+this.y_padding_top+this.normalise(this.start_value),y=0,coords=[],i;for(i=0;i<data.length;i++)y=y_offset-data[i],x+=this.step,coords.push([x,y]);return coords},drawFocusHint:function(){var length=5,x=this.x_padding_left+length/2-1,y=this.options.height-this.y_padding_bottom,pathString="";pathString+="M"+x+","+y,pathString+="L"+(x-length)+","+(y-length),pathString+="M"+x+","+(y-length),pathString+="L"+(x-length)+","+(y-length*2),this.paper.path(pathString).attr({stroke:this.options.label_colour,"stroke-width":2})},drawMeanLine:function(data){var offset=Helpers.sum(data)/data.length,pathString="";pathString+="M"+(this.x_padding_left-1)+","+(this.options.height-this.y_padding_bottom-offset),pathString+="L"+(this.graph_width+this.x_padding_left)+","+(this.options.height-this.y_padding_bottom-offset),this.paper.path(pathString).attr(this.options.meanline)},drawAxis:function(){var pathString="";pathString+="M"+(this.x_padding_left-1)+","+(this.options.height-this.y_padding_bottom),pathString+="L"+(this.graph_width+this.x_padding_left)+","+(this.options.height-this.y_padding_bottom),pathString+="M"+(this.x_padding_left-1)+","+(this.options.height-this.y_padding_bottom),pathString+="L"+(this.x_padding_left-1)+","+this.y_padding_top,this.paper.path(pathString).attr({stroke:this.options.label_colour})},makeValueLabels:function(steps){var step=this.label_step,label=this.start_value,labels=[];for(var i=0;i<steps;i++)label=this.roundValue(label+step,2),labels.push(label);return labels},drawMarkers:function(labels,direction,step,start_offset,font_offsets,extra_font_options){function x_offset(value){return value*direction[0]}function y_offset(value){return value*direction[1]}var x=this.x_padding_left-1+x_offset(start_offset),y=this.options.height-this.y_padding_bottom+y_offset(start_offset),pathString="",i,font_options={};Helpers.extend(font_options,this.font_options),Helpers.extend(font_options,extra_font_options||{});for(i=0;i<labels.length;i++)pathString+="M"+x+","+y,typeof labels[i]!="undefined"&&(labels[i]+"").length>0&&(pathString+="L"+(x+y_offset(5))+","+(y+x_offset(5)),this.paper.text(x+font_offsets[0],y-font_offsets[1],labels[i]).attr(font_options).toFront()),x+=x_offset(step),y+=y_offset(step);this.paper.path(pathString).attr({stroke:this.options.label_colour})},drawVerticalLabels:function(){var y_step=this.graph_height/this.y_label_count;this.drawMarkers(this.value_labels,[0,-1],y_step,y_step,[-8,-2],{"text-anchor":"end"})},drawHorizontalLabels:function(){this.drawMarkers(this.options.labels,[1,0],this.step,this.options.plot_padding,[0,(this.options.font_size+7)*-1])}}),Ico.BarGraph=function(){this.initialize.apply(this,arguments)},Helpers.extend(Ico.BarGraph.prototype,Ico.BaseGraph.prototype),Helpers.extend(Ico.BarGraph.prototype,{chartDefaults:function(){return{plot_padding:0}},normaliserOptions:function(){return{start_value:0}},setChartSpecificOptions:function(){this.bar_padding=this.options.bar_padding||5,this.bar_width=this.options.bar_size||this.calculateBarWidth(),this.options.bar_size&&!this.options.bar_padding&&(this.bar_padding=this.graph_width/this.data_size),this.options.plot_padding=this.bar_width/2-this.bar_padding/2,this.step=this.calculateStep(),this.grid_start_offset=this.bar_padding-1,this.start_y=this.options.height-this.y_padding_bottom},calculateBarWidth:function(){var width=this.graph_width/this.data_size-this.bar_padding;return this.options.max_bar_size&&width>this.options.max_bar_size&&(width=this.options.max_bar_size,this.bar_padding=this.graph_width/this.data_size),width},calculateStep:function(){return(this.graph_width-this.options.plot_padding*2-this.bar_padding*2)/validStepDivider(this.data_size)},drawPlot:function(index,pathString,x,y,colour){return x+=this.bar_padding,pathString+="M"+x+","+this.start_y,pathString+="L"+x+","+y,this.paper.path(pathString).attr({stroke:colour,"stroke-width":this.bar_width+"px"}),pathString="",x+=this.step,pathString+="M"+x+","+this.start_y,pathString},drawHorizontalLabels:function(){var x_start=this.bar_padding+this.options.plot_padding;this.drawMarkers(this.options.labels,[1,0],this.step,x_start,[0,(this.options.font_size+7)*-1])},drawBarMarkers:function(){if(this.plottedCoords.length===0)return;var i,length=this.flat_data.length,x,y,label,font_options={};Helpers.extend(font_options,this.font_options),font_options["text-anchor"]="center";for(i=0;i<length;i++)label=this.roundValue(this.flat_data[i],2).toString(),x=this.plottedCoords[i][0],y=this.roundValue(this.plottedCoords[i][1],0),this.paper.text(x,y-this.options.font_size,label).attr(font_options).toFront()}}),Ico.HorizontalBarGraph=function(){this.initialize.apply(this,arguments)},Helpers.extend(Ico.HorizontalBarGraph.prototype,Ico.BaseGraph.prototype),Helpers.extend(Ico.HorizontalBarGraph.prototype,{setChartSpecificOptions:function(){this.y_padding_top=0,this.x_padding_left=20+this.longestLabel()*(this.options.font_size/2),this.bar_padding=this.options.bar_padding||5,this.bar_width=this.options.bar_size||this.calculateBarHeight(),this.options.bar_size&&!this.options.bar_padding&&(this.bar_padding=this.graph_height/this.data_size),this.options.plot_padding=0,this.step=this.calculateStep()},normalise:function(value){var offset=this.x_padding_left;return value/this.range*(this.graph_width-offset)},calculateBarHeight:function(){var height=this.graph_height/this.data_size-this.bar_padding;return this.options.max_bar_size&&height>this.options.max_bar_size&&(height=this.options.max_bar_size,this.bar_padding=this.graph_height/this.data_size),height},calculateStep:function(){return(this.options.height-this.y_padding_bottom)/validStepDivider(this.data_size)},drawLines:function(label,colour,data){var x=this.x_padding_left+this.options.plot_padding*2,y=this.options.height-this.y_padding_bottom-this.step/2,pathString="M"+x+","+y,i;for(i=0;i<data.length;i++)pathString+="L"+(x+data[i]-this.normalise(this.start_value))+","+y,y-=this.step,pathString+="M"+x+","+y;this.paper.path(pathString).attr({stroke:colour,"stroke-width":this.bar_width+"px"})},drawFocusHint:function(){var length=5,x=this.x_padding_left+this.step*2,y=this.options.height-this.y_padding_bottom,pathString="";pathString+="M"+x+","+y,pathString+="L"+(x-length)+","+(y+length),pathString+="M"+(x-length)+","+y,pathString+="L"+(x-length*2)+","+(y+length),this.paper.path(pathString).attr({stroke:this.options.label_colour,"stroke-width":2})},drawVerticalLabels:function(){var y_start=this.step/2-this.options.plot_padding*2;this.drawMarkers(this.options.labels,[0,-1],this.step,y_start,[-8,this.options.font_size/8],{"text-anchor":"end"})},drawHorizontalLabels:function(){var x_step=this.graph_width/this.y_label_count,x_labels=this.makeValueLabels(this.y_label_count);this.drawMarkers(x_labels,[1,0],x_step,x_step,[0,(this.options.font_size+7)*-1])}}),Ico.LineGraph=function(){this.initialize.apply(this,arguments)},Helpers.extend(Ico.LineGraph.prototype,Ico.BaseGraph.prototype),Helpers.extend(Ico.LineGraph.prototype,{normalise:function(value){var total=this.start_value===0?this.top_value:this.top_value-this.start_value;return value/total*this.graph_height},chartDefaults:function(){return{plot_padding:10}},setChartSpecificOptions:function(){var longestLabel=this.longestLabel(this.value_labels);this.x_padding_left=30+longestLabel*(this.options.font_size/2),typeof this.options.curve_amount=="undefined"&&(this.options.curve_amount=10)},normaliserOptions:function(){return{start_value:this.options.start_value}},calculateStep:function(){return(this.graph_width-this.options.plot_padding*2)/validStepDivider(this.data_size)},startPlot:function(pathString,x,y,colour){return this.lastPoint={x:x,y:y},pathString+"M"+x+","+y},drawPlot:function(index,pathString,x,y,colour){var w=this.options.curve_amount;if(this.options.markers==="circle"){var circle=this.paper.circle(x,y,this.options.marker_size);circle.attr({"stroke-width":"1px",stroke:this.options.background_colour,fill:colour})}return index===0?this.startPlot(pathString,x,y,colour):(w?pathString+=["C",this.lastPoint.x+w,this.lastPoint.y,x-w,y,x,y]:pathString+="L"+x+","+y,this.lastPoint={x:x,y:y},pathString)}}),Ico.SparkLine=function(){this.initialize.apply(this,arguments)},Ico.SparkLine.prototype={initialize:function(element,data,options){this.element=element,this.data=data,this.options={width:parseInt(getStyle(element,"width"),10),height:parseInt(getStyle(element,"height"),10),highlight:!1,background_colour:getStyle(element,"backgroundColor")||"#ffffff",colour:"#036"},Helpers.extend(this.options,options||{}),this.step=this.calculateStep(),this.paper=Raphael(this.element,this.options.width,this.options.height),this.options.acceptable_range?this.background=this.paper.rect(0,this.options.height-this.normalise(this.options.acceptable_range[1]),this.options.width,this.options.height-this.normalise(this.options.acceptable_range[0])):this.background=this.paper.rect(0,0,this.options.width,this.options.height),this.background.attr({fill:this.options.background_colour,stroke:"none"}),this.draw()},calculateStep:function(){return this.options.width/validStepDivider(this.data.length)},normalise:function(value){return this.options.height/Helpers.max(this.data)*value},draw:function(){var data=this.normaliseData(this.data);this.drawLines("",this.options.colour,data),this.options.highlight&&this.showHighlight(data)},drawLines:function(label,colour,data){var pathString="",x=0,values=data.slice(1),i=0;pathString="M0,"+(this.options.height-data[0]);for(i=1;i<data.length;i++)x+=this.step,pathString+="L"+x+","+Ico.round(this.options.height-data[i],2);this.paper.path(pathString).attr({stroke:colour}),this.lastPoint={x:0,y:this.options.height-data[0]}},showHighlight:function(data){var size=2,x=this.options.width-size,i=this.options.highlight.index||data.length-1,y=data[i]+Math.round(size/2);typeof this.options.highlight.index!="undefined"&&(x=this.step*this.options.highlight.index);var circle=this.paper.circle(x,this.options.height-y,size);circle.attr({stroke:!1,fill:this.options.highlight.colour})}},Helpers.extend(Ico.SparkLine.prototype,Ico.Base),Ico.SparkBar=function(){this.initialize.apply(this,arguments)},Helpers.extend(Ico.SparkBar.prototype,Ico.SparkLine.prototype),Helpers.extend(Ico.SparkBar.prototype,{calculateStep:function(){return this.options.width/validStepDivider(this.data.length)},drawLines:function(label,colour,data){var width=this.step>2?this.step-1:this.step,x=width,pathString="",i=0;for(i=0;i<data.length;i++)pathString+="M"+x+","+(this.options.height-data[i]),pathString+="L"+x+","+this.options.height,x+=this.step;this.paper.path(pathString).attr({stroke:colour,"stroke-width":width})}}),global.Ico=Ico,typeof exports!="undefined"&&(module.exports=Ico)})(typeof window=="undefined"?this:window);
+ */(function(global){var Ico={VERSION:"0.3.5",round:function(num,dec){var result=Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);return result}};function validStepDivider(value){return value>1?value-1:1}function getStyle(el,styleProp){if(typeof window=="undefined")return;var style;return el.currentStyle?style=el.currentStyle[styleProp]:window.getComputedStyle&&(style=document.defaultView.getComputedStyle(el,null).getPropertyValue(styleProp)),style&&style.length===0&&(style=null),style}var Helpers={};Helpers.sum=function(a){var i,sum;for(i=0,sum=0;i<a.length;sum+=a[i++]);return sum},typeof Array.prototype.max=="undefined"?Helpers.max=function(a){return Math.max.apply({},a)}:Helpers.max=function(a){return a.max()},typeof Array.prototype.min=="undefined"?Helpers.min=function(a){return Math.min.apply({},a)}:Helpers.min=function(a){return a.min()},Helpers.mean=function(a){return Helpers.sum(a)/a.length},Helpers.variance=function(a){var mean=Helpers.mean(a),variance=0;for(var i=0;i<a.length;i++)variance+=Math.pow(a[i]-mean,2);return variance/(a.length-1)},Helpers.standard_deviation=function(a){return Math.sqrt(Helpers.variance(a))},typeof Object.extend=="undefined"?Helpers.extend=function(destination,source){for(var property in source)source.hasOwnProperty(property)&&(destination[property]=source[property]);return destination}:Helpers.extend=Object.extend,Object.keys?Helpers.keys=Object.keys:Helpers.keys=function(o){if(o!==Object(o))throw new TypeError("Object.keys called on non-object");var ret=[],p;for(p in o)Object.prototype.hasOwnProperty.call(o,p)&&ret.push(p);return ret},Ico.Normaliser=function(data,options){this.options={start_value:null},typeof options!="undefined"&&(this.options=options),this.min=Helpers.min(data),this.max=Helpers.max(data),this.standard_deviation=Helpers.standard_deviation(data),this.range=0,this.step=this.labelStep(this.max-this.min),this.start_value=this.calculateStart(),this.process()},Ico.Normaliser.prototype={calculateStart:function(){var min=typeof this.options.start_value!="undefined"&&this.min>=0?this.options.start_value:this.min,start_value=this.round(min,1);return this.min>0&&start_value>this.min?0:this.min===this.max?0:start_value},round:function(value,offset){offset=offset||1;var roundedValue=value;if(this.standard_deviation>.1){var multiplier=Math.pow(10,-offset);roundedValue=Math.round(value*multiplier)/multiplier;if(roundedValue>this.min)return this.round(value-this.step)}return roundedValue},process:function(){this.range=this.max-this.start_value,this.step=this.labelStep(this.range)},labelStep:function(value){return Math.pow(10,Math.round(Math.log(value)/Math.LN10)-1)}},Ico.Base={normaliseData:function(data){var values=[],i=0;for(i=0;i<data.length;i++)values.push(this.normalise(data[i]));return values},flatten:function(data){var flat_data=[];if(typeof data.length=="undefined"){if(typeof data!="object")return[];for(var key in data)data.hasOwnProperty(key)&&(flat_data=flat_data.concat(this.flatten(data[key])))}for(var i=0;i<data.length;i++)typeof data[i].length=="number"?flat_data=flat_data.concat(this.flatten(data[i])):flat_data.push(data[i]);return flat_data},makeRange:function(start,end){var values=[],i;for(i=start;i<end;i++)values.push(i);return values}},Ico.BaseGraph=function(){this.initialize.apply(this,arguments)},Helpers.extend(Ico.BaseGraph.prototype,Ico.Base),Helpers.extend(Ico.BaseGraph.prototype,{initialize:function(element,data,options){options=options||{},this.element=element,this.data_sets=this.buildDataSets(data,options),this.flat_data=this.flatten(data),this.data_size=this.longestDataSetLength(),this.plottedCoords=[];if(options&&options.colour){options.colours={};for(var key in this.data_sets)this.data_sets.hasOwnProperty(key)&&(options.colours[key]=options.colour)}this.options={width:parseInt(getStyle(element,"width"),10),height:parseInt(getStyle(element,"height"),10),labels:this.makeRange(1,this.data_size+1),plot_padding:10,font_size:10,show_horizontal_labels:!0,show_vertical_labels:!0,background_colour:getStyle(element,"backgroundColor")||"#ffffff",label_colour:"#666",markers:!1,marker_size:5,meanline:!1,grid:!1,grid_colour:"#ccc",y_padding_top:20,draw:!0,bar_labels:!1},Helpers.extend(this.options,this.chartDefaults()||{}),Helpers.extend(this.options,options),this.font_options={font:this.options.font_size+'px "Arial"',stroke:"none",fill:"#000"},this.normaliser=new Ico.Normaliser(this.flat_data,this.normaliserOptions()),this.label_step=options.label_step||this.normaliser.step,this.range=this.normaliser.range,this.start_value=options.start_value||this.normaliser.start_value,this.x_padding_left=10+this.paddingLeftOffset(),this.x_padding_right=20,this.x_padding=this.x_padding_left+this.x_padding_right,this.y_padding_top=this.options.y_padding_top,this.y_padding_bottom=20+this.paddingBottomOffset(),this.y_padding=this.y_padding_top+this.y_padding_bottom,this.graph_width=this.options.width-this.x_padding,this.graph_height=this.options.height-this.y_padding,this.step=this.calculateStep(),this.options.bar_labels&&(this.range+=this.normaliser.step),options.label_count?this.y_label_count=options.label_count:(this.y_label_count=Math.ceil(this.range/this.label_step),this.normaliser.min+this.y_label_count*this.normaliser.step<this.normaliser.max&&(this.y_label_count+=1)),this.value_labels=this.makeValueLabels(this.y_label_count),this.top_value=this.value_labels[this.value_labels.length-1],this.grid_start_offset=-1,this.options.draw&&(typeof this.options.colours=="undefined"&&(this.options.colours=this.makeRandomColours()),this.paper=Raphael(this.element,this.options.width,this.options.height),this.background=this.paper.rect(0,0,this.options.width,this.options.height),this.background.attr({fill:this.options.background_colour,stroke:"none"}),this.options.meanline===!0&&(this.options.meanline={"stroke-width":"2px",stroke:"#BBBBBB"}),this.setChartSpecificOptions(),this.lastPoint={x:0,y:0},this.draw())},buildDataSets:function(data,options){return typeof data.length!="undefined"?{one:data}:data},normaliserOptions:function(){return{}},chartDefaults:function(){return{}},drawPlot:function(index,pathString,x,y,colour){},calculateStep:function(){},makeRandomColours:function(){var colours={};for(var key in this.data_sets)colours.hasOwnProperty(key)||(colours[key]=Raphael.hsb2rgb(Math.random(),1,.75).hex);return colours},longestDataSetLength:function(){if(this.grouped)return this.flat_data.length;var length=0;for(var key in this.data_sets)this.data_sets.hasOwnProperty(key)&&(length=this.data_sets[key].length>length?this.data_sets[key].length:length);return length},roundValue:function(value,length){var multiplier=Math.pow(10,length);return value*=multiplier,value=Math.round(value)/multiplier,value},roundValues:function(data,length){var roundedData=[],i;for(i=0;i<data.length;i++)roundedData.push(this.roundValue(data[i],length));return roundedData},longestLabel:function(values){var labels=Array.prototype.slice.call(values||this.options.labels,0);return labels.length?labels.sort(function(a,b){return a.toString().length<b.toString().length})[0].toString().length:0},paddingLeftOffset:function(){var data=this.roundValues(this.flat_data,2),longest_label_length=0;return longest_label_length=data.sort(function(a,b){return a.toString().length<b.toString().length})[0].toString().length,longest_label_length=longest_label_length>2?longest_label_length-1:longest_label_length,10+longest_label_length*this.options.font_size},paddingBottomOffset:function(){return this.options.font_size},normalise:function(value){var total=this.start_value===0?this.top_value:this.range;return value/total*this.graph_height},draw:function(){this.options.grid&&this.drawGrid(),this.options.meanline&&this.drawMeanLine(this.normaliseData(this.flat_data)),this.drawAxis(),this.options.show_vertical_labels&&this.drawVerticalLabels(),this.options.show_horizontal_labels&&this.drawHorizontalLabels();if(this.grouped)this.drawLines(key,null,this.normaliseData(this.flat_data));else for(var key in this.data_sets)if(this.data_sets.hasOwnProperty(key)){var data=this.data_sets[key];this.drawLines(key,this.options.colours[key],this.normaliseData(data))}this.start_value!==0&&this.drawFocusHint()},drawGrid:function(){var pathString="",i,y;if(this.options.show_vertical_labels){y=this.graph_height+this.y_padding_top;for(i=0;i<this.y_label_count;i++)y-=this.graph_height/this.y_label_count,pathString+="M"+this.x_padding_left+","+y,pathString+="L"+(this.x_padding_left+this.graph_width)+","+y}if(this.options.show_horizontal_labels){var x=this.x_padding_left+this.options.plot_padding+this.grid_start_offset,x_labels=this.grouped?this.flat_data.length:this.options.labels.length,i;for(i=0;i<x_labels;i++)pathString+="M"+x+","+this.y_padding_top,pathString+="L"+x+","+(this.y_padding_top+this.graph_height),x+=this.step;x=x-this.options.plot_padding-1,pathString+="M"+x+","+this.y_padding_top,pathString+="L"+x+","+(this.y_padding_top+this.graph_height)}this.paper.path(pathString).attr({stroke:this.options.grid_colour,"stroke-width":"1px"})},drawLines:function(label,colour,data){var coords=this.calculateCoords(data),pathString="",i,x,y;for(i=0;i<coords.length;i++)x=coords[i][0]||0,y=coords[i][1]||0,this.grouped&&this.options.colours&&(colour=this.options.colours[i%this.group_size]),this.plottedCoords.push([x+this.bar_padding,y]),pathString=this.drawPlot(i,pathString,x,y,colour);this.paper.path(pathString).attr({stroke:colour,"stroke-width":"3px"}),this.options.bar_labels&&this.drawBarMarkers()},calculateCoords:function(data){var x=this.x_padding_left+this.options.plot_padding-this.step,y_offset=this.graph_height+this.y_padding_top+this.normalise(this.start_value),y=0,coords=[],i;for(i=0;i<data.length;i++)y=y_offset-data[i],x+=this.step,coords.push([x,y]);return coords},drawFocusHint:function(){var length=5,x=this.x_padding_left+length/2-1,y=this.options.height-this.y_padding_bottom,pathString="";pathString+="M"+x+","+y,pathString+="L"+(x-length)+","+(y-length),pathString+="M"+x+","+(y-length),pathString+="L"+(x-length)+","+(y-length*2),this.paper.path(pathString).attr({stroke:this.options.label_colour,"stroke-width":2})},drawMeanLine:function(data){var offset=Helpers.sum(data)/data.length,pathString="";pathString+="M"+(this.x_padding_left-1)+","+(this.options.height-this.y_padding_bottom-offset),pathString+="L"+(this.graph_width+this.x_padding_left)+","+(this.options.height-this.y_padding_bottom-offset),this.paper.path(pathString).attr(this.options.meanline)},drawAxis:function(){var pathString="";pathString+="M"+(this.x_padding_left-1)+","+(this.options.height-this.y_padding_bottom),pathString+="L"+(this.graph_width+this.x_padding_left)+","+(this.options.height-this.y_padding_bottom),pathString+="M"+(this.x_padding_left-1)+","+(this.options.height-this.y_padding_bottom),pathString+="L"+(this.x_padding_left-1)+","+this.y_padding_top,this.paper.path(pathString).attr({stroke:this.options.label_colour})},makeValueLabels:function(steps){var step=this.label_step,label=this.start_value,labels=[];for(var i=0;i<steps;i++)label=this.roundValue(label+step,2),labels.push(label);return labels},drawMarkers:function(labels,direction,step,start_offset,font_offsets,extra_font_options){function x_offset(value){return value*direction[0]}function y_offset(value){return value*direction[1]}var x=this.x_padding_left-1+x_offset(start_offset),y=this.options.height-this.y_padding_bottom+y_offset(start_offset),pathString="",i,font_options={};Helpers.extend(font_options,this.font_options),Helpers.extend(font_options,extra_font_options||{});for(i=0;i<labels.length;i++)pathString+="M"+x+","+y,typeof labels[i]!="undefined"&&(labels[i]+"").length>0&&(pathString+="L"+(x+y_offset(5))+","+(y+x_offset(5)),this.paper.text(x+font_offsets[0],y-font_offsets[1],labels[i]).attr(font_options).toFront()),x+=x_offset(step),y+=y_offset(step);this.paper.path(pathString).attr({stroke:this.options.label_colour})},drawVerticalLabels:function(){var y_step=this.graph_height/this.y_label_count;this.drawMarkers(this.value_labels,[0,-1],y_step,y_step,[-8,-2],{"text-anchor":"end"})},drawHorizontalLabels:function(){this.drawMarkers(this.options.labels,[1,0],this.step,this.options.plot_padding,[0,(this.options.font_size+7)*-1])}}),Ico.BarGraph=function(){this.initialize.apply(this,arguments)},Helpers.extend(Ico.BarGraph.prototype,Ico.BaseGraph.prototype),Helpers.extend(Ico.BarGraph.prototype,{buildDataSets:function(data,options){if(typeof data.length!="undefined"){if(typeof data[0].length!="undefined"){this.grouped=!0,this.group_size=data[0].length;var o={},k,i=0;for(k in options.labels)k=options.labels[k],o[k]=data[i],i++;return o}return{one:data}}return data},chartDefaults:function(){return{plot_padding:0}},normaliserOptions:function(){return{start_value:0}},setChartSpecificOptions:function(){this.bar_padding=this.options.bar_padding||5,this.bar_width=this.options.bar_size||this.calculateBarWidth(),this.options.bar_size&&!this.options.bar_padding&&(this.bar_padding=this.graph_width/this.data_size),this.options.plot_padding=this.bar_width/2-this.bar_padding/2,this.step=this.calculateStep(),this.grid_start_offset=this.bar_padding-1,this.start_y=this.options.height-this.y_padding_bottom},calculateBarWidth:function(){var width=this.graph_width/this.data_size-this.bar_padding;return!this.grouped,this.options.max_bar_size&&width>this.options.max_bar_size&&(width=this.options.max_bar_size,this.bar_padding=this.graph_width/this.data_size),width},calculateStep:function(){return(this.graph_width-this.options.plot_padding*2-this.bar_padding*2)/validStepDivider(this.data_size)},drawPlot:function(index,pathString,x,y,colour){return x+=this.bar_padding,pathString+="M"+x+","+this.start_y,pathString+="L"+x+","+y,this.paper.path(pathString).attr({stroke:colour,"stroke-width":this.bar_width+"px"}),pathString="",x+=this.step,pathString+="M"+x+","+this.start_y,pathString},drawHorizontalLabels:function(){var x_start=this.bar_padding+this.options.plot_padding,step=this.step;this.grouped&&(step*=this.group_size,x_start=(this.bar_width*this.group_size+this.bar_padding*this.group_size)/2,x_start=this.roundValue(x_start,0)),this.drawMarkers(this.options.labels,[1,0],step,x_start,[0,(this.options.font_size+7)*-1])},drawBarMarkers:function(){if(this.plottedCoords.length===0)return;var i,length=this.flat_data.length,x,y,label,font_options={};Helpers.extend(font_options,this.font_options),font_options["text-anchor"]="center";for(i=0;i<length;i++)label=this.roundValue(this.flat_data[i],2).toString(),x=this.plottedCoords[i][0],y=this.roundValue(this.plottedCoords[i][1],0),this.paper.text(x,y-this.options.font_size,label).attr(font_options).toFront()}}),Ico.HorizontalBarGraph=function(){this.initialize.apply(this,arguments)},Helpers.extend(Ico.HorizontalBarGraph.prototype,Ico.BaseGraph.prototype),Helpers.extend(Ico.HorizontalBarGraph.prototype,{setChartSpecificOptions:function(){this.y_padding_top=0,this.x_padding_left=20+this.longestLabel()*(this.options.font_size/2),this.bar_padding=this.options.bar_padding||5,this.bar_width=this.options.bar_size||this.calculateBarHeight(),this.options.bar_size&&!this.options.bar_padding&&(this.bar_padding=this.graph_height/this.data_size),this.options.plot_padding=0,this.step=this.calculateStep()},normalise:function(value){var offset=this.x_padding_left;return value/this.range*(this.graph_width-offset)},calculateBarHeight:function(){var height=this.graph_height/this.data_size-this.bar_padding;return this.options.max_bar_size&&height>this.options.max_bar_size&&(height=this.options.max_bar_size,this.bar_padding=this.graph_height/this.data_size),height},calculateStep:function(){return(this.options.height-this.y_padding_bottom)/validStepDivider(this.data_size)},drawLines:function(label,colour,data){var x=this.x_padding_left+this.options.plot_padding*2,y=this.options.height-this.y_padding_bottom-this.step/2,pathString="M"+x+","+y,i;for(i=0;i<data.length;i++)pathString+="L"+(x+data[i]-this.normalise(this.start_value))+","+y,y-=this.step,pathString+="M"+x+","+y;this.paper.path(pathString).attr({stroke:colour,"stroke-width":this.bar_width+"px"})},drawFocusHint:function(){var length=5,x=this.x_padding_left+this.step*2,y=this.options.height-this.y_padding_bottom,pathString="";pathString+="M"+x+","+y,pathString+="L"+(x-length)+","+(y+length),pathString+="M"+(x-length)+","+y,pathString+="L"+(x-length*2)+","+(y+length),this.paper.path(pathString).attr({stroke:this.options.label_colour,"stroke-width":2})},drawVerticalLabels:function(){var y_start=this.step/2-this.options.plot_padding*2;this.drawMarkers(this.options.labels,[0,-1],this.step,y_start,[-8,this.options.font_size/8],{"text-anchor":"end"})},drawHorizontalLabels:function(){var x_step=this.graph_width/this.y_label_count,x_labels=this.makeValueLabels(this.y_label_count);this.drawMarkers(x_labels,[1,0],x_step,x_step,[0,(this.options.font_size+7)*-1])}}),Ico.LineGraph=function(){this.initialize.apply(this,arguments)},Helpers.extend(Ico.LineGraph.prototype,Ico.BaseGraph.prototype),Helpers.extend(Ico.LineGraph.prototype,{normalise:function(value){var total=this.start_value===0?this.top_value:this.top_value-this.start_value;return value/total*this.graph_height},chartDefaults:function(){return{plot_padding:10}},setChartSpecificOptions:function(){var longestLabel=this.longestLabel(this.value_labels);this.x_padding_left=30+longestLabel*(this.options.font_size/2),typeof this.options.curve_amount=="undefined"&&(this.options.curve_amount=10)},normaliserOptions:function(){return{start_value:this.options.start_value}},calculateStep:function(){return(this.graph_width-this.options.plot_padding*2)/validStepDivider(this.data_size)},startPlot:function(pathString,x,y,colour){return this.lastPoint={x:x,y:y},pathString+"M"+x+","+y},drawPlot:function(index,pathString,x,y,colour){var w=this.options.curve_amount;if(this.options.markers==="circle"){var circle=this.paper.circle(x,y,this.options.marker_size);circle.attr({"stroke-width":"1px",stroke:this.options.background_colour,fill:colour})}return index===0?this.startPlot(pathString,x,y,colour):(w?pathString+=["C",this.lastPoint.x+w,this.lastPoint.y,x-w,y,x,y]:pathString+="L"+x+","+y,this.lastPoint={x:x,y:y},pathString)}}),Ico.SparkLine=function(){this.initialize.apply(this,arguments)},Ico.SparkLine.prototype={initialize:function(element,data,options){this.element=element,this.data=data,this.options={width:parseInt(getStyle(element,"width"),10),height:parseInt(getStyle(element,"height"),10),highlight:!1,background_colour:getStyle(element,"backgroundColor")||"#ffffff",colour:"#036"},Helpers.extend(this.options,options||{}),this.step=this.calculateStep(),this.paper=Raphael(this.element,this.options.width,this.options.height),this.options.acceptable_range?this.background=this.paper.rect(0,this.options.height-this.normalise(this.options.acceptable_range[1]),this.options.width,this.options.height-this.normalise(this.options.acceptable_range[0])):this.background=this.paper.rect(0,0,this.options.width,this.options.height),this.background.attr({fill:this.options.background_colour,stroke:"none"}),this.draw()},calculateStep:function(){return this.options.width/validStepDivider(this.data.length)},normalise:function(value){return this.options.height/Helpers.max(this.data)*value},draw:function(){var data=this.normaliseData(this.data);this.drawLines("",this.options.colour,data),this.options.highlight&&this.showHighlight(data)},drawLines:function(label,colour,data){var pathString="",x=0,values=data.slice(1),i=0;pathString="M0,"+(this.options.height-data[0]);for(i=1;i<data.length;i++)x+=this.step,pathString+="L"+x+","+Ico.round(this.options.height-data[i],2);this.paper.path(pathString).attr({stroke:colour}),this.lastPoint={x:0,y:this.options.height-data[0]}},showHighlight:function(data){var size=2,x=this.options.width-size,i=this.options.highlight.index||data.length-1,y=data[i]+Math.round(size/2);typeof this.options.highlight.index!="undefined"&&(x=this.step*this.options.highlight.index);var circle=this.paper.circle(x,this.options.height-y,size);circle.attr({stroke:!1,fill:this.options.highlight.colour})}},Helpers.extend(Ico.SparkLine.prototype,Ico.Base),Ico.SparkBar=function(){this.initialize.apply(this,arguments)},Helpers.extend(Ico.SparkBar.prototype,Ico.SparkLine.prototype),Helpers.extend(Ico.SparkBar.prototype,{calculateStep:function(){return this.options.width/validStepDivider(this.data.length)},drawLines:function(label,colour,data){var width=this.step>2?this.step-1:this.step,x=width,pathString="",i=0;for(i=0;i<data.length;i++)pathString+="M"+x+","+(this.options.height-data[i]),pathString+="L"+x+","+this.options.height,x+=this.step;this.paper.path(pathString).attr({stroke:colour,"stroke-width":width})}}),global.Ico=Ico,typeof exports!="undefined"&&(module.exports=Ico)})(typeof window=="undefined"?this:window);
View
2 src/end.js
@@ -6,5 +6,5 @@
if (typeof exports !== 'undefined') {
module.exports = Ico;
}
-})(typeof window === 'undefined' ? this : window);
+}(typeof window === 'undefined' ? this : window));
View
38 src/graphs/bar.js
@@ -9,6 +9,28 @@
Ico.BarGraph = function() { this.initialize.apply(this, arguments); };
Helpers.extend(Ico.BarGraph.prototype, Ico.BaseGraph.prototype);
Helpers.extend(Ico.BarGraph.prototype, {
+ // Overridden to handle grouped bar graphs
+ buildDataSets: function(data, options) {
+ if (typeof data.length !== 'undefined') {
+ if (typeof data[0].length !== 'undefined') {
+ this.grouped = true;
+ // TODO: Find longest?
+ this.group_size = data[0].length;
+ var o = {}, k, i = 0;
+ for (k in options.labels) {
+ k = options.labels[k];
+ o[k] = data[i];
+ i++;
+ }
+ return o;
+ } else {
+ return { 'one': data };
+ }
+ } else {
+ return data;
+ }
+ },
+
/**
* Sensible defaults for BarGraph.
*/
@@ -48,6 +70,10 @@ Helpers.extend(Ico.BarGraph.prototype, {
calculateBarWidth: function() {
var width = (this.graph_width / this.data_size) - this.bar_padding;
+ if (this.grouped) {
+ //width = width / this.group_size - (this.bar_padding * this.group_size);
+ }
+
if (this.options.max_bar_size && width > this.options.max_bar_size) {
width = this.options.max_bar_size;
this.bar_padding = this.graph_width / this.data_size;
@@ -79,7 +105,7 @@ Helpers.extend(Ico.BarGraph.prototype, {
x = x + this.bar_padding;
pathString += 'M' + x + ',' + this.start_y;
pathString += 'L' + x + ',' + y;
- this.paper.path(pathString).attr({stroke: colour, 'stroke-width': this.bar_width + 'px'});
+ this.paper.path(pathString).attr({ stroke: colour, 'stroke-width': this.bar_width + 'px' });
pathString = '';
x = x + this.step;
pathString += 'M' + x + ',' + this.start_y;
@@ -88,8 +114,14 @@ Helpers.extend(Ico.BarGraph.prototype, {
/* Change the standard options to correctly offset against the bars */
drawHorizontalLabels: function() {
- var x_start = this.bar_padding + this.options.plot_padding;
- this.drawMarkers(this.options.labels, [1, 0], this.step, x_start, [0, (this.options.font_size + 7) * -1]);
+ var x_start = this.bar_padding + this.options.plot_padding,
+ step = this.step;
+ if (this.grouped) {
+ step = step * this.group_size;
+ x_start = ((this.bar_width * this.group_size) + (this.bar_padding * this.group_size)) / 2
+ x_start = this.roundValue(x_start, 0);
+ }
+ this.drawMarkers(this.options.labels, [1, 0], step, x_start, [0, (this.options.font_size + 7) * -1]);
},
drawBarMarkers: function() {
View
38 src/graphs/base.js
@@ -156,6 +156,12 @@ Helpers.extend(Ico.BaseGraph.prototype, {
},
longestDataSetLength: function() {
+ if (this.grouped) {
+ // Return the total number of grouped values rather than
+ // the longest array of data
+ return this.flat_data.length;
+ }
+
var length = 0;
for (var key in this.data_sets) {
if (this.data_sets.hasOwnProperty(key)) {
@@ -173,8 +179,8 @@ Helpers.extend(Ico.BaseGraph.prototype, {
},
roundValues: function(data, length) {
- var roundedData = [];
- for (var i = 0; i < data.length; i++) {
+ var roundedData = [], i;
+ for (i = 0; i < data.length; i++) {
roundedData.push(this.roundValue(data[i], length));
}
return roundedData;
@@ -230,10 +236,14 @@ Helpers.extend(Ico.BaseGraph.prototype, {
this.drawHorizontalLabels();
}
- for (var key in this.data_sets) {
- if (this.data_sets.hasOwnProperty(key)) {
- var data = this.data_sets[key];
- this.drawLines(key, this.options.colours[key], this.normaliseData(data));
+ if (this.grouped) {
+ this.drawLines(key, null, this.normaliseData(this.flat_data));
+ } else {
+ for (var key in this.data_sets) {
+ if (this.data_sets.hasOwnProperty(key)) {
+ var data = this.data_sets[key];
+ this.drawLines(key, this.options.colours[key], this.normaliseData(data));
+ }
}
}
@@ -253,10 +263,10 @@ Helpers.extend(Ico.BaseGraph.prototype, {
pathString += 'L' + (this.x_padding_left + this.graph_width) + ',' + y;
}
}
-
+
if (this.options.show_horizontal_labels) {
var x = this.x_padding_left + this.options.plot_padding + this.grid_start_offset,
- x_labels = this.options.labels.length,
+ x_labels = this.grouped ? this.flat_data.length : this.options.labels.length,
i;
for (i = 0; i < x_labels; i++) {
@@ -281,10 +291,15 @@ Helpers.extend(Ico.BaseGraph.prototype, {
for (i = 0; i < coords.length; i++) {
x = coords[i][0] || 0;
y = coords[i][1] || 0;
- this.plottedCoords.push([x + this.bar_padding, y]);
+
+ if (this.grouped && this.options.colours) {
+ colour = this.options.colours[i % this.group_size];
+ }
+
+ this.plottedCoords.push([x + this.bar_padding, y]);
pathString = this.drawPlot(i, pathString, x, y, colour);
}
-
+
this.paper.path(pathString).attr({ stroke: colour, 'stroke-width': '3px' });
if (this.options.bar_labels) {
@@ -370,7 +385,7 @@ Helpers.extend(Ico.BaseGraph.prototype, {
font_options = {};
Helpers.extend(font_options, this.font_options);
Helpers.extend(font_options, extra_font_options || {});
-
+
for (i = 0; i < labels.length; i++) {
pathString += 'M' + x + ',' + y;
if (typeof labels[i] !== 'undefined' && (labels[i] + '').length > 0) {
@@ -393,4 +408,3 @@ Helpers.extend(Ico.BaseGraph.prototype, {
this.drawMarkers(this.options.labels, [1, 0], this.step, this.options.plot_padding, [0, (this.options.font_size + 7) * -1]);
}
});
-
View
25 src/helpers.js
@@ -17,7 +17,9 @@ function validStepDivider(value) {
* @returns {Object} The style value
*/
function getStyle(el, styleProp) {
- if (typeof window === 'undefined') return;
+ if (typeof window === 'undefined') {
+ return;
+ }
var style;
if (el.currentStyle) {
@@ -34,7 +36,8 @@ function getStyle(el, styleProp) {
var Helpers = {};
Helpers.sum = function(a) {
- for (var i = 0, sum = 0; i < a.length; sum += a[i++]) {}
+ var i, sum;
+ for (i = 0, sum = 0; i < a.length; sum += a[i++]) {}
return sum;
};
@@ -87,3 +90,21 @@ if (typeof Object.extend === 'undefined') {
} else {
Helpers.extend = Object.extend;
}
+
+if (Object.keys) {
+ Helpers.keys = Object.keys;
+} else {
+ Helpers.keys = function(o) {
+ if (o !== Object(o)) {
+ throw new TypeError('Object.keys called on non-object');
+ }
+
+ var ret = [], p;
+ for (p in o) {
+ if (Object.prototype.hasOwnProperty.call(o,p)) {
+ ret.push(p);
+ }
+ }
+ return ret;
+ }
+}

0 comments on commit 793f9eb

Please sign in to comment.