123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437 |
- /**
- * jqPlot
- * Pure JavaScript plotting plugin using jQuery
- *
- * Version: @VERSION
- * Revision: @REVISION
- *
- * Copyright (c) 2009-2013 Chris Leonello
- * jqPlot is currently available for use in all personal or commercial projects
- * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
- * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
- * choose the license that best suits your project and use it accordingly.
- *
- * Although not required, the author would appreciate an email letting him
- * know of any substantial use of jqPlot. You can reach the author at:
- * chris at jqplot dot com or see http://www.jqplot.com/info.php .
- *
- * If you are feeling kind and generous, consider supporting the project by
- * making a donation at: http://www.jqplot.com/donate.php .
- *
- * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
- *
- * version 2007.04.27
- * author Ash Searle
- * http://hexmen.com/blog/2007/03/printf-sprintf/
- * http://hexmen.com/js/sprintf.js
- * The author (Ash Searle) has placed this code in the public domain:
- * "This code is unrestricted: you are free to use it however you like."
- *
- */
- (function($) {
- /**
- * Class: $.jqplot.MekkoRenderer
- * Draws a Mekko style chart which shows 3 dimensional data on a 2 dimensional graph.
- * the <$.jqplot.MekkoAxisRenderer> should be used with mekko charts. The mekko renderer
- * overrides the default legend renderer with its own $.jqplot.MekkoLegendRenderer
- * which allows more flexibility to specify number of rows and columns in the legend.
- *
- * Data is specified per bar in the chart. You can specify data as an array of y values, or as
- * an array of [label, value] pairs. Note that labels are used only on the first series.
- * Labels on subsequent series are ignored:
- *
- * > bar1 = [['shirts', 8],['hats', 14],['shoes', 6],['gloves', 16],['dolls', 12]];
- * > bar2 = [15,6,9,13,6];
- * > bar3 = [['grumpy',4],['sneezy',2],['happy',7],['sleepy',9],['doc',7]];
- *
- * If you want to place labels for each bar under the axis, you use the barLabels option on
- * the axes. The bar labels can be styled with the ".jqplot-mekko-barLabel" css class.
- *
- * > barLabels = ['Mickey Mouse', 'Donald Duck', 'Goofy'];
- * > axes:{xaxis:{barLabels:barLabels}}
- *
- */
-
-
- $.jqplot.MekkoRenderer = function(){
- this.shapeRenderer = new $.jqplot.ShapeRenderer();
- // prop: borderColor
- // color of the borders between areas on the chart
- this.borderColor = null;
- // prop: showBorders
- // True to draw borders lines between areas on the chart.
- // False will draw borders lines with the same color as the area.
- this.showBorders = true;
- };
-
- // called with scope of series.
- $.jqplot.MekkoRenderer.prototype.init = function(options, plot) {
- this.fill = false;
- this.fillRect = true;
- this.strokeRect = true;
- this.shadow = false;
- // width of bar on x axis.
- this._xwidth = 0;
- this._xstart = 0;
- $.extend(true, this.renderer, options);
- // set the shape renderer options
- var opts = {lineJoin:'miter', lineCap:'butt', isarc:false, fillRect:this.fillRect, strokeRect:this.strokeRect};
- this.renderer.shapeRenderer.init(opts);
- plot.axes.x2axis._series.push(this);
- this._type = 'mekko';
- };
-
- // Method: setGridData
- // converts the user data values to grid coordinates and stores them
- // in the gridData array. Will convert user data into appropriate
- // rectangles.
- // Called with scope of a series.
- $.jqplot.MekkoRenderer.prototype.setGridData = function(plot) {
- // recalculate the grid data
- var xp = this._xaxis.series_u2p;
- var yp = this._yaxis.series_u2p;
- var data = this._plotData;
- this.gridData = [];
- // figure out width on x axis.
- // this._xwidth = this._sumy / plot._sumy * this.canvas.getWidth();
- this._xwidth = xp(this._sumy) - xp(0);
- if (this.index>0) {
- this._xstart = plot.series[this.index-1]._xstart + plot.series[this.index-1]._xwidth;
- }
- var totheight = this.canvas.getHeight();
- var sumy = 0;
- var cury;
- var curheight;
- for (var i=0; i<data.length; i++) {
- if (data[i] != null) {
- sumy += data[i][1];
- cury = totheight - (sumy / this._sumy * totheight);
- curheight = data[i][1] / this._sumy * totheight;
- this.gridData.push([this._xstart, cury, this._xwidth, curheight]);
- }
- }
- };
-
- // Method: makeGridData
- // converts any arbitrary data values to grid coordinates and
- // returns them. This method exists so that plugins can use a series'
- // linerenderer to generate grid data points without overwriting the
- // grid data associated with that series.
- // Called with scope of a series.
- $.jqplot.MekkoRenderer.prototype.makeGridData = function(data, plot) {
- // recalculate the grid data
- // figure out width on x axis.
- var xp = this._xaxis.series_u2p;
- var totheight = this.canvas.getHeight();
- var sumy = 0;
- var cury;
- var curheight;
- var gd = [];
- for (var i=0; i<data.length; i++) {
- if (data[i] != null) {
- sumy += data[i][1];
- cury = totheight - (sumy / this._sumy * totheight);
- curheight = data[i][1] / this._sumy * totheight;
- gd.push([this._xstart, cury, this._xwidth, curheight]);
- }
- }
- return gd;
- };
-
- // called within scope of series.
- $.jqplot.MekkoRenderer.prototype.draw = function(ctx, gd, options) {
- var i;
- var opts = (options != undefined) ? options : {};
- var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
- var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors);
- ctx.save();
- if (gd.length) {
- if (showLine) {
- for (i=0; i<gd.length; i++){
- opts.fillStyle = colorGenerator.next();
- if (this.renderer.showBorders) {
- opts.strokeStyle = this.renderer.borderColor;
- }
- else {
- opts.strokeStyle = opts.fillStyle;
- }
- this.renderer.shapeRenderer.draw(ctx, gd[i], opts);
- }
- }
- }
-
- ctx.restore();
- };
-
- $.jqplot.MekkoRenderer.prototype.drawShadow = function(ctx, gd, options) {
- // This is a no-op, no shadows on mekko charts.
- };
-
- /**
- * Class: $.jqplot.MekkoLegendRenderer
- * Legend renderer used by mekko charts with options for
- * controlling number or rows and columns as well as placement
- * outside of plot area.
- *
- */
- $.jqplot.MekkoLegendRenderer = function(){
- //
- };
-
- $.jqplot.MekkoLegendRenderer.prototype.init = function(options) {
- // prop: numberRows
- // Maximum number of rows in the legend. 0 or null for unlimited.
- this.numberRows = null;
- // prop: numberColumns
- // Maximum number of columns in the legend. 0 or null for unlimited.
- this.numberColumns = null;
- // this will override the placement option on the Legend object
- this.placement = "outside";
- $.extend(true, this, options);
- };
-
- // called with scope of legend
- $.jqplot.MekkoLegendRenderer.prototype.draw = function() {
- var legend = this;
- if (this.show) {
- var series = this._series;
- var ss = 'position:absolute;';
- ss += (this.background) ? 'background:'+this.background+';' : '';
- ss += (this.border) ? 'border:'+this.border+';' : '';
- ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
- ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : '';
- ss += (this.textColor) ? 'color:'+this.textColor+';' : '';
- this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>');
- // Mekko charts legends don't go by number of series, but by number of data points
- // in the series. Refactor things here for that.
-
- var pad = false,
- reverse = true, // mekko charts are always stacked, so reverse
- nr, nc;
- var s = series[0];
- var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors);
-
- if (s.show) {
- var pd = s.data;
- if (this.numberRows) {
- nr = this.numberRows;
- if (!this.numberColumns){
- nc = Math.ceil(pd.length/nr);
- }
- else{
- nc = this.numberColumns;
- }
- }
- else if (this.numberColumns) {
- nc = this.numberColumns;
- nr = Math.ceil(pd.length/this.numberColumns);
- }
- else {
- nr = pd.length;
- nc = 1;
- }
-
- var i, j, tr, td1, td2, lt, rs, color;
- var idx = 0;
-
- for (i=0; i<nr; i++) {
- if (reverse){
- tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem);
- }
- else{
- tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem);
- }
- for (j=0; j<nc; j++) {
- if (idx < pd.length) {
- lt = this.labels[idx] || pd[idx][0].toString();
- color = colorGenerator.next();
- if (!reverse){
- if (i>0){
- pad = true;
- }
- else{
- pad = false;
- }
- }
- else{
- if (i == nr -1){
- pad = false;
- }
- else{
- pad = true;
- }
- }
- rs = (pad) ? this.rowSpacing : '0';
-
- td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+
- '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+
- '</div></td>');
- td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
- if (this.escapeHtml){
- td2.text(lt);
- }
- else {
- td2.html(lt);
- }
- if (reverse) {
- td2.prependTo(tr);
- td1.prependTo(tr);
- }
- else {
- td1.appendTo(tr);
- td2.appendTo(tr);
- }
- pad = true;
- }
- idx++;
- }
- }
- tr = null;
- td1 = null;
- td2 = null;
- }
- }
- return this._elem;
- };
-
- $.jqplot.MekkoLegendRenderer.prototype.pack = function(offsets) {
- if (this.show) {
- // fake a grid for positioning
- var grid = {_top:offsets.top, _left:offsets.left, _right:offsets.right, _bottom:this._plotDimensions.height - offsets.bottom};
- if (this.placement == 'insideGrid') {
- switch (this.location) {
- case 'nw':
- var a = grid._left + this.xoffset;
- var b = grid._top + this.yoffset;
- this._elem.css('left', a);
- this._elem.css('top', b);
- break;
- case 'n':
- var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
- var b = grid._top + this.yoffset;
- this._elem.css('left', a);
- this._elem.css('top', b);
- break;
- case 'ne':
- var a = offsets.right + this.xoffset;
- var b = grid._top + this.yoffset;
- this._elem.css({right:a, top:b});
- break;
- case 'e':
- var a = offsets.right + this.xoffset;
- var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
- this._elem.css({right:a, top:b});
- break;
- case 'se':
- var a = offsets.right + this.xoffset;
- var b = offsets.bottom + this.yoffset;
- this._elem.css({right:a, bottom:b});
- break;
- case 's':
- var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
- var b = offsets.bottom + this.yoffset;
- this._elem.css({left:a, bottom:b});
- break;
- case 'sw':
- var a = grid._left + this.xoffset;
- var b = offsets.bottom + this.yoffset;
- this._elem.css({left:a, bottom:b});
- break;
- case 'w':
- var a = grid._left + this.xoffset;
- var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
- this._elem.css({left:a, top:b});
- break;
- default: // same as 'se'
- var a = grid._right - this.xoffset;
- var b = grid._bottom + this.yoffset;
- this._elem.css({right:a, bottom:b});
- break;
- }
-
- }
- else {
- switch (this.location) {
- case 'nw':
- var a = this._plotDimensions.width - grid._left + this.xoffset;
- var b = grid._top + this.yoffset;
- this._elem.css('right', a);
- this._elem.css('top', b);
- break;
- case 'n':
- var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
- var b = this._plotDimensions.height - grid._top + this.yoffset;
- this._elem.css('left', a);
- this._elem.css('bottom', b);
- break;
- case 'ne':
- var a = this._plotDimensions.width - offsets.right + this.xoffset;
- var b = grid._top + this.yoffset;
- this._elem.css({left:a, top:b});
- break;
- case 'e':
- var a = this._plotDimensions.width - offsets.right + this.xoffset;
- var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
- this._elem.css({left:a, top:b});
- break;
- case 'se':
- var a = this._plotDimensions.width - offsets.right + this.xoffset;
- var b = offsets.bottom + this.yoffset;
- this._elem.css({left:a, bottom:b});
- break;
- case 's':
- var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
- var b = this._plotDimensions.height - offsets.bottom + this.yoffset;
- this._elem.css({left:a, top:b});
- break;
- case 'sw':
- var a = this._plotDimensions.width - grid._left + this.xoffset;
- var b = offsets.bottom + this.yoffset;
- this._elem.css({right:a, bottom:b});
- break;
- case 'w':
- var a = this._plotDimensions.width - grid._left + this.xoffset;
- var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
- this._elem.css({right:a, top:b});
- break;
- default: // same as 'se'
- var a = grid._right - this.xoffset;
- var b = grid._bottom + this.yoffset;
- this._elem.css({right:a, bottom:b});
- break;
- }
- }
- }
- };
-
- // setup default renderers for axes and legend so user doesn't have to
- // called with scope of plot
- function preInit(target, data, options) {
- options = options || {};
- options.axesDefaults = options.axesDefaults || {};
- options.legend = options.legend || {};
- options.seriesDefaults = options.seriesDefaults || {};
- var setopts = false;
- if (options.seriesDefaults.renderer == $.jqplot.MekkoRenderer) {
- setopts = true;
- }
- else if (options.series) {
- for (var i=0; i < options.series.length; i++) {
- if (options.series[i].renderer == $.jqplot.MekkoRenderer) {
- setopts = true;
- }
- }
- }
-
- if (setopts) {
- options.axesDefaults.renderer = $.jqplot.MekkoAxisRenderer;
- options.legend.renderer = $.jqplot.MekkoLegendRenderer;
- options.legend.preDraw = true;
- }
- }
-
- $.jqplot.preInitHooks.push(preInit);
-
- })(jQuery);
|