@@ -1,4 +1,5 @@ | |||||
*.pyc | *.pyc | ||||
*.comp.js | *.comp.js | ||||
*.DS_Store | *.DS_Store | ||||
user_files | |||||
defs.py | defs.py |
@@ -43,7 +43,7 @@ class DocType: | |||||
r.save(1) | r.save(1) | ||||
def on_update(self, from_update=0): | def on_update(self, from_update=0): | ||||
import webnotes.defs | |||||
from webnotes import defs | |||||
from webnotes.utils.transfer import in_transfer | from webnotes.utils.transfer import in_transfer | ||||
if (not in_transfer) and getattr(defs,'developer_mode', 0): | if (not in_transfer) and getattr(defs,'developer_mode', 0): | ||||
@@ -1244,13 +1244,11 @@ ul.box_tabs li.box_tab_selected a { | |||||
/* background:url("../images/ui/rc/tab-right-CCC.gif") no-repeat right top; */ | /* background:url("../images/ui/rc/tab-right-CCC.gif") no-repeat right top; */ | ||||
} | } | ||||
/******* jqPlot ***********/ | |||||
/*rules for the plot target div. These will be cascaded down to all plot elements according to css rules*/ | /*rules for the plot target div. These will be cascaded down to all plot elements according to css rules*/ | ||||
.jqplot-target { | .jqplot-target { | ||||
position: relative; | position: relative; | ||||
color: #666666; | color: #666666; | ||||
font-family: Arial, Helvetica, sans-serif; | |||||
font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; | |||||
font-size: 1em; | font-size: 1em; | ||||
/* height: 300px; | /* height: 300px; | ||||
width: 400px;*/ | width: 400px;*/ | ||||
@@ -1308,6 +1306,15 @@ ul.box_tabs li.box_tab_selected a { | |||||
text-align: right; | text-align: right; | ||||
} | } | ||||
.jqplot-yaxis-tick.jqplot-breakTick { | |||||
right: -20px; | |||||
margin-right: 0px; | |||||
padding:1px 5px 1px 5px; | |||||
/* background-color: white;*/ | |||||
z-index: 2; | |||||
font-size: 1.5em; | |||||
} | |||||
.jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick { | .jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick { | ||||
left: 0px; | left: 0px; | ||||
/* initial position untill tick is drawn in proper place */ | /* initial position untill tick is drawn in proper place */ | ||||
@@ -1317,6 +1324,15 @@ ul.box_tabs li.box_tab_selected a { | |||||
text-align: left; | text-align: left; | ||||
} | } | ||||
.jqplot-meterGauge-tick { | |||||
font-size: 0.75em; | |||||
color: #999999; | |||||
} | |||||
.jqplot-meterGauge-label { | |||||
font-size: 1em; | |||||
color: #999999; | |||||
} | |||||
.jqplot-xaxis-label { | .jqplot-xaxis-label { | ||||
margin-top: 10px; | margin-top: 10px; | ||||
font-size: 11pt; | font-size: 11pt; | ||||
@@ -1342,6 +1358,13 @@ ul.box_tabs li.box_tab_selected a { | |||||
position: absolute; | position: absolute; | ||||
} | } | ||||
table.jqplot-table-legend { | |||||
margin-top: 12px; | |||||
margin-bottom: 12px; | |||||
margin-left: 12px; | |||||
margin-right: 12px; | |||||
} | |||||
table.jqplot-table-legend, table.jqplot-cursor-legend { | table.jqplot-table-legend, table.jqplot-cursor-legend { | ||||
background-color: rgba(255,255,255,0.6); | background-color: rgba(255,255,255,0.6); | ||||
border: 1px solid #cccccc; | border: 1px solid #cccccc; | ||||
@@ -1353,18 +1376,22 @@ td.jqplot-table-legend { | |||||
vertical-align:middle; | vertical-align:middle; | ||||
} | } | ||||
td.jqplot-seriesToggle:hover, td.jqplot-seriesToggle:active { | |||||
cursor: pointer; | |||||
} | |||||
td.jqplot-table-legend > div { | td.jqplot-table-legend > div { | ||||
border:1px solid #cccccc; | |||||
padding:0.2em; | |||||
border: 1px solid #cccccc; | |||||
padding:1px; | |||||
} | } | ||||
div.jqplot-table-legend-swatch { | div.jqplot-table-legend-swatch { | ||||
width:0px; | width:0px; | ||||
height:0px; | height:0px; | ||||
border-top-width: 0.35em; | |||||
border-bottom-width: 0.35em; | |||||
border-left-width: 0.6em; | |||||
border-right-width: 0.6em; | |||||
border-top-width: 5px; | |||||
border-bottom-width: 5px; | |||||
border-left-width: 6px; | |||||
border-right-width: 6px; | |||||
border-top-style: solid; | border-top-style: solid; | ||||
border-bottom-style: solid; | border-bottom-style: solid; | ||||
border-left-style: solid; | border-left-style: solid; | ||||
@@ -1402,6 +1429,7 @@ table.jqplot-cursor-tooltip { | |||||
.jqplot-point-label { | .jqplot-point-label { | ||||
font-size: 0.75em; | font-size: 0.75em; | ||||
z-index: 2; | |||||
} | } | ||||
td.jqplot-cursor-legend-swatch { | td.jqplot-cursor-legend-swatch { | ||||
@@ -1414,6 +1442,35 @@ width:1.2em; | |||||
height:0.7em; | height:0.7em; | ||||
} | } | ||||
.jqplot-error { | |||||
/* Styles added to the plot target container when there is an error go here.*/ | |||||
text-align: center; | |||||
} | |||||
.jqplot-error-message { | |||||
/* Styling of the custom error message div goes here.*/ | |||||
position: relative; | |||||
top: 46%; | |||||
display: inline-block; | |||||
} | |||||
div.jqplot-bubble-label { | |||||
font-size: 0.8em; | |||||
/* background: rgba(90%, 90%, 90%, 0.15);*/ | |||||
padding-left: 2px; | |||||
padding-right: 2px; | |||||
color: rgb(20%, 20%, 20%); | |||||
} | |||||
div.jqplot-bubble-label.jqplot-bubble-label-highlight { | |||||
background: rgba(90%, 90%, 90%, 0.7); | |||||
} | |||||
div.jqplot-noData-container { | |||||
text-align: center; | |||||
background-color: rgba(96%, 96%, 96%, 0.3); | |||||
} | |||||
/** general icons **/ | /** general icons **/ | ||||
@@ -1,10 +1,8 @@ | |||||
/******* jqPlot ***********/ | |||||
/*rules for the plot target div. These will be cascaded down to all plot elements according to css rules*/ | /*rules for the plot target div. These will be cascaded down to all plot elements according to css rules*/ | ||||
.jqplot-target { | .jqplot-target { | ||||
position: relative; | position: relative; | ||||
color: #666666; | color: #666666; | ||||
font-family: Arial, Helvetica, sans-serif; | |||||
font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; | |||||
font-size: 1em; | font-size: 1em; | ||||
/* height: 300px; | /* height: 300px; | ||||
width: 400px;*/ | width: 400px;*/ | ||||
@@ -62,6 +60,15 @@ | |||||
text-align: right; | text-align: right; | ||||
} | } | ||||
.jqplot-yaxis-tick.jqplot-breakTick { | |||||
right: -20px; | |||||
margin-right: 0px; | |||||
padding:1px 5px 1px 5px; | |||||
/* background-color: white;*/ | |||||
z-index: 2; | |||||
font-size: 1.5em; | |||||
} | |||||
.jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick { | .jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick { | ||||
left: 0px; | left: 0px; | ||||
/* initial position untill tick is drawn in proper place */ | /* initial position untill tick is drawn in proper place */ | ||||
@@ -71,6 +78,15 @@ | |||||
text-align: left; | text-align: left; | ||||
} | } | ||||
.jqplot-meterGauge-tick { | |||||
font-size: 0.75em; | |||||
color: #999999; | |||||
} | |||||
.jqplot-meterGauge-label { | |||||
font-size: 1em; | |||||
color: #999999; | |||||
} | |||||
.jqplot-xaxis-label { | .jqplot-xaxis-label { | ||||
margin-top: 10px; | margin-top: 10px; | ||||
font-size: 11pt; | font-size: 11pt; | ||||
@@ -96,6 +112,13 @@ | |||||
position: absolute; | position: absolute; | ||||
} | } | ||||
table.jqplot-table-legend { | |||||
margin-top: 12px; | |||||
margin-bottom: 12px; | |||||
margin-left: 12px; | |||||
margin-right: 12px; | |||||
} | |||||
table.jqplot-table-legend, table.jqplot-cursor-legend { | table.jqplot-table-legend, table.jqplot-cursor-legend { | ||||
background-color: rgba(255,255,255,0.6); | background-color: rgba(255,255,255,0.6); | ||||
border: 1px solid #cccccc; | border: 1px solid #cccccc; | ||||
@@ -107,18 +130,22 @@ td.jqplot-table-legend { | |||||
vertical-align:middle; | vertical-align:middle; | ||||
} | } | ||||
td.jqplot-seriesToggle:hover, td.jqplot-seriesToggle:active { | |||||
cursor: pointer; | |||||
} | |||||
td.jqplot-table-legend > div { | td.jqplot-table-legend > div { | ||||
border:1px solid #cccccc; | |||||
padding:0.2em; | |||||
border: 1px solid #cccccc; | |||||
padding:1px; | |||||
} | } | ||||
div.jqplot-table-legend-swatch { | div.jqplot-table-legend-swatch { | ||||
width:0px; | width:0px; | ||||
height:0px; | height:0px; | ||||
border-top-width: 0.35em; | |||||
border-bottom-width: 0.35em; | |||||
border-left-width: 0.6em; | |||||
border-right-width: 0.6em; | |||||
border-top-width: 5px; | |||||
border-bottom-width: 5px; | |||||
border-left-width: 6px; | |||||
border-right-width: 6px; | |||||
border-top-style: solid; | border-top-style: solid; | ||||
border-bottom-style: solid; | border-bottom-style: solid; | ||||
border-left-style: solid; | border-left-style: solid; | ||||
@@ -156,6 +183,7 @@ table.jqplot-cursor-tooltip { | |||||
.jqplot-point-label { | .jqplot-point-label { | ||||
font-size: 0.75em; | font-size: 0.75em; | ||||
z-index: 2; | |||||
} | } | ||||
td.jqplot-cursor-legend-swatch { | td.jqplot-cursor-legend-swatch { | ||||
@@ -167,3 +195,32 @@ div.jqplot-cursor-legend-swatch { | |||||
width:1.2em; | width:1.2em; | ||||
height:0.7em; | height:0.7em; | ||||
} | } | ||||
.jqplot-error { | |||||
/* Styles added to the plot target container when there is an error go here.*/ | |||||
text-align: center; | |||||
} | |||||
.jqplot-error-message { | |||||
/* Styling of the custom error message div goes here.*/ | |||||
position: relative; | |||||
top: 46%; | |||||
display: inline-block; | |||||
} | |||||
div.jqplot-bubble-label { | |||||
font-size: 0.8em; | |||||
/* background: rgba(90%, 90%, 90%, 0.15);*/ | |||||
padding-left: 2px; | |||||
padding-right: 2px; | |||||
color: rgb(20%, 20%, 20%); | |||||
} | |||||
div.jqplot-bubble-label.jqplot-bubble-label-highlight { | |||||
background: rgba(90%, 90%, 90%, 0.7); | |||||
} | |||||
div.jqplot-noData-container { | |||||
text-align: center; | |||||
background-color: rgba(96%, 96%, 96%, 0.3); | |||||
} |
@@ -0,0 +1,312 @@ | |||||
/** | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 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.BezierCurveRenderer.js | |||||
// Renderer which draws lines as stacked bezier curves. | |||||
// Data for the line will not be specified as an array of | |||||
// [x, y] data point values, but as a an array of [start piont, bezier curve] | |||||
// So, the line is specified as: [[xstart, ystart], [cp1x, cp1y, cp2x, cp2y, xend, yend]]. | |||||
$.jqplot.BezierCurveRenderer = function(){ | |||||
$.jqplot.LineRenderer.call(this); | |||||
}; | |||||
$.jqplot.BezierCurveRenderer.prototype = new $.jqplot.LineRenderer(); | |||||
$.jqplot.BezierCurveRenderer.prototype.constructor = $.jqplot.BezierCurveRenderer; | |||||
// Method: setGridData | |||||
// converts the user data values to grid coordinates and stores them | |||||
// in the gridData array. | |||||
// Called with scope of a series. | |||||
$.jqplot.BezierCurveRenderer.prototype.setGridData = function(plot) { | |||||
// recalculate the grid data | |||||
var xp = this._xaxis.series_u2p; | |||||
var yp = this._yaxis.series_u2p; | |||||
// this._plotData should be same as this.data | |||||
var data = this.data; | |||||
this.gridData = []; | |||||
this._prevGridData = []; | |||||
// if seriesIndex = 0, fill to x axis. | |||||
// if seriesIndex > 0, fill to previous series data. | |||||
var idx = this.index; | |||||
if (data.length == 2) { | |||||
if (idx == 0) { | |||||
this.gridData = [ | |||||
[xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], | |||||
[xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), | |||||
xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]), | |||||
xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])], | |||||
[xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, this._yaxis.min)], | |||||
[xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)] | |||||
]; | |||||
} | |||||
else { | |||||
var psd = plot.series[idx-1].data; | |||||
this.gridData = [ | |||||
[xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], | |||||
[xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), | |||||
xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]), | |||||
xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])], | |||||
[xp.call(this._xaxis, psd[1][4]), yp.call(this._yaxis, psd[1][5])], | |||||
[xp.call(this._xaxis, psd[1][2]), yp.call(this._yaxis, psd[1][3]), | |||||
xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]), | |||||
xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])] | |||||
]; | |||||
} | |||||
} | |||||
else { | |||||
if (idx == 0) { | |||||
this.gridData = [ | |||||
[xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], | |||||
[xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), | |||||
xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]), | |||||
xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])], | |||||
[xp.call(this._xaxis, data[3][1]), yp.call(this._yaxis, this._yaxis.min)], | |||||
[xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)] | |||||
]; | |||||
} | |||||
else { | |||||
var psd = plot.series[idx-1].data; | |||||
this.gridData = [ | |||||
[xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], | |||||
[xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), | |||||
xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]), | |||||
xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])], | |||||
[xp.call(this._xaxis, psd[3][0]), yp.call(this._yaxis, psd[3][1])], | |||||
[xp.call(this._xaxis, psd[2][0]), yp.call(this._yaxis, psd[2][1]), | |||||
xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]), | |||||
xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])] | |||||
]; | |||||
} | |||||
} | |||||
}; | |||||
// 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.BezierCurveRenderer.prototype.makeGridData = function(data, plot) { | |||||
// recalculate the grid data | |||||
var xp = this._xaxis.series_u2p; | |||||
var yp = this._yaxis.series_u2p; | |||||
var gd = []; | |||||
var pgd = []; | |||||
// if seriesIndex = 0, fill to x axis. | |||||
// if seriesIndex > 0, fill to previous series data. | |||||
var idx = this.index; | |||||
if (data.length == 2) { | |||||
if (idx == 0) { | |||||
gd = [ | |||||
[xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], | |||||
[xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), | |||||
xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]), | |||||
xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])], | |||||
[xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, this._yaxis.min)], | |||||
[xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)] | |||||
]; | |||||
} | |||||
else { | |||||
var psd = plot.series[idx-1].data; | |||||
gd = [ | |||||
[xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], | |||||
[xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), | |||||
xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]), | |||||
xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])], | |||||
[xp.call(this._xaxis, psd[1][4]), yp.call(this._yaxis, psd[1][5])], | |||||
[xp.call(this._xaxis, psd[1][2]), yp.call(this._yaxis, psd[1][3]), | |||||
xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]), | |||||
xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])] | |||||
]; | |||||
} | |||||
} | |||||
else { | |||||
if (idx == 0) { | |||||
gd = [ | |||||
[xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], | |||||
[xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), | |||||
xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]), | |||||
xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])], | |||||
[xp.call(this._xaxis, data[3][1]), yp.call(this._yaxis, this._yaxis.min)], | |||||
[xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)] | |||||
]; | |||||
} | |||||
else { | |||||
var psd = plot.series[idx-1].data; | |||||
gd = [ | |||||
[xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], | |||||
[xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), | |||||
xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]), | |||||
xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])], | |||||
[xp.call(this._xaxis, psd[3][0]), yp.call(this._yaxis, psd[3][1])], | |||||
[xp.call(this._xaxis, psd[2][0]), yp.call(this._yaxis, psd[2][1]), | |||||
xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]), | |||||
xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])] | |||||
]; | |||||
} | |||||
} | |||||
return gd; | |||||
}; | |||||
// called within scope of series. | |||||
$.jqplot.BezierCurveRenderer.prototype.draw = function(ctx, gd, options) { | |||||
var i; | |||||
ctx.save(); | |||||
if (gd.length) { | |||||
if (this.showLine) { | |||||
ctx.save(); | |||||
var opts = (options != null) ? options : {}; | |||||
ctx.fillStyle = opts.fillStyle || this.color; | |||||
ctx.beginPath(); | |||||
ctx.moveTo(gd[0][0], gd[0][1]); | |||||
ctx.bezierCurveTo(gd[1][0], gd[1][1], gd[1][2], gd[1][3], gd[1][4], gd[1][5]); | |||||
ctx.lineTo(gd[2][0], gd[2][1]); | |||||
if (gd[3].length == 2) { | |||||
ctx.lineTo(gd[3][0], gd[3][1]); | |||||
} | |||||
else { | |||||
ctx.bezierCurveTo(gd[3][0], gd[3][1], gd[3][2], gd[3][3], gd[3][4], gd[3][5]); | |||||
} | |||||
ctx.closePath(); | |||||
ctx.fill(); | |||||
ctx.restore(); | |||||
} | |||||
} | |||||
ctx.restore(); | |||||
}; | |||||
$.jqplot.BezierCurveRenderer.prototype.drawShadow = function(ctx, gd, options) { | |||||
// This is a no-op, shadows drawn with lines. | |||||
}; | |||||
$.jqplot.BezierAxisRenderer = function() { | |||||
$.jqplot.LinearAxisRenderer.call(this); | |||||
}; | |||||
$.jqplot.BezierAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); | |||||
$.jqplot.BezierAxisRenderer.prototype.constructor = $.jqplot.BezierAxisRenderer; | |||||
// Axes on a plot with Bezier Curves | |||||
$.jqplot.BezierAxisRenderer.prototype.init = function(options){ | |||||
$.extend(true, this, options); | |||||
var db = this._dataBounds; | |||||
// Go through all the series attached to this axis and find | |||||
// the min/max bounds for this axis. | |||||
for (var i=0; i<this._series.length; i++) { | |||||
var s = this._series[i]; | |||||
var d = s.data; | |||||
if (d.length == 4) { | |||||
for (var j=0; j<d.length; j++) { | |||||
if (this.name == 'xaxis' || this.name == 'x2axis') { | |||||
if (d[j][0] < db.min || db.min == null) { | |||||
db.min = d[j][0]; | |||||
} | |||||
if (d[j][0] > db.max || db.max == null) { | |||||
db.max = d[j][0]; | |||||
} | |||||
} | |||||
else { | |||||
if (d[j][1] < db.min || db.min == null) { | |||||
db.min = d[j][1]; | |||||
} | |||||
if (d[j][1] > db.max || db.max == null) { | |||||
db.max = d[j][1]; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
else { | |||||
if (this.name == 'xaxis' || this.name == 'x2axis') { | |||||
if (d[0][0] < db.min || db.min == null) { | |||||
db.min = d[0][0]; | |||||
} | |||||
if (d[0][0] > db.max || db.max == null) { | |||||
db.max = d[0][0]; | |||||
} | |||||
for (var j=0; j<5; j+=2) { | |||||
if (d[1][j] < db.min || db.min == null) { | |||||
db.min = d[1][j]; | |||||
} | |||||
if (d[1][j] > db.max || db.max == null) { | |||||
db.max = d[1][j]; | |||||
} | |||||
} | |||||
} | |||||
else { | |||||
if (d[0][1] < db.min || db.min == null) { | |||||
db.min = d[0][1]; | |||||
} | |||||
if (d[0][1] > db.max || db.max == null) { | |||||
db.max = d[0][1]; | |||||
} | |||||
for (var j=1; j<6; j+=2) { | |||||
if (d[1][j] < db.min || db.min == null) { | |||||
db.min = d[1][j]; | |||||
} | |||||
if (d[1][j] > db.max || db.max == null) { | |||||
db.max = d[1][j]; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
}; | |||||
// 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 = $.extend(true, {pad:0}, options.axesDefaults); | |||||
options.legend = $.extend(true, {placement:'outside'}, options.legend); | |||||
// only set these if there is a pie series | |||||
var setopts = false; | |||||
if (options.seriesDefaults.renderer == $.jqplot.BezierCurveRenderer) { | |||||
setopts = true; | |||||
} | |||||
else if (options.series) { | |||||
for (var i=0; i < options.series.length; i++) { | |||||
if (options.series[i].renderer == $.jqplot.BezierCurveRenderer) { | |||||
setopts = true; | |||||
} | |||||
} | |||||
} | |||||
if (setopts) { | |||||
options.axesDefaults.renderer = $.jqplot.BezierAxisRenderer; | |||||
} | |||||
} | |||||
$.jqplot.preInitHooks.push(preInit); | |||||
})(jQuery); |
@@ -1,18 +1,30 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* The author would appreciate an email letting him know of any substantial | |||||
* use of jqPlot. You can reach the author at: chris dot leonello at gmail | |||||
* dot com or see http://www.jqplot.com/info.php . This is, of course, | |||||
* not required. | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * making a donation at: http://www.jqplot.com/donate.php . | ||||
* | * | ||||
* Thanks for using jqPlot! | |||||
* 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($) { | (function($) { | ||||
@@ -29,7 +41,7 @@ | |||||
$.jqplot.BarRenderer.prototype.constructor = $.jqplot.BarRenderer; | $.jqplot.BarRenderer.prototype.constructor = $.jqplot.BarRenderer; | ||||
// called with scope of series. | // called with scope of series. | ||||
$.jqplot.BarRenderer.prototype.init = function(options) { | |||||
$.jqplot.BarRenderer.prototype.init = function(options, plot) { | |||||
// Group: Properties | // Group: Properties | ||||
// | // | ||||
// prop: barPadding | // prop: barPadding | ||||
@@ -58,9 +70,35 @@ | |||||
// prop: waterfall | // prop: waterfall | ||||
// true to enable waterfall plot. | // true to enable waterfall plot. | ||||
this.waterfall = false; | this.waterfall = false; | ||||
// prop: groups | |||||
// group bars into this many groups | |||||
this.groups = 1; | |||||
// prop: varyBarColor | // prop: varyBarColor | ||||
// true to color each bar separately. | |||||
// true to color each bar of a series separately rather than | |||||
// have every bar of a given series the same color. | |||||
// If used for non-stacked multiple series bar plots, user should | |||||
// specify a separate 'seriesColors' array for each series. | |||||
// Otherwise, each series will set their bars to the same color array. | |||||
// This option has no Effect for stacked bar charts and is disabled. | |||||
this.varyBarColor = false; | this.varyBarColor = false; | ||||
// prop: highlightMouseOver | |||||
// True to highlight slice when moused over. | |||||
// This must be false to enable highlightMouseDown to highlight when clicking on a slice. | |||||
this.highlightMouseOver = true; | |||||
// prop: highlightMouseDown | |||||
// True to highlight when a mouse button is pressed over a slice. | |||||
// This will be disabled if highlightMouseOver is true. | |||||
this.highlightMouseDown = false; | |||||
// prop: highlightColors | |||||
// an array of colors to use when highlighting a bar. | |||||
this.highlightColors = []; | |||||
this._type = 'bar'; | |||||
// if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver | |||||
if (options.highlightMouseDown && options.highlightMouseOver == null) { | |||||
options.highlightMouseOver = false; | |||||
} | |||||
$.extend(true, this, options); | $.extend(true, this, options); | ||||
// fill is still needed to properly draw the legend. | // fill is still needed to properly draw the legend. | ||||
// bars have to be filled. | // bars have to be filled. | ||||
@@ -81,12 +119,28 @@ | |||||
this._stackAxis = 'x'; | this._stackAxis = 'x'; | ||||
this.fillAxis = 'x'; | this.fillAxis = 'x'; | ||||
} | } | ||||
// index of the currenty highlighted point, if any | |||||
this._highlightedPoint = null; | |||||
// total number of values for all bar series, total number of bar series, and position of this series | |||||
this._plotSeriesInfo = null; | |||||
// Array of actual data colors used for each data point. | |||||
this._dataColors = []; | |||||
this._barPoints = []; | |||||
// set the shape renderer options | // set the shape renderer options | ||||
var opts = {lineJoin:'miter', lineCap:'round', fill:true, isarc:false, strokeStyle:this.color, fillStyle:this.color, closePath:this.fill}; | var opts = {lineJoin:'miter', lineCap:'round', fill:true, isarc:false, strokeStyle:this.color, fillStyle:this.color, closePath:this.fill}; | ||||
this.renderer.shapeRenderer.init(opts); | this.renderer.shapeRenderer.init(opts); | ||||
// set the shadow renderer options | // set the shadow renderer options | ||||
var sopts = {lineJoin:'miter', lineCap:'round', fill:true, isarc:false, angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, depth:this.shadowDepth, closePath:this.fill}; | var sopts = {lineJoin:'miter', lineCap:'round', fill:true, isarc:false, angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, depth:this.shadowDepth, closePath:this.fill}; | ||||
this.renderer.shadowRenderer.init(sopts); | this.renderer.shadowRenderer.init(sopts); | ||||
plot.postInitHooks.addOnce(postInit); | |||||
plot.postDrawHooks.addOnce(postPlotDraw); | |||||
plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); | |||||
plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown); | |||||
plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp); | |||||
plot.eventListenerHooks.addOnce('jqplotClick', handleClick); | |||||
plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); | |||||
}; | }; | ||||
// called with scope of series | // called with scope of series | ||||
@@ -108,6 +162,24 @@ | |||||
this.data[this.data.length] = (pos == 1) ? [this.data.length+1, sum] : [sum, this.data.length+1]; | this.data[this.data.length] = (pos == 1) ? [this.data.length+1, sum] : [sum, this.data.length+1]; | ||||
this._data[this._data.length] = (pos == 1) ? [this._data.length+1, sum] : [sum, this._data.length+1]; | this._data[this._data.length] = (pos == 1) ? [this._data.length+1, sum] : [sum, this._data.length+1]; | ||||
} | } | ||||
if (this.rendererOptions.groups > 1) { | |||||
this.breakOnNull = true; | |||||
var l = this.data.length; | |||||
var skip = parseInt(l/this.rendererOptions.groups, 10); | |||||
var count = 0; | |||||
for (var i=skip; i<l; i+=skip) { | |||||
this.data.splice(i+count, 0, [null, null]); | |||||
count++; | |||||
} | |||||
for (i=0; i<this.data.length; i++) { | |||||
if (this._primaryAxis == '_xaxis') { | |||||
this.data[i][0] = i+1; | |||||
} | |||||
else { | |||||
this.data[i][1] = i+1; | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
$.jqplot.preSeriesInitHooks.push(barPreInit); | $.jqplot.preSeriesInitHooks.push(barPreInit); | ||||
@@ -131,6 +203,7 @@ | |||||
nseries += 1; | nseries += 1; | ||||
} | } | ||||
} | } | ||||
// return total number of values for all bar series, total number of bar series, and position of this series | |||||
return [nvals, nseries, pos]; | return [nvals, nseries, pos]; | ||||
}; | }; | ||||
@@ -141,7 +214,7 @@ | |||||
var nseries = 0; | var nseries = 0; | ||||
var paxis = this[this._primaryAxis]; | var paxis = this[this._primaryAxis]; | ||||
var s, series, pos; | var s, series, pos; | ||||
var temp = this.renderer.calcSeriesNumbers.call(this); | |||||
var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this); | |||||
nvals = temp[0]; | nvals = temp[0]; | ||||
nseries = temp[1]; | nseries = temp[1]; | ||||
var nticks = paxis.numberTicks; | var nticks = paxis.numberTicks; | ||||
@@ -167,10 +240,27 @@ | |||||
} | } | ||||
return [nvals, nseries]; | return [nvals, nseries]; | ||||
}; | }; | ||||
function computeHighlightColors (colors) { | |||||
var ret = []; | |||||
for (var i=0; i<colors.length; i++){ | |||||
var rgba = $.jqplot.getColorComponents(colors[i]); | |||||
var newrgb = [rgba[0], rgba[1], rgba[2]]; | |||||
var sum = newrgb[0] + newrgb[1] + newrgb[2]; | |||||
for (var j=0; j<3; j++) { | |||||
// when darkening, lowest color component can be is 60. | |||||
newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]); | |||||
newrgb[j] = parseInt(newrgb[j], 10); | |||||
} | |||||
ret.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')'); | |||||
} | |||||
return ret; | |||||
} | |||||
$.jqplot.BarRenderer.prototype.draw = function(ctx, gridData, options) { | $.jqplot.BarRenderer.prototype.draw = function(ctx, gridData, options) { | ||||
var i; | var i; | ||||
var opts = (options != undefined) ? options : {}; | |||||
// Ughhh, have to make a copy of options b/c it may be modified later. | |||||
var opts = $.extend({}, options); | |||||
var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; | var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; | ||||
var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; | var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; | ||||
var fill = (opts.fill != undefined) ? opts.fill : this.fill; | var fill = (opts.fill != undefined) ? opts.fill : this.fill; | ||||
@@ -178,16 +268,20 @@ | |||||
var yaxis = this.yaxis; | var yaxis = this.yaxis; | ||||
var xp = this._xaxis.series_u2p; | var xp = this._xaxis.series_u2p; | ||||
var yp = this._yaxis.series_u2p; | var yp = this._yaxis.series_u2p; | ||||
var pointx, pointy, nvals, nseries, pos; | |||||
var pointx, pointy; | |||||
// clear out data colors. | |||||
this._dataColors = []; | |||||
this._barPoints = []; | |||||
if (this.barWidth == null) { | if (this.barWidth == null) { | ||||
this.renderer.setBarWidth.call(this); | this.renderer.setBarWidth.call(this); | ||||
} | } | ||||
var temp = this.renderer.calcSeriesNumbers.call(this); | |||||
nvals = temp[0]; | |||||
nseries = temp[1]; | |||||
pos = temp[2]; | |||||
var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this); | |||||
var nvals = temp[0]; | |||||
var nseries = temp[1]; | |||||
var pos = temp[2]; | |||||
var points = []; | |||||
if (this._stack) { | if (this._stack) { | ||||
this._barNudge = 0; | this._barNudge = 0; | ||||
@@ -203,12 +297,18 @@ | |||||
negativeColor = opts.fillStyle; | negativeColor = opts.fillStyle; | ||||
} | } | ||||
var positiveColor = opts.fillStyle; | var positiveColor = opts.fillStyle; | ||||
var base; | |||||
var xstart; | |||||
var ystart; | |||||
if (this.barDirection == 'vertical') { | if (this.barDirection == 'vertical') { | ||||
for (var i=0; i<gridData.length; i++) { | for (var i=0; i<gridData.length; i++) { | ||||
if (this.data[i][1] == null) { | |||||
continue; | |||||
} | |||||
points = []; | points = []; | ||||
var base = gridData[i][0] + this._barNudge; | |||||
var ystart; | |||||
base = gridData[i][0] + this._barNudge; | |||||
ystart; | |||||
// stacked | // stacked | ||||
if (this._stack && this._prevGridData.length) { | if (this._stack && this._prevGridData.length) { | ||||
@@ -222,12 +322,34 @@ | |||||
else if (this.waterfall && i > 0 && i < this.gridData.length-1) { | else if (this.waterfall && i > 0 && i < this.gridData.length-1) { | ||||
ystart = this.gridData[i-1][1]; | ystart = this.gridData[i-1][1]; | ||||
} | } | ||||
else if (this.waterfall && i == 0 && i < this.gridData.length-1) { | |||||
if (this._yaxis.min <= 0 && this._yaxis.max >= 0) { | |||||
ystart = this._yaxis.series_u2p(0); | |||||
} | |||||
else if (this._yaxis.min > 0) { | |||||
ystart = ctx.canvas.height; | |||||
} | |||||
else { | |||||
ystart = 0; | |||||
} | |||||
} | |||||
else if (this.waterfall && i == this.gridData.length - 1) { | |||||
if (this._yaxis.min <= 0 && this._yaxis.max >= 0) { | |||||
ystart = this._yaxis.series_u2p(0); | |||||
} | |||||
else if (this._yaxis.min > 0) { | |||||
ystart = ctx.canvas.height; | |||||
} | |||||
else { | |||||
ystart = 0; | |||||
} | |||||
} | |||||
else { | else { | ||||
ystart = ctx.canvas.height; | ystart = ctx.canvas.height; | ||||
} | } | ||||
} | } | ||||
if ((this.fillToZero && this._plotData[i][1] < 0) || (this.waterfall && this._data[i][1] < 0)) { | if ((this.fillToZero && this._plotData[i][1] < 0) || (this.waterfall && this._data[i][1] < 0)) { | ||||
if (this.varyBarColor) { | |||||
if (this.varyBarColor && !this._stack) { | |||||
if (this.useNegativeColors) { | if (this.useNegativeColors) { | ||||
opts.fillStyle = negativeColors.next(); | opts.fillStyle = negativeColors.next(); | ||||
} | } | ||||
@@ -240,32 +362,50 @@ | |||||
} | } | ||||
} | } | ||||
else { | else { | ||||
if (this.varyBarColor) { | |||||
if (this.varyBarColor && !this._stack) { | |||||
opts.fillStyle = positiveColors.next(); | opts.fillStyle = positiveColors.next(); | ||||
} | } | ||||
else { | else { | ||||
opts.fillStyle = positiveColor; | opts.fillStyle = positiveColor; | ||||
} | } | ||||
} | } | ||||
points.push([base-this.barWidth/2, ystart]); | |||||
points.push([base-this.barWidth/2, gridData[i][1]]); | |||||
points.push([base+this.barWidth/2, gridData[i][1]]); | |||||
points.push([base+this.barWidth/2, ystart]); | |||||
if (!this.fillToZero || this._plotData[i][1] >= 0) { | |||||
points.push([base-this.barWidth/2, ystart]); | |||||
points.push([base-this.barWidth/2, gridData[i][1]]); | |||||
points.push([base+this.barWidth/2, gridData[i][1]]); | |||||
points.push([base+this.barWidth/2, ystart]); | |||||
} | |||||
// for negative bars make sure points are always ordered clockwise | |||||
else { | |||||
points.push([base-this.barWidth/2, gridData[i][1]]); | |||||
points.push([base-this.barWidth/2, ystart]); | |||||
points.push([base+this.barWidth/2, ystart]); | |||||
points.push([base+this.barWidth/2, gridData[i][1]]); | |||||
} | |||||
this._barPoints.push(points); | |||||
// now draw the shadows if not stacked. | // now draw the shadows if not stacked. | ||||
// for stacked plots, they are predrawn by drawShadow | // for stacked plots, they are predrawn by drawShadow | ||||
if (shadow && !this._stack) { | if (shadow && !this._stack) { | ||||
this.renderer.shadowRenderer.draw(ctx, points, opts); | |||||
var sopts = $.extend(true, {}, opts); | |||||
// need to get rid of fillStyle on shadow. | |||||
delete sopts.fillStyle; | |||||
this.renderer.shadowRenderer.draw(ctx, points, sopts); | |||||
} | } | ||||
var clr = opts.fillStyle || this.color; | |||||
this._dataColors.push(clr); | |||||
this.renderer.shapeRenderer.draw(ctx, points, opts); | this.renderer.shapeRenderer.draw(ctx, points, opts); | ||||
} | } | ||||
} | } | ||||
else if (this.barDirection == 'horizontal'){ | else if (this.barDirection == 'horizontal'){ | ||||
for (var i=0; i<gridData.length; i++) { | for (var i=0; i<gridData.length; i++) { | ||||
if (this.data[i][0] == null) { | |||||
continue; | |||||
} | |||||
points = []; | points = []; | ||||
var base = gridData[i][1] - this._barNudge; | |||||
var xstart; | |||||
base = gridData[i][1] - this._barNudge; | |||||
xstart; | |||||
if (this._stack && this._prevGridData.length) { | if (this._stack && this._prevGridData.length) { | ||||
xstart = this._prevGridData[i][0]; | xstart = this._prevGridData[i][0]; | ||||
@@ -278,12 +418,34 @@ | |||||
else if (this.waterfall && i > 0 && i < this.gridData.length-1) { | else if (this.waterfall && i > 0 && i < this.gridData.length-1) { | ||||
xstart = this.gridData[i-1][1]; | xstart = this.gridData[i-1][1]; | ||||
} | } | ||||
else if (this.waterfall && i == 0 && i < this.gridData.length-1) { | |||||
if (this._xaxis.min <= 0 && this._xaxis.max >= 0) { | |||||
xstart = this._xaxis.series_u2p(0); | |||||
} | |||||
else if (this._xaxis.min > 0) { | |||||
xstart = 0; | |||||
} | |||||
else { | |||||
xstart = ctx.canvas.width; | |||||
} | |||||
} | |||||
else if (this.waterfall && i == this.gridData.length - 1) { | |||||
if (this._xaxis.min <= 0 && this._xaxis.max >= 0) { | |||||
xstart = this._xaxis.series_u2p(0); | |||||
} | |||||
else if (this._xaxis.min > 0) { | |||||
xstart = 0; | |||||
} | |||||
else { | |||||
xstart = ctx.canvas.width; | |||||
} | |||||
} | |||||
else { | else { | ||||
xstart = 0; | xstart = 0; | ||||
} | } | ||||
} | } | ||||
if ((this.fillToZero && this._plotData[i][1] < 0) || (this.waterfall && this._data[i][1] < 0)) { | if ((this.fillToZero && this._plotData[i][1] < 0) || (this.waterfall && this._data[i][1] < 0)) { | ||||
if (this.varyBarColor) { | |||||
if (this.varyBarColor && !this._stack) { | |||||
if (this.useNegativeColors) { | if (this.useNegativeColors) { | ||||
opts.fillStyle = negativeColors.next(); | opts.fillStyle = negativeColors.next(); | ||||
} | } | ||||
@@ -293,7 +455,7 @@ | |||||
} | } | ||||
} | } | ||||
else { | else { | ||||
if (this.varyBarColor) { | |||||
if (this.varyBarColor && !this._stack) { | |||||
opts.fillStyle = positiveColors.next(); | opts.fillStyle = positiveColors.next(); | ||||
} | } | ||||
else { | else { | ||||
@@ -302,19 +464,36 @@ | |||||
} | } | ||||
points.push([xstart, base+this.barWidth/2]); | points.push([xstart, base+this.barWidth/2]); | ||||
points.push([gridData[i][0], base+this.barWidth/2]); | |||||
points.push([gridData[i][0], base-this.barWidth/2]); | |||||
points.push([xstart, base-this.barWidth/2]); | points.push([xstart, base-this.barWidth/2]); | ||||
points.push([gridData[i][0], base-this.barWidth/2]); | |||||
points.push([gridData[i][0], base+this.barWidth/2]); | |||||
this._barPoints.push(points); | |||||
// now draw the shadows if not stacked. | // now draw the shadows if not stacked. | ||||
// for stacked plots, they are predrawn by drawShadow | // for stacked plots, they are predrawn by drawShadow | ||||
if (shadow && !this._stack) { | if (shadow && !this._stack) { | ||||
this.renderer.shadowRenderer.draw(ctx, points, opts); | |||||
var sopts = $.extend(true, {}, opts); | |||||
delete sopts.fillStyle; | |||||
this.renderer.shadowRenderer.draw(ctx, points, sopts); | |||||
} | } | ||||
var clr = opts.fillStyle || this.color; | |||||
this._dataColors.push(clr); | |||||
this.renderer.shapeRenderer.draw(ctx, points, opts); | this.renderer.shapeRenderer.draw(ctx, points, opts); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (this.highlightColors.length == 0) { | |||||
this.highlightColors = computeHighlightColors(this._dataColors); | |||||
} | |||||
else if (typeof(this.highlightColors) == 'string') { | |||||
var temp = this.highlightColors; | |||||
this.highlightColors = []; | |||||
for (var i=0; i<this._dataColors.length; i++) { | |||||
this.highlightColors.push(temp); | |||||
} | |||||
} | |||||
}; | }; | ||||
@@ -329,14 +508,14 @@ | |||||
var yaxis = this.yaxis; | var yaxis = this.yaxis; | ||||
var xp = this._xaxis.series_u2p; | var xp = this._xaxis.series_u2p; | ||||
var yp = this._yaxis.series_u2p; | var yp = this._yaxis.series_u2p; | ||||
var pointx, pointy, nvals, nseries, pos; | |||||
var pointx, points, pointy, nvals, nseries, pos; | |||||
if (this._stack && this.shadow) { | if (this._stack && this.shadow) { | ||||
if (this.barWidth == null) { | if (this.barWidth == null) { | ||||
this.renderer.setBarWidth.call(this); | this.renderer.setBarWidth.call(this); | ||||
} | } | ||||
var temp = this.renderer.calcSeriesNumbers.call(this); | |||||
var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this); | |||||
nvals = temp[0]; | nvals = temp[0]; | ||||
nseries = temp[1]; | nseries = temp[1]; | ||||
pos = temp[2]; | pos = temp[2]; | ||||
@@ -351,6 +530,9 @@ | |||||
if (this.barDirection == 'vertical') { | if (this.barDirection == 'vertical') { | ||||
for (var i=0; i<gridData.length; i++) { | for (var i=0; i<gridData.length; i++) { | ||||
if (this.data[i][1] == null) { | |||||
continue; | |||||
} | |||||
points = []; | points = []; | ||||
var base = gridData[i][0] + this._barNudge; | var base = gridData[i][0] + this._barNudge; | ||||
var ystart; | var ystart; | ||||
@@ -377,6 +559,9 @@ | |||||
else if (this.barDirection == 'horizontal'){ | else if (this.barDirection == 'horizontal'){ | ||||
for (var i=0; i<gridData.length; i++) { | for (var i=0; i<gridData.length; i++) { | ||||
if (this.data[i][0] == null) { | |||||
continue; | |||||
} | |||||
points = []; | points = []; | ||||
var base = gridData[i][1] - this._barNudge; | var base = gridData[i][1] - this._barNudge; | ||||
var xstart; | var xstart; | ||||
@@ -398,7 +583,127 @@ | |||||
} | } | ||||
} | } | ||||
}; | }; | ||||
function postInit(target, data, options) { | |||||
for (var i=0; i<this.series.length; i++) { | |||||
if (this.series[i].renderer.constructor == $.jqplot.BarRenderer) { | |||||
// don't allow mouseover and mousedown at same time. | |||||
if (this.series[i].highlightMouseOver) { | |||||
this.series[i].highlightMouseDown = false; | |||||
} | |||||
} | |||||
} | |||||
this.target.bind('mouseout', {plot:this}, function (ev) { unhighlight(ev.data.plot); }); | |||||
} | |||||
// called within context of plot | |||||
// create a canvas which we can draw on. | |||||
// insert it before the eventCanvas, so eventCanvas will still capture events. | |||||
function postPlotDraw() { | |||||
// Memory Leaks patch | |||||
if (this.plugins.barRenderer && this.plugins.barRenderer.highlightCanvas) { | |||||
this.plugins.barRenderer.highlightCanvas.resetCanvas(); | |||||
this.plugins.barRenderer.highlightCanvas = null; | |||||
} | |||||
this.plugins.barRenderer = {highlightedSeriesIndex:null}; | |||||
this.plugins.barRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); | |||||
this.eventCanvas._elem.before(this.plugins.barRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-barRenderer-highlight-canvas', this._plotDimensions, this)); | |||||
this.plugins.barRenderer.highlightCanvas.setContext(); | |||||
} | |||||
function highlight (plot, sidx, pidx, points) { | |||||
var s = plot.series[sidx]; | |||||
var canvas = plot.plugins.barRenderer.highlightCanvas; | |||||
canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height); | |||||
s._highlightedPoint = pidx; | |||||
plot.plugins.barRenderer.highlightedSeriesIndex = sidx; | |||||
var opts = {fillStyle: s.highlightColors[pidx]}; | |||||
s.renderer.shapeRenderer.draw(canvas._ctx, points, opts); | |||||
canvas = null; | |||||
} | |||||
function unhighlight (plot) { | |||||
var canvas = plot.plugins.barRenderer.highlightCanvas; | |||||
canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); | |||||
for (var i=0; i<plot.series.length; i++) { | |||||
plot.series[i]._highlightedPoint = null; | |||||
} | |||||
plot.plugins.barRenderer.highlightedSeriesIndex = null; | |||||
plot.target.trigger('jqplotDataUnhighlight'); | |||||
canvas = null; | |||||
} | |||||
function handleMove(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; | |||||
var evt1 = jQuery.Event('jqplotDataMouseOver'); | |||||
evt1.pageX = ev.pageX; | |||||
evt1.pageY = ev.pageY; | |||||
plot.target.trigger(evt1, ins); | |||||
if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.barRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { | |||||
var evt = jQuery.Event('jqplotDataHighlight'); | |||||
evt.pageX = ev.pageX; | |||||
evt.pageY = ev.pageY; | |||||
plot.target.trigger(evt, ins); | |||||
highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points); | |||||
} | |||||
} | |||||
else if (neighbor == null) { | |||||
unhighlight (plot); | |||||
} | |||||
} | |||||
function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; | |||||
if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.barRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { | |||||
var evt = jQuery.Event('jqplotDataHighlight'); | |||||
evt.pageX = ev.pageX; | |||||
evt.pageY = ev.pageY; | |||||
plot.target.trigger(evt, ins); | |||||
highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points); | |||||
} | |||||
} | |||||
else if (neighbor == null) { | |||||
unhighlight (plot); | |||||
} | |||||
} | |||||
function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { | |||||
var idx = plot.plugins.barRenderer.highlightedSeriesIndex; | |||||
if (idx != null && plot.series[idx].highlightMouseDown) { | |||||
unhighlight(plot); | |||||
} | |||||
} | |||||
function handleClick(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; | |||||
var evt = jQuery.Event('jqplotDataClick'); | |||||
evt.pageX = ev.pageX; | |||||
evt.pageY = ev.pageY; | |||||
plot.target.trigger(evt, ins); | |||||
} | |||||
} | |||||
function handleRightClick(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; | |||||
var idx = plot.plugins.barRenderer.highlightedSeriesIndex; | |||||
if (idx != null && plot.series[idx].highlightMouseDown) { | |||||
unhighlight(plot); | |||||
} | |||||
var evt = jQuery.Event('jqplotDataRightClick'); | |||||
evt.pageX = ev.pageX; | |||||
evt.pageY = ev.pageY; | |||||
plot.target.trigger(evt, ins); | |||||
} | |||||
} | |||||
})(jQuery); | })(jQuery); |
@@ -0,0 +1,234 @@ | |||||
/** | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 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.BlockRenderer | |||||
* Plugin renderer to draw a x-y block chart. A Block chart has data points displayed as | |||||
* colored squares with a text label inside. Data must be supplied in the form: | |||||
* | |||||
* > [[x1, y1, "label 1", {css}], [x2, y2, "label 2", {css}], ...] | |||||
* | |||||
* The label and css object are optional. If the label is ommitted, the | |||||
* box will collapse unless a css height and/or width is specified. | |||||
* | |||||
* The css object is an object specifying css properties | |||||
* such as: | |||||
* | |||||
* > {background:'#4f98a5', border:'3px solid gray', padding:'1px'} | |||||
* | |||||
* Note that css properties specified with the data point override defaults | |||||
* specified with the series. | |||||
* | |||||
*/ | |||||
$.jqplot.BlockRenderer = function(){ | |||||
$.jqplot.LineRenderer.call(this); | |||||
}; | |||||
$.jqplot.BlockRenderer.prototype = new $.jqplot.LineRenderer(); | |||||
$.jqplot.BlockRenderer.prototype.constructor = $.jqplot.BlockRenderer; | |||||
// called with scope of a series | |||||
$.jqplot.BlockRenderer.prototype.init = function(options) { | |||||
// Group: Properties | |||||
// | |||||
// prop: css | |||||
// default css styles that will be applied to all data blocks. | |||||
// these values will be overridden by css styles supplied with the | |||||
// individulal data points. | |||||
this.css = {padding:'2px', border:'1px solid #999', textAlign:'center'}; | |||||
// prop: escapeHtml | |||||
// true to escape html in the box label. | |||||
this.escapeHtml = false; | |||||
// prop: insertBreaks | |||||
// true to turn spaces in data block label into html breaks <br />. | |||||
this.insertBreaks = true; | |||||
// prop: varyBlockColors | |||||
// true to vary the color of each block in this series according to | |||||
// the seriesColors array. False to set each block to the color | |||||
// specified on this series. This has no effect if a css background color | |||||
// option is specified in the renderer css options. | |||||
this.varyBlockColors = false; | |||||
$.extend(true, this, options); | |||||
if (this.css.backgroundColor) { | |||||
this.color = this.css.backgroundColor; | |||||
} | |||||
else if (this.css.background) { | |||||
this.color = this.css.background; | |||||
} | |||||
else if (!this.varyBlockColors) { | |||||
this.css.background = this.color; | |||||
} | |||||
this.canvas = new $.jqplot.BlockCanvas(); | |||||
this.shadowCanvas = new $.jqplot.BlockCanvas(); | |||||
this.canvas._plotDimensions = this._plotDimensions; | |||||
this.shadowCanvas._plotDimensions = this._plotDimensions; | |||||
this._type = 'block'; | |||||
// group: Methods | |||||
// | |||||
// Method: moveBlock | |||||
// Moves an individual block. More efficient than redrawing | |||||
// the whole series by calling plot.drawSeries(). | |||||
// Properties: | |||||
// idx - the 0 based index of the block or point in this series. | |||||
// x - the x coordinate in data units (value on x axis) to move the block to. | |||||
// y - the y coordinate in data units (value on the y axis) to move the block to. | |||||
// duration - optional parameter to create an animated movement. Can be a | |||||
// number (higher is slower animation) or 'fast', 'normal' or 'slow'. If not | |||||
// provided, the element is moved without any animation. | |||||
this.moveBlock = function (idx, x, y, duration) { | |||||
// update plotData, stackData, data and gridData | |||||
// x and y are in data coordinates. | |||||
var el = this.canvas._elem.children(':eq('+idx+')'); | |||||
this.data[idx][0] = x; | |||||
this.data[idx][1] = y; | |||||
this._plotData[idx][0] = x; | |||||
this._plotData[idx][1] = y; | |||||
this._stackData[idx][0] = x; | |||||
this._stackData[idx][1] = y; | |||||
this.gridData[idx][0] = this._xaxis.series_u2p(x); | |||||
this.gridData[idx][1] = this._yaxis.series_u2p(y); | |||||
var w = el.outerWidth(); | |||||
var h = el.outerHeight(); | |||||
var left = this.gridData[idx][0] - w/2 + 'px'; | |||||
var top = this.gridData[idx][1] - h/2 + 'px'; | |||||
if (duration) { | |||||
if (parseInt(duration, 10)) { | |||||
duration = parseInt(duration, 10); | |||||
} | |||||
el.animate({left:left, top:top}, duration); | |||||
} | |||||
else { | |||||
el.css({left:left, top:top}); | |||||
} | |||||
el = null; | |||||
}; | |||||
}; | |||||
// called with scope of series | |||||
$.jqplot.BlockRenderer.prototype.draw = function (ctx, gd, options) { | |||||
if (this.plugins.pointLabels) { | |||||
this.plugins.pointLabels.show = false; | |||||
} | |||||
var i, el, d, gd, t, css, w, h, left, top; | |||||
var opts = (options != undefined) ? options : {}; | |||||
var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors); | |||||
this.canvas._elem.empty(); | |||||
for (i=0; i<this.gridData.length; i++) { | |||||
d = this.data[i]; | |||||
gd = this.gridData[i]; | |||||
t = ''; | |||||
css = {}; | |||||
if (typeof d[2] == 'string') { | |||||
t = d[2]; | |||||
} | |||||
else if (typeof d[2] == 'object') { | |||||
css = d[2]; | |||||
} | |||||
if (typeof d[3] == 'object') { | |||||
css = d[3]; | |||||
} | |||||
if (this.insertBreaks){ | |||||
t = t.replace(/ /g, '<br />'); | |||||
} | |||||
css = $.extend(true, {}, this.css, css); | |||||
// create a div | |||||
el = $('<div style="position:absolute;margin-left:auto;margin-right:auto;"></div>'); | |||||
this.canvas._elem.append(el); | |||||
// set text | |||||
this.escapeHtml ? el.text(t) : el.html(t); | |||||
// style it | |||||
// remove styles we don't want overridden. | |||||
delete css.position; | |||||
delete css.marginRight; | |||||
delete css.marginLeft; | |||||
if (!css.background && !css.backgroundColor && !css.backgroundImage){ | |||||
css.background = colorGenerator.next(); | |||||
} | |||||
el.css(css); | |||||
w = el.outerWidth(); | |||||
h = el.outerHeight(); | |||||
left = gd[0] - w/2 + 'px'; | |||||
top = gd[1] - h/2 + 'px'; | |||||
el.css({left:left, top:top}); | |||||
el = null; | |||||
} | |||||
}; | |||||
$.jqplot.BlockCanvas = function() { | |||||
$.jqplot.ElemContainer.call(this); | |||||
this._ctx; | |||||
}; | |||||
$.jqplot.BlockCanvas.prototype = new $.jqplot.ElemContainer(); | |||||
$.jqplot.BlockCanvas.prototype.constructor = $.jqplot.BlockCanvas; | |||||
$.jqplot.BlockCanvas.prototype.createElement = function(offsets, clss, plotDimensions) { | |||||
this._offsets = offsets; | |||||
var klass = 'jqplot-blockCanvas'; | |||||
if (clss != undefined) { | |||||
klass = clss; | |||||
} | |||||
var elem; | |||||
// if this canvas already has a dom element, don't make a new one. | |||||
if (this._elem) { | |||||
elem = this._elem.get(0); | |||||
} | |||||
else { | |||||
elem = document.createElement('div'); | |||||
} | |||||
// if new plotDimensions supplied, use them. | |||||
if (plotDimensions != undefined) { | |||||
this._plotDimensions = plotDimensions; | |||||
} | |||||
var w = this._plotDimensions.width - this._offsets.left - this._offsets.right + 'px'; | |||||
var h = this._plotDimensions.height - this._offsets.top - this._offsets.bottom + 'px'; | |||||
this._elem = $(elem); | |||||
this._elem.css({ position: 'absolute', width:w, height:h, left: this._offsets.left, top: this._offsets.top }); | |||||
this._elem.addClass(klass); | |||||
return this._elem; | |||||
}; | |||||
$.jqplot.BlockCanvas.prototype.setContext = function() { | |||||
this._ctx = { | |||||
canvas:{ | |||||
width:0, | |||||
height:0 | |||||
}, | |||||
clearRect:function(){return null;} | |||||
}; | |||||
return this._ctx; | |||||
}; | |||||
})(jQuery); | |||||
@@ -0,0 +1,30 @@ | |||||
/** | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 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(a){a.jqplot.BlockRenderer=function(){a.jqplot.LineRenderer.call(this)};a.jqplot.BlockRenderer.prototype=new a.jqplot.LineRenderer();a.jqplot.BlockRenderer.prototype.constructor=a.jqplot.BlockRenderer;a.jqplot.BlockRenderer.prototype.init=function(b){this.css={padding:"2px",border:"1px solid #999",textAlign:"center"};this.escapeHtml=false;this.insertBreaks=true;this.varyBlockColors=false;a.extend(true,this,b);if(this.css.backgroundColor){this.color=this.css.backgroundColor}else{if(this.css.background){this.color=this.css.background}else{if(!this.varyBlockColors){this.css.background=this.color}}}this.canvas=new a.jqplot.BlockCanvas();this.shadowCanvas=new a.jqplot.BlockCanvas();this.canvas._plotDimensions=this._plotDimensions;this.shadowCanvas._plotDimensions=this._plotDimensions;this._type="block";this.moveBlock=function(l,j,i,e){var c=this.canvas._elem.children(":eq("+l+")");this.data[l][0]=j;this.data[l][1]=i;this._plotData[l][0]=j;this._plotData[l][1]=i;this._stackData[l][0]=j;this._stackData[l][1]=i;this.gridData[l][0]=this._xaxis.series_u2p(j);this.gridData[l][1]=this._yaxis.series_u2p(i);var k=c.outerWidth();var f=c.outerHeight();var d=this.gridData[l][0]-k/2+"px";var g=this.gridData[l][1]-f/2+"px";if(e){if(parseInt(e,10)){e=parseInt(e,10)}c.animate({left:d,top:g},e)}else{c.css({left:d,top:g})}c=null}};a.jqplot.BlockRenderer.prototype.draw=function(q,o,r){if(this.plugins.pointLabels){this.plugins.pointLabels.show=false}var f,c,l,o,p,k,n,g,e,m;var b=(r!=undefined)?r:{};var j=new a.jqplot.ColorGenerator(this.seriesColors);this.canvas._elem.empty();for(f=0;f<this.gridData.length;f++){l=this.data[f];o=this.gridData[f];p="";k={};if(typeof l[2]=="string"){p=l[2]}else{if(typeof l[2]=="object"){k=l[2]}}if(typeof l[3]=="object"){k=l[3]}if(this.insertBreaks){p=p.replace(/ /g,"<br />")}k=a.extend(true,{},this.css,k);c=a('<div style="position:absolute;margin-left:auto;margin-right:auto;"></div>');this.canvas._elem.append(c);this.escapeHtml?c.text(p):c.html(p);delete k.position;delete k.marginRight;delete k.marginLeft;if(!k.background&&!k.backgroundColor&&!k.backgroundImage){k.background=j.next()}c.css(k);n=c.outerWidth();g=c.outerHeight();e=o[0]-n/2+"px";m=o[1]-g/2+"px";c.css({left:e,top:m});c=null}};a.jqplot.BlockCanvas=function(){a.jqplot.ElemContainer.call(this);this._ctx};a.jqplot.BlockCanvas.prototype=new a.jqplot.ElemContainer();a.jqplot.BlockCanvas.prototype.constructor=a.jqplot.BlockCanvas;a.jqplot.BlockCanvas.prototype.createElement=function(i,e,c){this._offsets=i;var b="jqplot-blockCanvas";if(e!=undefined){b=e}var g;if(this._elem){g=this._elem.get(0)}else{g=document.createElement("div")}if(c!=undefined){this._plotDimensions=c}var d=this._plotDimensions.width-this._offsets.left-this._offsets.right+"px";var f=this._plotDimensions.height-this._offsets.top-this._offsets.bottom+"px";this._elem=a(g);this._elem.css({position:"absolute",width:d,height:f,left:this._offsets.left,top:this._offsets.top});this._elem.addClass(b);return this._elem};a.jqplot.BlockCanvas.prototype.setContext=function(){this._ctx={canvas:{width:0,height:0},clearRect:function(){return null}};return this._ctx}})(jQuery); |
@@ -0,0 +1,754 @@ | |||||
/** | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 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($) { | |||||
var arrayMax = function( array ){ | |||||
return Math.max.apply( Math, array ); | |||||
}; | |||||
var arrayMin = function( array ){ | |||||
return Math.min.apply( Math, array ); | |||||
}; | |||||
/** | |||||
* Class: $.jqplot.BubbleRenderer | |||||
* Plugin renderer to draw a bubble chart. A Bubble chart has data points displayed as | |||||
* colored circles with an optional text label inside. To use | |||||
* the bubble renderer, you must include the bubble renderer like: | |||||
* | |||||
* > <script language="javascript" type="text/javascript" src="../src/plugins/jqplot.bubbleRenderer.js"></script> | |||||
* | |||||
* Data must be supplied in | |||||
* the form: | |||||
* | |||||
* > [[x1, y1, r1, <label or {label:'text', color:color}>], ...] | |||||
* | |||||
* where the label or options | |||||
* object is optional. | |||||
* | |||||
* Note that all bubble colors will be the same | |||||
* unless the "varyBubbleColors" option is set to true. Colors can be specified in the data array | |||||
* or in the seriesColors array option on the series. If no colors are defined, the default jqPlot | |||||
* series of 16 colors are used. Colors are automatically cycled around again if there are more | |||||
* bubbles than colors. | |||||
* | |||||
* Bubbles are autoscaled by default to fit within the chart area while maintaining | |||||
* relative sizes. If the "autoscaleBubbles" option is set to false, the r(adius) values | |||||
* in the data array a treated as literal pixel values for the radii of the bubbles. | |||||
* | |||||
* Properties are passed into the bubble renderer in the rendererOptions object of | |||||
* the series options like: | |||||
* | |||||
* > seriesDefaults: { | |||||
* > renderer: $.jqplot.BubbleRenderer, | |||||
* > rendererOptions: { | |||||
* > bubbleAlpha: 0.7, | |||||
* > varyBubbleColors: false | |||||
* > } | |||||
* > } | |||||
* | |||||
*/ | |||||
$.jqplot.BubbleRenderer = function(){ | |||||
$.jqplot.LineRenderer.call(this); | |||||
}; | |||||
$.jqplot.BubbleRenderer.prototype = new $.jqplot.LineRenderer(); | |||||
$.jqplot.BubbleRenderer.prototype.constructor = $.jqplot.BubbleRenderer; | |||||
// called with scope of a series | |||||
$.jqplot.BubbleRenderer.prototype.init = function(options, plot) { | |||||
// Group: Properties | |||||
// | |||||
// prop: varyBubbleColors | |||||
// True to vary the color of each bubble in this series according to | |||||
// the seriesColors array. False to set each bubble to the color | |||||
// specified on this series. This has no effect if a css background color | |||||
// option is specified in the renderer css options. | |||||
this.varyBubbleColors = true; | |||||
// prop: autoscaleBubbles | |||||
// True to scale the bubble radius based on plot size. | |||||
// False will use the radius value as provided as a raw pixel value for | |||||
// bubble radius. | |||||
this.autoscaleBubbles = true; | |||||
// prop: autoscaleMultiplier | |||||
// Multiplier the bubble size if autoscaleBubbles is true. | |||||
this.autoscaleMultiplier = 1.0; | |||||
// prop: autoscalePointsFactor | |||||
// Factor which decreases bubble size based on how many bubbles on on the chart. | |||||
// 0 means no adjustment for number of bubbles. Negative values will decrease | |||||
// size of bubbles as more bubbles are added. Values between 0 and -0.2 | |||||
// should work well. | |||||
this.autoscalePointsFactor = -0.07; | |||||
// prop: escapeHtml | |||||
// True to escape html in bubble label text. | |||||
this.escapeHtml = true; | |||||
// prop: highlightMouseOver | |||||
// True to highlight bubbles when moused over. | |||||
// This must be false to enable highlightMouseDown to highlight when clicking on a slice. | |||||
this.highlightMouseOver = true; | |||||
// prop: highlightMouseDown | |||||
// True to highlight when a mouse button is pressed over a bubble. | |||||
// This will be disabled if highlightMouseOver is true. | |||||
this.highlightMouseDown = false; | |||||
// prop: highlightColors | |||||
// An array of colors to use when highlighting a slice. Calculated automatically | |||||
// if not supplied. | |||||
this.highlightColors = []; | |||||
// prop: bubbleAlpha | |||||
// Alpha transparency to apply to all bubbles in this series. | |||||
this.bubbleAlpha = 1.0; | |||||
// prop: highlightAlpha | |||||
// Alpha transparency to apply when highlighting bubble. | |||||
// Set to value of bubbleAlpha by default. | |||||
this.highlightAlpha = null; | |||||
// prop: bubbleGradients | |||||
// True to color the bubbles with gradient fills instead of flat colors. | |||||
// NOT AVAILABLE IN IE due to lack of excanvas support for radial gradient fills. | |||||
// will be ignored in IE. | |||||
this.bubbleGradients = false; | |||||
// prop: showLabels | |||||
// True to show labels on bubbles (if any), false to not show. | |||||
this.showLabels = true; | |||||
// array of [point index, radius] which will be sorted in descending order to plot | |||||
// largest points below smaller points. | |||||
this.radii = []; | |||||
this.maxRadius = 0; | |||||
// index of the currenty highlighted point, if any | |||||
this._highlightedPoint = null; | |||||
// array of jQuery labels. | |||||
this.labels = []; | |||||
this.bubbleCanvases = []; | |||||
this._type = 'bubble'; | |||||
// if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver | |||||
if (options.highlightMouseDown && options.highlightMouseOver == null) { | |||||
options.highlightMouseOver = false; | |||||
} | |||||
$.extend(true, this, options); | |||||
if (this.highlightAlpha == null) { | |||||
this.highlightAlpha = this.bubbleAlpha; | |||||
if (this.bubbleGradients) { | |||||
this.highlightAlpha = 0.35; | |||||
} | |||||
} | |||||
this.autoscaleMultiplier = this.autoscaleMultiplier * Math.pow(this.data.length, this.autoscalePointsFactor); | |||||
// index of the currenty highlighted point, if any | |||||
this._highlightedPoint = null; | |||||
// adjust the series colors for options colors passed in with data or for alpha. | |||||
// note, this can leave undefined holes in the seriesColors array. | |||||
var comps; | |||||
for (var i=0; i<this.data.length; i++) { | |||||
var color = null; | |||||
var d = this.data[i]; | |||||
this.maxRadius = Math.max(this.maxRadius, d[2]); | |||||
if (d[3]) { | |||||
if (typeof(d[3]) == 'object') { | |||||
color = d[3]['color']; | |||||
} | |||||
} | |||||
if (color == null) { | |||||
if (this.seriesColors[i] != null) { | |||||
color = this.seriesColors[i]; | |||||
} | |||||
} | |||||
if (color && this.bubbleAlpha < 1.0) { | |||||
comps = $.jqplot.getColorComponents(color); | |||||
color = 'rgba('+comps[0]+', '+comps[1]+', '+comps[2]+', '+this.bubbleAlpha+')'; | |||||
} | |||||
if (color) { | |||||
this.seriesColors[i] = color; | |||||
} | |||||
} | |||||
if (!this.varyBubbleColors) { | |||||
this.seriesColors = [this.color]; | |||||
} | |||||
this.colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors); | |||||
// set highlight colors if none provided | |||||
if (this.highlightColors.length == 0) { | |||||
for (var i=0; i<this.seriesColors.length; i++){ | |||||
var rgba = $.jqplot.getColorComponents(this.seriesColors[i]); | |||||
var newrgb = [rgba[0], rgba[1], rgba[2]]; | |||||
var sum = newrgb[0] + newrgb[1] + newrgb[2]; | |||||
for (var j=0; j<3; j++) { | |||||
// when darkening, lowest color component can be is 60. | |||||
newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]); | |||||
newrgb[j] = parseInt(newrgb[j], 10); | |||||
} | |||||
this.highlightColors.push('rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+', '+this.highlightAlpha+')'); | |||||
} | |||||
} | |||||
this.highlightColorGenerator = new $.jqplot.ColorGenerator(this.highlightColors); | |||||
var sopts = {fill:true, isarc:true, angle:this.shadowAngle, alpha:this.shadowAlpha, closePath:true}; | |||||
this.renderer.shadowRenderer.init(sopts); | |||||
this.canvas = new $.jqplot.DivCanvas(); | |||||
this.canvas._plotDimensions = this._plotDimensions; | |||||
plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); | |||||
plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown); | |||||
plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp); | |||||
plot.eventListenerHooks.addOnce('jqplotClick', handleClick); | |||||
plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); | |||||
plot.postDrawHooks.addOnce(postPlotDraw); | |||||
}; | |||||
// converts the user data values to grid coordinates and stores them | |||||
// in the gridData array. | |||||
// Called with scope of a series. | |||||
$.jqplot.BubbleRenderer.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 = []; | |||||
var radii = []; | |||||
this.radii = []; | |||||
var dim = Math.min(plot._height, plot._width); | |||||
for (var i=0; i<this.data.length; i++) { | |||||
if (data[i] != null) { | |||||
this.gridData.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1]), data[i][2]]); | |||||
this.radii.push([i, data[i][2]]); | |||||
radii.push(data[i][2]); | |||||
} | |||||
} | |||||
var r, val, maxr = this.maxRadius = arrayMax(radii); | |||||
var l = this.gridData.length; | |||||
if (this.autoscaleBubbles) { | |||||
for (var i=0; i<l; i++) { | |||||
val = radii[i]/maxr; | |||||
r = this.autoscaleMultiplier * dim / 6; | |||||
this.gridData[i][2] = r * val; | |||||
} | |||||
} | |||||
this.radii.sort(function(a, b) { return b[1] - a[1]; }); | |||||
}; | |||||
// 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.BubbleRenderer.prototype.makeGridData = function(data, plot) { | |||||
// recalculate the grid data | |||||
var xp = this._xaxis.series_u2p; | |||||
var yp = this._yaxis.series_u2p; | |||||
var gd = []; | |||||
var radii = []; | |||||
this.radii = []; | |||||
var dim = Math.min(plot._height, plot._width); | |||||
for (var i=0; i<data.length; i++) { | |||||
if (data[i] != null) { | |||||
gd.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1]), data[i][2]]); | |||||
radii.push(data[i][2]); | |||||
this.radii.push([i, data[i][2]]); | |||||
} | |||||
} | |||||
var r, val, maxr = this.maxRadius = arrayMax(radii); | |||||
var l = this.gridData.length; | |||||
if (this.autoscaleBubbles) { | |||||
for (var i=0; i<l; i++) { | |||||
val = radii[i]/maxr; | |||||
r = this.autoscaleMultiplier * dim / 6; | |||||
gd[i][2] = r * val; | |||||
} | |||||
} | |||||
this.radii.sort(function(a, b) { return b[1] - a[1]; }); | |||||
return gd; | |||||
}; | |||||
// called with scope of series | |||||
$.jqplot.BubbleRenderer.prototype.draw = function (ctx, gd, options) { | |||||
if (this.plugins.pointLabels) { | |||||
this.plugins.pointLabels.show = false; | |||||
} | |||||
var opts = (options != undefined) ? options : {}; | |||||
var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; | |||||
this.canvas._elem.empty(); | |||||
for (var i=0; i<this.radii.length; i++) { | |||||
var idx = this.radii[i][0]; | |||||
var t=null; | |||||
var color = null; | |||||
var el = null; | |||||
var tel = null; | |||||
var d = this.data[idx]; | |||||
var gd = this.gridData[idx]; | |||||
if (d[3]) { | |||||
if (typeof(d[3]) == 'object') { | |||||
t = d[3]['label']; | |||||
} | |||||
else if (typeof(d[3]) == 'string') { | |||||
t = d[3]; | |||||
} | |||||
} | |||||
// color = (this.varyBubbleColors) ? this.colorGenerator.get(idx) : this.color; | |||||
color = this.colorGenerator.get(idx); | |||||
// If we're drawing a shadow, expand the canvas dimensions to accomodate. | |||||
var canvasRadius = gd[2]; | |||||
var offset, depth; | |||||
if (this.shadow) { | |||||
offset = (0.7 + gd[2]/40).toFixed(1); | |||||
depth = 1 + Math.ceil(gd[2]/15); | |||||
canvasRadius += offset*depth; | |||||
} | |||||
this.bubbleCanvases[idx] = new $.jqplot.BubbleCanvas(); | |||||
this.canvas._elem.append(this.bubbleCanvases[idx].createElement(gd[0], gd[1], canvasRadius)); | |||||
this.bubbleCanvases[idx].setContext(); | |||||
var ctx = this.bubbleCanvases[idx]._ctx; | |||||
var x = ctx.canvas.width/2; | |||||
var y = ctx.canvas.height/2; | |||||
if (this.shadow) { | |||||
this.renderer.shadowRenderer.draw(ctx, [x, y, gd[2], 0, 2*Math.PI], {offset: offset, depth: depth}); | |||||
} | |||||
this.bubbleCanvases[idx].draw(gd[2], color, this.bubbleGradients, this.shadowAngle/180*Math.PI); | |||||
// now draw label. | |||||
if (t && this.showLabels) { | |||||
tel = $('<div style="position:absolute;" class="jqplot-bubble-label"></div>'); | |||||
if (this.escapeHtml) { | |||||
tel.text(t); | |||||
} | |||||
else { | |||||
tel.html(t); | |||||
} | |||||
this.canvas._elem.append(tel); | |||||
var h = $(tel).outerHeight(); | |||||
var w = $(tel).outerWidth(); | |||||
var top = gd[1] - 0.5*h; | |||||
var left = gd[0] - 0.5*w; | |||||
tel.css({top: top, left: left}); | |||||
this.labels[idx] = $(tel); | |||||
} | |||||
} | |||||
}; | |||||
$.jqplot.DivCanvas = function() { | |||||
$.jqplot.ElemContainer.call(this); | |||||
this._ctx; | |||||
}; | |||||
$.jqplot.DivCanvas.prototype = new $.jqplot.ElemContainer(); | |||||
$.jqplot.DivCanvas.prototype.constructor = $.jqplot.DivCanvas; | |||||
$.jqplot.DivCanvas.prototype.createElement = function(offsets, clss, plotDimensions) { | |||||
this._offsets = offsets; | |||||
var klass = 'jqplot-DivCanvas'; | |||||
if (clss != undefined) { | |||||
klass = clss; | |||||
} | |||||
var elem; | |||||
// if this canvas already has a dom element, don't make a new one. | |||||
if (this._elem) { | |||||
elem = this._elem.get(0); | |||||
} | |||||
else { | |||||
elem = document.createElement('div'); | |||||
} | |||||
// if new plotDimensions supplied, use them. | |||||
if (plotDimensions != undefined) { | |||||
this._plotDimensions = plotDimensions; | |||||
} | |||||
var w = this._plotDimensions.width - this._offsets.left - this._offsets.right + 'px'; | |||||
var h = this._plotDimensions.height - this._offsets.top - this._offsets.bottom + 'px'; | |||||
this._elem = $(elem); | |||||
this._elem.css({ position: 'absolute', width:w, height:h, left: this._offsets.left, top: this._offsets.top }); | |||||
this._elem.addClass(klass); | |||||
return this._elem; | |||||
}; | |||||
$.jqplot.DivCanvas.prototype.setContext = function() { | |||||
this._ctx = { | |||||
canvas:{ | |||||
width:0, | |||||
height:0 | |||||
}, | |||||
clearRect:function(){return null;} | |||||
}; | |||||
return this._ctx; | |||||
}; | |||||
$.jqplot.BubbleCanvas = function() { | |||||
$.jqplot.ElemContainer.call(this); | |||||
this._ctx; | |||||
}; | |||||
$.jqplot.BubbleCanvas.prototype = new $.jqplot.ElemContainer(); | |||||
$.jqplot.BubbleCanvas.prototype.constructor = $.jqplot.BubbleCanvas; | |||||
// initialize with the x,y pont of bubble center and the bubble radius. | |||||
$.jqplot.BubbleCanvas.prototype.createElement = function(x, y, r) { | |||||
var klass = 'jqplot-bubble-point'; | |||||
var elem; | |||||
// if this canvas already has a dom element, don't make a new one. | |||||
if (this._elem) { | |||||
elem = this._elem.get(0); | |||||
} | |||||
else { | |||||
elem = document.createElement('canvas'); | |||||
} | |||||
elem.width = (r != null) ? 2*r : elem.width; | |||||
elem.height = (r != null) ? 2*r : elem.height; | |||||
this._elem = $(elem); | |||||
var l = (x != null && r != null) ? x - r : this._elem.css('left'); | |||||
var t = (y != null && r != null) ? y - r : this._elem.css('top'); | |||||
this._elem.css({ position: 'absolute', left: l, top: t }); | |||||
this._elem.addClass(klass); | |||||
if ($.jqplot.use_excanvas) { | |||||
window.G_vmlCanvasManager.init_(document); | |||||
elem = window.G_vmlCanvasManager.initElement(elem); | |||||
} | |||||
return this._elem; | |||||
}; | |||||
$.jqplot.BubbleCanvas.prototype.draw = function(r, color, gradients, angle) { | |||||
var ctx = this._ctx; | |||||
// r = Math.floor(r*1.04); | |||||
// var x = Math.round(ctx.canvas.width/2); | |||||
// var y = Math.round(ctx.canvas.height/2); | |||||
var x = ctx.canvas.width/2; | |||||
var y = ctx.canvas.height/2; | |||||
ctx.save(); | |||||
if (gradients && !$.jqplot.use_excanvas) { | |||||
r = r*1.04; | |||||
var comps = $.jqplot.getColorComponents(color); | |||||
var colorinner = 'rgba('+Math.round(comps[0]+0.8*(255-comps[0]))+', '+Math.round(comps[1]+0.8*(255-comps[1]))+', '+Math.round(comps[2]+0.8*(255-comps[2]))+', '+comps[3]+')'; | |||||
var colorend = 'rgba('+comps[0]+', '+comps[1]+', '+comps[2]+', 0)'; | |||||
// var rinner = Math.round(0.35 * r); | |||||
// var xinner = Math.round(x - Math.cos(angle) * 0.33 * r); | |||||
// var yinner = Math.round(y - Math.sin(angle) * 0.33 * r); | |||||
var rinner = 0.35 * r; | |||||
var xinner = x - Math.cos(angle) * 0.33 * r; | |||||
var yinner = y - Math.sin(angle) * 0.33 * r; | |||||
var radgrad = ctx.createRadialGradient(xinner, yinner, rinner, x, y, r); | |||||
radgrad.addColorStop(0, colorinner); | |||||
radgrad.addColorStop(0.93, color); | |||||
radgrad.addColorStop(0.96, colorend); | |||||
radgrad.addColorStop(1, colorend); | |||||
// radgrad.addColorStop(.98, colorend); | |||||
ctx.fillStyle = radgrad; | |||||
ctx.fillRect(0,0, ctx.canvas.width, ctx.canvas.height); | |||||
} | |||||
else { | |||||
ctx.fillStyle = color; | |||||
ctx.strokeStyle = color; | |||||
ctx.lineWidth = 1; | |||||
ctx.beginPath(); | |||||
var ang = 2*Math.PI; | |||||
ctx.arc(x, y, r, 0, ang, 0); | |||||
ctx.closePath(); | |||||
ctx.fill(); | |||||
} | |||||
ctx.restore(); | |||||
}; | |||||
$.jqplot.BubbleCanvas.prototype.setContext = function() { | |||||
this._ctx = this._elem.get(0).getContext("2d"); | |||||
return this._ctx; | |||||
}; | |||||
$.jqplot.BubbleAxisRenderer = function() { | |||||
$.jqplot.LinearAxisRenderer.call(this); | |||||
}; | |||||
$.jqplot.BubbleAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); | |||||
$.jqplot.BubbleAxisRenderer.prototype.constructor = $.jqplot.BubbleAxisRenderer; | |||||
// called with scope of axis object. | |||||
$.jqplot.BubbleAxisRenderer.prototype.init = function(options){ | |||||
$.extend(true, this, options); | |||||
var db = this._dataBounds; | |||||
var minsidx = 0, | |||||
minpidx = 0, | |||||
maxsidx = 0, | |||||
maxpidx = 0, | |||||
maxr = 0, | |||||
minr = 0, | |||||
minMaxRadius = 0, | |||||
maxMaxRadius = 0, | |||||
maxMult = 0, | |||||
minMult = 0; | |||||
// Go through all the series attached to this axis and find | |||||
// the min/max bounds for this axis. | |||||
for (var i=0; i<this._series.length; i++) { | |||||
var s = this._series[i]; | |||||
var d = s._plotData; | |||||
for (var j=0; j<d.length; j++) { | |||||
if (this.name == 'xaxis' || this.name == 'x2axis') { | |||||
if (d[j][0] < db.min || db.min == null) { | |||||
db.min = d[j][0]; | |||||
minsidx=i; | |||||
minpidx=j; | |||||
minr = d[j][2]; | |||||
minMaxRadius = s.maxRadius; | |||||
minMult = s.autoscaleMultiplier; | |||||
} | |||||
if (d[j][0] > db.max || db.max == null) { | |||||
db.max = d[j][0]; | |||||
maxsidx=i; | |||||
maxpidx=j; | |||||
maxr = d[j][2]; | |||||
maxMaxRadius = s.maxRadius; | |||||
maxMult = s.autoscaleMultiplier; | |||||
} | |||||
} | |||||
else { | |||||
if (d[j][1] < db.min || db.min == null) { | |||||
db.min = d[j][1]; | |||||
minsidx=i; | |||||
minpidx=j; | |||||
minr = d[j][2]; | |||||
minMaxRadius = s.maxRadius; | |||||
minMult = s.autoscaleMultiplier; | |||||
} | |||||
if (d[j][1] > db.max || db.max == null) { | |||||
db.max = d[j][1]; | |||||
maxsidx=i; | |||||
maxpidx=j; | |||||
maxr = d[j][2]; | |||||
maxMaxRadius = s.maxRadius; | |||||
maxMult = s.autoscaleMultiplier; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
var minRatio = minr/minMaxRadius; | |||||
var maxRatio = maxr/maxMaxRadius; | |||||
// need to estimate the effect of the radius on total axis span and adjust axis accordingly. | |||||
var span = db.max - db.min; | |||||
// var dim = (this.name == 'xaxis' || this.name == 'x2axis') ? this._plotDimensions.width : this._plotDimensions.height; | |||||
var dim = Math.min(this._plotDimensions.width, this._plotDimensions.height); | |||||
var minfact = minRatio * minMult/3 * span; | |||||
var maxfact = maxRatio * maxMult/3 * span; | |||||
db.max += maxfact; | |||||
db.min -= minfact; | |||||
}; | |||||
function highlight (plot, sidx, pidx) { | |||||
plot.plugins.bubbleRenderer.highlightLabelCanvas.empty(); | |||||
var s = plot.series[sidx]; | |||||
var canvas = plot.plugins.bubbleRenderer.highlightCanvas; | |||||
var ctx = canvas._ctx; | |||||
ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); | |||||
s._highlightedPoint = pidx; | |||||
plot.plugins.bubbleRenderer.highlightedSeriesIndex = sidx; | |||||
var color = s.highlightColorGenerator.get(pidx); | |||||
var x = s.gridData[pidx][0], | |||||
y = s.gridData[pidx][1], | |||||
r = s.gridData[pidx][2]; | |||||
ctx.save(); | |||||
ctx.fillStyle = color; | |||||
ctx.strokeStyle = color; | |||||
ctx.lineWidth = 1; | |||||
ctx.beginPath(); | |||||
ctx.arc(x, y, r, 0, 2*Math.PI, 0); | |||||
ctx.closePath(); | |||||
ctx.fill(); | |||||
ctx.restore(); | |||||
// bring label to front | |||||
if (s.labels[pidx]) { | |||||
plot.plugins.bubbleRenderer.highlightLabel = s.labels[pidx].clone(); | |||||
plot.plugins.bubbleRenderer.highlightLabel.appendTo(plot.plugins.bubbleRenderer.highlightLabelCanvas); | |||||
plot.plugins.bubbleRenderer.highlightLabel.addClass('jqplot-bubble-label-highlight'); | |||||
} | |||||
} | |||||
function unhighlight (plot) { | |||||
var canvas = plot.plugins.bubbleRenderer.highlightCanvas; | |||||
var sidx = plot.plugins.bubbleRenderer.highlightedSeriesIndex; | |||||
plot.plugins.bubbleRenderer.highlightLabelCanvas.empty(); | |||||
canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); | |||||
for (var i=0; i<plot.series.length; i++) { | |||||
plot.series[i]._highlightedPoint = null; | |||||
} | |||||
plot.plugins.bubbleRenderer.highlightedSeriesIndex = null; | |||||
plot.target.trigger('jqplotDataUnhighlight'); | |||||
} | |||||
function handleMove(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var si = neighbor.seriesIndex; | |||||
var pi = neighbor.pointIndex; | |||||
var ins = [si, pi, neighbor.data, plot.series[si].gridData[pi][2]]; | |||||
var evt1 = jQuery.Event('jqplotDataMouseOver'); | |||||
evt1.pageX = ev.pageX; | |||||
evt1.pageY = ev.pageY; | |||||
plot.target.trigger(evt1, ins); | |||||
if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.bubbleRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { | |||||
var evt = jQuery.Event('jqplotDataHighlight'); | |||||
evt.pageX = ev.pageX; | |||||
evt.pageY = ev.pageY; | |||||
plot.target.trigger(evt, ins); | |||||
highlight (plot, ins[0], ins[1]); | |||||
} | |||||
} | |||||
else if (neighbor == null) { | |||||
unhighlight (plot); | |||||
} | |||||
} | |||||
function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var si = neighbor.seriesIndex; | |||||
var pi = neighbor.pointIndex; | |||||
var ins = [si, pi, neighbor.data, plot.series[si].gridData[pi][2]]; | |||||
if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.bubbleRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { | |||||
var evt = jQuery.Event('jqplotDataHighlight'); | |||||
evt.pageX = ev.pageX; | |||||
evt.pageY = ev.pageY; | |||||
plot.target.trigger(evt, ins); | |||||
highlight (plot, ins[0], ins[1]); | |||||
} | |||||
} | |||||
else if (neighbor == null) { | |||||
unhighlight (plot); | |||||
} | |||||
} | |||||
function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { | |||||
var idx = plot.plugins.bubbleRenderer.highlightedSeriesIndex; | |||||
if (idx != null && plot.series[idx].highlightMouseDown) { | |||||
unhighlight(plot); | |||||
} | |||||
} | |||||
function handleClick(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var si = neighbor.seriesIndex; | |||||
var pi = neighbor.pointIndex; | |||||
var ins = [si, pi, neighbor.data, plot.series[si].gridData[pi][2]]; | |||||
var evt = jQuery.Event('jqplotDataClick'); | |||||
evt.pageX = ev.pageX; | |||||
evt.pageY = ev.pageY; | |||||
plot.target.trigger(evt, ins); | |||||
} | |||||
} | |||||
function handleRightClick(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var si = neighbor.seriesIndex; | |||||
var pi = neighbor.pointIndex; | |||||
var ins = [si, pi, neighbor.data, plot.series[si].gridData[pi][2]]; | |||||
var idx = plot.plugins.bubbleRenderer.highlightedSeriesIndex; | |||||
if (idx != null && plot.series[idx].highlightMouseDown) { | |||||
unhighlight(plot); | |||||
} | |||||
var evt = jQuery.Event('jqplotDataRightClick'); | |||||
evt.pageX = ev.pageX; | |||||
evt.pageY = ev.pageY; | |||||
plot.target.trigger(evt, ins); | |||||
} | |||||
} | |||||
// called within context of plot | |||||
// create a canvas which we can draw on. | |||||
// insert it before the eventCanvas, so eventCanvas will still capture events. | |||||
function postPlotDraw() { | |||||
// Memory Leaks patch | |||||
if (this.plugins.bubbleRenderer && this.plugins.bubbleRenderer.highlightCanvas) { | |||||
this.plugins.bubbleRenderer.highlightCanvas.resetCanvas(); | |||||
this.plugins.bubbleRenderer.highlightCanvas = null; | |||||
} | |||||
this.plugins.bubbleRenderer = {highlightedSeriesIndex:null}; | |||||
this.plugins.bubbleRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); | |||||
this.plugins.bubbleRenderer.highlightLabel = null; | |||||
this.plugins.bubbleRenderer.highlightLabelCanvas = $('<div style="position:absolute;"></div>'); | |||||
var top = this._gridPadding.top; | |||||
var left = this._gridPadding.left; | |||||
var width = this._plotDimensions.width - this._gridPadding.left - this._gridPadding.right; | |||||
var height = this._plotDimensions.height - this._gridPadding.top - this._gridPadding.bottom; | |||||
this.plugins.bubbleRenderer.highlightLabelCanvas.css({top:top, left:left, width:width+'px', height:height+'px'}); | |||||
this.eventCanvas._elem.before(this.plugins.bubbleRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-bubbleRenderer-highlight-canvas', this._plotDimensions, this)); | |||||
this.eventCanvas._elem.before(this.plugins.bubbleRenderer.highlightLabelCanvas); | |||||
var hctx = this.plugins.bubbleRenderer.highlightCanvas.setContext(); | |||||
} | |||||
// 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.seriesDefaults = options.seriesDefaults || {}; | |||||
// only set these if there is a Bubble series | |||||
var setopts = false; | |||||
if (options.seriesDefaults.renderer == $.jqplot.BubbleRenderer) { | |||||
setopts = true; | |||||
} | |||||
else if (options.series) { | |||||
for (var i=0; i < options.series.length; i++) { | |||||
if (options.series[i].renderer == $.jqplot.BubbleRenderer) { | |||||
setopts = true; | |||||
} | |||||
} | |||||
} | |||||
if (setopts) { | |||||
options.axesDefaults.renderer = $.jqplot.BubbleAxisRenderer; | |||||
options.sortData = false; | |||||
} | |||||
} | |||||
$.jqplot.preInitHooks.push(preInit); | |||||
})(jQuery); | |||||
@@ -1,18 +1,30 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* The author would appreciate an email letting him know of any substantial | |||||
* use of jqPlot. You can reach the author at: chris dot leonello at gmail | |||||
* dot com or see http://www.jqplot.com/info.php . This is, of course, | |||||
* not required. | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * making a donation at: http://www.jqplot.com/donate.php . | ||||
* | * | ||||
* Thanks for using jqPlot! | |||||
* 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($) { | (function($) { | ||||
@@ -69,7 +81,7 @@ | |||||
// true to turn on native canvas font support in Mozilla 3.5+ and Safari 4+. | // true to turn on native canvas font support in Mozilla 3.5+ and Safari 4+. | ||||
// If true, label will be drawn with canvas tag native support for fonts. | // If true, label will be drawn with canvas tag native support for fonts. | ||||
// If false, label will be drawn with Hershey font metrics. | // If false, label will be drawn with Hershey font metrics. | ||||
this.enableFontSupport = false; | |||||
this.enableFontSupport = true; | |||||
// prop: pt2px | // prop: pt2px | ||||
// Point to pixel scaling factor, used for computing height of bounding box | // Point to pixel scaling factor, used for computing height of bounding box | ||||
// around a label. The labels text renderer has a default setting of 1.4, which | // around a label. The labels text renderer has a default setting of 1.4, which | ||||
@@ -97,27 +109,14 @@ | |||||
} | } | ||||
if (this.enableFontSupport) { | if (this.enableFontSupport) { | ||||
if ($.browser.safari) { | |||||
var p = $.browser.version.split('.'); | |||||
for (var i=0; i<p.length; i++) { p[i] = Number(p[i]); } | |||||
if (p[0] > 528 || (p[0] == 528 && p[1] >= 16)) { | |||||
this._textRenderer = new $.jqplot.CanvasFontRenderer(ropts); | |||||
} | |||||
} | |||||
else if ($.browser.mozilla) { | |||||
var p = $.browser.version.split("."); | |||||
if (p[0] > 1 || (p[0] == 1 && p[1] >= 9 && p[2] > 0) ) { | |||||
this._textRenderer = new $.jqplot.CanvasFontRenderer(ropts); | |||||
} | |||||
else { | |||||
this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); | |||||
} | |||||
function support_canvas_text() { | |||||
return !!(document.createElement('canvas').getContext && typeof document.createElement('canvas').getContext('2d').fillText == 'function'); | |||||
} | } | ||||
// TODO: test and enable this | |||||
// else if ($.browser.msie) { | |||||
// this._textRenderer = new $.jqplot.CanvasFontRenderer(ropts); | |||||
// } | |||||
if (support_canvas_text()) { | |||||
this._textRenderer = new $.jqplot.CanvasFontRenderer(ropts); | |||||
} | |||||
else { | else { | ||||
this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); | this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); | ||||
@@ -168,33 +167,41 @@ | |||||
return a; | return a; | ||||
}; | }; | ||||
$.jqplot.CanvasAxisLabelRenderer.prototype.draw = function(ctx) { | |||||
$.jqplot.CanvasAxisLabelRenderer.prototype.draw = function(ctx, plot) { | |||||
// Memory Leaks patch | |||||
if (this._elem) { | |||||
if ($.jqplot.use_excanvas) { | |||||
window.G_vmlCanvasManager.uninitElement(this._elem.get(0)); | |||||
} | |||||
this._elem.emptyForce(); | |||||
this._elem = null; | |||||
} | |||||
// create a canvas here, but can't draw on it untill it is appended | // create a canvas here, but can't draw on it untill it is appended | ||||
// to dom for IE compatability. | // to dom for IE compatability. | ||||
var domelem = document.createElement('canvas'); | |||||
var elem = plot.canvasManager.getCanvas(); | |||||
this._textRenderer.setText(this.label, ctx); | this._textRenderer.setText(this.label, ctx); | ||||
var w = this.getWidth(ctx); | var w = this.getWidth(ctx); | ||||
var h = this.getHeight(ctx); | var h = this.getHeight(ctx); | ||||
domelem.width = w; | |||||
domelem.height = h; | |||||
domelem.style.width = w; | |||||
domelem.style.height = h; | |||||
// domelem.style.textAlign = 'center'; | |||||
domelem.style.position = 'absolute'; | |||||
this._domelem = domelem; | |||||
this._elem = $(domelem); | |||||
elem.width = w; | |||||
elem.height = h; | |||||
elem.style.width = w; | |||||
elem.style.height = h; | |||||
elem = plot.canvasManager.initCanvas(elem); | |||||
this._elem = $(elem); | |||||
this._elem.css({ position: 'absolute'}); | |||||
this._elem.addClass('jqplot-'+this.axis+'-label'); | this._elem.addClass('jqplot-'+this.axis+'-label'); | ||||
elem = null; | |||||
return this._elem; | return this._elem; | ||||
}; | }; | ||||
$.jqplot.CanvasAxisLabelRenderer.prototype.pack = function() { | $.jqplot.CanvasAxisLabelRenderer.prototype.pack = function() { | ||||
if ($.browser.msie) { | |||||
window.G_vmlCanvasManager.init_(document); | |||||
this._domelem = window.G_vmlCanvasManager.initElement(this._domelem); | |||||
} | |||||
var ctx = this._elem.get(0).getContext("2d"); | |||||
this._textRenderer.draw(ctx, this.label); | |||||
this._textRenderer.draw(this._elem.get(0).getContext("2d"), this.label); | |||||
}; | }; | ||||
})(jQuery); | })(jQuery); |
@@ -1,14 +1,30 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* Although not required, the author would appreciate an email letting him | * Although not required, the author would appreciate an email letting him | ||||
* know of any substantial use of jqPlot. You can reach the author at: | * know of any substantial use of jqPlot. You can reach the author at: | ||||
* chris dot leonello at gmail dot com or see http://www.jqplot.com/info.php . | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * 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(a){a.jqplot.CanvasAxisLabelRenderer=function(b){this.angle=0;this.axis;this.show=true;this.showLabel=true;this.label="";this.fontFamily='"Trebuchet MS", Arial, Helvetica, sans-serif';this.fontSize="11pt";this.fontWeight="normal";this.fontStretch=1;this.textColor="#666666";this.enableFontSupport=false;this.pt2px=null;this._elem;this._ctx;this._plotWidth;this._plotHeight;this._plotDimensions={height:null,width:null};a.extend(true,this,b);if(b.angle==null&&this.axis!="xaxis"&&this.axis!="x2axis"){this.angle=-90}var e={fontSize:this.fontSize,fontWeight:this.fontWeight,fontStretch:this.fontStretch,fillStyle:this.textColor,angle:this.getAngleRad(),fontFamily:this.fontFamily};if(this.pt2px){e.pt2px=this.pt2px}if(this.enableFontSupport){if(a.browser.safari){var d=a.browser.version.split(".");for(var c=0;c<d.length;c++){d[c]=Number(d[c])}if(d[0]>528||(d[0]==528&&d[1]>=16)){this._textRenderer=new a.jqplot.CanvasFontRenderer(e)}}else{if(a.browser.mozilla){var d=a.browser.version.split(".");if(d[0]>1||(d[0]==1&&d[1]>=9&&d[2]>0)){this._textRenderer=new a.jqplot.CanvasFontRenderer(e)}else{this._textRenderer=new a.jqplot.CanvasTextRenderer(e)}}else{this._textRenderer=new a.jqplot.CanvasTextRenderer(e)}}}else{this._textRenderer=new a.jqplot.CanvasTextRenderer(e)}};a.jqplot.CanvasAxisLabelRenderer.prototype.init=function(b){a.extend(true,this,b);this._textRenderer.init({fontSize:this.fontSize,fontWeight:this.fontWeight,fontStretch:this.fontStretch,fillStyle:this.textColor,angle:this.getAngleRad(),fontFamily:this.fontFamily})};a.jqplot.CanvasAxisLabelRenderer.prototype.getWidth=function(d){if(this._elem){return this._elem.outerWidth(true)}else{var f=this._textRenderer;var c=f.getWidth(d);var e=f.getHeight(d);var b=Math.abs(Math.sin(f.angle)*e)+Math.abs(Math.cos(f.angle)*c);return b}};a.jqplot.CanvasAxisLabelRenderer.prototype.getHeight=function(d){if(this._elem){return this._elem.outerHeight(true)}else{var f=this._textRenderer;var c=f.getWidth(d);var e=f.getHeight(d);var b=Math.abs(Math.cos(f.angle)*e)+Math.abs(Math.sin(f.angle)*c);return b}};a.jqplot.CanvasAxisLabelRenderer.prototype.getAngleRad=function(){var b=this.angle*Math.PI/180;return b};a.jqplot.CanvasAxisLabelRenderer.prototype.draw=function(c){var e=document.createElement("canvas");this._textRenderer.setText(this.label,c);var b=this.getWidth(c);var d=this.getHeight(c);e.width=b;e.height=d;e.style.width=b;e.style.height=d;e.style.position="absolute";this._domelem=e;this._elem=a(e);this._elem.addClass("jqplot-"+this.axis+"-label");return this._elem};a.jqplot.CanvasAxisLabelRenderer.prototype.pack=function(){if(a.browser.msie){window.G_vmlCanvasManager.init_(document);this._domelem=window.G_vmlCanvasManager.initElement(this._domelem)}var b=this._elem.get(0).getContext("2d");this._textRenderer.draw(b,this.label)}})(jQuery); | |||||
(function(a){a.jqplot.CanvasAxisLabelRenderer=function(b){this.angle=0;this.axis;this.show=true;this.showLabel=true;this.label="";this.fontFamily='"Trebuchet MS", Arial, Helvetica, sans-serif';this.fontSize="11pt";this.fontWeight="normal";this.fontStretch=1;this.textColor="#666666";this.enableFontSupport=true;this.pt2px=null;this._elem;this._ctx;this._plotWidth;this._plotHeight;this._plotDimensions={height:null,width:null};a.extend(true,this,b);if(b.angle==null&&this.axis!="xaxis"&&this.axis!="x2axis"){this.angle=-90}var c={fontSize:this.fontSize,fontWeight:this.fontWeight,fontStretch:this.fontStretch,fillStyle:this.textColor,angle:this.getAngleRad(),fontFamily:this.fontFamily};if(this.pt2px){c.pt2px=this.pt2px}if(this.enableFontSupport){function d(){return !!(document.createElement("canvas").getContext&&typeof document.createElement("canvas").getContext("2d").fillText=="function")}if(d()){this._textRenderer=new a.jqplot.CanvasFontRenderer(c)}else{this._textRenderer=new a.jqplot.CanvasTextRenderer(c)}}else{this._textRenderer=new a.jqplot.CanvasTextRenderer(c)}};a.jqplot.CanvasAxisLabelRenderer.prototype.init=function(b){a.extend(true,this,b);this._textRenderer.init({fontSize:this.fontSize,fontWeight:this.fontWeight,fontStretch:this.fontStretch,fillStyle:this.textColor,angle:this.getAngleRad(),fontFamily:this.fontFamily})};a.jqplot.CanvasAxisLabelRenderer.prototype.getWidth=function(d){if(this._elem){return this._elem.outerWidth(true)}else{var f=this._textRenderer;var c=f.getWidth(d);var e=f.getHeight(d);var b=Math.abs(Math.sin(f.angle)*e)+Math.abs(Math.cos(f.angle)*c);return b}};a.jqplot.CanvasAxisLabelRenderer.prototype.getHeight=function(d){if(this._elem){return this._elem.outerHeight(true)}else{var f=this._textRenderer;var c=f.getWidth(d);var e=f.getHeight(d);var b=Math.abs(Math.cos(f.angle)*e)+Math.abs(Math.sin(f.angle)*c);return b}};a.jqplot.CanvasAxisLabelRenderer.prototype.getAngleRad=function(){var b=this.angle*Math.PI/180;return b};a.jqplot.CanvasAxisLabelRenderer.prototype.draw=function(c,f){if(this._elem){if(a.jqplot.use_excanvas){window.G_vmlCanvasManager.uninitElement(this._elem.get(0))}this._elem.emptyForce();this._elem=null}var e=f.canvasManager.getCanvas();this._textRenderer.setText(this.label,c);var b=this.getWidth(c);var d=this.getHeight(c);e.width=b;e.height=d;e.style.width=b;e.style.height=d;e=f.canvasManager.initCanvas(e);this._elem=a(e);this._elem.css({position:"absolute"});this._elem.addClass("jqplot-"+this.axis+"-label");e=null;return this._elem};a.jqplot.CanvasAxisLabelRenderer.prototype.pack=function(){this._textRenderer.draw(this._elem.get(0).getContext("2d"),this.label)}})(jQuery); |
@@ -1,18 +1,30 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* The author would appreciate an email letting him know of any substantial | |||||
* use of jqPlot. You can reach the author at: chris dot leonello at gmail | |||||
* dot com or see http://www.jqplot.com/info.php . This is, of course, | |||||
* not required. | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * making a donation at: http://www.jqplot.com/donate.php . | ||||
* | * | ||||
* Thanks for using jqPlot! | |||||
* 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($) { | (function($) { | ||||
@@ -72,12 +84,16 @@ | |||||
// prop: formatString | // prop: formatString | ||||
// string passed to the formatter. | // string passed to the formatter. | ||||
this.formatString = ''; | this.formatString = ''; | ||||
// prop: prefix | |||||
// String to prepend to the tick label. | |||||
// Prefix is prepended to the formatted tick label. | |||||
this.prefix = ''; | |||||
// prop: fontFamily | // prop: fontFamily | ||||
// css spec for the font-family css attribute. | // css spec for the font-family css attribute. | ||||
this.fontFamily = '"Trebuchet MS", Arial, Helvetica, sans-serif'; | this.fontFamily = '"Trebuchet MS", Arial, Helvetica, sans-serif'; | ||||
// prop: fontSize | // prop: fontSize | ||||
// CSS spec for font size. | // CSS spec for font size. | ||||
this.fontSize = '11px'; | |||||
this.fontSize = '10pt'; | |||||
// prop: fontWeight | // prop: fontWeight | ||||
// CSS spec for fontWeight | // CSS spec for fontWeight | ||||
this.fontWeight = 'normal'; | this.fontWeight = 'normal'; | ||||
@@ -92,7 +108,7 @@ | |||||
// true to turn on native canvas font support in Mozilla 3.5+ and Safari 4+. | // true to turn on native canvas font support in Mozilla 3.5+ and Safari 4+. | ||||
// If true, tick label will be drawn with canvas tag native support for fonts. | // If true, tick label will be drawn with canvas tag native support for fonts. | ||||
// If false, tick label will be drawn with Hershey font metrics. | // If false, tick label will be drawn with Hershey font metrics. | ||||
this.enableFontSupport = false; | |||||
this.enableFontSupport = true; | |||||
// prop: pt2px | // prop: pt2px | ||||
// Point to pixel scaling factor, used for computing height of bounding box | // Point to pixel scaling factor, used for computing height of bounding box | ||||
// around a label. The labels text renderer has a default setting of 1.4, which | // around a label. The labels text renderer has a default setting of 1.4, which | ||||
@@ -116,27 +132,14 @@ | |||||
} | } | ||||
if (this.enableFontSupport) { | if (this.enableFontSupport) { | ||||
if ($.browser.safari) { | |||||
var p = $.browser.version.split('.'); | |||||
for (var i=0; i<p.length; i++) { p[i] = Number(p[i]); } | |||||
if (p[0] > 528 || (p[0] == 528 && p[1] >= 16)) { | |||||
this._textRenderer = new $.jqplot.CanvasFontRenderer(ropts); | |||||
} | |||||
} | |||||
else if ($.browser.mozilla) { | |||||
var p = $.browser.version.split("."); | |||||
if (p[0] > 1 || (p[0] == 1 && p[1] >= 9 && p[2] > 0) ) { | |||||
this._textRenderer = new $.jqplot.CanvasFontRenderer(ropts); | |||||
} | |||||
else { | |||||
this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); | |||||
} | |||||
function support_canvas_text() { | |||||
return !!(document.createElement('canvas').getContext && typeof document.createElement('canvas').getContext('2d').fillText == 'function'); | |||||
} | } | ||||
// TODO: test and enable this | |||||
// else if ($.browser.msie) { | |||||
// this._textRenderer = new $.jqplot.CanvasFontRenderer(ropts); | |||||
// } | |||||
if (support_canvas_text()) { | |||||
this._textRenderer = new $.jqplot.CanvasFontRenderer(ropts); | |||||
} | |||||
else { | else { | ||||
this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); | this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); | ||||
@@ -196,37 +199,49 @@ | |||||
return this; | return this; | ||||
}; | }; | ||||
$.jqplot.CanvasAxisTickRenderer.prototype.draw = function(ctx) { | |||||
$.jqplot.CanvasAxisTickRenderer.prototype.draw = function(ctx, plot) { | |||||
if (!this.label) { | if (!this.label) { | ||||
this.label = this.formatter(this.formatString, this.value); | |||||
this.label = this.prefix + this.formatter(this.formatString, this.value); | |||||
} | } | ||||
// Memory Leaks patch | |||||
if (this._elem) { | |||||
if ($.jqplot.use_excanvas) { | |||||
window.G_vmlCanvasManager.uninitElement(this._elem.get(0)); | |||||
} | |||||
this._elem.emptyForce(); | |||||
this._elem = null; | |||||
} | |||||
// create a canvas here, but can't draw on it untill it is appended | // create a canvas here, but can't draw on it untill it is appended | ||||
// to dom for IE compatability. | // to dom for IE compatability. | ||||
var domelem = document.createElement('canvas'); | |||||
var elem = plot.canvasManager.getCanvas(); | |||||
this._textRenderer.setText(this.label, ctx); | this._textRenderer.setText(this.label, ctx); | ||||
var w = this.getWidth(ctx); | var w = this.getWidth(ctx); | ||||
var h = this.getHeight(ctx); | var h = this.getHeight(ctx); | ||||
domelem.width = w; | |||||
domelem.height = h; | |||||
domelem.style.width = w; | |||||
domelem.style.height = h; | |||||
domelem.style.textAlign = 'left'; | |||||
domelem.style.position = 'absolute'; | |||||
this._domelem = domelem; | |||||
this._elem = $(domelem); | |||||
// canvases seem to need to have width and heigh attributes directly set. | |||||
elem.width = w; | |||||
elem.height = h; | |||||
elem.style.width = w; | |||||
elem.style.height = h; | |||||
elem.style.textAlign = 'left'; | |||||
elem.style.position = 'absolute'; | |||||
elem = plot.canvasManager.initCanvas(elem); | |||||
this._elem = $(elem); | |||||
this._elem.css(this._styles); | this._elem.css(this._styles); | ||||
this._elem.addClass('jqplot-'+this.axis+'-tick'); | this._elem.addClass('jqplot-'+this.axis+'-tick'); | ||||
elem = null; | |||||
return this._elem; | return this._elem; | ||||
}; | }; | ||||
$.jqplot.CanvasAxisTickRenderer.prototype.pack = function() { | $.jqplot.CanvasAxisTickRenderer.prototype.pack = function() { | ||||
if ($.browser.msie) { | |||||
window.G_vmlCanvasManager.init_(document); | |||||
this._domelem = window.G_vmlCanvasManager.initElement(this._domelem); | |||||
} | |||||
var ctx = this._elem.get(0).getContext("2d"); | |||||
this._textRenderer.draw(ctx, this.label); | |||||
this._textRenderer.draw(this._elem.get(0).getContext("2d"), this.label); | |||||
}; | }; | ||||
})(jQuery); | })(jQuery); |
@@ -1,14 +1,30 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* Although not required, the author would appreciate an email letting him | * Although not required, the author would appreciate an email letting him | ||||
* know of any substantial use of jqPlot. You can reach the author at: | * know of any substantial use of jqPlot. You can reach the author at: | ||||
* chris dot leonello at gmail dot com or see http://www.jqplot.com/info.php . | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * 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(a){a.jqplot.CanvasAxisTickRenderer=function(b){this.mark="outside";this.showMark=true;this.showGridline=true;this.isMinorTick=false;this.angle=0;this.markSize=4;this.show=true;this.showLabel=true;this.labelPosition="auto";this.label="";this.value=null;this._styles={};this.formatter=a.jqplot.DefaultTickFormatter;this.formatString="";this.fontFamily='"Trebuchet MS", Arial, Helvetica, sans-serif';this.fontSize="11px";this.fontWeight="normal";this.fontStretch=1;this.textColor="#666666";this.enableFontSupport=false;this.pt2px=null;this._elem;this._ctx;this._plotWidth;this._plotHeight;this._plotDimensions={height:null,width:null};a.extend(true,this,b);var e={fontSize:this.fontSize,fontWeight:this.fontWeight,fontStretch:this.fontStretch,fillStyle:this.textColor,angle:this.getAngleRad(),fontFamily:this.fontFamily};if(this.pt2px){e.pt2px=this.pt2px}if(this.enableFontSupport){if(a.browser.safari){var d=a.browser.version.split(".");for(var c=0;c<d.length;c++){d[c]=Number(d[c])}if(d[0]>528||(d[0]==528&&d[1]>=16)){this._textRenderer=new a.jqplot.CanvasFontRenderer(e)}}else{if(a.browser.mozilla){var d=a.browser.version.split(".");if(d[0]>1||(d[0]==1&&d[1]>=9&&d[2]>0)){this._textRenderer=new a.jqplot.CanvasFontRenderer(e)}else{this._textRenderer=new a.jqplot.CanvasTextRenderer(e)}}else{this._textRenderer=new a.jqplot.CanvasTextRenderer(e)}}}else{this._textRenderer=new a.jqplot.CanvasTextRenderer(e)}};a.jqplot.CanvasAxisTickRenderer.prototype.init=function(b){a.extend(true,this,b);this._textRenderer.init({fontSize:this.fontSize,fontWeight:this.fontWeight,fontStretch:this.fontStretch,fillStyle:this.textColor,angle:this.getAngleRad(),fontFamily:this.fontFamily})};a.jqplot.CanvasAxisTickRenderer.prototype.getWidth=function(d){if(this._elem){return this._elem.outerWidth(true)}else{var f=this._textRenderer;var c=f.getWidth(d);var e=f.getHeight(d);var b=Math.abs(Math.sin(f.angle)*e)+Math.abs(Math.cos(f.angle)*c);return b}};a.jqplot.CanvasAxisTickRenderer.prototype.getHeight=function(d){if(this._elem){return this._elem.outerHeight(true)}else{var f=this._textRenderer;var c=f.getWidth(d);var e=f.getHeight(d);var b=Math.abs(Math.cos(f.angle)*e)+Math.abs(Math.sin(f.angle)*c);return b}};a.jqplot.CanvasAxisTickRenderer.prototype.getAngleRad=function(){var b=this.angle*Math.PI/180;return b};a.jqplot.CanvasAxisTickRenderer.prototype.setTick=function(b,d,c){this.value=b;if(c){this.isMinorTick=true}return this};a.jqplot.CanvasAxisTickRenderer.prototype.draw=function(c){if(!this.label){this.label=this.formatter(this.formatString,this.value)}var e=document.createElement("canvas");this._textRenderer.setText(this.label,c);var b=this.getWidth(c);var d=this.getHeight(c);e.width=b;e.height=d;e.style.width=b;e.style.height=d;e.style.textAlign="left";e.style.position="absolute";this._domelem=e;this._elem=a(e);this._elem.css(this._styles);this._elem.addClass("jqplot-"+this.axis+"-tick");return this._elem};a.jqplot.CanvasAxisTickRenderer.prototype.pack=function(){if(a.browser.msie){window.G_vmlCanvasManager.init_(document);this._domelem=window.G_vmlCanvasManager.initElement(this._domelem)}var b=this._elem.get(0).getContext("2d");this._textRenderer.draw(b,this.label)}})(jQuery); | |||||
(function(a){a.jqplot.CanvasAxisTickRenderer=function(b){this.mark="outside";this.showMark=true;this.showGridline=true;this.isMinorTick=false;this.angle=0;this.markSize=4;this.show=true;this.showLabel=true;this.labelPosition="auto";this.label="";this.value=null;this._styles={};this.formatter=a.jqplot.DefaultTickFormatter;this.formatString="";this.prefix="";this.fontFamily='"Trebuchet MS", Arial, Helvetica, sans-serif';this.fontSize="10pt";this.fontWeight="normal";this.fontStretch=1;this.textColor="#666666";this.enableFontSupport=true;this.pt2px=null;this._elem;this._ctx;this._plotWidth;this._plotHeight;this._plotDimensions={height:null,width:null};a.extend(true,this,b);var c={fontSize:this.fontSize,fontWeight:this.fontWeight,fontStretch:this.fontStretch,fillStyle:this.textColor,angle:this.getAngleRad(),fontFamily:this.fontFamily};if(this.pt2px){c.pt2px=this.pt2px}if(this.enableFontSupport){function d(){return !!(document.createElement("canvas").getContext&&typeof document.createElement("canvas").getContext("2d").fillText=="function")}if(d()){this._textRenderer=new a.jqplot.CanvasFontRenderer(c)}else{this._textRenderer=new a.jqplot.CanvasTextRenderer(c)}}else{this._textRenderer=new a.jqplot.CanvasTextRenderer(c)}};a.jqplot.CanvasAxisTickRenderer.prototype.init=function(b){a.extend(true,this,b);this._textRenderer.init({fontSize:this.fontSize,fontWeight:this.fontWeight,fontStretch:this.fontStretch,fillStyle:this.textColor,angle:this.getAngleRad(),fontFamily:this.fontFamily})};a.jqplot.CanvasAxisTickRenderer.prototype.getWidth=function(d){if(this._elem){return this._elem.outerWidth(true)}else{var f=this._textRenderer;var c=f.getWidth(d);var e=f.getHeight(d);var b=Math.abs(Math.sin(f.angle)*e)+Math.abs(Math.cos(f.angle)*c);return b}};a.jqplot.CanvasAxisTickRenderer.prototype.getHeight=function(d){if(this._elem){return this._elem.outerHeight(true)}else{var f=this._textRenderer;var c=f.getWidth(d);var e=f.getHeight(d);var b=Math.abs(Math.cos(f.angle)*e)+Math.abs(Math.sin(f.angle)*c);return b}};a.jqplot.CanvasAxisTickRenderer.prototype.getAngleRad=function(){var b=this.angle*Math.PI/180;return b};a.jqplot.CanvasAxisTickRenderer.prototype.setTick=function(b,d,c){this.value=b;if(c){this.isMinorTick=true}return this};a.jqplot.CanvasAxisTickRenderer.prototype.draw=function(c,f){if(!this.label){this.label=this.prefix+this.formatter(this.formatString,this.value)}if(this._elem){if(a.jqplot.use_excanvas){window.G_vmlCanvasManager.uninitElement(this._elem.get(0))}this._elem.emptyForce();this._elem=null}var e=f.canvasManager.getCanvas();this._textRenderer.setText(this.label,c);var b=this.getWidth(c);var d=this.getHeight(c);e.width=b;e.height=d;e.style.width=b;e.style.height=d;e.style.textAlign="left";e.style.position="absolute";e=f.canvasManager.initCanvas(e);this._elem=a(e);this._elem.css(this._styles);this._elem.addClass("jqplot-"+this.axis+"-tick");e=null;return this._elem};a.jqplot.CanvasAxisTickRenderer.prototype.pack=function(){this._textRenderer.draw(this._elem.get(0).getContext("2d"),this.label)}})(jQuery); |
@@ -0,0 +1,705 @@ | |||||
/** | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 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.CanvasOverlay | |||||
$.jqplot.CanvasOverlay = function(opts){ | |||||
var options = opts || {}; | |||||
this.options = { | |||||
show: $.jqplot.config.enablePlugins, | |||||
deferDraw: false | |||||
}; | |||||
// prop: objects | |||||
this.objects = []; | |||||
this.objectNames = []; | |||||
this.canvas = null; | |||||
this.markerRenderer = new $.jqplot.MarkerRenderer({style:'line'}); | |||||
this.markerRenderer.init(); | |||||
if (options.objects) { | |||||
var objs = options.objects, | |||||
obj; | |||||
for (var i=0; i<objs.length; i++) { | |||||
obj = objs[i]; | |||||
for (var n in obj) { | |||||
switch (n) { | |||||
case 'line': | |||||
this.addLine(obj[n]); | |||||
break; | |||||
case 'horizontalLine': | |||||
this.addHorizontalLine(obj[n]); | |||||
break; | |||||
case 'dashedHorizontalLine': | |||||
this.addDashedHorizontalLine(obj[n]); | |||||
break; | |||||
case 'verticalLine': | |||||
this.addVerticalLine(obj[n]); | |||||
break; | |||||
case 'dashedVerticalLine': | |||||
this.addDashedVerticalLine(obj[n]); | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
$.extend(true, this.options, options); | |||||
}; | |||||
// called with scope of a plot object | |||||
$.jqplot.CanvasOverlay.postPlotInit = function (target, data, opts) { | |||||
var options = opts || {}; | |||||
// add a canvasOverlay attribute to the plot | |||||
this.plugins.canvasOverlay = new $.jqplot.CanvasOverlay(options.canvasOverlay); | |||||
}; | |||||
/** | |||||
* Class: Line | |||||
* A straight line. | |||||
*/ | |||||
function Line(options) { | |||||
this.type = 'line'; | |||||
this.options = { | |||||
// prop: name | |||||
// Optional name for this overlay object. | |||||
// Can be later used to retrieve the object by name. | |||||
name: null, | |||||
// prop: show | |||||
// true to show (draw), false to not draw. | |||||
show: true, | |||||
// prop: lineWidth | |||||
// Width of the line. | |||||
lineWidth: 2, | |||||
// prop: lineCap | |||||
// Type of ending placed on the line ['round', 'butt', 'square'] | |||||
lineCap: 'round', | |||||
// prop: color | |||||
// color of the line | |||||
color: '#666666', | |||||
// prop: shadow | |||||
// wether or not to draw a shadow on the line | |||||
shadow: true, | |||||
// prop: shadowAngle | |||||
// Shadow angle in degrees | |||||
shadowAngle: 45, | |||||
// prop: shadowOffset | |||||
// Shadow offset from line in pixels | |||||
shadowOffset: 1, | |||||
// prop: shadowDepth | |||||
// Number of times shadow is stroked, each stroke offset shadowOffset from the last. | |||||
shadowDepth: 3, | |||||
// prop: shadowAlpha | |||||
// Alpha channel transparency of shadow. 0 = transparent. | |||||
shadowAlpha: '0.07', | |||||
// prop: xaxis | |||||
// X axis to use for positioning/scaling the line. | |||||
xaxis: 'xaxis', | |||||
// prop: yaxis | |||||
// Y axis to use for positioning/scaling the line. | |||||
yaxis: 'yaxis', | |||||
// prop: start | |||||
// [x, y] coordinates for the start of the line. | |||||
start: [], | |||||
// prop: stop | |||||
// [x, y] coordinates for the end of the line. | |||||
stop: [] | |||||
}; | |||||
$.extend(true, this.options, options); | |||||
} | |||||
/** | |||||
* Class: HorizontalLine | |||||
* A straight horizontal line. | |||||
*/ | |||||
function HorizontalLine(options) { | |||||
this.type = 'horizontalLine'; | |||||
this.options = { | |||||
// prop: name | |||||
// Optional name for this overlay object. | |||||
// Can be later used to retrieve the object by name. | |||||
name: null, | |||||
// prop: show | |||||
// true to show (draw), false to not draw. | |||||
show: true, | |||||
// prop: lineWidth | |||||
// Width of the line. | |||||
lineWidth: 2, | |||||
// prop: lineCap | |||||
// Type of ending placed on the line ['round', 'butt', 'square'] | |||||
lineCap: 'round', | |||||
// prop: color | |||||
// color of the line | |||||
color: '#666666', | |||||
// prop: shadow | |||||
// wether or not to draw a shadow on the line | |||||
shadow: true, | |||||
// prop: shadowAngle | |||||
// Shadow angle in degrees | |||||
shadowAngle: 45, | |||||
// prop: shadowOffset | |||||
// Shadow offset from line in pixels | |||||
shadowOffset: 1, | |||||
// prop: shadowDepth | |||||
// Number of times shadow is stroked, each stroke offset shadowOffset from the last. | |||||
shadowDepth: 3, | |||||
// prop: shadowAlpha | |||||
// Alpha channel transparency of shadow. 0 = transparent. | |||||
shadowAlpha: '0.07', | |||||
// prop: xaxis | |||||
// X axis to use for positioning/scaling the line. | |||||
xaxis: 'xaxis', | |||||
// prop: yaxis | |||||
// Y axis to use for positioning/scaling the line. | |||||
yaxis: 'yaxis', | |||||
// prop: y | |||||
// y value to position the line | |||||
y: null, | |||||
// prop: xmin | |||||
// x value for the start of the line, null to scale to axis min. | |||||
xmin: null, | |||||
// prop: xmax | |||||
// x value for the end of the line, null to scale to axis max. | |||||
xmax: null, | |||||
// prop xOffset | |||||
// offset ends of the line inside the grid. Number | |||||
xOffset: '6px', // number or string. Number interpreted as units, string as pixels. | |||||
xminOffset: null, | |||||
xmaxOffset: null | |||||
}; | |||||
$.extend(true, this.options, options); | |||||
} | |||||
/** | |||||
* Class: DashedHorizontalLine | |||||
* A straight dashed horizontal line. | |||||
*/ | |||||
function DashedHorizontalLine(options) { | |||||
this.type = 'dashedHorizontalLine'; | |||||
this.options = { | |||||
// prop: name | |||||
// Optional name for this overlay object. | |||||
// Can be later used to retrieve the object by name. | |||||
name: null, | |||||
// prop: show | |||||
// true to show (draw), false to not draw. | |||||
show: true, | |||||
// prop: lineWidth | |||||
// Width of the line. | |||||
lineWidth: 2, | |||||
// prop: lineCap | |||||
// Type of ending placed on the line ['round', 'butt', 'square'] | |||||
lineCap: 'butt', | |||||
// prop: color | |||||
// color of the line | |||||
color: '#666666', | |||||
// prop: shadow | |||||
// wether or not to draw a shadow on the line | |||||
shadow: true, | |||||
// prop: shadowAngle | |||||
// Shadow angle in degrees | |||||
shadowAngle: 45, | |||||
// prop: shadowOffset | |||||
// Shadow offset from line in pixels | |||||
shadowOffset: 1, | |||||
// prop: shadowDepth | |||||
// Number of times shadow is stroked, each stroke offset shadowOffset from the last. | |||||
shadowDepth: 3, | |||||
// prop: shadowAlpha | |||||
// Alpha channel transparency of shadow. 0 = transparent. | |||||
shadowAlpha: '0.07', | |||||
// prop: xaxis | |||||
// X axis to use for positioning/scaling the line. | |||||
xaxis: 'xaxis', | |||||
// prop: yaxis | |||||
// Y axis to use for positioning/scaling the line. | |||||
yaxis: 'yaxis', | |||||
y: null, | |||||
xmin: null, | |||||
xmax: null, | |||||
xOffset: '6px', // number or string. Number interpreted as units, string as pixels. | |||||
xminOffset: null, | |||||
xmaxOffset: null, | |||||
// prop: dashPattern | |||||
// Array of line, space settings in pixels. | |||||
// Default is 8 pixel of line, 8 pixel of space. | |||||
// Note, limit to a 2 element array b/c of bug with higher order arrays. | |||||
dashPattern: [8,8] | |||||
}; | |||||
$.extend(true, this.options, options); | |||||
} | |||||
/** | |||||
* Class: VerticalLine | |||||
* A straight vertical line. | |||||
*/ | |||||
function VerticalLine(options) { | |||||
this.type = 'verticalLine'; | |||||
this.options = { | |||||
// prop: name | |||||
// Optional name for this overlay object. | |||||
// Can be later used to retrieve the object by name. | |||||
name: null, | |||||
// prop: show | |||||
// true to show (draw), false to not draw. | |||||
show: true, | |||||
// prop: lineWidth | |||||
// Width of the line. | |||||
lineWidth: 2, | |||||
// prop: lineCap | |||||
// Type of ending placed on the line ['round', 'butt', 'square'] | |||||
lineCap: 'round', | |||||
// prop: color | |||||
// color of the line | |||||
color: '#666666', | |||||
// prop: shadow | |||||
// wether or not to draw a shadow on the line | |||||
shadow: true, | |||||
// prop: shadowAngle | |||||
// Shadow angle in degrees | |||||
shadowAngle: 45, | |||||
// prop: shadowOffset | |||||
// Shadow offset from line in pixels | |||||
shadowOffset: 1, | |||||
// prop: shadowDepth | |||||
// Number of times shadow is stroked, each stroke offset shadowOffset from the last. | |||||
shadowDepth: 3, | |||||
// prop: shadowAlpha | |||||
// Alpha channel transparency of shadow. 0 = transparent. | |||||
shadowAlpha: '0.07', | |||||
// prop: xaxis | |||||
// X axis to use for positioning/scaling the line. | |||||
xaxis: 'xaxis', | |||||
// prop: yaxis | |||||
// Y axis to use for positioning/scaling the line. | |||||
yaxis: 'yaxis', | |||||
x: null, | |||||
ymin: null, | |||||
ymax: null, | |||||
yOffset: '6px', // number or string. Number interpreted as units, string as pixels. | |||||
yminOffset: null, | |||||
ymaxOffset: null | |||||
}; | |||||
$.extend(true, this.options, options); | |||||
} | |||||
/** | |||||
* Class: DashedVerticalLine | |||||
* A straight dashed vertical line. | |||||
*/ | |||||
function DashedVerticalLine(options) { | |||||
this.type = 'dashedVerticalLine'; | |||||
this.options = { | |||||
// prop: name | |||||
// Optional name for this overlay object. | |||||
// Can be later used to retrieve the object by name. | |||||
name: null, | |||||
// prop: show | |||||
// true to show (draw), false to not draw. | |||||
show: true, | |||||
// prop: lineWidth | |||||
// Width of the line. | |||||
lineWidth: 2, | |||||
// prop: lineCap | |||||
// Type of ending placed on the line ['round', 'butt', 'square'] | |||||
lineCap: 'butt', | |||||
// prop: color | |||||
// color of the line | |||||
color: '#666666', | |||||
// prop: shadow | |||||
// wether or not to draw a shadow on the line | |||||
shadow: true, | |||||
// prop: shadowAngle | |||||
// Shadow angle in degrees | |||||
shadowAngle: 45, | |||||
// prop: shadowOffset | |||||
// Shadow offset from line in pixels | |||||
shadowOffset: 1, | |||||
// prop: shadowDepth | |||||
// Number of times shadow is stroked, each stroke offset shadowOffset from the last. | |||||
shadowDepth: 3, | |||||
// prop: shadowAlpha | |||||
// Alpha channel transparency of shadow. 0 = transparent. | |||||
shadowAlpha: '0.07', | |||||
// prop: xaxis | |||||
// X axis to use for positioning/scaling the line. | |||||
xaxis: 'xaxis', | |||||
// prop: yaxis | |||||
// Y axis to use for positioning/scaling the line. | |||||
yaxis: 'yaxis', | |||||
x: null, | |||||
ymin: null, | |||||
ymax: null, | |||||
yOffset: '6px', // number or string. Number interpreted as units, string as pixels. | |||||
yminOffset: null, | |||||
ymaxOffset: null, | |||||
// prop: dashPattern | |||||
// Array of line, space settings in pixels. | |||||
// Default is 8 pixel of line, 8 pixel of space. | |||||
// Note, limit to a 2 element array b/c of bug with higher order arrays. | |||||
dashPattern: [8,8] | |||||
}; | |||||
$.extend(true, this.options, options); | |||||
} | |||||
$.jqplot.CanvasOverlay.prototype.addLine = function(opts) { | |||||
var line = new Line(opts); | |||||
this.objects.push(line); | |||||
this.objectNames.push(line.options.name); | |||||
}; | |||||
$.jqplot.CanvasOverlay.prototype.addHorizontalLine = function(opts) { | |||||
var line = new HorizontalLine(opts); | |||||
this.objects.push(line); | |||||
this.objectNames.push(line.options.name); | |||||
}; | |||||
$.jqplot.CanvasOverlay.prototype.addDashedHorizontalLine = function(opts) { | |||||
var line = new DashedHorizontalLine(opts); | |||||
this.objects.push(line); | |||||
this.objectNames.push(line.options.name); | |||||
}; | |||||
$.jqplot.CanvasOverlay.prototype.addVerticalLine = function(opts) { | |||||
var line = new VerticalLine(opts); | |||||
this.objects.push(line); | |||||
this.objectNames.push(line.options.name); | |||||
}; | |||||
$.jqplot.CanvasOverlay.prototype.addDashedVerticalLine = function(opts) { | |||||
var line = new DashedVerticalLine(opts); | |||||
this.objects.push(line); | |||||
this.objectNames.push(line.options.name); | |||||
}; | |||||
$.jqplot.CanvasOverlay.prototype.removeObject = function(idx) { | |||||
// check if integer, remove by index | |||||
if ($.type(idx) == 'number') { | |||||
this.objects.splice(idx, 1); | |||||
this.objectNames.splice(idx, 1); | |||||
} | |||||
// if string, remove by name | |||||
else { | |||||
var id = $.inArray(idx, this.objectNames); | |||||
if (id != -1) { | |||||
this.objects.splice(id, 1); | |||||
this.objectNames.splice(id, 1); | |||||
} | |||||
} | |||||
}; | |||||
$.jqplot.CanvasOverlay.prototype.getObject = function(idx) { | |||||
// check if integer, remove by index | |||||
if ($.type(idx) == 'number') { | |||||
return this.objects[idx]; | |||||
} | |||||
// if string, remove by name | |||||
else { | |||||
var id = $.inArray(idx, this.objectNames); | |||||
if (id != -1) { | |||||
return this.objects[id]; | |||||
} | |||||
} | |||||
}; | |||||
// Set get as alias for getObject. | |||||
$.jqplot.CanvasOverlay.prototype.get = $.jqplot.CanvasOverlay.prototype.getObject; | |||||
$.jqplot.CanvasOverlay.prototype.clear = function(plot) { | |||||
this.canvas._ctx.clearRect(0,0,this.canvas.getWidth(), this.canvas.getHeight()); | |||||
}; | |||||
$.jqplot.CanvasOverlay.prototype.draw = function(plot) { | |||||
var obj, | |||||
objs = this.objects, | |||||
mr = this.markerRenderer, | |||||
start, | |||||
stop; | |||||
if (this.options.show) { | |||||
this.canvas._ctx.clearRect(0,0,this.canvas.getWidth(), this.canvas.getHeight()); | |||||
for (var k=0; k<objs.length; k++) { | |||||
obj = objs[k]; | |||||
var opts = $.extend(true, {}, obj.options); | |||||
if (obj.options.show) { | |||||
// style and shadow properties should be set before | |||||
// every draw of marker renderer. | |||||
mr.shadow = obj.options.shadow; | |||||
switch (obj.type) { | |||||
case 'line': | |||||
// style and shadow properties should be set before | |||||
// every draw of marker renderer. | |||||
mr.style = 'line'; | |||||
opts.closePath = false; | |||||
start = [plot.axes[obj.options.xaxis].series_u2p(obj.options.start[0]), plot.axes[obj.options.yaxis].series_u2p(obj.options.start[1])]; | |||||
stop = [plot.axes[obj.options.xaxis].series_u2p(obj.options.stop[0]), plot.axes[obj.options.yaxis].series_u2p(obj.options.stop[1])]; | |||||
mr.draw(start, stop, this.canvas._ctx, opts); | |||||
break; | |||||
case 'horizontalLine': | |||||
// style and shadow properties should be set before | |||||
// every draw of marker renderer. | |||||
if (obj.options.y != null) { | |||||
mr.style = 'line'; | |||||
opts.closePath = false; | |||||
var xaxis = plot.axes[obj.options.xaxis], | |||||
xstart, | |||||
xstop, | |||||
y = plot.axes[obj.options.yaxis].series_u2p(obj.options.y), | |||||
xminoff = obj.options.xminOffset || obj.options.xOffset, | |||||
xmaxoff = obj.options.xmaxOffset || obj.options.xOffset; | |||||
if (obj.options.xmin != null) { | |||||
xstart = xaxis.series_u2p(obj.options.xmin); | |||||
} | |||||
else if (xminoff != null) { | |||||
if ($.type(xminoff) == "number") { | |||||
xstart = xaxis.series_u2p(xaxis.min + xminoff); | |||||
} | |||||
else if ($.type(xminoff) == "string") { | |||||
xstart = xaxis.series_u2p(xaxis.min) + parseFloat(xminoff); | |||||
} | |||||
} | |||||
if (obj.options.xmax != null) { | |||||
xstop = xaxis.series_u2p(obj.options.xmax); | |||||
} | |||||
else if (xmaxoff != null) { | |||||
if ($.type(xmaxoff) == "number") { | |||||
xstop = xaxis.series_u2p(xaxis.max - xmaxoff); | |||||
} | |||||
else if ($.type(xmaxoff) == "string") { | |||||
xstop = xaxis.series_u2p(xaxis.max) - parseFloat(xmaxoff); | |||||
} | |||||
} | |||||
if (xstop != null && xstart != null) { | |||||
mr.draw([xstart, y], [xstop, y], this.canvas._ctx, opts); | |||||
} | |||||
} | |||||
break; | |||||
case 'dashedHorizontalLine': | |||||
var dashPat = obj.options.dashPattern; | |||||
var dashPatLen = 0; | |||||
for (var i=0; i<dashPat.length; i++) { | |||||
dashPatLen += dashPat[i]; | |||||
} | |||||
// style and shadow properties should be set before | |||||
// every draw of marker renderer. | |||||
if (obj.options.y != null) { | |||||
mr.style = 'line'; | |||||
opts.closePath = false; | |||||
var xaxis = plot.axes[obj.options.xaxis], | |||||
xstart, | |||||
xstop, | |||||
y = plot.axes[obj.options.yaxis].series_u2p(obj.options.y), | |||||
xminoff = obj.options.xminOffset || obj.options.xOffset, | |||||
xmaxoff = obj.options.xmaxOffset || obj.options.xOffset; | |||||
if (obj.options.xmin != null) { | |||||
xstart = xaxis.series_u2p(obj.options.xmin); | |||||
} | |||||
else if (xminoff != null) { | |||||
if ($.type(xminoff) == "number") { | |||||
xstart = xaxis.series_u2p(xaxis.min + xminoff); | |||||
} | |||||
else if ($.type(xminoff) == "string") { | |||||
xstart = xaxis.series_u2p(xaxis.min) + parseFloat(xminoff); | |||||
} | |||||
} | |||||
if (obj.options.xmax != null) { | |||||
xstop = xaxis.series_u2p(obj.options.xmax); | |||||
} | |||||
else if (xmaxoff != null) { | |||||
if ($.type(xmaxoff) == "number") { | |||||
xstop = xaxis.series_u2p(xaxis.max - xmaxoff); | |||||
} | |||||
else if ($.type(xmaxoff) == "string") { | |||||
xstop = xaxis.series_u2p(xaxis.max) - parseFloat(xmaxoff); | |||||
} | |||||
} | |||||
if (xstop != null && xstart != null) { | |||||
var numDash = Math.ceil((xstop - xstart)/dashPatLen); | |||||
var b=xstart, e; | |||||
for (var i=0; i<numDash; i++) { | |||||
for (var j=0; j<dashPat.length; j+=2) { | |||||
e = b+dashPat[j]; | |||||
mr.draw([b, y], [e, y], this.canvas._ctx, opts); | |||||
b += dashPat[j]; | |||||
if (j < dashPat.length-1) { | |||||
b += dashPat[j+1]; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
break; | |||||
case 'verticalLine': | |||||
// style and shadow properties should be set before | |||||
// every draw of marker renderer. | |||||
if (obj.options.x != null) { | |||||
mr.style = 'line'; | |||||
opts.closePath = false; | |||||
var yaxis = plot.axes[obj.options.yaxis], | |||||
ystart, | |||||
ystop, | |||||
x = plot.axes[obj.options.xaxis].series_u2p(obj.options.x), | |||||
yminoff = obj.options.yminOffset || obj.options.yOffset, | |||||
ymaxoff = obj.options.ymaxOffset || obj.options.yOffset; | |||||
if (obj.options.ymin != null) { | |||||
ystart = yaxis.series_u2p(obj.options.ymin); | |||||
} | |||||
else if (yminoff != null) { | |||||
if ($.type(yminoff) == "number") { | |||||
ystart = yaxis.series_u2p(yaxis.min - yminoff); | |||||
} | |||||
else if ($.type(yminoff) == "string") { | |||||
ystart = yaxis.series_u2p(yaxis.min) - parseFloat(yminoff); | |||||
} | |||||
} | |||||
if (obj.options.ymax != null) { | |||||
ystop = yaxis.series_u2p(obj.options.ymax); | |||||
} | |||||
else if (ymaxoff != null) { | |||||
if ($.type(ymaxoff) == "number") { | |||||
ystop = yaxis.series_u2p(yaxis.max + ymaxoff); | |||||
} | |||||
else if ($.type(ymaxoff) == "string") { | |||||
ystop = yaxis.series_u2p(yaxis.max) + parseFloat(ymaxoff); | |||||
} | |||||
} | |||||
if (ystop != null && ystart != null) { | |||||
mr.draw([x, ystart], [x, ystop], this.canvas._ctx, opts); | |||||
} | |||||
} | |||||
break; | |||||
case 'dashedVerticalLine': | |||||
var dashPat = obj.options.dashPattern; | |||||
var dashPatLen = 0; | |||||
for (var i=0; i<dashPat.length; i++) { | |||||
dashPatLen += dashPat[i]; | |||||
} | |||||
// style and shadow properties should be set before | |||||
// every draw of marker renderer. | |||||
if (obj.options.x != null) { | |||||
mr.style = 'line'; | |||||
opts.closePath = false; | |||||
var yaxis = plot.axes[obj.options.yaxis], | |||||
ystart, | |||||
ystop, | |||||
x = plot.axes[obj.options.xaxis].series_u2p(obj.options.x), | |||||
yminoff = obj.options.yminOffset || obj.options.yOffset, | |||||
ymaxoff = obj.options.ymaxOffset || obj.options.yOffset; | |||||
if (obj.options.ymin != null) { | |||||
ystart = yaxis.series_u2p(obj.options.ymin); | |||||
} | |||||
else if (yminoff != null) { | |||||
if ($.type(yminoff) == "number") { | |||||
ystart = yaxis.series_u2p(yaxis.min - yminoff); | |||||
} | |||||
else if ($.type(yminoff) == "string") { | |||||
ystart = yaxis.series_u2p(yaxis.min) - parseFloat(yminoff); | |||||
} | |||||
} | |||||
if (obj.options.ymax != null) { | |||||
ystop = yaxis.series_u2p(obj.options.ymax); | |||||
} | |||||
else if (ymaxoff != null) { | |||||
if ($.type(ymaxoff) == "number") { | |||||
ystop = yaxis.series_u2p(yaxis.max + ymaxoff); | |||||
} | |||||
else if ($.type(ymaxoff) == "string") { | |||||
ystop = yaxis.series_u2p(yaxis.max) + parseFloat(ymaxoff); | |||||
} | |||||
} | |||||
if (ystop != null && ystart != null) { | |||||
var numDash = Math.ceil((ystart - ystop)/dashPatLen); | |||||
var firstDashAdjust = ((numDash * dashPatLen) - (ystart - ystop))/2.0; | |||||
var b=ystart, e, bs, es; | |||||
for (var i=0; i<numDash; i++) { | |||||
for (var j=0; j<dashPat.length; j+=2) { | |||||
e = b - dashPat[j]; | |||||
if (e < ystop) { | |||||
e = ystop; | |||||
} | |||||
if (b < ystop) { | |||||
b = ystop; | |||||
} | |||||
// es = e; | |||||
// if (i == 0) { | |||||
// es += firstDashAdjust; | |||||
// } | |||||
mr.draw([x, b], [x, e], this.canvas._ctx, opts); | |||||
b -= dashPat[j]; | |||||
if (j < dashPat.length-1) { | |||||
b -= dashPat[j+1]; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
}; | |||||
// called within context of plot | |||||
// create a canvas which we can draw on. | |||||
// insert it before the eventCanvas, so eventCanvas will still capture events. | |||||
$.jqplot.CanvasOverlay.postPlotDraw = function() { | |||||
// Memory Leaks patch | |||||
if (this.plugins.canvasOverlay && this.plugins.canvasOverlay.highlightCanvas) { | |||||
this.plugins.canvasOverlay.highlightCanvas.resetCanvas(); | |||||
this.plugins.canvasOverlay.highlightCanvas = null; | |||||
} | |||||
this.plugins.canvasOverlay.canvas = new $.jqplot.GenericCanvas(); | |||||
this.eventCanvas._elem.before(this.plugins.canvasOverlay.canvas.createElement(this._gridPadding, 'jqplot-overlayCanvas-canvas', this._plotDimensions, this)); | |||||
this.plugins.canvasOverlay.canvas.setContext(); | |||||
if (!this.plugins.canvasOverlay.deferDraw) { | |||||
this.plugins.canvasOverlay.draw(this); | |||||
} | |||||
}; | |||||
$.jqplot.postInitHooks.push($.jqplot.CanvasOverlay.postPlotInit); | |||||
$.jqplot.postDrawHooks.push($.jqplot.CanvasOverlay.postPlotDraw); | |||||
})(jQuery); |
@@ -1,18 +1,30 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* The author would appreciate an email letting him know of any substantial | |||||
* use of jqPlot. You can reach the author at: chris dot leonello at gmail | |||||
* dot com or see http://www.jqplot.com/info.php . This is, of course, | |||||
* not required. | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * making a donation at: http://www.jqplot.com/donate.php . | ||||
* | * | ||||
* Thanks for using jqPlot! | |||||
* 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($) { | (function($) { | ||||
@@ -52,7 +64,7 @@ | |||||
// returns float | // returns float | ||||
$.jqplot.CanvasTextRenderer.prototype.normalizeFontSize = function(sz) { | $.jqplot.CanvasTextRenderer.prototype.normalizeFontSize = function(sz) { | ||||
sz = String(sz); | sz = String(sz); | ||||
n = parseFloat(sz); | |||||
var n = parseFloat(sz); | |||||
if (sz.indexOf('px') > -1) { | if (sz.indexOf('px') > -1) { | ||||
return n/this.pt2px; | return n/this.pt2px; | ||||
} | } | ||||
@@ -161,7 +173,7 @@ | |||||
var total = 0; | var total = 0; | ||||
var len = str.length; | var len = str.length; | ||||
for ( i = 0; i < len; i++) { | |||||
for (var i = 0; i < len; i++) { | |||||
var c = this.letter(str.charAt(i)); | var c = this.letter(str.charAt(i)); | ||||
if (c) { | if (c) { | ||||
total += c.width * this.normalizedFontSize / 25.0 * this.fontStretch; | total += c.width * this.normalizedFontSize / 25.0 * this.fontStretch; | ||||
@@ -1,25 +1,36 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* The author would appreciate an email letting him know of any substantial | |||||
* use of jqPlot. You can reach the author at: chris dot leonello at gmail | |||||
* dot com or see http://www.jqplot.com/info.php . This is, of course, | |||||
* not required. | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * making a donation at: http://www.jqplot.com/donate.php . | ||||
* | * | ||||
* Thanks for using jqPlot! | |||||
* 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($) { | (function($) { | ||||
/** | /** | ||||
* class: $.jqplot.CategoryAxisRenderer | * class: $.jqplot.CategoryAxisRenderer | ||||
* A plugin for jqPlot to render a category style axis, with equal pixel spacing between y data values of a series. | * A plugin for jqPlot to render a category style axis, with equal pixel spacing between y data values of a series. | ||||
* This renderer has no options beyond those supplied by the <Axis> class. | |||||
* | * | ||||
* To use this renderer, include the plugin in your source | * To use this renderer, include the plugin in your source | ||||
* > <script type="text/javascript" language="javascript" src="plugins/jqplot.categoryAxisRenderer.js"></script> | * > <script type="text/javascript" language="javascript" src="plugins/jqplot.categoryAxisRenderer.js"></script> | ||||
@@ -28,14 +39,36 @@ | |||||
* | * | ||||
* > {axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer}}} | * > {axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer}}} | ||||
**/ | **/ | ||||
$.jqplot.CategoryAxisRenderer = function() { | |||||
$.jqplot.CategoryAxisRenderer = function(options) { | |||||
$.jqplot.LinearAxisRenderer.call(this); | $.jqplot.LinearAxisRenderer.call(this); | ||||
// prop: sortMergedLabels | |||||
// True to sort tick labels when labels are created by merging | |||||
// x axis values from multiple series. That is, say you have | |||||
// two series like: | |||||
// > line1 = [[2006, 4], [2008, 9], [2009, 16]]; | |||||
// > line2 = [[2006, 3], [2007, 7], [2008, 6]]; | |||||
// If no label array is specified, tick labels will be collected | |||||
// from the x values of the series. With sortMergedLabels | |||||
// set to true, tick labels will be: | |||||
// > [2006, 2007, 2008, 2009] | |||||
// With sortMergedLabels set to false, tick labels will be: | |||||
// > [2006, 2008, 2009, 2007] | |||||
// | |||||
// Note, this property is specified on the renderOptions for the | |||||
// axes when creating a plot: | |||||
// > axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer, rendererOptions:{sortMergedLabels:true}}} | |||||
this.sortMergedLabels = false; | |||||
}; | }; | ||||
$.jqplot.CategoryAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); | $.jqplot.CategoryAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); | ||||
$.jqplot.CategoryAxisRenderer.prototype.constructor = $.jqplot.CategoryAxisRenderer; | $.jqplot.CategoryAxisRenderer.prototype.constructor = $.jqplot.CategoryAxisRenderer; | ||||
$.jqplot.CategoryAxisRenderer.prototype.init = function(options){ | $.jqplot.CategoryAxisRenderer.prototype.init = function(options){ | ||||
this.groups = 1; | |||||
this.groupLabels = []; | |||||
this._groupLabels = []; | |||||
this._grouped = false; | |||||
this._barsPerGroup = null; | |||||
// prop: tickRenderer | // prop: tickRenderer | ||||
// A class of a rendering engine for creating the ticks labels displayed on the plot, | // A class of a rendering engine for creating the ticks labels displayed on the plot, | ||||
// See <$.jqplot.AxisTickRenderer>. | // See <$.jqplot.AxisTickRenderer>. | ||||
@@ -47,6 +80,9 @@ | |||||
// the min/max bounds for this axis. | // the min/max bounds for this axis. | ||||
for (var i=0; i<this._series.length; i++) { | for (var i=0; i<this._series.length; i++) { | ||||
var s = this._series[i]; | var s = this._series[i]; | ||||
if (s.groups) { | |||||
this.groups = s.groups; | |||||
} | |||||
var d = s.data; | var d = s.data; | ||||
for (var j=0; j<d.length; j++) { | for (var j=0; j<d.length; j++) { | ||||
@@ -68,6 +104,10 @@ | |||||
} | } | ||||
} | } | ||||
} | } | ||||
if (this.groupLabels.length) { | |||||
this.groups = this.groupLabels.length; | |||||
} | |||||
}; | }; | ||||
@@ -85,6 +125,17 @@ | |||||
// if we already have ticks, use them. | // if we already have ticks, use them. | ||||
if (userTicks.length) { | if (userTicks.length) { | ||||
// adjust with blanks if we have groups | |||||
if (this.groups > 1 && !this._grouped) { | |||||
var l = userTicks.length; | |||||
var skip = parseInt(l/this.groups, 10); | |||||
var count = 0; | |||||
for (var i=skip; i<l; i+=skip) { | |||||
userTicks.splice(i+count, 0, ' '); | |||||
count++; | |||||
} | |||||
this._grouped = true; | |||||
} | |||||
this.min = 0.5; | this.min = 0.5; | ||||
this.max = userTicks.length + 0.5; | this.max = userTicks.length + 0.5; | ||||
var range = this.max - this.min; | var range = this.max - this.min; | ||||
@@ -94,12 +145,12 @@ | |||||
// need a marker before and after the tick | // need a marker before and after the tick | ||||
var t = new this.tickRenderer(this.tickOptions); | var t = new this.tickRenderer(this.tickOptions); | ||||
t.showLabel = false; | t.showLabel = false; | ||||
t.showMark = true; | |||||
// t.showMark = true; | |||||
t.setTick(tt, this.name); | t.setTick(tt, this.name); | ||||
this._ticks.push(t); | this._ticks.push(t); | ||||
var t = new this.tickRenderer(this.tickOptions); | var t = new this.tickRenderer(this.tickOptions); | ||||
t.label = userTicks[i]; | t.label = userTicks[i]; | ||||
t.showLabel = true; | |||||
// t.showLabel = true; | |||||
t.showMark = false; | t.showMark = false; | ||||
t.showGridline = false; | t.showGridline = false; | ||||
t.setTick(tt+0.5, this.name); | t.setTick(tt+0.5, this.name); | ||||
@@ -108,7 +159,7 @@ | |||||
// now add the last tick at the end | // now add the last tick at the end | ||||
var t = new this.tickRenderer(this.tickOptions); | var t = new this.tickRenderer(this.tickOptions); | ||||
t.showLabel = false; | t.showLabel = false; | ||||
t.showMark = true; | |||||
// t.showMark = true; | |||||
t.setTick(tt+1, this.name); | t.setTick(tt+1, this.name); | ||||
this._ticks.push(t); | this._ticks.push(t); | ||||
} | } | ||||
@@ -139,6 +190,7 @@ | |||||
var numcats = 0; | var numcats = 0; | ||||
var min = 0.5; | var min = 0.5; | ||||
var max, val; | var max, val; | ||||
var isMerged = false; | |||||
for (var i=0; i<this._series.length; i++) { | for (var i=0; i<this._series.length; i++) { | ||||
var s = this._series[i]; | var s = this._series[i]; | ||||
for (var j=0; j<s.data.length; j++) { | for (var j=0; j<s.data.length; j++) { | ||||
@@ -149,12 +201,17 @@ | |||||
val = s.data[j][1]; | val = s.data[j][1]; | ||||
} | } | ||||
if ($.inArray(val, labels) == -1) { | if ($.inArray(val, labels) == -1) { | ||||
isMerged = true; | |||||
numcats += 1; | numcats += 1; | ||||
labels.push(val); | labels.push(val); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (isMerged && this.sortMergedLabels) { | |||||
labels.sort(function(a,b) { return a - b; }); | |||||
} | |||||
// keep a reference to these tick labels to use for redrawing plot (see bug #57) | // keep a reference to these tick labels to use for redrawing plot (see bug #57) | ||||
this.ticks = labels; | this.ticks = labels; | ||||
@@ -179,6 +236,17 @@ | |||||
} | } | ||||
} | } | ||||
} | } | ||||
// adjust with blanks if we have groups | |||||
if (this.groups > 1 && !this._grouped) { | |||||
var l = labels.length; | |||||
var skip = parseInt(l/this.groups, 10); | |||||
var count = 0; | |||||
for (var i=skip; i<l; i+=skip+1) { | |||||
labels[i] = ' '; | |||||
} | |||||
this._grouped = true; | |||||
} | |||||
max = numcats + 0.5; | max = numcats + 0.5; | ||||
if (this.numberTicks == null) { | if (this.numberTicks == null) { | ||||
@@ -221,18 +289,348 @@ | |||||
t.showMark = false; | t.showMark = false; | ||||
t.showGridline = false; | t.showGridline = false; | ||||
} | } | ||||
if (!this.showTicks) { | |||||
t.showLabel = false; | |||||
t.showMark = false; | |||||
} | |||||
else if (!this.showTickMarks) { | |||||
t.showMark = false; | |||||
} | |||||
t.setTick(tt, this.name); | t.setTick(tt, this.name); | ||||
this._ticks.push(t); | this._ticks.push(t); | ||||
} | } | ||||
} | } | ||||
}; | |||||
// called with scope of axis | |||||
$.jqplot.CategoryAxisRenderer.prototype.draw = function(ctx, plot) { | |||||
if (this.show) { | |||||
// populate the axis label and value properties. | |||||
// createTicks is a method on the renderer, but | |||||
// call it within the scope of the axis. | |||||
this.renderer.createTicks.call(this); | |||||
// fill a div with axes labels in the right direction. | |||||
// Need to pregenerate each axis to get it's bounds and | |||||
// position it and the labels correctly on the plot. | |||||
var dim=0; | |||||
var temp; | |||||
// Added for theming. | |||||
if (this._elem) { | |||||
// this._elem.empty(); | |||||
// Memory Leaks patch | |||||
this._elem.emptyForce(); | |||||
} | |||||
this._elem = this._elem || $('<div class="jqplot-axis jqplot-'+this.name+'" style="position:absolute;"></div>'); | |||||
if (this.name == 'xaxis' || this.name == 'x2axis') { | |||||
this._elem.width(this._plotDimensions.width); | |||||
} | |||||
else { | |||||
this._elem.height(this._plotDimensions.height); | |||||
} | |||||
// create a _label object. | |||||
this.labelOptions.axis = this.name; | |||||
this._label = new this.labelRenderer(this.labelOptions); | |||||
if (this._label.show) { | |||||
var elem = this._label.draw(ctx, plot); | |||||
elem.appendTo(this._elem); | |||||
} | |||||
var t = this._ticks; | |||||
for (var i=0; i<t.length; i++) { | |||||
var tick = t[i]; | |||||
if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) { | |||||
var elem = tick.draw(ctx, plot); | |||||
elem.appendTo(this._elem); | |||||
} | |||||
} | |||||
this._groupLabels = []; | |||||
// now make group labels | |||||
for (var i=0; i<this.groupLabels.length; i++) | |||||
{ | |||||
var elem = $('<div style="position:absolute;" class="jqplot-'+this.name+'-groupLabel"></div>'); | |||||
elem.html(this.groupLabels[i]); | |||||
this._groupLabels.push(elem); | |||||
elem.appendTo(this._elem); | |||||
} | |||||
} | |||||
return this._elem; | |||||
}; | |||||
// called with scope of axis | |||||
$.jqplot.CategoryAxisRenderer.prototype.set = function() { | |||||
var dim = 0; | |||||
var temp; | |||||
var w = 0; | |||||
var h = 0; | |||||
var lshow = (this._label == null) ? false : this._label.show; | |||||
if (this.show) { | |||||
var t = this._ticks; | |||||
for (var i=0; i<t.length; i++) { | |||||
var tick = t[i]; | |||||
if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) { | |||||
if (this.name == 'xaxis' || this.name == 'x2axis') { | |||||
temp = tick._elem.outerHeight(true); | |||||
} | |||||
else { | |||||
temp = tick._elem.outerWidth(true); | |||||
} | |||||
if (temp > dim) { | |||||
dim = temp; | |||||
} | |||||
} | |||||
} | |||||
var dim2 = 0; | |||||
for (var i=0; i<this._groupLabels.length; i++) { | |||||
var l = this._groupLabels[i]; | |||||
if (this.name == 'xaxis' || this.name == 'x2axis') { | |||||
temp = l.outerHeight(true); | |||||
} | |||||
else { | |||||
temp = l.outerWidth(true); | |||||
} | |||||
if (temp > dim2) { | |||||
dim2 = temp; | |||||
} | |||||
} | |||||
if (lshow) { | |||||
w = this._label._elem.outerWidth(true); | |||||
h = this._label._elem.outerHeight(true); | |||||
} | |||||
if (this.name == 'xaxis') { | |||||
dim += dim2 + h; | |||||
this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'}); | |||||
} | |||||
else if (this.name == 'x2axis') { | |||||
dim += dim2 + h; | |||||
this._elem.css({'height':dim+'px', left:'0px', top:'0px'}); | |||||
} | |||||
else if (this.name == 'yaxis') { | |||||
dim += dim2 + w; | |||||
this._elem.css({'width':dim+'px', left:'0px', top:'0px'}); | |||||
if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { | |||||
this._label._elem.css('width', w+'px'); | |||||
} | |||||
} | |||||
else { | |||||
dim += dim2 + w; | |||||
this._elem.css({'width':dim+'px', right:'0px', top:'0px'}); | |||||
if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { | |||||
this._label._elem.css('width', w+'px'); | |||||
} | |||||
} | |||||
} | |||||
}; | }; | ||||
// called with scope of axis | |||||
$.jqplot.CategoryAxisRenderer.prototype.pack = function(pos, offsets) { | |||||
var ticks = this._ticks; | |||||
var max = this.max; | |||||
var min = this.min; | |||||
var offmax = offsets.max; | |||||
var offmin = offsets.min; | |||||
var lshow = (this._label == null) ? false : this._label.show; | |||||
var i; | |||||
for (var p in pos) { | |||||
this._elem.css(p, pos[p]); | |||||
} | |||||
this._offsets = offsets; | |||||
// pixellength will be + for x axes and - for y axes becasue pixels always measured from top left. | |||||
var pixellength = offmax - offmin; | |||||
var unitlength = max - min; | |||||
// point to unit and unit to point conversions references to Plot DOM element top left corner. | |||||
this.p2u = function(p){ | |||||
return (p - offmin) * unitlength / pixellength + min; | |||||
}; | |||||
this.u2p = function(u){ | |||||
return (u - min) * pixellength / unitlength + offmin; | |||||
}; | |||||
if (this.name == 'xaxis' || this.name == 'x2axis'){ | |||||
this.series_u2p = function(u){ | |||||
return (u - min) * pixellength / unitlength; | |||||
}; | |||||
this.series_p2u = function(p){ | |||||
return p * unitlength / pixellength + min; | |||||
}; | |||||
} | |||||
else { | |||||
this.series_u2p = function(u){ | |||||
return (u - max) * pixellength / unitlength; | |||||
}; | |||||
this.series_p2u = function(p){ | |||||
return p * unitlength / pixellength + max; | |||||
}; | |||||
} | |||||
if (this.show) { | |||||
if (this.name == 'xaxis' || this.name == 'x2axis') { | |||||
for (i=0; i<ticks.length; i++) { | |||||
var t = ticks[i]; | |||||
if (t.show && t.showLabel) { | |||||
var shim; | |||||
if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { | |||||
// will need to adjust auto positioning based on which axis this is. | |||||
var temp = (this.name == 'xaxis') ? 1 : -1; | |||||
switch (t.labelPosition) { | |||||
case 'auto': | |||||
// position at end | |||||
if (temp * t.angle < 0) { | |||||
shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; | |||||
} | |||||
// position at start | |||||
else { | |||||
shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; | |||||
} | |||||
break; | |||||
case 'end': | |||||
shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; | |||||
break; | |||||
case 'start': | |||||
shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; | |||||
break; | |||||
case 'middle': | |||||
shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; | |||||
break; | |||||
default: | |||||
shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; | |||||
break; | |||||
} | |||||
} | |||||
else { | |||||
shim = -t.getWidth()/2; | |||||
} | |||||
var val = this.u2p(t.value) + shim + 'px'; | |||||
t._elem.css('left', val); | |||||
t.pack(); | |||||
} | |||||
} | |||||
var labeledge=['bottom', 0]; | |||||
if (lshow) { | |||||
var w = this._label._elem.outerWidth(true); | |||||
this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px'); | |||||
if (this.name == 'xaxis') { | |||||
this._label._elem.css('bottom', '0px'); | |||||
labeledge = ['bottom', this._label._elem.outerHeight(true)]; | |||||
} | |||||
else { | |||||
this._label._elem.css('top', '0px'); | |||||
labeledge = ['top', this._label._elem.outerHeight(true)]; | |||||
} | |||||
this._label.pack(); | |||||
} | |||||
// draw the group labels | |||||
var step = parseInt(this._ticks.length/this.groups, 10); | |||||
for (i=0; i<this._groupLabels.length; i++) { | |||||
var mid = 0; | |||||
var count = 0; | |||||
for (var j=i*step; j<=(i+1)*step; j++) { | |||||
if (this._ticks[j]._elem && this._ticks[j].label != " ") { | |||||
var t = this._ticks[j]._elem; | |||||
var p = t.position(); | |||||
mid += p.left + t.outerWidth(true)/2; | |||||
count++; | |||||
} | |||||
} | |||||
mid = mid/count; | |||||
this._groupLabels[i].css({'left':(mid - this._groupLabels[i].outerWidth(true)/2)}); | |||||
this._groupLabels[i].css(labeledge[0], labeledge[1]); | |||||
} | |||||
} | |||||
else { | |||||
for (i=0; i<ticks.length; i++) { | |||||
var t = ticks[i]; | |||||
if (t.show && t.showLabel) { | |||||
var shim; | |||||
if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { | |||||
// will need to adjust auto positioning based on which axis this is. | |||||
var temp = (this.name == 'yaxis') ? 1 : -1; | |||||
switch (t.labelPosition) { | |||||
case 'auto': | |||||
// position at end | |||||
case 'end': | |||||
if (temp * t.angle < 0) { | |||||
shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; | |||||
} | |||||
else { | |||||
shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; | |||||
} | |||||
break; | |||||
case 'start': | |||||
if (t.angle > 0) { | |||||
shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; | |||||
} | |||||
else { | |||||
shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; | |||||
} | |||||
break; | |||||
case 'middle': | |||||
// if (t.angle > 0) { | |||||
// shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; | |||||
// } | |||||
// else { | |||||
// shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; | |||||
// } | |||||
shim = -t.getHeight()/2; | |||||
break; | |||||
default: | |||||
shim = -t.getHeight()/2; | |||||
break; | |||||
} | |||||
} | |||||
else { | |||||
shim = -t.getHeight()/2; | |||||
} | |||||
var val = this.u2p(t.value) + shim + 'px'; | |||||
t._elem.css('top', val); | |||||
t.pack(); | |||||
} | |||||
} | |||||
var labeledge=['left', 0]; | |||||
if (lshow) { | |||||
var h = this._label._elem.outerHeight(true); | |||||
this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px'); | |||||
if (this.name == 'yaxis') { | |||||
this._label._elem.css('left', '0px'); | |||||
labeledge = ['left', this._label._elem.outerWidth(true)]; | |||||
} | |||||
else { | |||||
this._label._elem.css('right', '0px'); | |||||
labeledge = ['right', this._label._elem.outerWidth(true)]; | |||||
} | |||||
this._label.pack(); | |||||
} | |||||
// draw the group labels, position top here, do left after label position. | |||||
var step = parseInt(this._ticks.length/this.groups, 10); | |||||
for (i=0; i<this._groupLabels.length; i++) { | |||||
var mid = 0; | |||||
var count = 0; | |||||
for (var j=i*step; j<=(i+1)*step; j++) { | |||||
if (this._ticks[j]._elem && this._ticks[j].label != " ") { | |||||
var t = this._ticks[j]._elem; | |||||
var p = t.position(); | |||||
mid += p.top + t.outerHeight()/2; | |||||
count++; | |||||
} | |||||
} | |||||
mid = mid/count; | |||||
this._groupLabels[i].css({'top':mid - this._groupLabels[i].outerHeight()/2}); | |||||
this._groupLabels[i].css(labeledge[0], labeledge[1]); | |||||
} | |||||
} | |||||
} | |||||
}; | |||||
})(jQuery); | })(jQuery); |
@@ -0,0 +1,115 @@ | |||||
/** | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 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.ciParser | |||||
* Data Renderer function which converts a custom JSON data object into jqPlot data format. | |||||
* Set this as a callable on the jqplot dataRenderer plot option: | |||||
* | |||||
* > plot = $.jqplot('mychart', [data], { dataRenderer: $.jqplot.ciParser, ... }); | |||||
* | |||||
* Where data is an object in JSON format or a JSON encoded string conforming to the | |||||
* City Index API spec. | |||||
* | |||||
* Note that calling the renderer function is handled internally by jqPlot. The | |||||
* user does not have to call the function. The parameters described below will | |||||
* automatically be passed to the ciParser function. | |||||
* | |||||
* Parameters: | |||||
* data - JSON encoded string or object. | |||||
* plot - reference to jqPlot Plot object. | |||||
* | |||||
* Returns: | |||||
* data array in jqPlot format. | |||||
* | |||||
*/ | |||||
$.jqplot.ciParser = function (data, plot) { | |||||
var ret = [], | |||||
line, | |||||
temp, | |||||
i, j, k, kk; | |||||
if (typeof(data) == "string") { | |||||
data = $.jqplot.JSON.parse(data, handleStrings); | |||||
} | |||||
else if (typeof(data) == "object") { | |||||
for (k in data) { | |||||
for (i=0; i<data[k].length; i++) { | |||||
for (kk in data[k][i]) { | |||||
data[k][i][kk] = handleStrings(kk, data[k][i][kk]); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
else { | |||||
return null; | |||||
} | |||||
// function handleStrings | |||||
// Checks any JSON encoded strings to see if they are | |||||
// encoded dates. If so, pull out the timestamp. | |||||
// Expects dates to be represented by js timestamps. | |||||
function handleStrings(key, value) { | |||||
var a; | |||||
if (value != null) { | |||||
if (value.toString().indexOf('Date') >= 0) { | |||||
//here we will try to extract the ticks from the Date string in the "value" fields of JSON returned data | |||||
a = /^\/Date\((-?[0-9]+)\)\/$/.exec(value); | |||||
if (a) { | |||||
return parseInt(a[1], 10); | |||||
} | |||||
} | |||||
return value; | |||||
} | |||||
} | |||||
for (var prop in data) { | |||||
line = []; | |||||
temp = data[prop]; | |||||
switch (prop) { | |||||
case "PriceTicks": | |||||
for (i=0; i<temp.length; i++) { | |||||
line.push([temp[i]['TickDate'], temp[i]['Price']]); | |||||
} | |||||
break; | |||||
case "PriceBars": | |||||
for (i=0; i<temp.length; i++) { | |||||
line.push([temp[i]['BarDate'], temp[i]['Open'], temp[i]['High'], temp[i]['Low'], temp[i]['Close']]); | |||||
} | |||||
break; | |||||
} | |||||
ret.push(line); | |||||
} | |||||
return ret; | |||||
}; | |||||
})(jQuery); |
@@ -0,0 +1,30 @@ | |||||
/** | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 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(a){a.jqplot.ciParser=function(g,l){var m=[],o,n,h,f,e,c;if(typeof(g)=="string"){g=a.jqplot.JSON.parse(g,d)}else{if(typeof(g)=="object"){for(e in g){for(h=0;h<g[e].length;h++){for(c in g[e][h]){g[e][h][c]=d(c,g[e][h][c])}}}}else{return null}}function d(j,k){var i;if(k!=null){if(k.toString().indexOf("Date")>=0){i=/^\/Date\((-?[0-9]+)\)\/$/.exec(k);if(i){return parseInt(i[1],10)}}return k}}for(var b in g){o=[];n=g[b];switch(b){case"PriceTicks":for(h=0;h<n.length;h++){o.push([n[h]["TickDate"],n[h]["Price"]])}break;case"PriceBars":for(h=0;h<n.length;h++){o.push([n[h]["BarDate"],n[h]["Open"],n[h]["High"],n[h]["Low"],n[h]["Close"]])}break}m.push(o)}return m}})(jQuery); |
@@ -1,18 +1,30 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* The author would appreciate an email letting him know of any substantial | |||||
* use of jqPlot. You can reach the author at: chris dot leonello at gmail | |||||
* dot com or see http://www.jqplot.com/info.php . This is, of course, | |||||
* not required. | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * making a donation at: http://www.jqplot.com/donate.php . | ||||
* | * | ||||
* Thanks for using jqPlot! | |||||
* 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($) { | (function($) { | ||||
@@ -32,7 +44,8 @@ | |||||
// wether to show the cursor or not. | // wether to show the cursor or not. | ||||
this.show = $.jqplot.config.enablePlugins; | this.show = $.jqplot.config.enablePlugins; | ||||
// prop: showTooltip | // prop: showTooltip | ||||
// show a cursor position tooltip near the cursor | |||||
// show a cursor position tooltip. Location of the tooltip | |||||
// will be controlled by followMouse and tooltipLocation. | |||||
this.showTooltip = true; | this.showTooltip = true; | ||||
// prop: followMouse | // prop: followMouse | ||||
// Tooltip follows the mouse, it is not at a fixed location. | // Tooltip follows the mouse, it is not at a fixed location. | ||||
@@ -79,6 +92,10 @@ | |||||
// They Will be set through call to zoomProxy method. | // They Will be set through call to zoomProxy method. | ||||
this.zoomProxy = false; | this.zoomProxy = false; | ||||
this.zoomTarget = false; | this.zoomTarget = false; | ||||
// prop: looseZoom | |||||
// Will expand zoom range to provide more rounded tick values. | |||||
// Works only with linear axes and date axes. | |||||
this.looseZoom = false; | |||||
// prop: clickReset | // prop: clickReset | ||||
// Will reset plot zoom if single click on plot without drag. | // Will reset plot zoom if single click on plot without drag. | ||||
this.clickReset = false; | this.clickReset = false; | ||||
@@ -101,7 +118,7 @@ | |||||
// // auatoscale the adjacent axis. | // // auatoscale the adjacent axis. | ||||
// this.autoscaleConstraint = true; | // this.autoscaleConstraint = true; | ||||
this.shapeRenderer = new $.jqplot.ShapeRenderer(); | this.shapeRenderer = new $.jqplot.ShapeRenderer(); | ||||
this._zoom = {start:[], end:[], started: false, zooming:false, isZoomed:false, axes:{start:{}, end:{}}}; | |||||
this._zoom = {start:[], end:[], started: false, zooming:false, isZoomed:false, axes:{start:{}, end:{}}, gridpos:{}, datapos:{}}; | |||||
this._tooltipElem; | this._tooltipElem; | ||||
this.zoomCanvas; | this.zoomCanvas; | ||||
this.cursorCanvas; | this.cursorCanvas; | ||||
@@ -116,6 +133,17 @@ | |||||
// Format string used in the cursor legend. If showTooltipDataPosition is true, | // Format string used in the cursor legend. If showTooltipDataPosition is true, | ||||
// this will also be the default format string used by tooltipFormatString. | // this will also be the default format string used by tooltipFormatString. | ||||
this.cursorLegendFormatString = $.jqplot.Cursor.cursorLegendFormatString; | this.cursorLegendFormatString = $.jqplot.Cursor.cursorLegendFormatString; | ||||
// whether the cursor is over the grid or not. | |||||
this._oldHandlers = {onselectstart: null, ondrag: null, onmousedown: null}; | |||||
// prop: constrainOutsideZoom | |||||
// True to limit actual zoom area to edges of grid, even when zooming | |||||
// outside of plot area. That is, can't zoom out by mousing outside plot. | |||||
this.constrainOutsideZoom = true; | |||||
// prop: showTooltipOutsideZoom | |||||
// True will keep updating the tooltip when zooming of the grid. | |||||
this.showTooltipOutsideZoom = false; | |||||
// true if mouse is over grid, false if not. | |||||
this.onGrid = false; | |||||
$.extend(true, this, options); | $.extend(true, this, options); | ||||
}; | }; | ||||
@@ -142,7 +170,6 @@ | |||||
if (c.zoom) { | if (c.zoom) { | ||||
$.jqplot.eventListenerHooks.push(['jqplotMouseDown', handleMouseDown]); | $.jqplot.eventListenerHooks.push(['jqplotMouseDown', handleMouseDown]); | ||||
$.jqplot.eventListenerHooks.push(['jqplotMouseUp', handleMouseUp]); | |||||
if (c.clickReset) { | if (c.clickReset) { | ||||
$.jqplot.eventListenerHooks.push(['jqplotClick', handleClick]); | $.jqplot.eventListenerHooks.push(['jqplotClick', handleClick]); | ||||
@@ -150,7 +177,7 @@ | |||||
if (c.dblClickReset) { | if (c.dblClickReset) { | ||||
$.jqplot.eventListenerHooks.push(['jqplotDblClick', handleDblClick]); | $.jqplot.eventListenerHooks.push(['jqplotDblClick', handleDblClick]); | ||||
} | |||||
} | |||||
} | } | ||||
this.resetZoom = function() { | this.resetZoom = function() { | ||||
@@ -158,12 +185,17 @@ | |||||
if (!c.zoomProxy) { | if (!c.zoomProxy) { | ||||
for (var ax in axes) { | for (var ax in axes) { | ||||
axes[ax].reset(); | axes[ax].reset(); | ||||
axes[ax]._ticks = []; | |||||
// fake out tick creation algorithm to make sure original auto | |||||
// computed format string is used if _overrideFormatString is true | |||||
axes[ax]._autoFormatString = c._zoom.axes[ax].tickFormatString; | |||||
} | } | ||||
this.redraw(); | this.redraw(); | ||||
} | } | ||||
else { | else { | ||||
var ctx = this.plugins.cursor.zoomCanvas._ctx; | var ctx = this.plugins.cursor.zoomCanvas._ctx; | ||||
ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); | ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); | ||||
ctx = null; | |||||
} | } | ||||
this.plugins.cursor._zoom.isZoomed = false; | this.plugins.cursor._zoom.isZoomed = false; | ||||
this.target.trigger('jqplotResetZoom', [this, this.plugins.cursor]); | this.target.trigger('jqplotResetZoom', [this, this.plugins.cursor]); | ||||
@@ -183,17 +215,49 @@ | |||||
// called with context of plot | // called with context of plot | ||||
$.jqplot.Cursor.postDraw = function() { | $.jqplot.Cursor.postDraw = function() { | ||||
var c = this.plugins.cursor; | var c = this.plugins.cursor; | ||||
// if (c.zoom) { | |||||
c.zoomCanvas = new $.jqplot.GenericCanvas(); | |||||
this.eventCanvas._elem.before(c.zoomCanvas.createElement(this._gridPadding, 'jqplot-zoom-canvas', this._plotDimensions)); | |||||
var zctx = c.zoomCanvas.setContext(); | |||||
// } | |||||
c._tooltipElem = $('<div class="jqplot-cursor-tooltip" style="position:absolute;display:none"></div>'); | |||||
c.zoomCanvas._elem.before(c._tooltipElem); | |||||
// Memory Leaks patch | |||||
if (c.zoomCanvas) { | |||||
c.zoomCanvas.resetCanvas(); | |||||
c.zoomCanvas = null; | |||||
} | |||||
if (c.cursorCanvas) { | |||||
c.cursorCanvas.resetCanvas(); | |||||
c.cursorCanvas = null; | |||||
} | |||||
if (c._tooltipElem) { | |||||
c._tooltipElem.emptyForce(); | |||||
c._tooltipElem = null; | |||||
} | |||||
if (c.zoom) { | |||||
c.zoomCanvas = new $.jqplot.GenericCanvas(); | |||||
this.eventCanvas._elem.before(c.zoomCanvas.createElement(this._gridPadding, 'jqplot-zoom-canvas', this._plotDimensions, this)); | |||||
c.zoomCanvas.setContext(); | |||||
} | |||||
var elem = document.createElement('div'); | |||||
c._tooltipElem = $(elem); | |||||
elem = null; | |||||
c._tooltipElem.addClass('jqplot-cursor-tooltip'); | |||||
c._tooltipElem.css({position:'absolute', display:'none'}); | |||||
if (c.zoomCanvas) { | |||||
c.zoomCanvas._elem.before(c._tooltipElem); | |||||
} | |||||
else { | |||||
this.eventCanvas._elem.before(c._tooltipElem); | |||||
} | |||||
if (c.showVerticalLine || c.showHorizontalLine) { | if (c.showVerticalLine || c.showHorizontalLine) { | ||||
c.cursorCanvas = new $.jqplot.GenericCanvas(); | c.cursorCanvas = new $.jqplot.GenericCanvas(); | ||||
this.eventCanvas._elem.before(c.cursorCanvas.createElement(this._gridPadding, 'jqplot-cursor-canvas', this._plotDimensions)); | |||||
var zctx = c.cursorCanvas.setContext(); | |||||
this.eventCanvas._elem.before(c.cursorCanvas.createElement(this._gridPadding, 'jqplot-cursor-canvas', this._plotDimensions, this)); | |||||
c.cursorCanvas.setContext(); | |||||
} | } | ||||
// if we are showing the positions in unit coordinates, and no axes groups | // if we are showing the positions in unit coordinates, and no axes groups | ||||
@@ -252,13 +316,18 @@ | |||||
var cax = cursor._zoom.axes; | var cax = cursor._zoom.axes; | ||||
if (!plot.plugins.cursor.zoomProxy && cursor._zoom.isZoomed) { | if (!plot.plugins.cursor.zoomProxy && cursor._zoom.isZoomed) { | ||||
for (var ax in axes) { | for (var ax in axes) { | ||||
// axes[ax]._ticks = []; | |||||
// axes[ax].min = cax[ax].min; | |||||
// axes[ax].max = cax[ax].max; | |||||
// axes[ax].numberTicks = cax[ax].numberTicks; | |||||
// axes[ax].tickInterval = cax[ax].tickInterval; | |||||
// // for date axes | |||||
// axes[ax].daTickInterval = cax[ax].daTickInterval; | |||||
axes[ax].reset(); | |||||
axes[ax]._ticks = []; | axes[ax]._ticks = []; | ||||
axes[ax].min = cax[ax].min; | |||||
axes[ax].max = cax[ax].max; | |||||
axes[ax].numberTicks = cax[ax].numberTicks; | |||||
axes[ax].tickInterval = cax[ax].tickInterval; | |||||
// for date axes | |||||
axes[ax].daTickInterval = cax[ax].daTickInterval; | |||||
// fake out tick creation algorithm to make sure original auto | |||||
// computed format string is used if _overrideFormatString is true | |||||
axes[ax]._autoFormatString = cax[ax].tickFormatString; | |||||
} | } | ||||
plot.redraw(); | plot.redraw(); | ||||
cursor._zoom.isZoomed = false; | cursor._zoom.isZoomed = false; | ||||
@@ -266,6 +335,7 @@ | |||||
else { | else { | ||||
var ctx = cursor.zoomCanvas._ctx; | var ctx = cursor.zoomCanvas._ctx; | ||||
ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); | ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); | ||||
ctx = null; | |||||
} | } | ||||
plot.target.trigger('jqplotResetZoom', [plot, cursor]); | plot.target.trigger('jqplotResetZoom', [plot, cursor]); | ||||
}; | }; | ||||
@@ -280,9 +350,9 @@ | |||||
var zaxes = c._zoom.axes; | var zaxes = c._zoom.axes; | ||||
var start = zaxes.start; | var start = zaxes.start; | ||||
var end = zaxes.end; | var end = zaxes.end; | ||||
var min, max; | |||||
var min, max, dp, span; | |||||
var ctx = plot.plugins.cursor.zoomCanvas._ctx; | var ctx = plot.plugins.cursor.zoomCanvas._ctx; | ||||
// don't zoom is zoom area is too small (in pixels) | |||||
// don't zoom if zoom area is too small (in pixels) | |||||
if ((c.constrainZoomTo == 'none' && Math.abs(gridpos.x - c._zoom.start[0]) > 6 && Math.abs(gridpos.y - c._zoom.start[1]) > 6) || (c.constrainZoomTo == 'x' && Math.abs(gridpos.x - c._zoom.start[0]) > 6) || (c.constrainZoomTo == 'y' && Math.abs(gridpos.y - c._zoom.start[1]) > 6)) { | if ((c.constrainZoomTo == 'none' && Math.abs(gridpos.x - c._zoom.start[0]) > 6 && Math.abs(gridpos.y - c._zoom.start[1]) > 6) || (c.constrainZoomTo == 'x' && Math.abs(gridpos.x - c._zoom.start[0]) > 6) || (c.constrainZoomTo == 'y' && Math.abs(gridpos.y - c._zoom.start[1]) > 6)) { | ||||
if (!plot.plugins.cursor.zoomProxy) { | if (!plot.plugins.cursor.zoomProxy) { | ||||
for (var ax in datapos) { | for (var ax in datapos) { | ||||
@@ -295,22 +365,42 @@ | |||||
c._zoom.axes[ax].daTickInterval = axes[ax].daTickInterval; | c._zoom.axes[ax].daTickInterval = axes[ax].daTickInterval; | ||||
c._zoom.axes[ax].min = axes[ax].min; | c._zoom.axes[ax].min = axes[ax].min; | ||||
c._zoom.axes[ax].max = axes[ax].max; | c._zoom.axes[ax].max = axes[ax].max; | ||||
c._zoom.axes[ax].tickFormatString = (axes[ax].tickOptions != null) ? axes[ax].tickOptions.formatString : ''; | |||||
} | } | ||||
if ((c.constrainZoomTo == 'none') || (c.constrainZoomTo == 'x' && ax.charAt(0) == 'x') || (c.constrainZoomTo == 'y' && ax.charAt(0) == 'y')) { | if ((c.constrainZoomTo == 'none') || (c.constrainZoomTo == 'x' && ax.charAt(0) == 'x') || (c.constrainZoomTo == 'y' && ax.charAt(0) == 'y')) { | ||||
dp = datapos[ax]; | dp = datapos[ax]; | ||||
if (dp != null) { | |||||
if (dp != null) { | |||||
var newmin, newmax; | |||||
if (dp > start[ax]) { | if (dp > start[ax]) { | ||||
axes[ax].min = start[ax]; | |||||
axes[ax].max = dp; | |||||
newmin = start[ax]; | |||||
newmax = dp; | |||||
} | } | ||||
else { | else { | ||||
span = start[ax] - dp; | span = start[ax] - dp; | ||||
axes[ax].max = start[ax]; | |||||
axes[ax].min = dp; | |||||
newmin = dp; | |||||
newmax = start[ax]; | |||||
} | } | ||||
axes[ax].tickInterval = null; | |||||
// for date axes... | |||||
axes[ax].daTickInterval = null; | |||||
if (this.looseZoom && (axes[ax].renderer.constructor === $.jqplot.LinearAxisRenderer || axes[ax].renderer.constructor === $.jqplot.DateAxisRenderer)) { | |||||
var ret = $.jqplot.LinearTickGenerator(newmin, newmax); | |||||
axes[ax].min = ret[0]; | |||||
axes[ax].max = ret[1]; | |||||
axes[ax]._autoFormatString = ret[3]; | |||||
axes[ax].numberTicks = ret[2]; | |||||
axes[ax].tickInterval = ret[4]; | |||||
// for date axes... | |||||
axes[ax].daTickInterval = [ret[4]/1000, 'seconds']; | |||||
} | |||||
else { | |||||
axes[ax].min = newmin; | |||||
axes[ax].max = newmax; | |||||
axes[ax].tickInterval = null; | |||||
// for date axes... | |||||
axes[ax].daTickInterval = null; | |||||
} | |||||
axes[ax]._ticks = []; | axes[ax]._ticks = []; | ||||
} | } | ||||
} | } | ||||
@@ -326,6 +416,7 @@ | |||||
ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); | ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); | ||||
plot.redraw(); | plot.redraw(); | ||||
c._zoom.isZoomed = true; | c._zoom.isZoomed = true; | ||||
ctx = null; | |||||
} | } | ||||
plot.target.trigger('jqplotZoom', [gridpos, datapos, plot, cursor]); | plot.target.trigger('jqplotZoom', [gridpos, datapos, plot, cursor]); | ||||
} | } | ||||
@@ -445,11 +536,12 @@ | |||||
} | } | ||||
} | } | ||||
} | } | ||||
ctx = null; | |||||
} | } | ||||
function getIntersectingPoints(plot, x, y) { | function getIntersectingPoints(plot, x, y) { | ||||
var ret = {indices:[], data:[]}; | var ret = {indices:[], data:[]}; | ||||
var s, i, d0, d, j, r; | |||||
var s, i, d0, d, j, r, p; | |||||
var threshold; | var threshold; | ||||
var c = plot.plugins.cursor; | var c = plot.plugins.cursor; | ||||
for (var i=0; i<plot.series.length; i++) { | for (var i=0; i<plot.series.length; i++) { | ||||
@@ -517,8 +609,9 @@ | |||||
break; | break; | ||||
} | } | ||||
c._tooltipElem.css('left', x); | |||||
c._tooltipElem.css('top', y); | |||||
elem.css('left', x); | |||||
elem.css('top', y); | |||||
elem = null; | |||||
} | } | ||||
function positionTooltip(plot) { | function positionTooltip(plot) { | ||||
@@ -575,66 +668,83 @@ | |||||
elem.css({right:a, bottom:b}); | elem.css({right:a, bottom:b}); | ||||
break; | break; | ||||
} | } | ||||
elem = null; | |||||
} | } | ||||
function handleClick (ev, gridpos, datapos, neighbor, plot) { | function handleClick (ev, gridpos, datapos, neighbor, plot) { | ||||
ev.stopPropagation(); | |||||
ev.preventDefault(); | ev.preventDefault(); | ||||
ev.stopImmediatePropagation(); | |||||
var c = plot.plugins.cursor; | var c = plot.plugins.cursor; | ||||
if (c.clickReset) { | if (c.clickReset) { | ||||
c.resetZoom(plot, c); | c.resetZoom(plot, c); | ||||
} | } | ||||
var sel = window.getSelection; | |||||
if (document.selection && document.selection.empty) | |||||
{ | |||||
document.selection.empty(); | |||||
} | |||||
else if (sel && !sel().isCollapsed) { | |||||
sel().collapse(); | |||||
} | |||||
return false; | return false; | ||||
} | } | ||||
function handleDblClick (ev, gridpos, datapos, neighbor, plot) { | function handleDblClick (ev, gridpos, datapos, neighbor, plot) { | ||||
ev.stopPropagation(); | |||||
ev.preventDefault(); | ev.preventDefault(); | ||||
ev.stopImmediatePropagation(); | |||||
var c = plot.plugins.cursor; | var c = plot.plugins.cursor; | ||||
if (c.dblClickReset) { | if (c.dblClickReset) { | ||||
c.resetZoom(plot, c); | c.resetZoom(plot, c); | ||||
} | } | ||||
var sel = window.getSelection; | |||||
if (document.selection && document.selection.empty) | |||||
{ | |||||
document.selection.empty(); | |||||
} | |||||
else if (sel && !sel().isCollapsed) { | |||||
sel().collapse(); | |||||
} | |||||
return false; | return false; | ||||
} | } | ||||
function handleMouseLeave(ev, gridpos, datapos, neighbor, plot) { | function handleMouseLeave(ev, gridpos, datapos, neighbor, plot) { | ||||
var c = plot.plugins.cursor; | var c = plot.plugins.cursor; | ||||
c.onGrid = false; | |||||
if (c.show) { | if (c.show) { | ||||
$(ev.target).css('cursor', c.previousCursor); | $(ev.target).css('cursor', c.previousCursor); | ||||
if (c.showTooltip) { | |||||
if (c.showTooltip && !(c._zoom.zooming && c.showTooltipOutsideZoom && !c.constrainOutsideZoom)) { | |||||
c._tooltipElem.hide(); | c._tooltipElem.hide(); | ||||
} | } | ||||
if (c.zoom) { | if (c.zoom) { | ||||
c._zoom.started = false; | |||||
c._zoom.zooming = false; | |||||
if (!c.zoomProxy) { | |||||
var ctx = c.zoomCanvas._ctx; | |||||
ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); | |||||
} | |||||
c._zoom.gridpos = gridpos; | |||||
c._zoom.datapos = datapos; | |||||
} | } | ||||
if (c.showVerticalLine || c.showHorizontalLine) { | if (c.showVerticalLine || c.showHorizontalLine) { | ||||
var ctx = c.cursorCanvas._ctx; | var ctx = c.cursorCanvas._ctx; | ||||
ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); | ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); | ||||
} if (c.showCursorLegend) { | |||||
var cells = $(plot.targetId + ' td.jqplot-cursor-legend-label'); | |||||
for (var i=0; i<cells.length; i++) { | |||||
var idx = $(cells[i]).data('seriesIndex'); | |||||
var series = plot.series[idx]; | |||||
var label = series.label.toString(); | |||||
if (plot.legend.escapeHtml) { | |||||
$(cells[i]).text($.jqplot.sprintf(c.cursorLegendFormatString, label, undefined, undefined)); | |||||
} | |||||
else { | |||||
$(cells[i]).html($.jqplot.sprintf(c.cursorLegendFormatString, label, undefined, undefined)); | |||||
} | |||||
ctx = null; | |||||
} | |||||
if (c.showCursorLegend) { | |||||
var cells = $(plot.targetId + ' td.jqplot-cursor-legend-label'); | |||||
for (var i=0; i<cells.length; i++) { | |||||
var idx = $(cells[i]).data('seriesIndex'); | |||||
var series = plot.series[idx]; | |||||
var label = series.label.toString(); | |||||
if (plot.legend.escapeHtml) { | |||||
$(cells[i]).text($.jqplot.sprintf(c.cursorLegendFormatString, label, undefined, undefined)); | |||||
} | |||||
else { | |||||
$(cells[i]).html($.jqplot.sprintf(c.cursorLegendFormatString, label, undefined, undefined)); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
} | } | ||||
function handleMouseEnter(ev, gridpos, datapos, neighbor, plot) { | function handleMouseEnter(ev, gridpos, datapos, neighbor, plot) { | ||||
var c = plot.plugins.cursor; | var c = plot.plugins.cursor; | ||||
c.onGrid = true; | |||||
if (c.show) { | if (c.show) { | ||||
c.previousCursor = ev.target.style.cursor; | c.previousCursor = ev.target.style.cursor; | ||||
ev.target.style.cursor = c.style; | ev.target.style.cursor = c.style; | ||||
@@ -652,11 +762,11 @@ | |||||
moveLine(gridpos, plot); | moveLine(gridpos, plot); | ||||
} | } | ||||
} | } | ||||
} | |||||
} | |||||
function handleMouseMove(ev, gridpos, datapos, neighbor, plot) { | function handleMouseMove(ev, gridpos, datapos, neighbor, plot) { | ||||
var c = plot.plugins.cursor; | var c = plot.plugins.cursor; | ||||
var ctx = c.zoomCanvas._ctx; | |||||
if (c.show) { | if (c.show) { | ||||
if (c.showTooltip) { | if (c.showTooltip) { | ||||
updateTooltip(gridpos, datapos, plot); | updateTooltip(gridpos, datapos, plot); | ||||
@@ -664,32 +774,95 @@ | |||||
moveTooltip(gridpos, plot); | moveTooltip(gridpos, plot); | ||||
} | } | ||||
} | } | ||||
if (c.zoom && c._zoom.started && !c.zoomTarget) { | |||||
c._zoom.zooming = true; | |||||
if (c.constrainZoomTo == 'x') { | |||||
c._zoom.end = [gridpos.x, ctx.canvas.height]; | |||||
} | |||||
else if (c.constrainZoomTo == 'y') { | |||||
c._zoom.end = [ctx.canvas.width, gridpos.y]; | |||||
} | |||||
else { | |||||
c._zoom.end = [gridpos.x, gridpos.y]; | |||||
} | |||||
drawZoomBox.call(c); | |||||
} | |||||
if (c.showVerticalLine || c.showHorizontalLine) { | if (c.showVerticalLine || c.showHorizontalLine) { | ||||
moveLine(gridpos, plot); | moveLine(gridpos, plot); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
function getEventPosition(ev) { | |||||
var plot = ev.data.plot; | |||||
var go = plot.eventCanvas._elem.offset(); | |||||
var gridPos = {x:ev.pageX - go.left, y:ev.pageY - go.top}; | |||||
var dataPos = {xaxis:null, yaxis:null, x2axis:null, y2axis:null, y3axis:null, y4axis:null, y5axis:null, y6axis:null, y7axis:null, y8axis:null, y9axis:null}; | |||||
var an = ['xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis']; | |||||
var ax = plot.axes; | |||||
var n, axis; | |||||
for (n=11; n>0; n--) { | |||||
axis = an[n-1]; | |||||
if (ax[axis].show) { | |||||
dataPos[axis] = ax[axis].series_p2u(gridPos[axis.charAt(0)]); | |||||
} | |||||
} | |||||
return {offsets:go, gridPos:gridPos, dataPos:dataPos}; | |||||
} | |||||
function handleZoomMove(ev) { | |||||
var plot = ev.data.plot; | |||||
var c = plot.plugins.cursor; | |||||
// don't do anything if not on grid. | |||||
if (c.show && c.zoom && c._zoom.started && !c.zoomTarget) { | |||||
var ctx = c.zoomCanvas._ctx; | |||||
var positions = getEventPosition(ev); | |||||
var gridpos = positions.gridPos; | |||||
var datapos = positions.dataPos; | |||||
c._zoom.gridpos = gridpos; | |||||
c._zoom.datapos = datapos; | |||||
c._zoom.zooming = true; | |||||
var xpos = gridpos.x; | |||||
var ypos = gridpos.y; | |||||
var height = ctx.canvas.height; | |||||
var width = ctx.canvas.width; | |||||
if (c.showTooltip && !c.onGrid && c.showTooltipOutsideZoom) { | |||||
updateTooltip(gridpos, datapos, plot); | |||||
if (c.followMouse) { | |||||
moveTooltip(gridpos, plot); | |||||
} | |||||
} | |||||
if (c.constrainZoomTo == 'x') { | |||||
c._zoom.end = [xpos, height]; | |||||
} | |||||
else if (c.constrainZoomTo == 'y') { | |||||
c._zoom.end = [width, ypos]; | |||||
} | |||||
else { | |||||
c._zoom.end = [xpos, ypos]; | |||||
} | |||||
var sel = window.getSelection; | |||||
if (document.selection && document.selection.empty) | |||||
{ | |||||
document.selection.empty(); | |||||
} | |||||
else if (sel && !sel().isCollapsed) { | |||||
sel().collapse(); | |||||
} | |||||
drawZoomBox.call(c); | |||||
ctx = null; | |||||
} | |||||
} | |||||
function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { | function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { | ||||
var c = plot.plugins.cursor; | var c = plot.plugins.cursor; | ||||
$(document).one('mouseup.jqplot_cursor', {plot:plot}, handleMouseUp); | |||||
var axes = plot.axes; | var axes = plot.axes; | ||||
if (document.onselectstart != undefined) { | |||||
c._oldHandlers.onselectstart = document.onselectstart; | |||||
document.onselectstart = function () { return false; }; | |||||
} | |||||
if (document.ondrag != undefined) { | |||||
c._oldHandlers.ondrag = document.ondrag; | |||||
document.ondrag = function () { return false; }; | |||||
} | |||||
if (document.onmousedown != undefined) { | |||||
c._oldHandlers.onmousedown = document.onmousedown; | |||||
document.onmousedown = function () { return false; }; | |||||
} | |||||
if (c.zoom) { | if (c.zoom) { | ||||
if (!c.zoomProxy) { | if (!c.zoomProxy) { | ||||
var ctx = c.zoomCanvas._ctx; | var ctx = c.zoomCanvas._ctx; | ||||
ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); | ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); | ||||
ctx = null; | |||||
} | } | ||||
if (c.constrainZoomTo == 'x') { | if (c.constrainZoomTo == 'x') { | ||||
c._zoom.start = [gridpos.x, 0]; | c._zoom.start = [gridpos.x, 0]; | ||||
@@ -704,17 +877,69 @@ | |||||
for (var ax in datapos) { | for (var ax in datapos) { | ||||
// get zoom starting position. | // get zoom starting position. | ||||
c._zoom.axes.start[ax] = datapos[ax]; | c._zoom.axes.start[ax] = datapos[ax]; | ||||
} | |||||
} | |||||
$(document).bind('mousemove.jqplotCursor', {plot:plot}, handleZoomMove); | |||||
} | } | ||||
} | } | ||||
function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { | |||||
function handleMouseUp(ev) { | |||||
var plot = ev.data.plot; | |||||
var c = plot.plugins.cursor; | var c = plot.plugins.cursor; | ||||
if (c.zoom && c._zoom.zooming && !c.zoomTarget) { | if (c.zoom && c._zoom.zooming && !c.zoomTarget) { | ||||
c.doZoom(gridpos, datapos, plot, c); | |||||
var xpos = c._zoom.gridpos.x; | |||||
var ypos = c._zoom.gridpos.y; | |||||
var datapos = c._zoom.datapos; | |||||
var height = c.zoomCanvas._ctx.canvas.height; | |||||
var width = c.zoomCanvas._ctx.canvas.width; | |||||
var axes = plot.axes; | |||||
if (c.constrainOutsideZoom && !c.onGrid) { | |||||
if (xpos < 0) { xpos = 0; } | |||||
else if (xpos > width) { xpos = width; } | |||||
if (ypos < 0) { ypos = 0; } | |||||
else if (ypos > height) { ypos = height; } | |||||
for (var axis in datapos) { | |||||
if (datapos[axis]) { | |||||
if (axis.charAt(0) == 'x') { | |||||
datapos[axis] = axes[axis].series_p2u(xpos); | |||||
} | |||||
else { | |||||
datapos[axis] = axes[axis].series_p2u(ypos); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
if (c.constrainZoomTo == 'x') { | |||||
ypos = height; | |||||
} | |||||
else if (c.constrainZoomTo == 'y') { | |||||
xpos = width; | |||||
} | |||||
c._zoom.end = [xpos, ypos]; | |||||
c._zoom.gridpos = {x:xpos, y:ypos}; | |||||
c.doZoom(c._zoom.gridpos, datapos, plot, c); | |||||
} | } | ||||
c._zoom.started = false; | c._zoom.started = false; | ||||
c._zoom.zooming = false; | c._zoom.zooming = false; | ||||
$(document).unbind('mousemove.jqplotCursor', handleZoomMove); | |||||
if (document.onselectstart != undefined && c._oldHandlers.onselectstart != null){ | |||||
document.onselectstart = c._oldHandlers.onselectstart; | |||||
c._oldHandlers.onselectstart = null; | |||||
} | |||||
if (document.ondrag != undefined && c._oldHandlers.ondrag != null){ | |||||
document.ondrag = c._oldHandlers.ondrag; | |||||
c._oldHandlers.ondrag = null; | |||||
} | |||||
if (document.onmousedown != undefined && c._oldHandlers.onmousedown != null){ | |||||
document.onmousedown = c._oldHandlers.onmousedown; | |||||
c._oldHandlers.onmousedown = null; | |||||
} | |||||
} | } | ||||
function drawZoomBox() { | function drawZoomBox() { | ||||
@@ -746,6 +971,7 @@ | |||||
ctx.clearRect(l, t, w, h); | ctx.clearRect(l, t, w, h); | ||||
// IE won't show transparent fill rect, so stroke a rect also. | // IE won't show transparent fill rect, so stroke a rect also. | ||||
ctx.strokeRect(l,t,w,h); | ctx.strokeRect(l,t,w,h); | ||||
ctx = null; | |||||
} | } | ||||
$.jqplot.CursorLegendRenderer = function(options) { | $.jqplot.CursorLegendRenderer = function(options) { | ||||
@@ -758,15 +984,23 @@ | |||||
// called in context of a Legend | // called in context of a Legend | ||||
$.jqplot.CursorLegendRenderer.prototype.draw = function() { | $.jqplot.CursorLegendRenderer.prototype.draw = function() { | ||||
if (this._elem) { | |||||
this._elem.emptyForce(); | |||||
this._elem = null; | |||||
} | |||||
if (this.show) { | if (this.show) { | ||||
var series = this._series; | |||||
var series = this._series, s; | |||||
// make a table. one line label per row. | // make a table. one line label per row. | ||||
this._elem = $('<table class="jqplot-legend jqplot-cursor-legend" style="position:absolute"></table>'); | |||||
var elem = document.createElement('div'); | |||||
this._elem = $(elem); | |||||
elem = null; | |||||
this._elem.addClass('jqplot-legend jqplot-cursor-legend'); | |||||
this._elem.css('position', 'absolute'); | |||||
var pad = false; | var pad = false; | ||||
for (var i = 0; i< series.length; i++) { | for (var i = 0; i< series.length; i++) { | ||||
s = series[i]; | s = series[i]; | ||||
if (s.show) { | |||||
if (s.show && s.showLabel) { | |||||
var lt = $.jqplot.sprintf(this.formatString, s.label.toString()); | var lt = $.jqplot.sprintf(this.formatString, s.label.toString()); | ||||
if (lt) { | if (lt) { | ||||
var color = s.color; | var color = s.color; | ||||
@@ -786,6 +1020,9 @@ | |||||
} | } | ||||
} | } | ||||
} | } | ||||
series = s = null; | |||||
delete series; | |||||
delete s; | |||||
} | } | ||||
function addrow(label, color, pad, idx) { | function addrow(label, color, pad, idx) { | ||||
@@ -805,6 +1042,8 @@ | |||||
else { | else { | ||||
td.html(label); | td.html(label); | ||||
} | } | ||||
tr = null; | |||||
td = null; | |||||
} | } | ||||
return this._elem; | return this._elem; | ||||
}; | }; | ||||
@@ -1,18 +1,30 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* The author would appreciate an email letting him know of any substantial | |||||
* use of jqPlot. You can reach the author at: chris dot leonello at gmail | |||||
* dot com or see http://www.jqplot.com/info.php . This is, of course, | |||||
* not required. | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * making a donation at: http://www.jqplot.com/donate.php . | ||||
* | * | ||||
* Thanks for using jqPlot! | |||||
* 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($) { | (function($) { | ||||
@@ -94,6 +106,7 @@ | |||||
*/ | */ | ||||
$.jqplot.DateAxisRenderer = function() { | $.jqplot.DateAxisRenderer = function() { | ||||
$.jqplot.LinearAxisRenderer.call(this); | $.jqplot.LinearAxisRenderer.call(this); | ||||
this.date = new $.jsDate(); | |||||
}; | }; | ||||
$.jqplot.DateAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); | $.jqplot.DateAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); | ||||
@@ -103,7 +116,7 @@ | |||||
if (!format) { | if (!format) { | ||||
format = '%Y/%m/%d'; | format = '%Y/%m/%d'; | ||||
} | } | ||||
return Date.create(val).strftime(format); | |||||
return $.jsDate.strftime(val, format); | |||||
}; | }; | ||||
$.jqplot.DateAxisRenderer.prototype.init = function(options){ | $.jqplot.DateAxisRenderer.prototype.init = function(options){ | ||||
@@ -115,41 +128,94 @@ | |||||
this.tickOptions.formatter = $.jqplot.DateTickFormatter; | this.tickOptions.formatter = $.jqplot.DateTickFormatter; | ||||
this.daTickInterval = null; | this.daTickInterval = null; | ||||
this._daTickInterval = null; | this._daTickInterval = null; | ||||
$.extend(true, this, options); | $.extend(true, this, options); | ||||
var db = this._dataBounds; | |||||
var db = this._dataBounds, | |||||
stats, | |||||
sum, | |||||
s, | |||||
d, | |||||
pd, | |||||
sd, | |||||
intv; | |||||
// Go through all the series attached to this axis and find | // Go through all the series attached to this axis and find | ||||
// the min/max bounds for this axis. | // the min/max bounds for this axis. | ||||
for (var i=0; i<this._series.length; i++) { | for (var i=0; i<this._series.length; i++) { | ||||
var s = this._series[i]; | |||||
var d = s.data; | |||||
var pd = s._plotData; | |||||
var sd = s._stackData; | |||||
stats = {intervals:[], frequencies:{}, sortedIntervals:[], min:null, max:null, mean:null}; | |||||
sum = 0; | |||||
s = this._series[i]; | |||||
d = s.data; | |||||
pd = s._plotData; | |||||
sd = s._stackData; | |||||
intv = 0; | |||||
for (var j=0; j<d.length; j++) { | for (var j=0; j<d.length; j++) { | ||||
if (this.name == 'xaxis' || this.name == 'x2axis') { | if (this.name == 'xaxis' || this.name == 'x2axis') { | ||||
d[j][0] = Date.create(d[j][0]).getTime(); | |||||
pd[j][0] = Date.create(d[j][0]).getTime(); | |||||
sd[j][0] = Date.create(d[j][0]).getTime(); | |||||
if (d[j][0] < db.min || db.min == null) { | |||||
d[j][0] = new $.jsDate(d[j][0]).getTime(); | |||||
pd[j][0] = new $.jsDate(d[j][0]).getTime(); | |||||
sd[j][0] = new $.jsDate(d[j][0]).getTime(); | |||||
if ((d[j][0] != null && d[j][0] < db.min) || db.min == null) { | |||||
db.min = d[j][0]; | db.min = d[j][0]; | ||||
} | } | ||||
if (d[j][0] > db.max || db.max == null) { | |||||
if ((d[j][0] != null && d[j][0] > db.max) || db.max == null) { | |||||
db.max = d[j][0]; | db.max = d[j][0]; | ||||
} | } | ||||
if (j>0) { | |||||
intv = Math.abs(d[j][0] - d[j-1][0]); | |||||
stats.intervals.push(intv); | |||||
if (stats.frequencies.hasOwnProperty(intv)) { | |||||
stats.frequencies[intv] += 1; | |||||
} | |||||
else { | |||||
stats.frequencies[intv] = 1; | |||||
} | |||||
} | |||||
sum += intv; | |||||
} | } | ||||
else { | else { | ||||
d[j][1] = Date.create(d[j][1]).getTime(); | |||||
pd[j][1] = Date.create(d[j][1]).getTime(); | |||||
sd[j][1] = Date.create(d[j][1]).getTime(); | |||||
if (d[j][1] < db.min || db.min == null) { | |||||
d[j][1] = new $.jsDate(d[j][1]).getTime(); | |||||
pd[j][1] = new $.jsDate(d[j][1]).getTime(); | |||||
sd[j][1] = new $.jsDate(d[j][1]).getTime(); | |||||
if ((d[j][1] != null && d[j][1] < db.min) || db.min == null) { | |||||
db.min = d[j][1]; | db.min = d[j][1]; | ||||
} | } | ||||
if (d[j][1] > db.max || db.max == null) { | |||||
if ((d[j][1] != null && d[j][1] > db.max) || db.max == null) { | |||||
db.max = d[j][1]; | db.max = d[j][1]; | ||||
} | } | ||||
} | |||||
if (j>0) { | |||||
intv = Math.abs(d[j][1] - d[j-1][1]); | |||||
stats.intervals.push(intv); | |||||
if (stats.frequencies.hasOwnProperty(intv)) { | |||||
stats.frequencies[intv] += 1; | |||||
} | |||||
else { | |||||
stats.frequencies[intv] = 1; | |||||
} | |||||
} | |||||
} | |||||
sum += intv; | |||||
} | } | ||||
var tempf = 0, | |||||
tempn=0; | |||||
for (var n in stats.frequencies) { | |||||
stats.sortedIntervals.push({interval:n, frequency:stats.frequencies[n]}); | |||||
} | |||||
stats.sortedIntervals.sort(function(a, b){ | |||||
return b.frequency - a.frequency; | |||||
}); | |||||
stats.min = $.jqplot.arrayMin(stats.intervals); | |||||
stats.max = $.jqplot.arrayMax(stats.intervals); | |||||
stats.mean = sum/d.length; | |||||
this._intervalStats.push(stats); | |||||
stats = sum = s = d = pd = sd = null; | |||||
} | } | ||||
db = null; | |||||
}; | }; | ||||
// called with scope of an axis | // called with scope of an axis | ||||
@@ -169,6 +235,7 @@ | |||||
var name = this.name; | var name = this.name; | ||||
// databounds were set on axis initialization. | // databounds were set on axis initialization. | ||||
var db = this._dataBounds; | var db = this._dataBounds; | ||||
var iv = this._intervalStats; | |||||
var dim, interval; | var dim, interval; | ||||
var min, max; | var min, max; | ||||
var pos1, pos2; | var pos1, pos2; | ||||
@@ -177,13 +244,18 @@ | |||||
// if we already have ticks, use them. | // if we already have ticks, use them. | ||||
// ticks must be in order of increasing value. | // ticks must be in order of increasing value. | ||||
min = ((this.min != null) ? new $.jsDate(this.min).getTime() : db.min); | |||||
max = ((this.max != null) ? new $.jsDate(this.max).getTime() : db.max); | |||||
var range = max - min; | |||||
if (userTicks.length) { | if (userTicks.length) { | ||||
// ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed | // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed | ||||
for (i=0; i<userTicks.length; i++){ | for (i=0; i<userTicks.length; i++){ | ||||
var ut = userTicks[i]; | var ut = userTicks[i]; | ||||
var t = new this.tickRenderer(this.tickOptions); | var t = new this.tickRenderer(this.tickOptions); | ||||
if (ut.constructor == Array) { | if (ut.constructor == Array) { | ||||
t.value = Date.create(ut[0]).getTime(); | |||||
t.value = new $.jsDate(ut[0]).getTime(); | |||||
t.label = ut[1]; | t.label = ut[1]; | ||||
if (!this.showTicks) { | if (!this.showTicks) { | ||||
t.showLabel = false; | t.showLabel = false; | ||||
@@ -197,7 +269,7 @@ | |||||
} | } | ||||
else { | else { | ||||
t.value = Date.create(ut).getTime(); | |||||
t.value = new $.jsDate(ut).getTime(); | |||||
if (!this.showTicks) { | if (!this.showTicks) { | ||||
t.showLabel = false; | t.showLabel = false; | ||||
t.showMark = false; | t.showMark = false; | ||||
@@ -214,16 +286,61 @@ | |||||
this.max = this._ticks[this.numberTicks-1].value; | this.max = this._ticks[this.numberTicks-1].value; | ||||
this.daTickInterval = [(this.max - this.min) / (this.numberTicks - 1)/1000, 'seconds']; | this.daTickInterval = [(this.max - this.min) / (this.numberTicks - 1)/1000, 'seconds']; | ||||
} | } | ||||
//////// | |||||
// We don't have any ticks yet, let's make some! | |||||
// Doing complete autoscaling, no user options specified | |||||
//////// | |||||
// we don't have any ticks yet, let's make some! | |||||
else { | |||||
else if (this.tickInterval == null && this.min == null && this.max == null && this.numberTicks == null) { | |||||
var ret = $.jqplot.LinearTickGenerator(min, max); | |||||
// calculate a padded max and min, points should be less than these | |||||
// so that they aren't too close to the edges of the plot. | |||||
// User can adjust how much padding is allowed with pad, padMin and PadMax options. | |||||
var tumin = min + range*(this.padMin - 1); | |||||
var tumax = max - range*(this.padMax - 1); | |||||
if (min <=tumin || max >= tumax) { | |||||
tumin = min - range*(this.padMin - 1); | |||||
tumax = max + range*(this.padMax - 1); | |||||
ret = $.jqplot.LinearTickGenerator(tumin, tumax); | |||||
} | |||||
this.min = ret[0]; | |||||
this.max = ret[1]; | |||||
this.numberTicks = ret[2]; | |||||
this.tickInterval = ret[4]; | |||||
this.daTickInterval = [this.tickInterval/1000, 'seconds']; | |||||
for (var i=0; i<this.numberTicks; i++){ | |||||
var min = new $.jsDate(this.min); | |||||
tt = min.add(i*this.daTickInterval[0], this.daTickInterval[1]).getTime(); | |||||
var t = new this.tickRenderer(this.tickOptions); | |||||
// var t = new $.jqplot.AxisTickRenderer(this.tickOptions); | |||||
if (!this.showTicks) { | |||||
t.showLabel = false; | |||||
t.showMark = false; | |||||
} | |||||
else if (!this.showTickMarks) { | |||||
t.showMark = false; | |||||
} | |||||
t.setTick(tt, this.name); | |||||
this._ticks.push(t); | |||||
} | |||||
} | |||||
//////// | |||||
// Some option(s) specified, work around that. | |||||
//////// | |||||
else { | |||||
if (name == 'xaxis' || name == 'x2axis') { | if (name == 'xaxis' || name == 'x2axis') { | ||||
dim = this._plotDimensions.width; | dim = this._plotDimensions.width; | ||||
} | } | ||||
else { | else { | ||||
dim = this._plotDimensions.height; | dim = this._plotDimensions.height; | ||||
} | } | ||||
// if min, max and number of ticks specified, user can't specify interval. | // if min, max and number of ticks specified, user can't specify interval. | ||||
if (this.min != null && this.max != null && this.numberTicks != null) { | if (this.min != null && this.max != null && this.numberTicks != null) { | ||||
this.tickInterval = null; | this.tickInterval = null; | ||||
@@ -248,9 +365,6 @@ | |||||
} | } | ||||
} | } | ||||
} | } | ||||
min = ((this.min != null) ? Date.create(this.min).getTime() : db.min); | |||||
max = ((this.max != null) ? Date.create(this.max).getTime() : db.max); | |||||
// if min and max are same, space them out a bit | // if min and max are same, space them out a bit | ||||
if (min == max) { | if (min == max) { | ||||
@@ -259,11 +373,21 @@ | |||||
max += adj; | max += adj; | ||||
} | } | ||||
var range = max - min; | |||||
range = max - min; | |||||
var optNumTicks = 2 + parseInt(Math.max(0, dim-100)/100, 10); | |||||
// Here try to set ticks based on data spacing. | |||||
// if (this.min == null && this.max == null && this.numberTicks == null && this.tickInterval == null) { | |||||
// // | |||||
// } | |||||
var rmin, rmax; | var rmin, rmax; | ||||
rmin = (this.min != null) ? Date.create(this.min).getTime() : min - range/2*(this.padMin - 1); | |||||
rmax = (this.max != null) ? Date.create(this.max).getTime() : max + range/2*(this.padMax - 1); | |||||
rmin = (this.min != null) ? new $.jsDate(this.min).getTime() : min - range/2*(this.padMin - 1); | |||||
rmax = (this.max != null) ? new $.jsDate(this.max).getTime() : max + range/2*(this.padMax - 1); | |||||
this.min = rmin; | this.min = rmin; | ||||
this.max = rmax; | this.max = rmax; | ||||
range = this.max - this.min; | range = this.max - this.min; | ||||
@@ -272,10 +396,10 @@ | |||||
// if tickInterval is specified by user, we will ignore computed maximum. | // if tickInterval is specified by user, we will ignore computed maximum. | ||||
// max will be equal or greater to fit even # of ticks. | // max will be equal or greater to fit even # of ticks. | ||||
if (this.daTickInterval != null) { | if (this.daTickInterval != null) { | ||||
var nc = Date.create(this.max).diff(this.min, this.daTickInterval[1], true); | |||||
var nc = new $.jsDate(this.max).diff(this.min, this.daTickInterval[1], true); | |||||
this.numberTicks = Math.ceil(nc/this.daTickInterval[0]) +1; | this.numberTicks = Math.ceil(nc/this.daTickInterval[0]) +1; | ||||
// this.max = Date.create(this.min).add(this.numberTicks-1, this.daTickInterval[1]).getTime(); | |||||
this.max = Date.create(this.min).add((this.numberTicks-1) * this.daTickInterval[0], this.daTickInterval[1]).getTime(); | |||||
// this.max = new $.jsDate(this.min).add(this.numberTicks-1, this.daTickInterval[1]).getTime(); | |||||
this.max = new $.jsDate(this.min).add((this.numberTicks-1) * this.daTickInterval[0], this.daTickInterval[1]).getTime(); | |||||
} | } | ||||
else if (dim > 200) { | else if (dim > 200) { | ||||
this.numberTicks = parseInt(3+(dim-200)/100, 10); | this.numberTicks = parseInt(3+(dim-200)/100, 10); | ||||
@@ -289,7 +413,7 @@ | |||||
this.daTickInterval = [range / (this.numberTicks-1)/1000, 'seconds']; | this.daTickInterval = [range / (this.numberTicks-1)/1000, 'seconds']; | ||||
} | } | ||||
for (var i=0; i<this.numberTicks; i++){ | for (var i=0; i<this.numberTicks; i++){ | ||||
var min = Date.create(this.min); | |||||
var min = new $.jsDate(this.min); | |||||
tt = min.add(i*this.daTickInterval[0], this.daTickInterval[1]).getTime(); | tt = min.add(i*this.daTickInterval[0], this.daTickInterval[1]).getTime(); | ||||
var t = new this.tickRenderer(this.tickOptions); | var t = new this.tickRenderer(this.tickOptions); | ||||
// var t = new $.jqplot.AxisTickRenderer(this.tickOptions); | // var t = new $.jqplot.AxisTickRenderer(this.tickOptions); | ||||
@@ -304,6 +428,8 @@ | |||||
this._ticks.push(t); | this._ticks.push(t); | ||||
} | } | ||||
} | } | ||||
if (this._daTickInterval == null) { | if (this._daTickInterval == null) { | ||||
this._daTickInterval = this.daTickInterval; | this._daTickInterval = this.daTickInterval; | ||||
} | } | ||||
@@ -0,0 +1,910 @@ | |||||
/** | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 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.DonutRenderer | |||||
* Plugin renderer to draw a donut chart. | |||||
* x values, if present, will be used as slice labels. | |||||
* y values give slice size. | |||||
* | |||||
* To use this renderer, you need to include the | |||||
* donut renderer plugin, for example: | |||||
* | |||||
* > <script type="text/javascript" src="plugins/jqplot.donutRenderer.js"></script> | |||||
* | |||||
* Properties described here are passed into the $.jqplot function | |||||
* as options on the series renderer. For example: | |||||
* | |||||
* > plot2 = $.jqplot('chart2', [s1, s2], { | |||||
* > seriesDefaults: { | |||||
* > renderer:$.jqplot.DonutRenderer, | |||||
* > rendererOptions:{ | |||||
* > sliceMargin: 2, | |||||
* > innerDiameter: 110, | |||||
* > startAngle: -90 | |||||
* > } | |||||
* > } | |||||
* > }); | |||||
* | |||||
* A donut plot will trigger events on the plot target | |||||
* according to user interaction. All events return the event object, | |||||
* the series index, the point (slice) index, and the point data for | |||||
* the appropriate slice. | |||||
* | |||||
* 'jqplotDataMouseOver' - triggered when user mouseing over a slice. | |||||
* 'jqplotDataHighlight' - triggered the first time user mouses over a slice, | |||||
* if highlighting is enabled. | |||||
* 'jqplotDataUnhighlight' - triggered when a user moves the mouse out of | |||||
* a highlighted slice. | |||||
* 'jqplotDataClick' - triggered when the user clicks on a slice. | |||||
* 'jqplotDataRightClick' - tiggered when the user right clicks on a slice if | |||||
* the "captureRightClick" option is set to true on the plot. | |||||
*/ | |||||
$.jqplot.DonutRenderer = function(){ | |||||
$.jqplot.LineRenderer.call(this); | |||||
}; | |||||
$.jqplot.DonutRenderer.prototype = new $.jqplot.LineRenderer(); | |||||
$.jqplot.DonutRenderer.prototype.constructor = $.jqplot.DonutRenderer; | |||||
// called with scope of a series | |||||
$.jqplot.DonutRenderer.prototype.init = function(options, plot) { | |||||
// Group: Properties | |||||
// | |||||
// prop: diameter | |||||
// Outer diameter of the donut, auto computed by default | |||||
this.diameter = null; | |||||
// prop: innerDiameter | |||||
// Inner diameter of the donut, auto calculated by default. | |||||
// If specified will override thickness value. | |||||
this.innerDiameter = null; | |||||
// prop: thickness | |||||
// thickness of the donut, auto computed by default | |||||
// Overridden by if innerDiameter is specified. | |||||
this.thickness = null; | |||||
// prop: padding | |||||
// padding between the donut and plot edges, legend, etc. | |||||
this.padding = 20; | |||||
// prop: sliceMargin | |||||
// angular spacing between donut slices in degrees. | |||||
this.sliceMargin = 0; | |||||
// prop: ringMargin | |||||
// pixel distance between rings, or multiple series in a donut plot. | |||||
// null will compute ringMargin based on sliceMargin. | |||||
this.ringMargin = null; | |||||
// prop: fill | |||||
// true or false, wether to fil the slices. | |||||
this.fill = true; | |||||
// prop: shadowOffset | |||||
// offset of the shadow from the slice and offset of | |||||
// each succesive stroke of the shadow from the last. | |||||
this.shadowOffset = 2; | |||||
// prop: shadowAlpha | |||||
// transparency of the shadow (0 = transparent, 1 = opaque) | |||||
this.shadowAlpha = 0.07; | |||||
// prop: shadowDepth | |||||
// number of strokes to apply to the shadow, | |||||
// each stroke offset shadowOffset from the last. | |||||
this.shadowDepth = 5; | |||||
// prop: highlightMouseOver | |||||
// True to highlight slice when moused over. | |||||
// This must be false to enable highlightMouseDown to highlight when clicking on a slice. | |||||
this.highlightMouseOver = true; | |||||
// prop: highlightMouseDown | |||||
// True to highlight when a mouse button is pressed over a slice. | |||||
// This will be disabled if highlightMouseOver is true. | |||||
this.highlightMouseDown = false; | |||||
// prop: highlightColors | |||||
// an array of colors to use when highlighting a slice. | |||||
this.highlightColors = []; | |||||
// prop: dataLabels | |||||
// Either 'label', 'value', 'percent' or an array of labels to place on the pie slices. | |||||
// Defaults to percentage of each pie slice. | |||||
this.dataLabels = 'percent'; | |||||
// prop: showDataLabels | |||||
// true to show data labels on slices. | |||||
this.showDataLabels = false; | |||||
// prop: dataLabelFormatString | |||||
// Format string for data labels. If none, '%s' is used for "label" and for arrays, '%d' for value and '%d%%' for percentage. | |||||
this.dataLabelFormatString = null; | |||||
// prop: dataLabelThreshold | |||||
// Threshhold in percentage (0 - 100) of pie area, below which no label will be displayed. | |||||
// This applies to all label types, not just to percentage labels. | |||||
this.dataLabelThreshold = 3; | |||||
// prop: dataLabelPositionFactor | |||||
// A Multiplier (0-1) of the pie radius which controls position of label on slice. | |||||
// Increasing will slide label toward edge of pie, decreasing will slide label toward center of pie. | |||||
this.dataLabelPositionFactor = 0.4; | |||||
// prop: dataLabelNudge | |||||
// Number of pixels to slide the label away from (+) or toward (-) the center of the pie. | |||||
this.dataLabelNudge = 0; | |||||
// prop: startAngle | |||||
// Angle to start drawing donut in degrees. | |||||
// According to orientation of canvas coordinate system: | |||||
// 0 = on the positive x axis | |||||
// -90 = on the positive y axis. | |||||
// 90 = on the negaive y axis. | |||||
// 180 or - 180 = on the negative x axis. | |||||
this.startAngle = 0; | |||||
this.tickRenderer = $.jqplot.DonutTickRenderer; | |||||
// Used as check for conditions where donut shouldn't be drawn. | |||||
this._drawData = true; | |||||
this._type = 'donut'; | |||||
// if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver | |||||
if (options.highlightMouseDown && options.highlightMouseOver == null) { | |||||
options.highlightMouseOver = false; | |||||
} | |||||
$.extend(true, this, options); | |||||
if (this.diameter != null) { | |||||
this.diameter = this.diameter - this.sliceMargin; | |||||
} | |||||
this._diameter = null; | |||||
this._innerDiameter = null; | |||||
this._radius = null; | |||||
this._innerRadius = null; | |||||
this._thickness = null; | |||||
// references to the previous series in the plot to properly calculate diameters | |||||
// and thicknesses of nested rings. | |||||
this._previousSeries = []; | |||||
this._numberSeries = 1; | |||||
// array of [start,end] angles arrays, one for each slice. In radians. | |||||
this._sliceAngles = []; | |||||
// index of the currenty highlighted point, if any | |||||
this._highlightedPoint = null; | |||||
// set highlight colors if none provided | |||||
if (this.highlightColors.length == 0) { | |||||
for (var i=0; i<this.seriesColors.length; i++){ | |||||
var rgba = $.jqplot.getColorComponents(this.seriesColors[i]); | |||||
var newrgb = [rgba[0], rgba[1], rgba[2]]; | |||||
var sum = newrgb[0] + newrgb[1] + newrgb[2]; | |||||
for (var j=0; j<3; j++) { | |||||
// when darkening, lowest color component can be is 60. | |||||
newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]); | |||||
newrgb[j] = parseInt(newrgb[j], 10); | |||||
} | |||||
this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')'); | |||||
} | |||||
} | |||||
plot.postParseOptionsHooks.addOnce(postParseOptions); | |||||
plot.postInitHooks.addOnce(postInit); | |||||
plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); | |||||
plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown); | |||||
plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp); | |||||
plot.eventListenerHooks.addOnce('jqplotClick', handleClick); | |||||
plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); | |||||
plot.postDrawHooks.addOnce(postPlotDraw); | |||||
}; | |||||
$.jqplot.DonutRenderer.prototype.setGridData = function(plot) { | |||||
// set gridData property. This will hold angle in radians of each data point. | |||||
var stack = []; | |||||
var td = []; | |||||
var sa = this.startAngle/180*Math.PI; | |||||
var tot = 0; | |||||
// don't know if we have any valid data yet, so set plot to not draw. | |||||
this._drawData = false; | |||||
for (var i=0; i<this.data.length; i++){ | |||||
if (this.data[i][1] != 0) { | |||||
// we have data, O.K. to draw. | |||||
this._drawData = true; | |||||
} | |||||
stack.push(this.data[i][1]); | |||||
td.push([this.data[i][0]]); | |||||
if (i>0) { | |||||
stack[i] += stack[i-1]; | |||||
} | |||||
tot += this.data[i][1]; | |||||
} | |||||
var fact = Math.PI*2/stack[stack.length - 1]; | |||||
for (var i=0; i<stack.length; i++) { | |||||
td[i][1] = stack[i] * fact; | |||||
td[i][2] = this.data[i][1]/tot; | |||||
} | |||||
this.gridData = td; | |||||
}; | |||||
$.jqplot.DonutRenderer.prototype.makeGridData = function(data, plot) { | |||||
var stack = []; | |||||
var td = []; | |||||
var tot = 0; | |||||
var sa = this.startAngle/180*Math.PI; | |||||
// don't know if we have any valid data yet, so set plot to not draw. | |||||
this._drawData = false; | |||||
for (var i=0; i<data.length; i++){ | |||||
if (this.data[i][1] != 0) { | |||||
// we have data, O.K. to draw. | |||||
this._drawData = true; | |||||
} | |||||
stack.push(data[i][1]); | |||||
td.push([data[i][0]]); | |||||
if (i>0) { | |||||
stack[i] += stack[i-1]; | |||||
} | |||||
tot += data[i][1]; | |||||
} | |||||
var fact = Math.PI*2/stack[stack.length - 1]; | |||||
for (var i=0; i<stack.length; i++) { | |||||
td[i][1] = stack[i] * fact; | |||||
td[i][2] = data[i][1]/tot; | |||||
} | |||||
return td; | |||||
}; | |||||
$.jqplot.DonutRenderer.prototype.drawSlice = function (ctx, ang1, ang2, color, isShadow) { | |||||
var r = this._diameter / 2; | |||||
var ri = r - this._thickness; | |||||
var fill = this.fill; | |||||
// var lineWidth = this.lineWidth; | |||||
ctx.save(); | |||||
ctx.translate(this._center[0], this._center[1]); | |||||
// ctx.translate(this.sliceMargin*Math.cos((ang1+ang2)/2), this.sliceMargin*Math.sin((ang1+ang2)/2)); | |||||
if (isShadow) { | |||||
for (var i=0; i<this.shadowDepth; i++) { | |||||
ctx.save(); | |||||
ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI)); | |||||
doDraw(); | |||||
} | |||||
} | |||||
else { | |||||
doDraw(); | |||||
} | |||||
function doDraw () { | |||||
// Fix for IE and Chrome that can't seem to draw circles correctly. | |||||
// ang2 should always be <= 2 pi since that is the way the data is converted. | |||||
if (ang2 > 6.282 + this.startAngle) { | |||||
ang2 = 6.282 + this.startAngle; | |||||
if (ang1 > ang2) { | |||||
ang1 = 6.281 + this.startAngle; | |||||
} | |||||
} | |||||
// Fix for IE, where it can't seem to handle 0 degree angles. Also avoids | |||||
// ugly line on unfilled donuts. | |||||
if (ang1 >= ang2) { | |||||
return; | |||||
} | |||||
ctx.beginPath(); | |||||
ctx.fillStyle = color; | |||||
ctx.strokeStyle = color; | |||||
// ctx.lineWidth = lineWidth; | |||||
ctx.arc(0, 0, r, ang1, ang2, false); | |||||
ctx.lineTo(ri*Math.cos(ang2), ri*Math.sin(ang2)); | |||||
ctx.arc(0,0, ri, ang2, ang1, true); | |||||
ctx.closePath(); | |||||
if (fill) { | |||||
ctx.fill(); | |||||
} | |||||
else { | |||||
ctx.stroke(); | |||||
} | |||||
} | |||||
if (isShadow) { | |||||
for (var i=0; i<this.shadowDepth; i++) { | |||||
ctx.restore(); | |||||
} | |||||
} | |||||
ctx.restore(); | |||||
}; | |||||
// called with scope of series | |||||
$.jqplot.DonutRenderer.prototype.draw = function (ctx, gd, options, plot) { | |||||
var i; | |||||
var opts = (options != undefined) ? options : {}; | |||||
// offset and direction of offset due to legend placement | |||||
var offx = 0; | |||||
var offy = 0; | |||||
var trans = 1; | |||||
// var colorGenerator = new this.colorGenerator(this.seriesColors); | |||||
if (options.legendInfo && options.legendInfo.placement == 'insideGrid') { | |||||
var li = options.legendInfo; | |||||
switch (li.location) { | |||||
case 'nw': | |||||
offx = li.width + li.xoffset; | |||||
break; | |||||
case 'w': | |||||
offx = li.width + li.xoffset; | |||||
break; | |||||
case 'sw': | |||||
offx = li.width + li.xoffset; | |||||
break; | |||||
case 'ne': | |||||
offx = li.width + li.xoffset; | |||||
trans = -1; | |||||
break; | |||||
case 'e': | |||||
offx = li.width + li.xoffset; | |||||
trans = -1; | |||||
break; | |||||
case 'se': | |||||
offx = li.width + li.xoffset; | |||||
trans = -1; | |||||
break; | |||||
case 'n': | |||||
offy = li.height + li.yoffset; | |||||
break; | |||||
case 's': | |||||
offy = li.height + li.yoffset; | |||||
trans = -1; | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
} | |||||
var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; | |||||
var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; | |||||
var fill = (opts.fill != undefined) ? opts.fill : this.fill; | |||||
var cw = ctx.canvas.width; | |||||
var ch = ctx.canvas.height; | |||||
var w = cw - offx - 2 * this.padding; | |||||
var h = ch - offy - 2 * this.padding; | |||||
var mindim = Math.min(w,h); | |||||
var d = mindim; | |||||
var ringmargin = (this.ringMargin == null) ? this.sliceMargin * 2.0 : this.ringMargin; | |||||
for (var i=0; i<this._previousSeries.length; i++) { | |||||
d -= 2.0 * this._previousSeries[i]._thickness + 2.0 * ringmargin; | |||||
} | |||||
this._diameter = this.diameter || d; | |||||
if (this.innerDiameter != null) { | |||||
var od = (this._numberSeries > 1 && this.index > 0) ? this._previousSeries[0]._diameter : this._diameter; | |||||
this._thickness = this.thickness || (od - this.innerDiameter - 2.0*ringmargin*this._numberSeries) / this._numberSeries/2.0; | |||||
} | |||||
else { | |||||
this._thickness = this.thickness || mindim / 2 / (this._numberSeries + 1) * 0.85; | |||||
} | |||||
var r = this._radius = this._diameter/2; | |||||
this._innerRadius = this._radius - this._thickness; | |||||
var sa = this.startAngle / 180 * Math.PI; | |||||
this._center = [(cw - trans * offx)/2 + trans * offx, (ch - trans*offy)/2 + trans * offy]; | |||||
if (this.shadow) { | |||||
var shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')'; | |||||
for (var i=0; i<gd.length; i++) { | |||||
var ang1 = (i == 0) ? sa : gd[i-1][1] + sa; | |||||
// Adjust ang1 and ang2 for sliceMargin | |||||
ang1 += this.sliceMargin/180*Math.PI; | |||||
this.renderer.drawSlice.call (this, ctx, ang1, gd[i][1]+sa, shadowColor, true); | |||||
} | |||||
} | |||||
for (var i=0; i<gd.length; i++) { | |||||
var ang1 = (i == 0) ? sa : gd[i-1][1] + sa; | |||||
// Adjust ang1 and ang2 for sliceMargin | |||||
ang1 += this.sliceMargin/180*Math.PI; | |||||
var ang2 = gd[i][1] + sa; | |||||
this._sliceAngles.push([ang1, ang2]); | |||||
this.renderer.drawSlice.call (this, ctx, ang1, ang2, this.seriesColors[i], false); | |||||
if (this.showDataLabels && gd[i][2]*100 >= this.dataLabelThreshold) { | |||||
var fstr, avgang = (ang1+ang2)/2, label; | |||||
if (this.dataLabels == 'label') { | |||||
fstr = this.dataLabelFormatString || '%s'; | |||||
label = $.jqplot.sprintf(fstr, gd[i][0]); | |||||
} | |||||
else if (this.dataLabels == 'value') { | |||||
fstr = this.dataLabelFormatString || '%d'; | |||||
label = $.jqplot.sprintf(fstr, this.data[i][1]); | |||||
} | |||||
else if (this.dataLabels == 'percent') { | |||||
fstr = this.dataLabelFormatString || '%d%%'; | |||||
label = $.jqplot.sprintf(fstr, gd[i][2]*100); | |||||
} | |||||
else if (this.dataLabels.constructor == Array) { | |||||
fstr = this.dataLabelFormatString || '%s'; | |||||
label = $.jqplot.sprintf(fstr, this.dataLabels[i]); | |||||
} | |||||
var fact = this._innerRadius + this._thickness * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge; | |||||
var x = this._center[0] + Math.cos(avgang) * fact + this.canvas._offsets.left; | |||||
var y = this._center[1] + Math.sin(avgang) * fact + this.canvas._offsets.top; | |||||
var labelelem = $('<span class="jqplot-donut-series jqplot-data-label" style="position:absolute;">' + label + '</span>').insertBefore(plot.eventCanvas._elem); | |||||
x -= labelelem.width()/2; | |||||
y -= labelelem.height()/2; | |||||
x = Math.round(x); | |||||
y = Math.round(y); | |||||
labelelem.css({left: x, top: y}); | |||||
} | |||||
} | |||||
}; | |||||
$.jqplot.DonutAxisRenderer = function() { | |||||
$.jqplot.LinearAxisRenderer.call(this); | |||||
}; | |||||
$.jqplot.DonutAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); | |||||
$.jqplot.DonutAxisRenderer.prototype.constructor = $.jqplot.DonutAxisRenderer; | |||||
// There are no traditional axes on a donut chart. We just need to provide | |||||
// dummy objects with properties so the plot will render. | |||||
// called with scope of axis object. | |||||
$.jqplot.DonutAxisRenderer.prototype.init = function(options){ | |||||
// | |||||
this.tickRenderer = $.jqplot.DonutTickRenderer; | |||||
$.extend(true, this, options); | |||||
// I don't think I'm going to need _dataBounds here. | |||||
// have to go Axis scaling in a way to fit chart onto plot area | |||||
// and provide u2p and p2u functionality for mouse cursor, etc. | |||||
// for convienence set _dataBounds to 0 and 100 and | |||||
// set min/max to 0 and 100. | |||||
this._dataBounds = {min:0, max:100}; | |||||
this.min = 0; | |||||
this.max = 100; | |||||
this.showTicks = false; | |||||
this.ticks = []; | |||||
this.showMark = false; | |||||
this.show = false; | |||||
}; | |||||
$.jqplot.DonutLegendRenderer = function(){ | |||||
$.jqplot.TableLegendRenderer.call(this); | |||||
}; | |||||
$.jqplot.DonutLegendRenderer.prototype = new $.jqplot.TableLegendRenderer(); | |||||
$.jqplot.DonutLegendRenderer.prototype.constructor = $.jqplot.DonutLegendRenderer; | |||||
/** | |||||
* Class: $.jqplot.DonutLegendRenderer | |||||
* Legend Renderer specific to donut plots. Set by default | |||||
* when user creates a donut plot. | |||||
*/ | |||||
$.jqplot.DonutLegendRenderer.prototype.init = function(options) { | |||||
// Group: Properties | |||||
// | |||||
// 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; | |||||
$.extend(true, this, options); | |||||
}; | |||||
// called with context of legend | |||||
$.jqplot.DonutLegendRenderer.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+';' : ''; | |||||
ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : ''; | |||||
ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : ''; | |||||
ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : ''; | |||||
ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : ''; | |||||
this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>'); | |||||
// Donut 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 = false, | |||||
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++; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
return this._elem; | |||||
}; | |||||
// $.jqplot.DonutLegendRenderer.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 || {}; | |||||
// only set these if there is a donut series | |||||
var setopts = false; | |||||
if (options.seriesDefaults.renderer == $.jqplot.DonutRenderer) { | |||||
setopts = true; | |||||
} | |||||
else if (options.series) { | |||||
for (var i=0; i < options.series.length; i++) { | |||||
if (options.series[i].renderer == $.jqplot.DonutRenderer) { | |||||
setopts = true; | |||||
} | |||||
} | |||||
} | |||||
if (setopts) { | |||||
options.axesDefaults.renderer = $.jqplot.DonutAxisRenderer; | |||||
options.legend.renderer = $.jqplot.DonutLegendRenderer; | |||||
options.legend.preDraw = true; | |||||
options.seriesDefaults.pointLabels = {show: false}; | |||||
} | |||||
} | |||||
// called with scope of plot. | |||||
function postInit(target, data, options) { | |||||
// if multiple series, add a reference to the previous one so that | |||||
// donut rings can nest. | |||||
for (var i=1; i<this.series.length; i++) { | |||||
if (!this.series[i]._previousSeries.length){ | |||||
for (var j=0; j<i; j++) { | |||||
if (this.series[i].renderer.constructor == $.jqplot.DonutRenderer && this.series[j].renderer.constructor == $.jqplot.DonutRenderer) { | |||||
this.series[i]._previousSeries.push(this.series[j]); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
for (i=0; i<this.series.length; i++) { | |||||
if (this.series[i].renderer.constructor == $.jqplot.DonutRenderer) { | |||||
this.series[i]._numberSeries = this.series.length; | |||||
// don't allow mouseover and mousedown at same time. | |||||
if (this.series[i].highlightMouseOver) { | |||||
this.series[i].highlightMouseDown = false; | |||||
} | |||||
} | |||||
} | |||||
this.target.bind('mouseout', {plot:this}, function (ev) { unhighlight(ev.data.plot); }); | |||||
} | |||||
var postParseOptionsRun = false; | |||||
// called with scope of plot | |||||
function postParseOptions(options) { | |||||
for (var i=0; i<this.series.length; i++) { | |||||
this.series[i].seriesColors = this.seriesColors; | |||||
this.series[i].colorGenerator = this.colorGenerator; | |||||
} | |||||
} | |||||
function highlight (plot, sidx, pidx) { | |||||
var s = plot.series[sidx]; | |||||
var canvas = plot.plugins.donutRenderer.highlightCanvas; | |||||
canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height); | |||||
s._highlightedPoint = pidx; | |||||
plot.plugins.donutRenderer.highlightedSeriesIndex = sidx; | |||||
s.renderer.drawSlice.call(s, canvas._ctx, s._sliceAngles[pidx][0], s._sliceAngles[pidx][1], s.highlightColors[pidx], false); | |||||
} | |||||
function unhighlight (plot) { | |||||
var canvas = plot.plugins.donutRenderer.highlightCanvas; | |||||
canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); | |||||
for (var i=0; i<plot.series.length; i++) { | |||||
plot.series[i]._highlightedPoint = null; | |||||
} | |||||
plot.plugins.donutRenderer.highlightedSeriesIndex = null; | |||||
plot.target.trigger('jqplotDataUnhighlight'); | |||||
} | |||||
function handleMove(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; | |||||
var evt1 = jQuery.Event('jqplotDataMouseOver'); | |||||
evt1.pageX = ev.pageX; | |||||
evt1.pageY = ev.pageY; | |||||
plot.target.trigger(evt1, ins); | |||||
if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.donutRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { | |||||
var evt = jQuery.Event('jqplotDataHighlight'); | |||||
evt.pageX = ev.pageX; | |||||
evt.pageY = ev.pageY; | |||||
plot.target.trigger(evt, ins); | |||||
highlight (plot, ins[0], ins[1]); | |||||
} | |||||
} | |||||
else if (neighbor == null) { | |||||
unhighlight (plot); | |||||
} | |||||
} | |||||
function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; | |||||
if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.donutRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { | |||||
var evt = jQuery.Event('jqplotDataHighlight'); | |||||
evt.pageX = ev.pageX; | |||||
evt.pageY = ev.pageY; | |||||
plot.target.trigger(evt, ins); | |||||
highlight (plot, ins[0], ins[1]); | |||||
} | |||||
} | |||||
else if (neighbor == null) { | |||||
unhighlight (plot); | |||||
} | |||||
} | |||||
function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { | |||||
var idx = plot.plugins.donutRenderer.highlightedSeriesIndex; | |||||
if (idx != null && plot.series[idx].highlightMouseDown) { | |||||
unhighlight(plot); | |||||
} | |||||
} | |||||
function handleClick(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; | |||||
var evt = jQuery.Event('jqplotDataClick'); | |||||
evt.pageX = ev.pageX; | |||||
evt.pageY = ev.pageY; | |||||
plot.target.trigger(evt, ins); | |||||
} | |||||
} | |||||
function handleRightClick(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; | |||||
var idx = plot.plugins.donutRenderer.highlightedSeriesIndex; | |||||
if (idx != null && plot.series[idx].highlightMouseDown) { | |||||
unhighlight(plot); | |||||
} | |||||
var evt = jQuery.Event('jqplotDataRightClick'); | |||||
evt.pageX = ev.pageX; | |||||
evt.pageY = ev.pageY; | |||||
plot.target.trigger(evt, ins); | |||||
} | |||||
} | |||||
// called within context of plot | |||||
// create a canvas which we can draw on. | |||||
// insert it before the eventCanvas, so eventCanvas will still capture events. | |||||
function postPlotDraw() { | |||||
// Memory Leaks patch | |||||
if (this.plugins.donutRenderer && this.plugins.donutRenderer.highlightCanvas) { | |||||
this.plugins.donutRenderer.highlightCanvas.resetCanvas(); | |||||
this.plugins.donutRenderer.highlightCanvas = null; | |||||
} | |||||
this.plugins.donutRenderer = {highlightedSeriesIndex:null}; | |||||
this.plugins.donutRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); | |||||
// do we have any data labels? if so, put highlight canvas before those | |||||
// Fix for broken jquery :first selector with canvas (VML) elements. | |||||
var labels = $(this.targetId+' .jqplot-data-label'); | |||||
if (labels.length) { | |||||
$(labels[0]).before(this.plugins.donutRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-donutRenderer-highlight-canvas', this._plotDimensions, this)); | |||||
} | |||||
// else put highlight canvas before event canvas. | |||||
else { | |||||
this.eventCanvas._elem.before(this.plugins.donutRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-donutRenderer-highlight-canvas', this._plotDimensions, this)); | |||||
} | |||||
var hctx = this.plugins.donutRenderer.highlightCanvas.setContext(); | |||||
} | |||||
$.jqplot.preInitHooks.push(preInit); | |||||
$.jqplot.DonutTickRenderer = function() { | |||||
$.jqplot.AxisTickRenderer.call(this); | |||||
}; | |||||
$.jqplot.DonutTickRenderer.prototype = new $.jqplot.AxisTickRenderer(); | |||||
$.jqplot.DonutTickRenderer.prototype.constructor = $.jqplot.DonutTickRenderer; | |||||
})(jQuery); | |||||
@@ -1,18 +1,30 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* The author would appreciate an email letting him know of any substantial | |||||
* use of jqPlot. You can reach the author at: chris dot leonello at gmail | |||||
* dot com or see http://www.jqplot.com/info.php . This is, of course, | |||||
* not required. | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * making a donation at: http://www.jqplot.com/donate.php . | ||||
* | * | ||||
* Thanks for using jqPlot! | |||||
* 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($) { | (function($) { | ||||
@@ -67,10 +79,16 @@ | |||||
// insert it before the eventCanvas, so eventCanvas will still capture events. | // insert it before the eventCanvas, so eventCanvas will still capture events. | ||||
// add a new DragCanvas object to the plot plugins to handle drawing on this new canvas. | // add a new DragCanvas object to the plot plugins to handle drawing on this new canvas. | ||||
$.jqplot.Dragable.postPlotDraw = function() { | $.jqplot.Dragable.postPlotDraw = function() { | ||||
// Memory Leaks patch | |||||
if (this.plugins.dragable && this.plugins.dragable.highlightCanvas) { | |||||
this.plugins.dragable.highlightCanvas.resetCanvas(); | |||||
this.plugins.dragable.highlightCanvas = null; | |||||
} | |||||
this.plugins.dragable = {previousCursor:'auto', isOver:false}; | this.plugins.dragable = {previousCursor:'auto', isOver:false}; | ||||
this.plugins.dragable.dragCanvas = new DragCanvas(); | this.plugins.dragable.dragCanvas = new DragCanvas(); | ||||
this.eventCanvas._elem.before(this.plugins.dragable.dragCanvas.createElement(this._gridPadding, 'jqplot-dragable-canvas', this._plotDimensions)); | |||||
this.eventCanvas._elem.before(this.plugins.dragable.dragCanvas.createElement(this._gridPadding, 'jqplot-dragable-canvas', this._plotDimensions, this)); | |||||
var dctx = this.plugins.dragable.dragCanvas.setContext(); | var dctx = this.plugins.dragable.dragCanvas.setContext(); | ||||
}; | }; | ||||
@@ -168,6 +186,7 @@ | |||||
initDragPoint(plot, neighbor); | initDragPoint(plot, neighbor); | ||||
drag.markerRenderer.draw(s.gridData[neighbor.pointIndex][0], s.gridData[neighbor.pointIndex][1], dc._ctx); | drag.markerRenderer.draw(s.gridData[neighbor.pointIndex][0], s.gridData[neighbor.pointIndex][1], dc._ctx); | ||||
ev.target.style.cursor = "move"; | ev.target.style.cursor = "move"; | ||||
plot.target.trigger('jqplotDragStart', [neighbor.seriesIndex, neighbor.pointIndex, gridpos, datapos]); | |||||
} | } | ||||
} | } | ||||
// Just in case of a hickup, we'll clear the drag canvas and reset. | // Just in case of a hickup, we'll clear the drag canvas and reset. | ||||
@@ -194,10 +213,12 @@ | |||||
var y = (drag.constrainTo == 'x') ? dp.data[1] : datapos[s.yaxis]; | var y = (drag.constrainTo == 'x') ? dp.data[1] : datapos[s.yaxis]; | ||||
// var x = datapos[s.xaxis]; | // var x = datapos[s.xaxis]; | ||||
// var y = datapos[s.yaxis]; | // var y = datapos[s.yaxis]; | ||||
s.data[dp.pointIndex] = [x,y]; | |||||
s.data[dp.pointIndex][0] = x; | |||||
s.data[dp.pointIndex][1] = y; | |||||
plot.drawSeries({preventJqPlotSeriesDrawTrigger:true}, dp.seriesIndex); | plot.drawSeries({preventJqPlotSeriesDrawTrigger:true}, dp.seriesIndex); | ||||
dc._neighbor = null; | dc._neighbor = null; | ||||
ev.target.style.cursor = dc._cursors.pop(); | ev.target.style.cursor = dc._cursors.pop(); | ||||
plot.target.trigger('jqplotDragStop', [gridpos, datapos]); | |||||
} | } | ||||
} | } | ||||
})(jQuery); | })(jQuery); |
@@ -1,14 +1,30 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* Although not required, the author would appreciate an email letting him | * Although not required, the author would appreciate an email letting him | ||||
* know of any substantial use of jqPlot. You can reach the author at: | * know of any substantial use of jqPlot. You can reach the author at: | ||||
* chris dot leonello at gmail dot com or see http://www.jqplot.com/info.php . | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * 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(d){d.jqplot.Dragable=function(g){this.markerRenderer=new d.jqplot.MarkerRenderer({shadow:false});this.shapeRenderer=new d.jqplot.ShapeRenderer();this.isDragging=false;this.isOver=false;this._ctx;this._elem;this._point;this._gridData;this.color;this.constrainTo="none";d.extend(true,this,g)};function b(){d.jqplot.GenericCanvas.call(this);this.isDragging=false;this.isOver=false;this._neighbor;this._cursors=[]}b.prototype=new d.jqplot.GenericCanvas();b.prototype.constructor=b;d.jqplot.Dragable.parseOptions=function(i,h){var g=h||{};this.plugins.dragable=new d.jqplot.Dragable(g.dragable);this.isDragable=d.jqplot.config.enablePlugins};d.jqplot.Dragable.postPlotDraw=function(){this.plugins.dragable={previousCursor:"auto",isOver:false};this.plugins.dragable.dragCanvas=new b();this.eventCanvas._elem.before(this.plugins.dragable.dragCanvas.createElement(this._gridPadding,"jqplot-dragable-canvas",this._plotDimensions));var g=this.plugins.dragable.dragCanvas.setContext()};d.jqplot.preParseSeriesOptionsHooks.push(d.jqplot.Dragable.parseOptions);d.jqplot.postDrawHooks.push(d.jqplot.Dragable.postPlotDraw);d.jqplot.eventListenerHooks.push(["jqplotMouseMove",e]);d.jqplot.eventListenerHooks.push(["jqplotMouseDown",c]);d.jqplot.eventListenerHooks.push(["jqplotMouseUp",a]);function f(n,p){var q=n.series[p.seriesIndex];var m=q.plugins.dragable;var h=q.markerRenderer;var i=m.markerRenderer;i.style=h.style;i.lineWidth=h.lineWidth+2.5;i.size=h.size+5;if(!m.color){var l=d.jqplot.getColorComponents(h.color);var o=[l[0],l[1],l[2]];var k=(l[3]>=0.6)?l[3]*0.6:l[3]*(2-l[3]);m.color="rgba("+o[0]+","+o[1]+","+o[2]+","+k+")"}i.color=m.color;i.init();var g=(p.pointIndex>0)?p.pointIndex-1:0;var j=p.pointIndex+2;m._gridData=q.gridData.slice(g,j)}function e(o,l,h,t,m){if(m.plugins.dragable.dragCanvas.isDragging){var u=m.plugins.dragable.dragCanvas;var i=u._neighbor;var w=m.series[i.seriesIndex];var k=w.plugins.dragable;var r=w.gridData;var p=(k.constrainTo=="y")?i.gridData[0]:l.x;var n=(k.constrainTo=="x")?i.gridData[1]:l.y;var g=w._xaxis.series_p2u(p);var q=w._yaxis.series_p2u(n);var v=u._ctx;v.clearRect(0,0,v.canvas.width,v.canvas.height);if(i.pointIndex>0){k._gridData[1]=[p,n]}else{k._gridData[0]=[p,n]}m.series[i.seriesIndex].draw(u._ctx,{gridData:k._gridData,shadow:false,preventJqPlotSeriesDrawTrigger:true,color:k.color,markerOptions:{color:k.color,shadow:false},trendline:{show:false}});m.target.trigger("jqplotSeriesPointChange",[i.seriesIndex,i.pointIndex,[g,q],[p,n]])}else{if(t!=null){var j=m.series[t.seriesIndex];if(j.isDragable){var u=m.plugins.dragable.dragCanvas;if(!u.isOver){u._cursors.push(o.target.style.cursor);o.target.style.cursor="pointer"}u.isOver=true}}else{if(t==null){var u=m.plugins.dragable.dragCanvas;if(u.isOver){o.target.style.cursor=u._cursors.pop();u.isOver=false}}}}}function c(k,i,g,l,j){var m=j.plugins.dragable.dragCanvas;m._cursors.push(k.target.style.cursor);if(l!=null){var o=j.series[l.seriesIndex];var h=o.plugins.dragable;if(o.isDragable&&!m.isDragging){m._neighbor=l;m.isDragging=true;f(j,l);h.markerRenderer.draw(o.gridData[l.pointIndex][0],o.gridData[l.pointIndex][1],m._ctx);k.target.style.cursor="move"}}else{var n=m._ctx;n.clearRect(0,0,n.canvas.width,n.canvas.height);m.isDragging=false}}function a(m,j,g,o,k){if(k.plugins.dragable.dragCanvas.isDragging){var p=k.plugins.dragable.dragCanvas;var q=p._ctx;q.clearRect(0,0,q.canvas.width,q.canvas.height);p.isDragging=false;var h=p._neighbor;var r=k.series[h.seriesIndex];var i=r.plugins.dragable;var n=(i.constrainTo=="y")?h.data[0]:g[r.xaxis];var l=(i.constrainTo=="x")?h.data[1]:g[r.yaxis];r.data[h.pointIndex]=[n,l];k.drawSeries({preventJqPlotSeriesDrawTrigger:true},h.seriesIndex);p._neighbor=null;m.target.style.cursor=p._cursors.pop()}}})(jQuery); | |||||
(function(d){d.jqplot.Dragable=function(g){this.markerRenderer=new d.jqplot.MarkerRenderer({shadow:false});this.shapeRenderer=new d.jqplot.ShapeRenderer();this.isDragging=false;this.isOver=false;this._ctx;this._elem;this._point;this._gridData;this.color;this.constrainTo="none";d.extend(true,this,g)};function b(){d.jqplot.GenericCanvas.call(this);this.isDragging=false;this.isOver=false;this._neighbor;this._cursors=[]}b.prototype=new d.jqplot.GenericCanvas();b.prototype.constructor=b;d.jqplot.Dragable.parseOptions=function(i,h){var g=h||{};this.plugins.dragable=new d.jqplot.Dragable(g.dragable);this.isDragable=d.jqplot.config.enablePlugins};d.jqplot.Dragable.postPlotDraw=function(){if(this.plugins.dragable&&this.plugins.dragable.highlightCanvas){this.plugins.dragable.highlightCanvas.resetCanvas();this.plugins.dragable.highlightCanvas=null}this.plugins.dragable={previousCursor:"auto",isOver:false};this.plugins.dragable.dragCanvas=new b();this.eventCanvas._elem.before(this.plugins.dragable.dragCanvas.createElement(this._gridPadding,"jqplot-dragable-canvas",this._plotDimensions,this));var g=this.plugins.dragable.dragCanvas.setContext()};d.jqplot.preParseSeriesOptionsHooks.push(d.jqplot.Dragable.parseOptions);d.jqplot.postDrawHooks.push(d.jqplot.Dragable.postPlotDraw);d.jqplot.eventListenerHooks.push(["jqplotMouseMove",e]);d.jqplot.eventListenerHooks.push(["jqplotMouseDown",c]);d.jqplot.eventListenerHooks.push(["jqplotMouseUp",a]);function f(n,p){var q=n.series[p.seriesIndex];var m=q.plugins.dragable;var h=q.markerRenderer;var i=m.markerRenderer;i.style=h.style;i.lineWidth=h.lineWidth+2.5;i.size=h.size+5;if(!m.color){var l=d.jqplot.getColorComponents(h.color);var o=[l[0],l[1],l[2]];var k=(l[3]>=0.6)?l[3]*0.6:l[3]*(2-l[3]);m.color="rgba("+o[0]+","+o[1]+","+o[2]+","+k+")"}i.color=m.color;i.init();var g=(p.pointIndex>0)?p.pointIndex-1:0;var j=p.pointIndex+2;m._gridData=q.gridData.slice(g,j)}function e(o,l,h,t,m){if(m.plugins.dragable.dragCanvas.isDragging){var u=m.plugins.dragable.dragCanvas;var i=u._neighbor;var w=m.series[i.seriesIndex];var k=w.plugins.dragable;var r=w.gridData;var p=(k.constrainTo=="y")?i.gridData[0]:l.x;var n=(k.constrainTo=="x")?i.gridData[1]:l.y;var g=w._xaxis.series_p2u(p);var q=w._yaxis.series_p2u(n);var v=u._ctx;v.clearRect(0,0,v.canvas.width,v.canvas.height);if(i.pointIndex>0){k._gridData[1]=[p,n]}else{k._gridData[0]=[p,n]}m.series[i.seriesIndex].draw(u._ctx,{gridData:k._gridData,shadow:false,preventJqPlotSeriesDrawTrigger:true,color:k.color,markerOptions:{color:k.color,shadow:false},trendline:{show:false}});m.target.trigger("jqplotSeriesPointChange",[i.seriesIndex,i.pointIndex,[g,q],[p,n]])}else{if(t!=null){var j=m.series[t.seriesIndex];if(j.isDragable){var u=m.plugins.dragable.dragCanvas;if(!u.isOver){u._cursors.push(o.target.style.cursor);o.target.style.cursor="pointer"}u.isOver=true}}else{if(t==null){var u=m.plugins.dragable.dragCanvas;if(u.isOver){o.target.style.cursor=u._cursors.pop();u.isOver=false}}}}}function c(k,i,g,l,j){var m=j.plugins.dragable.dragCanvas;m._cursors.push(k.target.style.cursor);if(l!=null){var o=j.series[l.seriesIndex];var h=o.plugins.dragable;if(o.isDragable&&!m.isDragging){m._neighbor=l;m.isDragging=true;f(j,l);h.markerRenderer.draw(o.gridData[l.pointIndex][0],o.gridData[l.pointIndex][1],m._ctx);k.target.style.cursor="move";j.target.trigger("jqplotDragStart",[l.seriesIndex,l.pointIndex,i,g])}}else{var n=m._ctx;n.clearRect(0,0,n.canvas.width,n.canvas.height);m.isDragging=false}}function a(m,j,g,o,k){if(k.plugins.dragable.dragCanvas.isDragging){var p=k.plugins.dragable.dragCanvas;var q=p._ctx;q.clearRect(0,0,q.canvas.width,q.canvas.height);p.isDragging=false;var h=p._neighbor;var r=k.series[h.seriesIndex];var i=r.plugins.dragable;var n=(i.constrainTo=="y")?h.data[0]:g[r.xaxis];var l=(i.constrainTo=="x")?h.data[1]:g[r.yaxis];r.data[h.pointIndex][0]=n;r.data[h.pointIndex][1]=l;k.drawSeries({preventJqPlotSeriesDrawTrigger:true},h.seriesIndex);p._neighbor=null;m.target.style.cursor=p._cursors.pop();k.target.trigger("jqplotDragStop",[j,g])}}})(jQuery); |
@@ -0,0 +1,199 @@ | |||||
/** | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 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.EnhancedLegendRenderer | |||||
// Legend renderer which can specify the number of rows and/or columns in the legend. | |||||
$.jqplot.EnhancedLegendRenderer = function(){ | |||||
$.jqplot.TableLegendRenderer.call(this); | |||||
}; | |||||
$.jqplot.EnhancedLegendRenderer.prototype = new $.jqplot.TableLegendRenderer(); | |||||
$.jqplot.EnhancedLegendRenderer.prototype.constructor = $.jqplot.EnhancedLegendRenderer; | |||||
// called with scope of legend. | |||||
$.jqplot.EnhancedLegendRenderer.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; | |||||
// prop: seriesToggle | |||||
// false to not enable series on/off toggling on the legend. | |||||
// true or a fadein/fadeout speed (number of milliseconds or 'fast', 'normal', 'slow') | |||||
// to enable show/hide of series on click of legend item. | |||||
this.seriesToggle = 'normal'; | |||||
// prop: disableIEFading | |||||
// true to toggle series with a show/hide method only and not allow fading in/out. | |||||
// This is to overcome poor performance of fade in some versions of IE. | |||||
this.disableIEFading = true; | |||||
$.extend(true, this, options); | |||||
if (this.seriesToggle) { | |||||
$.jqplot.postDrawHooks.push(postDraw); | |||||
} | |||||
}; | |||||
// called with scope of legend | |||||
$.jqplot.EnhancedLegendRenderer.prototype.draw = function() { | |||||
var legend = this; | |||||
if (this.show) { | |||||
var series = this._series; | |||||
var s; | |||||
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+';' : ''; | |||||
ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : ''; | |||||
ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : ''; | |||||
ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : ''; | |||||
ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : ''; | |||||
this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>'); | |||||
if (this.seriesToggle) { | |||||
this._elem.css('z-index', '3'); | |||||
} | |||||
var pad = false, | |||||
reverse = false, | |||||
nr, nc; | |||||
if (this.numberRows) { | |||||
nr = this.numberRows; | |||||
if (!this.numberColumns){ | |||||
nc = Math.ceil(series.length/nr); | |||||
} | |||||
else{ | |||||
nc = this.numberColumns; | |||||
} | |||||
} | |||||
else if (this.numberColumns) { | |||||
nc = this.numberColumns; | |||||
nr = Math.ceil(series.length/this.numberColumns); | |||||
} | |||||
else { | |||||
nr = series.length; | |||||
nc = 1; | |||||
} | |||||
var i, j, tr, td1, td2, lt, rs; | |||||
var idx = 0; | |||||
// check to see if we need to reverse | |||||
for (i=series.length-1; i>=0; i--) { | |||||
if (series[i]._stack || series[i].renderer.constructor == $.jqplot.BezierCurveRenderer){ | |||||
reverse = true; | |||||
} | |||||
} | |||||
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 < series.length && series[idx].show && series[idx].showLabel){ | |||||
s = series[idx]; | |||||
lt = this.labels[idx] || s.label.toString(); | |||||
if (lt) { | |||||
var color = s.color; | |||||
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="background-color:'+color+';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) { | |||||
if (this.showLabels) {td2.prependTo(tr);} | |||||
if (this.showSwatches) {td1.prependTo(tr);} | |||||
} | |||||
else { | |||||
if (this.showSwatches) {td1.appendTo(tr);} | |||||
if (this.showLabels) {td2.appendTo(tr);} | |||||
} | |||||
if (this.seriesToggle) { | |||||
var speed; | |||||
if (typeof(this.seriesToggle) == 'string' || typeof(this.seriesToggle) == 'number') { | |||||
if (!$.jqplot.use_excanvas || !this.disableIEFading) { | |||||
speed = this.seriesToggle; | |||||
} | |||||
} | |||||
if (this.showSwatches) { | |||||
td1.bind('click', {series:s, speed:speed}, s.toggleDisplay); | |||||
td1.addClass('jqplot-seriesToggle'); | |||||
} | |||||
if (this.showLabels) { | |||||
td2.bind('click', {series:s, speed:speed}, s.toggleDisplay); | |||||
td2.addClass('jqplot-seriesToggle'); | |||||
} | |||||
} | |||||
pad = true; | |||||
} | |||||
} | |||||
idx++; | |||||
} | |||||
} | |||||
} | |||||
return this._elem; | |||||
}; | |||||
// called with scope of plot. | |||||
var postDraw = function () { | |||||
if (this.legend.renderer.constructor == $.jqplot.EnhancedLegendRenderer && this.legend.seriesToggle){ | |||||
var e = this.legend._elem.detach(); | |||||
this.eventCanvas._elem.after(e); | |||||
} | |||||
}; | |||||
})(jQuery); |
@@ -0,0 +1,30 @@ | |||||
/** | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 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(b){b.jqplot.EnhancedLegendRenderer=function(){b.jqplot.TableLegendRenderer.call(this)};b.jqplot.EnhancedLegendRenderer.prototype=new b.jqplot.TableLegendRenderer();b.jqplot.EnhancedLegendRenderer.prototype.constructor=b.jqplot.EnhancedLegendRenderer;b.jqplot.EnhancedLegendRenderer.prototype.init=function(c){this.numberRows=null;this.numberColumns=null;this.seriesToggle="normal";this.disableIEFading=true;b.extend(true,this,c);if(this.seriesToggle){b.jqplot.postDrawHooks.push(a)}};b.jqplot.EnhancedLegendRenderer.prototype.draw=function(){var e=this;if(this.show){var n=this._series;var o;var q="position:absolute;";q+=(this.background)?"background:"+this.background+";":"";q+=(this.border)?"border:"+this.border+";":"";q+=(this.fontSize)?"font-size:"+this.fontSize+";":"";q+=(this.fontFamily)?"font-family:"+this.fontFamily+";":"";q+=(this.textColor)?"color:"+this.textColor+";":"";q+=(this.marginTop!=null)?"margin-top:"+this.marginTop+";":"";q+=(this.marginBottom!=null)?"margin-bottom:"+this.marginBottom+";":"";q+=(this.marginLeft!=null)?"margin-left:"+this.marginLeft+";":"";q+=(this.marginRight!=null)?"margin-right:"+this.marginRight+";":"";this._elem=b('<table class="jqplot-table-legend" style="'+q+'"></table>');if(this.seriesToggle){this._elem.css("z-index","3")}var w=false,m=false,c,k;if(this.numberRows){c=this.numberRows;if(!this.numberColumns){k=Math.ceil(n.length/c)}else{k=this.numberColumns}}else{if(this.numberColumns){k=this.numberColumns;c=Math.ceil(n.length/this.numberColumns)}else{c=n.length;k=1}}var v,t,d,g,f,h,l;var p=0;for(v=n.length-1;v>=0;v--){if(n[v]._stack||n[v].renderer.constructor==b.jqplot.BezierCurveRenderer){m=true}}for(v=0;v<c;v++){if(m){d=b('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem)}else{d=b('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem)}for(t=0;t<k;t++){if(p<n.length&&n[p].show&&n[p].showLabel){o=n[p];h=this.labels[p]||o.label.toString();if(h){var r=o.color;if(!m){if(v>0){w=true}else{w=false}}else{if(v==c-1){w=false}else{w=true}}l=(w)?this.rowSpacing:"0";g=b('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+l+';"><div><div class="jqplot-table-legend-swatch" style="background-color:'+r+";border-color:"+r+';"></div></div></td>');f=b('<td class="jqplot-table-legend" style="padding-top:'+l+';"></td>');if(this.escapeHtml){f.text(h)}else{f.html(h)}if(m){if(this.showLabels){f.prependTo(d)}if(this.showSwatches){g.prependTo(d)}}else{if(this.showSwatches){g.appendTo(d)}if(this.showLabels){f.appendTo(d)}}if(this.seriesToggle){var u;if(typeof(this.seriesToggle)=="string"||typeof(this.seriesToggle)=="number"){if(!b.jqplot.use_excanvas||!this.disableIEFading){u=this.seriesToggle}}if(this.showSwatches){g.bind("click",{series:o,speed:u},o.toggleDisplay);g.addClass("jqplot-seriesToggle")}if(this.showLabels){f.bind("click",{series:o,speed:u},o.toggleDisplay);f.addClass("jqplot-seriesToggle")}}w=true}}p++}}}return this._elem};var a=function(){if(this.legend.renderer.constructor==b.jqplot.EnhancedLegendRenderer&&this.legend.seriesToggle){var c=this.legend._elem.detach();this.eventCanvas._elem.after(c)}}})(jQuery); |
@@ -0,0 +1,938 @@ | |||||
/** | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 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.FunnelRenderer | |||||
* Plugin renderer to draw a funnel chart. | |||||
* x values, if present, will be used as labels. | |||||
* y values give area size. | |||||
* | |||||
* Funnel charts will draw a single series | |||||
* only. | |||||
* | |||||
* To use this renderer, you need to include the | |||||
* funnel renderer plugin, for example: | |||||
* | |||||
* > <script type="text/javascript" src="plugins/jqplot.funnelRenderer.js"></script> | |||||
* | |||||
* Properties described here are passed into the $.jqplot function | |||||
* as options on the series renderer. For example: | |||||
* | |||||
* > plot2 = $.jqplot('chart2', [s1, s2], { | |||||
* > seriesDefaults: { | |||||
* > renderer:$.jqplot.FunnelRenderer, | |||||
* > rendererOptions:{ | |||||
* > sectionMargin: 12, | |||||
* > widthRatio: 0.3 | |||||
* > } | |||||
* > } | |||||
* > }); | |||||
* | |||||
* IMPORTANT | |||||
* | |||||
* *The funnel renderer will reorder data in descending order* so the largest value in | |||||
* the data set is first and displayed on top of the funnel. Data will then | |||||
* be displayed in descending order down the funnel. The area of each funnel | |||||
* section will correspond to the value of each data point relative to the sum | |||||
* of all values. That is section area is proportional to section value divided by | |||||
* sum of all section values. | |||||
* | |||||
* If your data is not in descending order when passed into the plot, *it will be | |||||
* reordered* when stored in the series.data property. A copy of the unordered | |||||
* data is kept in the series._unorderedData property. | |||||
* | |||||
* A funnel plot will trigger events on the plot target | |||||
* according to user interaction. All events return the event object, | |||||
* the series index, the point (section) index, and the point data for | |||||
* the appropriate section. *Note* the point index will referr to the ordered | |||||
* data, not the original unordered data. | |||||
* | |||||
* 'jqplotDataMouseOver' - triggered when mousing over a section. | |||||
* 'jqplotDataHighlight' - triggered the first time user mouses over a section, | |||||
* if highlighting is enabled. | |||||
* 'jqplotDataUnhighlight' - triggered when a user moves the mouse out of | |||||
* a highlighted section. | |||||
* 'jqplotDataClick' - triggered when the user clicks on a section. | |||||
* 'jqplotDataRightClick' - tiggered when the user right clicks on a section if | |||||
* the "captureRightClick" option is set to true on the plot. | |||||
*/ | |||||
$.jqplot.FunnelRenderer = function(){ | |||||
$.jqplot.LineRenderer.call(this); | |||||
}; | |||||
$.jqplot.FunnelRenderer.prototype = new $.jqplot.LineRenderer(); | |||||
$.jqplot.FunnelRenderer.prototype.constructor = $.jqplot.FunnelRenderer; | |||||
// called with scope of a series | |||||
$.jqplot.FunnelRenderer.prototype.init = function(options, plot) { | |||||
// Group: Properties | |||||
// | |||||
// prop: padding | |||||
// padding between the funnel and plot edges, legend, etc. | |||||
this.padding = {top: 20, right: 20, bottom: 20, left: 20}; | |||||
// prop: sectionMargin | |||||
// spacing between funnel sections in pixels. | |||||
this.sectionMargin = 6; | |||||
// prop: fill | |||||
// true or false, wether to fill the areas. | |||||
this.fill = true; | |||||
// prop: shadowOffset | |||||
// offset of the shadow from the area and offset of | |||||
// each succesive stroke of the shadow from the last. | |||||
this.shadowOffset = 2; | |||||
// prop: shadowAlpha | |||||
// transparency of the shadow (0 = transparent, 1 = opaque) | |||||
this.shadowAlpha = 0.07; | |||||
// prop: shadowDepth | |||||
// number of strokes to apply to the shadow, | |||||
// each stroke offset shadowOffset from the last. | |||||
this.shadowDepth = 5; | |||||
// prop: highlightMouseOver | |||||
// True to highlight area when moused over. | |||||
// This must be false to enable highlightMouseDown to highlight when clicking on a area. | |||||
this.highlightMouseOver = true; | |||||
// prop: highlightMouseDown | |||||
// True to highlight when a mouse button is pressed over a area. | |||||
// This will be disabled if highlightMouseOver is true. | |||||
this.highlightMouseDown = false; | |||||
// prop: highlightColors | |||||
// array of colors to use when highlighting an area. | |||||
this.highlightColors = []; | |||||
// prop: widthRatio | |||||
// The ratio of the width of the top of the funnel to the bottom. | |||||
// a ratio of 0 will make an upside down pyramid. | |||||
this.widthRatio = 0.2; | |||||
// prop: lineWidth | |||||
// width of line if areas are stroked and not filled. | |||||
this.lineWidth = 2; | |||||
// prop: dataLabels | |||||
// Either 'label', 'value', 'percent' or an array of labels to place on the pie slices. | |||||
// Defaults to percentage of each pie slice. | |||||
this.dataLabels = 'percent'; | |||||
// prop: showDataLabels | |||||
// true to show data labels on slices. | |||||
this.showDataLabels = false; | |||||
// prop: dataLabelFormatString | |||||
// Format string for data labels. If none, '%s' is used for "label" and for arrays, '%d' for value and '%d%%' for percentage. | |||||
this.dataLabelFormatString = null; | |||||
// prop: dataLabelThreshold | |||||
// Threshhold in percentage (0 - 100) of pie area, below which no label will be displayed. | |||||
// This applies to all label types, not just to percentage labels. | |||||
this.dataLabelThreshold = 3; | |||||
this._type = 'funnel'; | |||||
this.tickRenderer = $.jqplot.FunnelTickRenderer; | |||||
// if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver | |||||
if (options.highlightMouseDown && options.highlightMouseOver == null) { | |||||
options.highlightMouseOver = false; | |||||
} | |||||
$.extend(true, this, options); | |||||
// index of the currenty highlighted point, if any | |||||
this._highlightedPoint = null; | |||||
// lengths of bases, or horizontal sides of areas of trapezoid. | |||||
this._bases = []; | |||||
// total area | |||||
this._atot; | |||||
// areas of segments. | |||||
this._areas = []; | |||||
// vertical lengths of segments. | |||||
this._lengths = []; | |||||
// angle of the funnel to vertical. | |||||
this._angle; | |||||
this._dataIndices = []; | |||||
// sort data | |||||
this._unorderedData = $.extend(true, [], this.data); | |||||
var idxs = $.extend(true, [], this.data); | |||||
for (var i=0; i<idxs.length; i++) { | |||||
idxs[i].push(i); | |||||
} | |||||
this.data.sort( function (a, b) { return b[1] - a[1]; } ); | |||||
idxs.sort( function (a, b) { return b[1] - a[1]; }); | |||||
for (var i=0; i<idxs.length; i++) { | |||||
this._dataIndices.push(idxs[i][2]); | |||||
} | |||||
// set highlight colors if none provided | |||||
if (this.highlightColors.length == 0) { | |||||
for (var i=0; i<this.seriesColors.length; i++){ | |||||
var rgba = $.jqplot.getColorComponents(this.seriesColors[i]); | |||||
var newrgb = [rgba[0], rgba[1], rgba[2]]; | |||||
var sum = newrgb[0] + newrgb[1] + newrgb[2]; | |||||
for (var j=0; j<3; j++) { | |||||
// when darkening, lowest color component can be is 60. | |||||
newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.4 * (255 - newrgb[j]); | |||||
newrgb[j] = parseInt(newrgb[j], 10); | |||||
} | |||||
this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')'); | |||||
} | |||||
} | |||||
plot.postParseOptionsHooks.addOnce(postParseOptions); | |||||
plot.postInitHooks.addOnce(postInit); | |||||
plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); | |||||
plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown); | |||||
plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp); | |||||
plot.eventListenerHooks.addOnce('jqplotClick', handleClick); | |||||
plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); | |||||
plot.postDrawHooks.addOnce(postPlotDraw); | |||||
}; | |||||
// gridData will be of form [label, percentage of total] | |||||
$.jqplot.FunnelRenderer.prototype.setGridData = function(plot) { | |||||
// set gridData property. This will hold angle in radians of each data point. | |||||
var sum = 0; | |||||
var td = []; | |||||
for (var i=0; i<this.data.length; i++){ | |||||
sum += this.data[i][1]; | |||||
td.push([this.data[i][0], this.data[i][1]]); | |||||
} | |||||
// normalize y values, so areas are proportional. | |||||
for (var i=0; i<td.length; i++) { | |||||
td[i][1] = td[i][1]/sum; | |||||
} | |||||
this._bases = new Array(td.length + 1); | |||||
this._lengths = new Array(td.length); | |||||
this.gridData = td; | |||||
}; | |||||
$.jqplot.FunnelRenderer.prototype.makeGridData = function(data, plot) { | |||||
// set gridData property. This will hold angle in radians of each data point. | |||||
var sum = 0; | |||||
var td = []; | |||||
for (var i=0; i<this.data.length; i++){ | |||||
sum += this.data[i][1]; | |||||
td.push([this.data[i][0], this.data[i][1]]); | |||||
} | |||||
// normalize y values, so areas are proportional. | |||||
for (var i=0; i<td.length; i++) { | |||||
td[i][1] = td[i][1]/sum; | |||||
} | |||||
this._bases = new Array(td.length + 1); | |||||
this._lengths = new Array(td.length); | |||||
return td; | |||||
}; | |||||
$.jqplot.FunnelRenderer.prototype.drawSection = function (ctx, vertices, color, isShadow) { | |||||
var fill = this.fill; | |||||
var lineWidth = this.lineWidth; | |||||
ctx.save(); | |||||
if (isShadow) { | |||||
for (var i=0; i<this.shadowDepth; i++) { | |||||
ctx.save(); | |||||
ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI)); | |||||
doDraw(); | |||||
} | |||||
} | |||||
else { | |||||
doDraw(); | |||||
} | |||||
function doDraw () { | |||||
ctx.beginPath(); | |||||
ctx.fillStyle = color; | |||||
ctx.strokeStyle = color; | |||||
ctx.lineWidth = lineWidth; | |||||
ctx.moveTo(vertices[0][0], vertices[0][1]); | |||||
for (var i=1; i<4; i++) { | |||||
ctx.lineTo(vertices[i][0], vertices[i][1]); | |||||
} | |||||
ctx.closePath(); | |||||
if (fill) { | |||||
ctx.fill(); | |||||
} | |||||
else { | |||||
ctx.stroke(); | |||||
} | |||||
} | |||||
if (isShadow) { | |||||
for (var i=0; i<this.shadowDepth; i++) { | |||||
ctx.restore(); | |||||
} | |||||
} | |||||
ctx.restore(); | |||||
}; | |||||
// called with scope of series | |||||
$.jqplot.FunnelRenderer.prototype.draw = function (ctx, gd, options, plot) { | |||||
var i; | |||||
var opts = (options != undefined) ? options : {}; | |||||
// offset and direction of offset due to legend placement | |||||
var offx = 0; | |||||
var offy = 0; | |||||
var trans = 1; | |||||
this._areas = []; | |||||
// var colorGenerator = new this.colorGenerator(this.seriesColors); | |||||
if (options.legendInfo && options.legendInfo.placement == 'insideGrid') { | |||||
var li = options.legendInfo; | |||||
switch (li.location) { | |||||
case 'nw': | |||||
offx = li.width + li.xoffset; | |||||
break; | |||||
case 'w': | |||||
offx = li.width + li.xoffset; | |||||
break; | |||||
case 'sw': | |||||
offx = li.width + li.xoffset; | |||||
break; | |||||
case 'ne': | |||||
offx = li.width + li.xoffset; | |||||
trans = -1; | |||||
break; | |||||
case 'e': | |||||
offx = li.width + li.xoffset; | |||||
trans = -1; | |||||
break; | |||||
case 'se': | |||||
offx = li.width + li.xoffset; | |||||
trans = -1; | |||||
break; | |||||
case 'n': | |||||
offy = li.height + li.yoffset; | |||||
break; | |||||
case 's': | |||||
offy = li.height + li.yoffset; | |||||
trans = -1; | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
} | |||||
var loff = (trans==1) ? this.padding.left + offx : this.padding.left; | |||||
var toff = (trans==1) ? this.padding.top + offy : this.padding.top; | |||||
var roff = (trans==-1) ? this.padding.right + offx : this.padding.right; | |||||
var boff = (trans==-1) ? this.padding.bottom + offy : this.padding.bottom; | |||||
var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; | |||||
var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; | |||||
var fill = (opts.fill != undefined) ? opts.fill : this.fill; | |||||
var cw = ctx.canvas.width; | |||||
var ch = ctx.canvas.height; | |||||
this._bases[0] = cw - loff - roff; | |||||
var ltot = this._length = ch - toff - boff; | |||||
var hend = this._bases[0]*this.widthRatio; | |||||
this._atot = ltot/2 * (this._bases[0] + this._bases[0]*this.widthRatio); | |||||
this._angle = Math.atan((this._bases[0] - hend)/2/ltot); | |||||
for (i=0; i<gd.length; i++) { | |||||
this._areas.push(gd[i][1] * this._atot); | |||||
} | |||||
var guess, err, count, lsum=0; | |||||
var tolerance = 0.0001; | |||||
for (i=0; i<this._areas.length; i++) { | |||||
guess = this._areas[i]/this._bases[i]; | |||||
err = 999999; | |||||
this._lengths[i] = guess; | |||||
count = 0; | |||||
while (err > this._lengths[i]*tolerance && count < 100) { | |||||
this._lengths[i] = this._areas[i]/(this._bases[i] - this._lengths[i] * Math.tan(this._angle)); | |||||
err = Math.abs(this._lengths[i] - guess); | |||||
this._bases[i+1] = this._bases[i] - (2*this._lengths[i]*Math.tan(this._angle)); | |||||
guess = this._lengths[i]; | |||||
count++; | |||||
} | |||||
lsum += this._lengths[i]; | |||||
} | |||||
// figure out vertices of each section | |||||
this._vertices = new Array(gd.length); | |||||
// these are 4 coners of entire trapezoid | |||||
var p0 = [loff, toff], | |||||
p1 = [loff+this._bases[0], toff], | |||||
p2 = [loff + (this._bases[0] - this._bases[this._bases.length-1])/2, toff + this._length], | |||||
p3 = [p2[0] + this._bases[this._bases.length-1], p2[1]]; | |||||
// equations of right and left sides, returns x, y values given height of section (y value) | |||||
function findleft (l) { | |||||
var m = (p0[1] - p2[1])/(p0[0] - p2[0]); | |||||
var b = p0[1] - m*p0[0]; | |||||
var y = l + p0[1]; | |||||
return [(y - b)/m, y]; | |||||
} | |||||
function findright (l) { | |||||
var m = (p1[1] - p3[1])/(p1[0] - p3[0]); | |||||
var b = p1[1] - m*p1[0]; | |||||
var y = l + p1[1]; | |||||
return [(y - b)/m, y]; | |||||
} | |||||
var x = offx, y = offy; | |||||
var h=0, adj=0; | |||||
for (i=0; i<gd.length; i++) { | |||||
this._vertices[i] = new Array(); | |||||
var v = this._vertices[i]; | |||||
var sm = this.sectionMargin; | |||||
if (i == 0) { | |||||
adj = 0; | |||||
} | |||||
if (i == 1) { | |||||
adj = sm/3; | |||||
} | |||||
else if (i > 0 && i < gd.length-1) { | |||||
adj = sm/2; | |||||
} | |||||
else if (i == gd.length -1) { | |||||
adj = 2*sm/3; | |||||
} | |||||
v.push(findleft(h+adj)); | |||||
v.push(findright(h+adj)); | |||||
h += this._lengths[i]; | |||||
if (i == 0) { | |||||
adj = -2*sm/3; | |||||
} | |||||
else if (i > 0 && i < gd.length-1) { | |||||
adj = -sm/2; | |||||
} | |||||
else if (i == gd.length - 1) { | |||||
adj = 0; | |||||
} | |||||
v.push(findright(h+adj)); | |||||
v.push(findleft(h+adj)); | |||||
} | |||||
if (this.shadow) { | |||||
var shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')'; | |||||
for (var i=0; i<gd.length; i++) { | |||||
this.renderer.drawSection.call (this, ctx, this._vertices[i], shadowColor, true); | |||||
} | |||||
} | |||||
for (var i=0; i<gd.length; i++) { | |||||
var v = this._vertices[i]; | |||||
this.renderer.drawSection.call (this, ctx, v, this.seriesColors[i]); | |||||
if (this.showDataLabels && gd[i][1]*100 >= this.dataLabelThreshold) { | |||||
var fstr, label; | |||||
if (this.dataLabels == 'label') { | |||||
fstr = this.dataLabelFormatString || '%s'; | |||||
label = $.jqplot.sprintf(fstr, gd[i][0]); | |||||
} | |||||
else if (this.dataLabels == 'value') { | |||||
fstr = this.dataLabelFormatString || '%d'; | |||||
label = $.jqplot.sprintf(fstr, this.data[i][1]); | |||||
} | |||||
else if (this.dataLabels == 'percent') { | |||||
fstr = this.dataLabelFormatString || '%d%%'; | |||||
label = $.jqplot.sprintf(fstr, gd[i][1]*100); | |||||
} | |||||
else if (this.dataLabels.constructor == Array) { | |||||
fstr = this.dataLabelFormatString || '%s'; | |||||
label = $.jqplot.sprintf(fstr, this.dataLabels[this._dataIndices[i]]); | |||||
} | |||||
var fact = (this._radius ) * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge; | |||||
var x = (v[0][0] + v[1][0])/2 + this.canvas._offsets.left; | |||||
var y = (v[1][1] + v[2][1])/2 + this.canvas._offsets.top; | |||||
var labelelem = $('<span class="jqplot-funnel-series jqplot-data-label" style="position:absolute;">' + label + '</span>').insertBefore(plot.eventCanvas._elem); | |||||
x -= labelelem.width()/2; | |||||
y -= labelelem.height()/2; | |||||
x = Math.round(x); | |||||
y = Math.round(y); | |||||
labelelem.css({left: x, top: y}); | |||||
} | |||||
} | |||||
}; | |||||
$.jqplot.FunnelAxisRenderer = function() { | |||||
$.jqplot.LinearAxisRenderer.call(this); | |||||
}; | |||||
$.jqplot.FunnelAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); | |||||
$.jqplot.FunnelAxisRenderer.prototype.constructor = $.jqplot.FunnelAxisRenderer; | |||||
// There are no traditional axes on a funnel chart. We just need to provide | |||||
// dummy objects with properties so the plot will render. | |||||
// called with scope of axis object. | |||||
$.jqplot.FunnelAxisRenderer.prototype.init = function(options){ | |||||
// | |||||
this.tickRenderer = $.jqplot.FunnelTickRenderer; | |||||
$.extend(true, this, options); | |||||
// I don't think I'm going to need _dataBounds here. | |||||
// have to go Axis scaling in a way to fit chart onto plot area | |||||
// and provide u2p and p2u functionality for mouse cursor, etc. | |||||
// for convienence set _dataBounds to 0 and 100 and | |||||
// set min/max to 0 and 100. | |||||
this._dataBounds = {min:0, max:100}; | |||||
this.min = 0; | |||||
this.max = 100; | |||||
this.showTicks = false; | |||||
this.ticks = []; | |||||
this.showMark = false; | |||||
this.show = false; | |||||
}; | |||||
/** | |||||
* Class: $.jqplot.FunnelLegendRenderer | |||||
* Legend Renderer specific to funnel plots. Set by default | |||||
* when the user creates a funnel plot. | |||||
*/ | |||||
$.jqplot.FunnelLegendRenderer = function(){ | |||||
$.jqplot.TableLegendRenderer.call(this); | |||||
}; | |||||
$.jqplot.FunnelLegendRenderer.prototype = new $.jqplot.TableLegendRenderer(); | |||||
$.jqplot.FunnelLegendRenderer.prototype.constructor = $.jqplot.FunnelLegendRenderer; | |||||
$.jqplot.FunnelLegendRenderer.prototype.init = function(options) { | |||||
// Group: Properties | |||||
// | |||||
// 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; | |||||
$.extend(true, this, options); | |||||
}; | |||||
// called with context of legend | |||||
$.jqplot.FunnelLegendRenderer.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+';' : ''; | |||||
ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : ''; | |||||
ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : ''; | |||||
ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : ''; | |||||
ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : ''; | |||||
this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>'); | |||||
// Funnel 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 = false, | |||||
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++; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
return this._elem; | |||||
}; | |||||
// $.jqplot.FunnelLegendRenderer.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 || {}; | |||||
// only set these if there is a funnel series | |||||
var setopts = false; | |||||
if (options.seriesDefaults.renderer == $.jqplot.FunnelRenderer) { | |||||
setopts = true; | |||||
} | |||||
else if (options.series) { | |||||
for (var i=0; i < options.series.length; i++) { | |||||
if (options.series[i].renderer == $.jqplot.FunnelRenderer) { | |||||
setopts = true; | |||||
} | |||||
} | |||||
} | |||||
if (setopts) { | |||||
options.axesDefaults.renderer = $.jqplot.FunnelAxisRenderer; | |||||
options.legend.renderer = $.jqplot.FunnelLegendRenderer; | |||||
options.legend.preDraw = true; | |||||
options.sortData = false; | |||||
options.seriesDefaults.pointLabels = {show: false}; | |||||
} | |||||
} | |||||
function postInit(target, data, options) { | |||||
// if multiple series, add a reference to the previous one so that | |||||
// funnel rings can nest. | |||||
for (var i=0; i<this.series.length; i++) { | |||||
if (this.series[i].renderer.constructor == $.jqplot.FunnelRenderer) { | |||||
// don't allow mouseover and mousedown at same time. | |||||
if (this.series[i].highlightMouseOver) { | |||||
this.series[i].highlightMouseDown = false; | |||||
} | |||||
} | |||||
} | |||||
this.target.bind('mouseout', {plot:this}, function (ev) { unhighlight(ev.data.plot); }); | |||||
} | |||||
// called with scope of plot | |||||
function postParseOptions(options) { | |||||
for (var i=0; i<this.series.length; i++) { | |||||
this.series[i].seriesColors = this.seriesColors; | |||||
this.series[i].colorGenerator = this.colorGenerator; | |||||
} | |||||
} | |||||
function highlight (plot, sidx, pidx) { | |||||
var s = plot.series[sidx]; | |||||
var canvas = plot.plugins.funnelRenderer.highlightCanvas; | |||||
canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height); | |||||
s._highlightedPoint = pidx; | |||||
plot.plugins.funnelRenderer.highlightedSeriesIndex = sidx; | |||||
s.renderer.drawSection.call(s, canvas._ctx, s._vertices[pidx], s.highlightColors[pidx], false); | |||||
} | |||||
function unhighlight (plot) { | |||||
var canvas = plot.plugins.funnelRenderer.highlightCanvas; | |||||
canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); | |||||
for (var i=0; i<plot.series.length; i++) { | |||||
plot.series[i]._highlightedPoint = null; | |||||
} | |||||
plot.plugins.funnelRenderer.highlightedSeriesIndex = null; | |||||
plot.target.trigger('jqplotDataUnhighlight'); | |||||
} | |||||
function handleMove(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; | |||||
var evt1 = jQuery.Event('jqplotDataMouseOver'); | |||||
evt1.pageX = ev.pageX; | |||||
evt1.pageY = ev.pageY; | |||||
plot.target.trigger(evt1, ins); | |||||
if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.funnelRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { | |||||
var evt = jQuery.Event('jqplotDataHighlight'); | |||||
evt.pageX = ev.pageX; | |||||
evt.pageY = ev.pageY; | |||||
plot.target.trigger(evt, ins); | |||||
highlight (plot, ins[0], ins[1]); | |||||
} | |||||
} | |||||
else if (neighbor == null) { | |||||
unhighlight (plot); | |||||
} | |||||
} | |||||
function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; | |||||
if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.funnelRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { | |||||
var evt = jQuery.Event('jqplotDataHighlight'); | |||||
evt.pageX = ev.pageX; | |||||
evt.pageY = ev.pageY; | |||||
plot.target.trigger(evt, ins); | |||||
highlight (plot, ins[0], ins[1]); | |||||
} | |||||
} | |||||
else if (neighbor == null) { | |||||
unhighlight (plot); | |||||
} | |||||
} | |||||
function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { | |||||
var idx = plot.plugins.funnelRenderer.highlightedSeriesIndex; | |||||
if (idx != null && plot.series[idx].highlightMouseDown) { | |||||
unhighlight(plot); | |||||
} | |||||
} | |||||
function handleClick(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; | |||||
var evt = jQuery.Event('jqplotDataClick'); | |||||
evt.pageX = ev.pageX; | |||||
evt.pageY = ev.pageY; | |||||
plot.target.trigger(evt, ins); | |||||
} | |||||
} | |||||
function handleRightClick(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; | |||||
var idx = plot.plugins.funnelRenderer.highlightedSeriesIndex; | |||||
if (idx != null && plot.series[idx].highlightMouseDown) { | |||||
unhighlight(plot); | |||||
} | |||||
var evt = jQuery.Event('jqplotDataRightClick'); | |||||
evt.pageX = ev.pageX; | |||||
evt.pageY = ev.pageY; | |||||
plot.target.trigger(evt, ins); | |||||
} | |||||
} | |||||
// called within context of plot | |||||
// create a canvas which we can draw on. | |||||
// insert it before the eventCanvas, so eventCanvas will still capture events. | |||||
function postPlotDraw() { | |||||
// Memory Leaks patch | |||||
if (this.plugins.funnelRenderer && this.plugins.funnelRenderer.highlightCanvas) { | |||||
this.plugins.funnelRenderer.highlightCanvas.resetCanvas(); | |||||
this.plugins.funnelRenderer.highlightCanvas = null; | |||||
} | |||||
this.plugins.funnelRenderer = {}; | |||||
this.plugins.funnelRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); | |||||
// do we have any data labels? if so, put highlight canvas before those | |||||
var labels = $(this.targetId+' .jqplot-data-label'); | |||||
if (labels.length) { | |||||
$(labels[0]).before(this.plugins.funnelRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-funnelRenderer-highlight-canvas', this._plotDimensions, this)); | |||||
} | |||||
// else put highlight canvas before event canvas. | |||||
else { | |||||
this.eventCanvas._elem.before(this.plugins.funnelRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-funnelRenderer-highlight-canvas', this._plotDimensions, this)); | |||||
} | |||||
var hctx = this.plugins.funnelRenderer.highlightCanvas.setContext(); | |||||
} | |||||
$.jqplot.preInitHooks.push(preInit); | |||||
$.jqplot.FunnelTickRenderer = function() { | |||||
$.jqplot.AxisTickRenderer.call(this); | |||||
}; | |||||
$.jqplot.FunnelTickRenderer.prototype = new $.jqplot.AxisTickRenderer(); | |||||
$.jqplot.FunnelTickRenderer.prototype.constructor = $.jqplot.FunnelTickRenderer; | |||||
})(jQuery); | |||||
@@ -1,18 +1,30 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* The author would appreciate an email letting him know of any substantial | |||||
* use of jqPlot. You can reach the author at: chris dot leonello at gmail | |||||
* dot com or see http://www.jqplot.com/info.php . This is, of course, | |||||
* not required. | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * making a donation at: http://www.jqplot.com/donate.php . | ||||
* | * | ||||
* Thanks for using jqPlot! | |||||
* 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($) { | (function($) { | ||||
@@ -86,7 +98,7 @@ | |||||
// prop: tooltipLocation | // prop: tooltipLocation | ||||
// Where to position tooltip, 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw' | // Where to position tooltip, 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw' | ||||
this.tooltipLocation = 'nw'; | this.tooltipLocation = 'nw'; | ||||
// prop: tooltipFade | |||||
// prop: fadeTooltip | |||||
// true = fade in/out tooltip, flase = show/hide tooltip | // true = fade in/out tooltip, flase = show/hide tooltip | ||||
this.fadeTooltip = true; | this.fadeTooltip = true; | ||||
// prop: tooltipFadeSpeed | // prop: tooltipFadeSpeed | ||||
@@ -102,6 +114,12 @@ | |||||
// prop; tooltipSeparator | // prop; tooltipSeparator | ||||
// String to use to separate x and y axes in tooltip. | // String to use to separate x and y axes in tooltip. | ||||
this.tooltipSeparator = ', '; | this.tooltipSeparator = ', '; | ||||
// prop; tooltipContentEditor | |||||
// Function used to edit/augment/replace the formatted tooltip contents. | |||||
// Called as str = tooltipContentEditor(str, seriesIndex, pointIndex) | |||||
// where str is the generated tooltip html and seriesIndex and pointIndex identify | |||||
// the data point being highlighted. Should return the html for the tooltip contents. | |||||
this.tooltipContentEditor = null; | |||||
// prop: useAxesFormatters | // prop: useAxesFormatters | ||||
// Use the x and y axes formatters to format the text in the tooltip. | // Use the x and y axes formatters to format the text in the tooltip. | ||||
this.useAxesFormatters = true; | this.useAxesFormatters = true; | ||||
@@ -126,12 +144,21 @@ | |||||
// Typically this is 1. Certain plots, like OHLC, will | // Typically this is 1. Certain plots, like OHLC, will | ||||
// have more y values in each data point array. | // have more y values in each data point array. | ||||
this.yvalues = 1; | this.yvalues = 1; | ||||
// prop: bringSeriesToFront | |||||
// This option requires jQuery 1.4+ | |||||
// True to bring the series of the highlighted point to the front | |||||
// of other series. | |||||
this.bringSeriesToFront = false; | |||||
this._tooltipElem; | this._tooltipElem; | ||||
this.isHighlighting = false; | this.isHighlighting = false; | ||||
$.extend(true, this, options); | $.extend(true, this, options); | ||||
}; | }; | ||||
var locations = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w']; | |||||
var locationIndicies = {'nw':0, 'n':1, 'ne':2, 'e':3, 'se':4, 's':5, 'sw':6, 'w':7}; | |||||
var oppositeLocations = ['se', 's', 'sw', 'w', 'nw', 'n', 'ne', 'e']; | |||||
// axis.renderer.tickrenderer.formatter | // axis.renderer.tickrenderer.formatter | ||||
// called with scope of plot | // called with scope of plot | ||||
@@ -143,6 +170,8 @@ | |||||
// called within scope of series | // called within scope of series | ||||
$.jqplot.Highlighter.parseOptions = function (defaults, options) { | $.jqplot.Highlighter.parseOptions = function (defaults, options) { | ||||
// Add a showHighlight option to the series | |||||
// and set it to true by default. | |||||
this.showHighlight = true; | this.showHighlight = true; | ||||
}; | }; | ||||
@@ -150,14 +179,29 @@ | |||||
// create a canvas which we can draw on. | // create a canvas which we can draw on. | ||||
// insert it before the eventCanvas, so eventCanvas will still capture events. | // insert it before the eventCanvas, so eventCanvas will still capture events. | ||||
$.jqplot.Highlighter.postPlotDraw = function() { | $.jqplot.Highlighter.postPlotDraw = function() { | ||||
// Memory Leaks patch | |||||
if (this.plugins.highlighter && this.plugins.highlighter.highlightCanvas) { | |||||
this.plugins.highlighter.highlightCanvas.resetCanvas(); | |||||
this.plugins.highlighter.highlightCanvas = null; | |||||
} | |||||
if (this.plugins.highlighter && this.plugins.highlighter._tooltipElem) { | |||||
this.plugins.highlighter._tooltipElem.emptyForce(); | |||||
this.plugins.highlighter._tooltipElem = null; | |||||
} | |||||
this.plugins.highlighter.highlightCanvas = new $.jqplot.GenericCanvas(); | this.plugins.highlighter.highlightCanvas = new $.jqplot.GenericCanvas(); | ||||
this.eventCanvas._elem.before(this.plugins.highlighter.highlightCanvas.createElement(this._gridPadding, 'jqplot-highlight-canvas', this._plotDimensions)); | |||||
var hctx = this.plugins.highlighter.highlightCanvas.setContext(); | |||||
this.eventCanvas._elem.before(this.plugins.highlighter.highlightCanvas.createElement(this._gridPadding, 'jqplot-highlight-canvas', this._plotDimensions, this)); | |||||
this.plugins.highlighter.highlightCanvas.setContext(); | |||||
var elem = document.createElement('div'); | |||||
this.plugins.highlighter._tooltipElem = $(elem); | |||||
elem = null; | |||||
this.plugins.highlighter._tooltipElem.addClass('jqplot-highlighter-tooltip'); | |||||
this.plugins.highlighter._tooltipElem.css({position:'absolute', display:'none'}); | |||||
var p = this.plugins.highlighter; | |||||
p._tooltipElem = $('<div class="jqplot-highlighter-tooltip" style="position:absolute;display:none"></div>'); | |||||
this.target.append(p._tooltipElem); | |||||
this.eventCanvas._elem.before(this.plugins.highlighter._tooltipElem); | |||||
}; | }; | ||||
$.jqplot.preInitHooks.push($.jqplot.Highlighter.init); | $.jqplot.preInitHooks.push($.jqplot.Highlighter.init); | ||||
@@ -244,10 +288,7 @@ | |||||
str = xstr; | str = xstr; | ||||
break; | break; | ||||
case 'y': | case 'y': | ||||
str = ''; | |||||
for (var i=0; i<ystrs.length; i++) { | |||||
str += ystrs[i] + hl.tooltipSeparator; | |||||
} | |||||
str = ystrs.join(hl.tooltipSeparator); | |||||
break; | break; | ||||
default: // same as 'xy' | default: // same as 'xy' | ||||
str = xstr; | str = xstr; | ||||
@@ -274,6 +315,11 @@ | |||||
str = $.jqplot.sprintf(hl.tooltipFormatString, neighbor.data[1]); | str = $.jqplot.sprintf(hl.tooltipFormatString, neighbor.data[1]); | ||||
} | } | ||||
} | } | ||||
if ($.isFunction(hl.tooltipContentEditor)) { | |||||
// args str, seriesIndex, pointIndex are essential so the hook can look up | |||||
// extra data for the point. | |||||
str = hl.tooltipContentEditor(str, neighbor.seriesIndex, neighbor.pointIndex, plot); | |||||
} | |||||
elem.html(str); | elem.html(str); | ||||
var gridpos = {x:neighbor.gridData[0], y:neighbor.gridData[1]}; | var gridpos = {x:neighbor.gridData[0], y:neighbor.gridData[1]}; | ||||
var ms = 0; | var ms = 0; | ||||
@@ -281,7 +327,13 @@ | |||||
if (series.markerRenderer.show == true) { | if (series.markerRenderer.show == true) { | ||||
ms = (series.markerRenderer.size + hl.sizeAdjust)/2; | ms = (series.markerRenderer.size + hl.sizeAdjust)/2; | ||||
} | } | ||||
switch (hl.tooltipLocation) { | |||||
var loc = locations; | |||||
if (series.fillToZero && series.fill && neighbor.data[1] < 0) { | |||||
loc = oppositeLocations; | |||||
} | |||||
switch (loc[locationIndicies[hl.tooltipLocation]]) { | |||||
case 'nw': | case 'nw': | ||||
var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - hl.tooltipOffset - fact * ms; | var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - hl.tooltipOffset - fact * ms; | ||||
var y = gridpos.y + plot._gridPadding.top - hl.tooltipOffset - elem.outerHeight(true) - fact * ms; | var y = gridpos.y + plot._gridPadding.top - hl.tooltipOffset - elem.outerHeight(true) - fact * ms; | ||||
@@ -322,16 +374,19 @@ | |||||
elem.css('left', x); | elem.css('left', x); | ||||
elem.css('top', y); | elem.css('top', y); | ||||
if (hl.fadeTooltip) { | if (hl.fadeTooltip) { | ||||
elem.fadeIn(hl.tooltipFadeSpeed); | |||||
// Fix for stacked up animations. Thnanks Trevor! | |||||
elem.stop(true,true).fadeIn(hl.tooltipFadeSpeed); | |||||
} | } | ||||
else { | else { | ||||
elem.show(); | elem.show(); | ||||
} | } | ||||
elem = null; | |||||
} | } | ||||
function handleMove(ev, gridpos, datapos, neighbor, plot) { | function handleMove(ev, gridpos, datapos, neighbor, plot) { | ||||
var hl = plot.plugins.highlighter; | var hl = plot.plugins.highlighter; | ||||
var c = plot.plugins.cursor; | |||||
if (hl.show) { | if (hl.show) { | ||||
if (neighbor == null && hl.isHighlighting) { | if (neighbor == null && hl.isHighlighting) { | ||||
var ctx = hl.highlightCanvas._ctx; | var ctx = hl.highlightCanvas._ctx; | ||||
@@ -342,17 +397,24 @@ | |||||
else { | else { | ||||
hl._tooltipElem.hide(); | hl._tooltipElem.hide(); | ||||
} | } | ||||
if (hl.bringSeriesToFront) { | |||||
plot.restorePreviousSeriesOrder(); | |||||
} | |||||
hl.isHighlighting = false; | hl.isHighlighting = false; | ||||
ctx = null; | |||||
} | } | ||||
if (neighbor != null && plot.series[neighbor.seriesIndex].showHighlight && !hl.isHighlighting) { | |||||
else if (neighbor != null && plot.series[neighbor.seriesIndex].showHighlight && !hl.isHighlighting) { | |||||
hl.isHighlighting = true; | hl.isHighlighting = true; | ||||
if (hl.showMarker) { | if (hl.showMarker) { | ||||
draw(plot, neighbor); | draw(plot, neighbor); | ||||
} | } | ||||
if (hl.showTooltip) { | |||||
if (hl.showTooltip && (!c || !c._zoom.started)) { | |||||
showTooltip(plot, plot.series[neighbor.seriesIndex], neighbor); | showTooltip(plot, plot.series[neighbor.seriesIndex], neighbor); | ||||
} | } | ||||
if (hl.bringSeriesToFront) { | |||||
plot.moveSeriesToFront(neighbor.seriesIndex); | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -0,0 +1,475 @@ | |||||
/* | |||||
2010-11-01 Chris Leonello | |||||
Slightly modified version of the original json2.js to put JSON | |||||
functions under the $.jqplot namespace. | |||||
licensing and orignal comments follow: | |||||
http://www.JSON.org/json2.js | |||||
2010-08-25 | |||||
Public Domain. | |||||
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. | |||||
See http://www.JSON.org/js.html | |||||
This code should be minified before deployment. | |||||
See http://javascript.crockford.com/jsmin.html | |||||
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO | |||||
NOT CONTROL. | |||||
This file creates a global JSON object containing two methods: stringify | |||||
and parse. | |||||
$.jqplot.JSON.stringify(value, replacer, space) | |||||
value any JavaScript value, usually an object or array. | |||||
replacer an optional parameter that determines how object | |||||
values are stringified for objects. It can be a | |||||
function or an array of strings. | |||||
space an optional parameter that specifies the indentation | |||||
of nested structures. If it is omitted, the text will | |||||
be packed without extra whitespace. If it is a number, | |||||
it will specify the number of spaces to indent at each | |||||
level. If it is a string (such as '\t' or ' '), | |||||
it contains the characters used to indent at each level. | |||||
This method produces a JSON text from a JavaScript value. | |||||
When an object value is found, if the object contains a toJSON | |||||
method, its toJSON method will be called and the result will be | |||||
stringified. A toJSON method does not serialize: it returns the | |||||
value represented by the name/value pair that should be serialized, | |||||
or undefined if nothing should be serialized. The toJSON method | |||||
will be passed the key associated with the value, and this will be | |||||
bound to the value | |||||
For example, this would serialize Dates as ISO strings. | |||||
Date.prototype.toJSON = function (key) { | |||||
function f(n) { | |||||
// Format integers to have at least two digits. | |||||
return n < 10 ? '0' + n : n; | |||||
} | |||||
return this.getUTCFullYear() + '-' + | |||||
f(this.getUTCMonth() + 1) + '-' + | |||||
f(this.getUTCDate()) + 'T' + | |||||
f(this.getUTCHours()) + ':' + | |||||
f(this.getUTCMinutes()) + ':' + | |||||
f(this.getUTCSeconds()) + 'Z'; | |||||
}; | |||||
You can provide an optional replacer method. It will be passed the | |||||
key and value of each member, with this bound to the containing | |||||
object. The value that is returned from your method will be | |||||
serialized. If your method returns undefined, then the member will | |||||
be excluded from the serialization. | |||||
If the replacer parameter is an array of strings, then it will be | |||||
used to select the members to be serialized. It filters the results | |||||
such that only members with keys listed in the replacer array are | |||||
stringified. | |||||
Values that do not have JSON representations, such as undefined or | |||||
functions, will not be serialized. Such values in objects will be | |||||
dropped; in arrays they will be replaced with null. You can use | |||||
a replacer function to replace those with JSON values. | |||||
$.jqplot.JSON.stringify(undefined) returns undefined. | |||||
The optional space parameter produces a stringification of the | |||||
value that is filled with line breaks and indentation to make it | |||||
easier to read. | |||||
If the space parameter is a non-empty string, then that string will | |||||
be used for indentation. If the space parameter is a number, then | |||||
the indentation will be that many spaces. | |||||
Example: | |||||
text = $.jqplot.JSON.stringify(['e', {pluribus: 'unum'}]); | |||||
// text is '["e",{"pluribus":"unum"}]' | |||||
text = $.jqplot.JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); | |||||
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' | |||||
text = $.jqplot.JSON.stringify([new Date()], function (key, value) { | |||||
return this[key] instanceof Date ? | |||||
'Date(' + this[key] + ')' : value; | |||||
}); | |||||
// text is '["Date(---current time---)"]' | |||||
$.jqplot.JSON.parse(text, reviver) | |||||
This method parses a JSON text to produce an object or array. | |||||
It can throw a SyntaxError exception. | |||||
The optional reviver parameter is a function that can filter and | |||||
transform the results. It receives each of the keys and values, | |||||
and its return value is used instead of the original value. | |||||
If it returns what it received, then the structure is not modified. | |||||
If it returns undefined then the member is deleted. | |||||
Example: | |||||
// Parse the text. Values that look like ISO date strings will | |||||
// be converted to Date objects. | |||||
myData = $.jqplot.JSON.parse(text, function (key, value) { | |||||
var a; | |||||
if (typeof value === 'string') { | |||||
a = | |||||
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); | |||||
if (a) { | |||||
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], | |||||
+a[5], +a[6])); | |||||
} | |||||
} | |||||
return value; | |||||
}); | |||||
myData = $.jqplot.JSON.parse('["Date(09/09/2001)"]', function (key, value) { | |||||
var d; | |||||
if (typeof value === 'string' && | |||||
value.slice(0, 5) === 'Date(' && | |||||
value.slice(-1) === ')') { | |||||
d = new Date(value.slice(5, -1)); | |||||
if (d) { | |||||
return d; | |||||
} | |||||
} | |||||
return value; | |||||
}); | |||||
This is a reference implementation. You are free to copy, modify, or | |||||
redistribute. | |||||
*/ | |||||
(function($) { | |||||
$.jqplot.JSON = window.JSON; | |||||
if (!window.JSON) { | |||||
$.jqplot.JSON = {}; | |||||
} | |||||
function f(n) { | |||||
// Format integers to have at least two digits. | |||||
return n < 10 ? '0' + n : n; | |||||
} | |||||
if (typeof Date.prototype.toJSON !== 'function') { | |||||
Date.prototype.toJSON = function (key) { | |||||
return isFinite(this.valueOf()) ? | |||||
this.getUTCFullYear() + '-' + | |||||
f(this.getUTCMonth() + 1) + '-' + | |||||
f(this.getUTCDate()) + 'T' + | |||||
f(this.getUTCHours()) + ':' + | |||||
f(this.getUTCMinutes()) + ':' + | |||||
f(this.getUTCSeconds()) + 'Z' : null; | |||||
}; | |||||
String.prototype.toJSON = | |||||
Number.prototype.toJSON = | |||||
Boolean.prototype.toJSON = function (key) { | |||||
return this.valueOf(); | |||||
}; | |||||
} | |||||
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, | |||||
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, | |||||
gap, | |||||
indent, | |||||
meta = { // table of character substitutions | |||||
'\b': '\\b', | |||||
'\t': '\\t', | |||||
'\n': '\\n', | |||||
'\f': '\\f', | |||||
'\r': '\\r', | |||||
'"' : '\\"', | |||||
'\\': '\\\\' | |||||
}, | |||||
rep; | |||||
function quote(string) { | |||||
// If the string contains no control characters, no quote characters, and no | |||||
// backslash characters, then we can safely slap some quotes around it. | |||||
// Otherwise we must also replace the offending characters with safe escape | |||||
// sequences. | |||||
escapable.lastIndex = 0; | |||||
return escapable.test(string) ? | |||||
'"' + string.replace(escapable, function (a) { | |||||
var c = meta[a]; | |||||
return typeof c === 'string' ? c : | |||||
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); | |||||
}) + '"' : | |||||
'"' + string + '"'; | |||||
} | |||||
function str(key, holder) { | |||||
// Produce a string from holder[key]. | |||||
var i, // The loop counter. | |||||
k, // The member key. | |||||
v, // The member value. | |||||
length, | |||||
mind = gap, | |||||
partial, | |||||
value = holder[key]; | |||||
// If the value has a toJSON method, call it to obtain a replacement value. | |||||
if (value && typeof value === 'object' && | |||||
typeof value.toJSON === 'function') { | |||||
value = value.toJSON(key); | |||||
} | |||||
// If we were called with a replacer function, then call the replacer to | |||||
// obtain a replacement value. | |||||
if (typeof rep === 'function') { | |||||
value = rep.call(holder, key, value); | |||||
} | |||||
// What happens next depends on the value's type. | |||||
switch (typeof value) { | |||||
case 'string': | |||||
return quote(value); | |||||
case 'number': | |||||
// JSON numbers must be finite. Encode non-finite numbers as null. | |||||
return isFinite(value) ? String(value) : 'null'; | |||||
case 'boolean': | |||||
case 'null': | |||||
// If the value is a boolean or null, convert it to a string. Note: | |||||
// typeof null does not produce 'null'. The case is included here in | |||||
// the remote chance that this gets fixed someday. | |||||
return String(value); | |||||
// If the type is 'object', we might be dealing with an object or an array or | |||||
// null. | |||||
case 'object': | |||||
// Due to a specification blunder in ECMAScript, typeof null is 'object', | |||||
// so watch out for that case. | |||||
if (!value) { | |||||
return 'null'; | |||||
} | |||||
// Make an array to hold the partial results of stringifying this object value. | |||||
gap += indent; | |||||
partial = []; | |||||
// Is the value an array? | |||||
if (Object.prototype.toString.apply(value) === '[object Array]') { | |||||
// The value is an array. Stringify every element. Use null as a placeholder | |||||
// for non-JSON values. | |||||
length = value.length; | |||||
for (i = 0; i < length; i += 1) { | |||||
partial[i] = str(i, value) || 'null'; | |||||
} | |||||
// Join all of the elements together, separated with commas, and wrap them in | |||||
// brackets. | |||||
v = partial.length === 0 ? '[]' : | |||||
gap ? '[\n' + gap + | |||||
partial.join(',\n' + gap) + '\n' + | |||||
mind + ']' : | |||||
'[' + partial.join(',') + ']'; | |||||
gap = mind; | |||||
return v; | |||||
} | |||||
// If the replacer is an array, use it to select the members to be stringified. | |||||
if (rep && typeof rep === 'object') { | |||||
length = rep.length; | |||||
for (i = 0; i < length; i += 1) { | |||||
k = rep[i]; | |||||
if (typeof k === 'string') { | |||||
v = str(k, value); | |||||
if (v) { | |||||
partial.push(quote(k) + (gap ? ': ' : ':') + v); | |||||
} | |||||
} | |||||
} | |||||
} else { | |||||
// Otherwise, iterate through all of the keys in the object. | |||||
for (k in value) { | |||||
if (Object.hasOwnProperty.call(value, k)) { | |||||
v = str(k, value); | |||||
if (v) { | |||||
partial.push(quote(k) + (gap ? ': ' : ':') + v); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
// Join all of the member texts together, separated with commas, | |||||
// and wrap them in braces. | |||||
v = partial.length === 0 ? '{}' : | |||||
gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + | |||||
mind + '}' : '{' + partial.join(',') + '}'; | |||||
gap = mind; | |||||
return v; | |||||
} | |||||
} | |||||
// If the JSON object does not yet have a stringify method, give it one. | |||||
if (typeof $.jqplot.JSON.stringify !== 'function') { | |||||
$.jqplot.JSON.stringify = function (value, replacer, space) { | |||||
// The stringify method takes a value and an optional replacer, and an optional | |||||
// space parameter, and returns a JSON text. The replacer can be a function | |||||
// that can replace values, or an array of strings that will select the keys. | |||||
// A default replacer method can be provided. Use of the space parameter can | |||||
// produce text that is more easily readable. | |||||
var i; | |||||
gap = ''; | |||||
indent = ''; | |||||
// If the space parameter is a number, make an indent string containing that | |||||
// many spaces. | |||||
if (typeof space === 'number') { | |||||
for (i = 0; i < space; i += 1) { | |||||
indent += ' '; | |||||
} | |||||
// If the space parameter is a string, it will be used as the indent string. | |||||
} else if (typeof space === 'string') { | |||||
indent = space; | |||||
} | |||||
// If there is a replacer, it must be a function or an array. | |||||
// Otherwise, throw an error. | |||||
rep = replacer; | |||||
if (replacer && typeof replacer !== 'function' && | |||||
(typeof replacer !== 'object' || | |||||
typeof replacer.length !== 'number')) { | |||||
throw new Error('$.jqplot.JSON.stringify'); | |||||
} | |||||
// Make a fake root object containing our value under the key of ''. | |||||
// Return the result of stringifying the value. | |||||
return str('', {'': value}); | |||||
}; | |||||
} | |||||
// If the JSON object does not yet have a parse method, give it one. | |||||
if (typeof $.jqplot.JSON.parse !== 'function') { | |||||
$.jqplot.JSON.parse = function (text, reviver) { | |||||
// The parse method takes a text and an optional reviver function, and returns | |||||
// a JavaScript value if the text is a valid JSON text. | |||||
var j; | |||||
function walk(holder, key) { | |||||
// The walk method is used to recursively walk the resulting structure so | |||||
// that modifications can be made. | |||||
var k, v, value = holder[key]; | |||||
if (value && typeof value === 'object') { | |||||
for (k in value) { | |||||
if (Object.hasOwnProperty.call(value, k)) { | |||||
v = walk(value, k); | |||||
if (v !== undefined) { | |||||
value[k] = v; | |||||
} else { | |||||
delete value[k]; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
return reviver.call(holder, key, value); | |||||
} | |||||
// Parsing happens in four stages. In the first stage, we replace certain | |||||
// Unicode characters with escape sequences. JavaScript handles many characters | |||||
// incorrectly, either silently deleting them, or treating them as line endings. | |||||
text = String(text); | |||||
cx.lastIndex = 0; | |||||
if (cx.test(text)) { | |||||
text = text.replace(cx, function (a) { | |||||
return '\\u' + | |||||
('0000' + a.charCodeAt(0).toString(16)).slice(-4); | |||||
}); | |||||
} | |||||
// In the second stage, we run the text against regular expressions that look | |||||
// for non-JSON patterns. We are especially concerned with '()' and 'new' | |||||
// because they can cause invocation, and '=' because it can cause mutation. | |||||
// But just to be safe, we want to reject all unexpected forms. | |||||
// We split the second stage into 4 regexp operations in order to work around | |||||
// crippling inefficiencies in IE's and Safari's regexp engines. First we | |||||
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we | |||||
// replace all simple value tokens with ']' characters. Third, we delete all | |||||
// open brackets that follow a colon or comma or that begin the text. Finally, | |||||
// we look to see that the remaining characters are only whitespace or ']' or | |||||
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. | |||||
if (/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { | |||||
// In the third stage we use the eval function to compile the text into a | |||||
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity | |||||
// in JavaScript: it can begin a block or an object literal. We wrap the text | |||||
// in parens to eliminate the ambiguity. | |||||
j = eval('(' + text + ')'); | |||||
// In the optional fourth stage, we recursively walk the new structure, passing | |||||
// each name/value pair to a reviver function for possible transformation. | |||||
return typeof reviver === 'function' ? | |||||
walk({'': j}, '') : j; | |||||
} | |||||
// If the text is not JSON parseable, then a SyntaxError is thrown. | |||||
throw new SyntaxError('$.jqplot.JSON.parse'); | |||||
}; | |||||
} | |||||
})(jQuery); |
@@ -0,0 +1,30 @@ | |||||
/** | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 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($){$.jqplot.JSON=window.JSON;if(!window.JSON){$.jqplot.JSON={}}function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(rep&&typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==="string"){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}}if(typeof $.jqplot.JSON.stringify!=="function"){$.jqplot.JSON.stringify=function(value,replacer,space){var i;gap="";indent="";if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}rep=replacer;if(replacer&&typeof replacer!=="function"&&(typeof replacer!=="object"||typeof replacer.length!=="number")){throw new Error("$.jqplot.JSON.stringify")}return str("",{"":value})}}if(typeof $.jqplot.JSON.parse!=="function"){$.jqplot.JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("$.jqplot.JSON.parse")}}})(jQuery); |
@@ -1,20 +1,32 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* The author would appreciate an email letting him know of any substantial | |||||
* use of jqPlot. You can reach the author at: chris dot leonello at gmail | |||||
* dot com or see http://www.jqplot.com/info.php . This is, of course, | |||||
* not required. | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * making a donation at: http://www.jqplot.com/donate.php . | ||||
* | * | ||||
* Thanks for using jqPlot! | |||||
* 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($) { | (function($) { | ||||
/** | /** | ||||
* class: $.jqplot.LogAxisRenderer | * class: $.jqplot.LogAxisRenderer | ||||
@@ -70,21 +82,21 @@ | |||||
for (var j=0; j<d.length; j++) { | for (var j=0; j<d.length; j++) { | ||||
if (this.name == 'xaxis' || this.name == 'x2axis') { | if (this.name == 'xaxis' || this.name == 'x2axis') { | ||||
if (d[j][0] > db.max || db.max == null) { | |||||
db.max = d[j][0]; | |||||
if ((d[j][0] != null && d[j][0] < db.min) || db.min == null) { | |||||
db.min = d[j][0]; | |||||
} | } | ||||
if (d[j][0] > db.max || db.max == null) { | |||||
if ((d[j][0] != null && d[j][0] > db.max) || db.max == null) { | |||||
db.max = d[j][0]; | db.max = d[j][0]; | ||||
} | } | ||||
} | } | ||||
else { | else { | ||||
if (d[j][1] < db.min || db.min == null) { | |||||
if ((d[j][1] != null && d[j][1] < db.min) || db.min == null) { | |||||
db.min = d[j][1]; | db.min = d[j][1]; | ||||
} | } | ||||
if (d[j][1] > db.max || db.max == null) { | |||||
if ((d[j][1] != null && d[j][1] > db.max) || db.max == null) { | |||||
db.max = d[j][1]; | db.max = d[j][1]; | ||||
} | } | ||||
} | |||||
} | |||||
} | } | ||||
} | } | ||||
}; | }; | ||||
@@ -273,8 +285,8 @@ | |||||
var ticks = this._ticks; | var ticks = this._ticks; | ||||
var trans = function (v) { return Math.log(v)/Math.log(lb); }; | var trans = function (v) { return Math.log(v)/Math.log(lb); }; | ||||
var invtrans = function (v) { return Math.pow(Math.E, (Math.log(lb)*v)); }; | var invtrans = function (v) { return Math.pow(Math.E, (Math.log(lb)*v)); }; | ||||
max = trans(this.max); | |||||
min = trans(this.min); | |||||
var max = trans(this.max); | |||||
var min = trans(this.min); | |||||
var offmax = offsets.max; | var offmax = offsets.max; | ||||
var offmin = offsets.min; | var offmin = offsets.min; | ||||
var lshow = (this._label == null) ? false : this._label.show; | var lshow = (this._label == null) ? false : this._label.show; | ||||
@@ -317,7 +329,7 @@ | |||||
if (this.show) { | if (this.show) { | ||||
if (this.name == 'xaxis' || this.name == 'x2axis') { | if (this.name == 'xaxis' || this.name == 'x2axis') { | ||||
for (i=0; i<ticks.length; i++) { | |||||
for (var i=0; i<ticks.length; i++) { | |||||
var t = ticks[i]; | var t = ticks[i]; | ||||
if (t.show && t.showLabel) { | if (t.show && t.showLabel) { | ||||
var shim; | var shim; | ||||
@@ -370,7 +382,7 @@ | |||||
} | } | ||||
} | } | ||||
else { | else { | ||||
for (i=0; i<ticks.length; i++) { | |||||
for (var i=0; i<ticks.length; i++) { | |||||
var t = ticks[i]; | var t = ticks[i]; | ||||
if (t.show && t.showLabel) { | if (t.show && t.showLabel) { | ||||
var shim; | var shim; | ||||
@@ -1,18 +1,30 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* The author would appreciate an email letting him know of any substantial | |||||
* use of jqPlot. You can reach the author at: chris dot leonello at gmail | |||||
* dot com or see http://www.jqplot.com/info.php . This is, of course, | |||||
* not required. | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * making a donation at: http://www.jqplot.com/donate.php . | ||||
* | * | ||||
* Thanks for using jqPlot! | |||||
* 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($) { | (function($) { | ||||
@@ -83,7 +95,11 @@ | |||||
var dim=0; | var dim=0; | ||||
var temp; | var temp; | ||||
this._elem = $('<div class="jqplot-axis jqplot-'+this.name+'" style="position:absolute;"></div>'); | |||||
var elem = document.createElement('div'); | |||||
this._elem = $(elem); | |||||
this._elem.addClass('jqplot-axis jqplot-'+this.name); | |||||
this._elem.css('position', 'absolute'); | |||||
elem = null; | |||||
if (this.name == 'xaxis' || this.name == 'x2axis') { | if (this.name == 'xaxis' || this.name == 'x2axis') { | ||||
this._elem.width(this._plotDimensions.width); | this._elem.width(this._plotDimensions.width); | ||||
@@ -97,8 +113,7 @@ | |||||
this.labelOptions.axis = this.name; | this.labelOptions.axis = this.name; | ||||
this._label = new this.labelRenderer(this.labelOptions); | this._label = new this.labelRenderer(this.labelOptions); | ||||
if (this._label.show) { | if (this._label.show) { | ||||
var elem = this._label.draw(ctx); | |||||
elem.appendTo(this._elem); | |||||
this._elem.append(this._label.draw(ctx)); | |||||
} | } | ||||
var t, tick, elem; | var t, tick, elem; | ||||
@@ -107,8 +122,7 @@ | |||||
for (var i=0; i<t.length; i++) { | for (var i=0; i<t.length; i++) { | ||||
tick = t[i]; | tick = t[i]; | ||||
if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) { | if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) { | ||||
elem = tick.draw(ctx); | |||||
elem.appendTo(this._elem); | |||||
this._elem.append(tick.draw(ctx)); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -127,6 +141,7 @@ | |||||
elem.addClass('jqplot-'+this.name+'-tick'); | elem.addClass('jqplot-'+this.name+'-tick'); | ||||
elem.addClass('jqplot-mekko-barLabel'); | elem.addClass('jqplot-mekko-barLabel'); | ||||
elem.appendTo(this._elem); | elem.appendTo(this._elem); | ||||
elem = null; | |||||
} | } | ||||
} | } | ||||
@@ -467,7 +482,7 @@ | |||||
if (this.show) { | if (this.show) { | ||||
if (this.name == 'xaxis' || this.name == 'x2axis') { | if (this.name == 'xaxis' || this.name == 'x2axis') { | ||||
for (i=0; i<ticks.length; i++) { | |||||
for (var i=0; i<ticks.length; i++) { | |||||
var t = ticks[i]; | var t = ticks[i]; | ||||
if (t.show && t.showLabel) { | if (t.show && t.showLabel) { | ||||
var shim; | var shim; | ||||
@@ -522,7 +537,7 @@ | |||||
} | } | ||||
// now show the labels under the bars. | // now show the labels under the bars. | ||||
var b, l, r; | var b, l, r; | ||||
for (i=0; i<this.barLabels.length; i++) { | |||||
for (var i=0; i<this.barLabels.length; i++) { | |||||
b = this._barLabels[i]; | b = this._barLabels[i]; | ||||
if (b.show) { | if (b.show) { | ||||
w = b.getWidth(); | w = b.getWidth(); | ||||
@@ -535,7 +550,7 @@ | |||||
} | } | ||||
} | } | ||||
else { | else { | ||||
for (i=0; i<ticks.length; i++) { | |||||
for (var i=0; i<ticks.length; i++) { | |||||
var t = ticks[i]; | var t = ticks[i]; | ||||
if (t.show && t.showLabel) { | if (t.show && t.showLabel) { | ||||
var shim; | var shim; | ||||
@@ -1,24 +1,66 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* The author would appreciate an email letting him know of any substantial | |||||
* use of jqPlot. You can reach the author at: chris dot leonello at gmail | |||||
* dot com or see http://www.jqplot.com/info.php . This is, of course, | |||||
* not required. | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * making a donation at: http://www.jqplot.com/donate.php . | ||||
* | * | ||||
* Thanks for using jqPlot! | |||||
* 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($) { | (function($) { | ||||
// Class: $.jqplot.MekkoRenderer | |||||
/** | |||||
* 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 it's 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(){ | $.jqplot.MekkoRenderer = function(){ | ||||
this.shapeRenderer = new $.jqplot.ShapeRenderer(); | 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. | // called with scope of series. | ||||
@@ -35,6 +77,7 @@ | |||||
var opts = {lineJoin:'miter', lineCap:'butt', isarc:false, fillRect:this.fillRect, strokeRect:this.strokeRect}; | var opts = {lineJoin:'miter', lineCap:'butt', isarc:false, fillRect:this.fillRect, strokeRect:this.strokeRect}; | ||||
this.renderer.shapeRenderer.init(opts); | this.renderer.shapeRenderer.init(opts); | ||||
plot.axes.x2axis._series.push(this); | plot.axes.x2axis._series.push(this); | ||||
this._type = 'mekko'; | |||||
}; | }; | ||||
// Method: setGridData | // Method: setGridData | ||||
@@ -106,6 +149,12 @@ | |||||
if (showLine) { | if (showLine) { | ||||
for (i=0; i<gd.length; i++){ | for (i=0; i<gd.length; i++){ | ||||
opts.fillStyle = colorGenerator.next(); | 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); | this.renderer.shapeRenderer.draw(ctx, gd[i], opts); | ||||
} | } | ||||
} | } | ||||
@@ -118,60 +167,139 @@ | |||||
// This is a no-op, no shadows on mekko charts. | // This is a no-op, no shadows on mekko charts. | ||||
}; | }; | ||||
// called with scope of legend renderer. | |||||
$.jqplot.MekkoLegendRenderer = function() { | |||||
$.jqplot.TableLegendRenderer.call(this); | |||||
/** | |||||
* 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 = new $.jqplot.TableLegendRenderer(); | |||||
$.jqplot.MekkoLegendRenderer.prototype.constructor = $.jqplot.MekkoLegendRenderer; | |||||
// called with scope of legend | |||||
$.jqplot.MekkoLegendRenderer.prototype.init = function(options) { | $.jqplot.MekkoLegendRenderer.prototype.init = function(options) { | ||||
this.labels = []; | |||||
// 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"; | this.placement = "outside"; | ||||
$.extend(true, this, options); | $.extend(true, this, options); | ||||
}; | }; | ||||
// called with context of legend | |||||
// called with scope of legend | |||||
$.jqplot.MekkoLegendRenderer.prototype.draw = function() { | $.jqplot.MekkoLegendRenderer.prototype.draw = function() { | ||||
var legend = this; | var legend = this; | ||||
if (this.show) { | if (this.show) { | ||||
var series = this._series; | var series = this._series; | ||||
// make a table. one line label per row. | |||||
var ss = 'position:absolute;'; | var ss = 'position:absolute;'; | ||||
ss += (this.background) ? 'background:'+this.background+';' : ''; | ss += (this.background) ? 'background:'+this.background+';' : ''; | ||||
ss += (this.border) ? 'border:'+this.border+';' : ''; | ss += (this.border) ? 'border:'+this.border+';' : ''; | ||||
ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : ''; | ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : ''; | ||||
ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : ''; | ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : ''; | ||||
ss += (this.textColor) ? 'color:'+this.textColor+';' : ''; | ss += (this.textColor) ? 'color:'+this.textColor+';' : ''; | ||||
this._elem = $('<table class="jqplot-table-legend jqplot-mekko-legend" style="'+ss+'"></table>'); | |||||
var pad = false, i, labels = [], colors = []; | |||||
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 s = series[0]; | ||||
var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors); | var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors); | ||||
if (s.show) { | if (s.show) { | ||||
var pd = s.data; | var pd = s.data; | ||||
for (i=0; i<pd.length; i++){ | |||||
labels.push(this.labels[i] || pd[i][0].toString()); | |||||
colors.push(colorGenerator.next()); | |||||
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); | |||||
} | } | ||||
for (i=pd.length-1; i>-1; i--) { | |||||
if (labels[i]) { | |||||
this.renderer.addrow.call(this, labels[i], colors[i], pad); | |||||
pad = true; | |||||
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; | return this._elem; | ||||
}; | }; | ||||
$.jqplot.MekkoLegendRenderer.prototype.pack = function(offsets) { | $.jqplot.MekkoLegendRenderer.prototype.pack = function(offsets) { | ||||
if (this.show) { | if (this.show) { | ||||
// fake a grid for positioning | // 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 == 'inside') { | |||||
var grid = {_top:offsets.top, _left:offsets.left, _right:offsets.right, _bottom:this._plotDimensions.height - offsets.bottom}; | |||||
if (this.placement == 'insideGrid') { | |||||
switch (this.location) { | switch (this.location) { | ||||
case 'nw': | case 'nw': | ||||
var a = grid._left + this.xoffset; | var a = grid._left + this.xoffset; | ||||
@@ -1,18 +1,30 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* The author would appreciate an email letting him know of any substantial | |||||
* use of jqPlot. You can reach the author at: chris dot leonello at gmail | |||||
* dot com or see http://www.jqplot.com/info.php . This is, of course, | |||||
* not required. | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * making a donation at: http://www.jqplot.com/donate.php . | ||||
* | * | ||||
* Thanks for using jqPlot! | |||||
* 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($) { | (function($) { | ||||
@@ -95,6 +107,10 @@ | |||||
// true if is a hi-low-close chart (no open price). | // true if is a hi-low-close chart (no open price). | ||||
// This is determined automatically from the series data. | // This is determined automatically from the series data. | ||||
this.hlc = false; | this.hlc = false; | ||||
// prop: lineWidth | |||||
// Width of the hi-low line and open/close ticks. | |||||
// Must be set in the rendererOptions for the series. | |||||
this.lineWidth = 1.5; | |||||
this._tickLength; | this._tickLength; | ||||
this._bodyWidth; | this._bodyWidth; | ||||
}; | }; | ||||
@@ -104,10 +120,13 @@ | |||||
// called with scope of series. | // called with scope of series. | ||||
$.jqplot.OHLCRenderer.prototype.init = function(options) { | $.jqplot.OHLCRenderer.prototype.init = function(options) { | ||||
// prop: lineWidth | |||||
// Width of the hi-low line and open/close ticks. | |||||
this.lineWidth = 1.5; | |||||
options = options || {}; | |||||
// lineWidth has to be set on the series, changes in renderer | |||||
// constructor have no effect. set the default here | |||||
// if no renderer option for lineWidth is specified. | |||||
this.lineWidth = options.lineWidth || 1.5; | |||||
$.jqplot.LineRenderer.prototype.init.call(this, options); | $.jqplot.LineRenderer.prototype.init.call(this, options); | ||||
this._type = 'ohlc'; | |||||
// set the yaxis data bounds here to account for hi and low values | // set the yaxis data bounds here to account for hi and low values | ||||
var db = this._yaxis._dataBounds; | var db = this._yaxis._dataBounds; | ||||
var d = this._plotData; | var d = this._plotData; | ||||
@@ -171,13 +190,23 @@ | |||||
xmaxidx = i+1; | xmaxidx = i+1; | ||||
} | } | ||||
} | } | ||||
var dwidth = this.gridData[xmaxidx-1][0] - this.gridData[xminidx][0]; | |||||
var nvisiblePoints = xmaxidx - xminidx; | |||||
try { | |||||
var dinterval = Math.abs(this._xaxis.series_u2p(parseInt(this._xaxis._intervalStats[0].sortedIntervals[0].interval)) - this._xaxis.series_u2p(0)); | |||||
} | |||||
catch (e) { | |||||
var dinterval = dwidth / nvisiblePoints; | |||||
} | |||||
if (r.candleStick) { | if (r.candleStick) { | ||||
if (typeof(r.bodyWidth) == 'number') { | if (typeof(r.bodyWidth) == 'number') { | ||||
r._bodyWidth = r.bodyWidth; | r._bodyWidth = r.bodyWidth; | ||||
} | } | ||||
else { | else { | ||||
r._bodyWidth = Math.min(20, ctx.canvas.width/(xmaxidx - xminidx)/2); | |||||
r._bodyWidth = Math.min(20, dinterval/1.75); | |||||
} | } | ||||
} | } | ||||
else { | else { | ||||
@@ -185,7 +214,7 @@ | |||||
r._tickLength = r.tickLength; | r._tickLength = r.tickLength; | ||||
} | } | ||||
else { | else { | ||||
r._tickLength = Math.min(10, ctx.canvas.width/(xmaxidx - xminidx)/4); | |||||
r._tickLength = Math.min(10, dinterval/3.5); | |||||
} | } | ||||
} | } | ||||
@@ -1,14 +1,30 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* Although not required, the author would appreciate an email letting him | * Although not required, the author would appreciate an email letting him | ||||
* know of any substantial use of jqPlot. You can reach the author at: | * know of any substantial use of jqPlot. You can reach the author at: | ||||
* chris dot leonello at gmail dot com or see http://www.jqplot.com/info.php . | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * 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(a){a.jqplot.OHLCRenderer=function(){a.jqplot.LineRenderer.call(this);this.candleStick=false;this.tickLength="auto";this.bodyWidth="auto";this.openColor=null;this.closeColor=null;this.wickColor=null;this.fillUpBody=false;this.fillDownBody=true;this.upBodyColor=null;this.downBodyColor=null;this.hlc=false;this._tickLength;this._bodyWidth};a.jqplot.OHLCRenderer.prototype=new a.jqplot.LineRenderer();a.jqplot.OHLCRenderer.prototype.constructor=a.jqplot.OHLCRenderer;a.jqplot.OHLCRenderer.prototype.init=function(e){this.lineWidth=1.5;a.jqplot.LineRenderer.prototype.init.call(this,e);var b=this._yaxis._dataBounds;var f=this._plotData;if(f[0].length<5){this.renderer.hlc=true;for(var c=0;c<f.length;c++){if(f[c][2]<b.min||b.min==null){b.min=f[c][2]}if(f[c][1]>b.max||b.max==null){b.max=f[c][1]}}}else{for(var c=0;c<f.length;c++){if(f[c][3]<b.min||b.min==null){b.min=f[c][3]}if(f[c][2]>b.max||b.max==null){b.max=f[c][2]}}}};a.jqplot.OHLCRenderer.prototype.draw=function(z,J,g){var G=this.data;var u=this._xaxis.min;var y=this._xaxis.max;var k=0;var H=G.length;var n=this._xaxis.series_u2p;var F=this._yaxis.series_u2p;var C,D,e,I,E,m,K,B;var v;var t=this.renderer;var q=(g!=undefined)?g:{};var j=(q.shadow!=undefined)?q.shadow:this.shadow;var A=(q.fill!=undefined)?q.fill:this.fill;var c=(q.fillAndStroke!=undefined)?q.fillAndStroke:this.fillAndStroke;t.bodyWidth=(q.bodyWidth!=undefined)?q.bodyWidth:t.bodyWidth;t.tickLength=(q.tickLength!=undefined)?q.tickLength:t.tickLength;z.save();if(this.show){var l,p,f,L,s;for(var C=0;C<G.length;C++){if(G[C][0]<u){k=C}else{if(G[C][0]<y){H=C+1}}}if(t.candleStick){if(typeof(t.bodyWidth)=="number"){t._bodyWidth=t.bodyWidth}else{t._bodyWidth=Math.min(20,z.canvas.width/(H-k)/2)}}else{if(typeof(t.tickLength)=="number"){t._tickLength=t.tickLength}else{t._tickLength=Math.min(10,z.canvas.width/(H-k)/4)}}for(var C=k;C<H;C++){l=n(G[C][0]);if(t.hlc){p=null;f=F(G[C][1]);L=F(G[C][2]);s=F(G[C][3])}else{p=F(G[C][1]);f=F(G[C][2]);L=F(G[C][3]);s=F(G[C][4])}v={};if(t.candleStick&&!t.hlc){m=t._bodyWidth;K=l-m/2;if(s<p){if(t.wickColor){v.color=t.wickColor}else{if(t.downBodyColor){v.color=t.upBodyColor}}e=a.extend(true,{},q,v);t.shapeRenderer.draw(z,[[l,f],[l,s]],e);t.shapeRenderer.draw(z,[[l,p],[l,L]],e);v={};I=s;E=p-s;if(t.fillUpBody){v.fillRect=true}else{v.strokeRect=true;m=m-this.lineWidth;K=l-m/2}if(t.upBodyColor){v.color=t.upBodyColor;v.fillStyle=t.upBodyColor}B=[K,I,m,E]}else{if(s>p){if(t.wickColor){v.color=t.wickColor}else{if(t.downBodyColor){v.color=t.downBodyColor}}e=a.extend(true,{},q,v);t.shapeRenderer.draw(z,[[l,f],[l,p]],e);t.shapeRenderer.draw(z,[[l,s],[l,L]],e);v={};I=p;E=s-p;if(t.fillDownBody){v.fillRect=true}else{v.strokeRect=true;m=m-this.lineWidth;K=l-m/2}if(t.downBodyColor){v.color=t.downBodyColor;v.fillStyle=t.downBodyColor}B=[K,I,m,E]}else{if(t.wickColor){v.color=t.wickColor}e=a.extend(true,{},q,v);t.shapeRenderer.draw(z,[[l,f],[l,L]],e);v={};v.fillRect=false;v.strokeRect=false;K=[l-m/2,p];I=[l+m/2,s];m=null;E=null;B=[K,I]}}e=a.extend(true,{},q,v);t.shapeRenderer.draw(z,B,e)}else{D=q.color;if(t.openColor){q.color=t.openColor}if(!t.hlc){t.shapeRenderer.draw(z,[[l-t._tickLength,p],[l,p]],q)}q.color=D;if(t.wickColor){q.color=t.wickColor}t.shapeRenderer.draw(z,[[l,f],[l,L]],q);q.color=D;if(t.closeColor){q.color=t.closeColor}t.shapeRenderer.draw(z,[[l,s],[l+t._tickLength,s]],q);q.color=D}}}z.restore()};a.jqplot.OHLCRenderer.prototype.drawShadow=function(b,d,c){};a.jqplot.OHLCRenderer.checkOptions=function(d,c,b){if(!b.highlighter){b.highlighter={showMarker:false,tooltipAxes:"y",yvalues:4,formatString:'<table class="jqplot-highlighter"><tr><td>date:</td><td>%s</td></tr><tr><td>open:</td><td>%s</td></tr><tr><td>hi:</td><td>%s</td></tr><tr><td>low:</td><td>%s</td></tr><tr><td>close:</td><td>%s</td></tr></table>'}}}})(jQuery); | |||||
(function(a){a.jqplot.OHLCRenderer=function(){a.jqplot.LineRenderer.call(this);this.candleStick=false;this.tickLength="auto";this.bodyWidth="auto";this.openColor=null;this.closeColor=null;this.wickColor=null;this.fillUpBody=false;this.fillDownBody=true;this.upBodyColor=null;this.downBodyColor=null;this.hlc=false;this.lineWidth=1.5;this._tickLength;this._bodyWidth};a.jqplot.OHLCRenderer.prototype=new a.jqplot.LineRenderer();a.jqplot.OHLCRenderer.prototype.constructor=a.jqplot.OHLCRenderer;a.jqplot.OHLCRenderer.prototype.init=function(e){e=e||{};this.lineWidth=e.lineWidth||1.5;a.jqplot.LineRenderer.prototype.init.call(this,e);this._type="ohlc";var b=this._yaxis._dataBounds;var f=this._plotData;if(f[0].length<5){this.renderer.hlc=true;for(var c=0;c<f.length;c++){if(f[c][2]<b.min||b.min==null){b.min=f[c][2]}if(f[c][1]>b.max||b.max==null){b.max=f[c][1]}}}else{for(var c=0;c<f.length;c++){if(f[c][3]<b.min||b.min==null){b.min=f[c][3]}if(f[c][2]>b.max||b.max==null){b.max=f[c][2]}}}};a.jqplot.OHLCRenderer.prototype.draw=function(A,N,j){var J=this.data;var v=this._xaxis.min;var z=this._xaxis.max;var l=0;var K=J.length;var p=this._xaxis.series_u2p;var G=this._yaxis.series_u2p;var D,E,f,M,F,n,O,C;var y;var u=this.renderer;var s=(j!=undefined)?j:{};var k=(s.shadow!=undefined)?s.shadow:this.shadow;var B=(s.fill!=undefined)?s.fill:this.fill;var c=(s.fillAndStroke!=undefined)?s.fillAndStroke:this.fillAndStroke;u.bodyWidth=(s.bodyWidth!=undefined)?s.bodyWidth:u.bodyWidth;u.tickLength=(s.tickLength!=undefined)?s.tickLength:u.tickLength;A.save();if(this.show){var m,q,g,Q,t;for(var D=0;D<J.length;D++){if(J[D][0]<v){l=D}else{if(J[D][0]<z){K=D+1}}}var I=this.gridData[K-1][0]-this.gridData[l][0];var L=K-l;try{var P=Math.abs(this._xaxis.series_u2p(parseInt(this._xaxis._intervalStats[0].sortedIntervals[0].interval))-this._xaxis.series_u2p(0))}catch(H){var P=I/L}if(u.candleStick){if(typeof(u.bodyWidth)=="number"){u._bodyWidth=u.bodyWidth}else{u._bodyWidth=Math.min(20,P/1.75)}}else{if(typeof(u.tickLength)=="number"){u._tickLength=u.tickLength}else{u._tickLength=Math.min(10,P/3.5)}}for(var D=l;D<K;D++){m=p(J[D][0]);if(u.hlc){q=null;g=G(J[D][1]);Q=G(J[D][2]);t=G(J[D][3])}else{q=G(J[D][1]);g=G(J[D][2]);Q=G(J[D][3]);t=G(J[D][4])}y={};if(u.candleStick&&!u.hlc){n=u._bodyWidth;O=m-n/2;if(t<q){if(u.wickColor){y.color=u.wickColor}else{if(u.downBodyColor){y.color=u.upBodyColor}}f=a.extend(true,{},s,y);u.shapeRenderer.draw(A,[[m,g],[m,t]],f);u.shapeRenderer.draw(A,[[m,q],[m,Q]],f);y={};M=t;F=q-t;if(u.fillUpBody){y.fillRect=true}else{y.strokeRect=true;n=n-this.lineWidth;O=m-n/2}if(u.upBodyColor){y.color=u.upBodyColor;y.fillStyle=u.upBodyColor}C=[O,M,n,F]}else{if(t>q){if(u.wickColor){y.color=u.wickColor}else{if(u.downBodyColor){y.color=u.downBodyColor}}f=a.extend(true,{},s,y);u.shapeRenderer.draw(A,[[m,g],[m,q]],f);u.shapeRenderer.draw(A,[[m,t],[m,Q]],f);y={};M=q;F=t-q;if(u.fillDownBody){y.fillRect=true}else{y.strokeRect=true;n=n-this.lineWidth;O=m-n/2}if(u.downBodyColor){y.color=u.downBodyColor;y.fillStyle=u.downBodyColor}C=[O,M,n,F]}else{if(u.wickColor){y.color=u.wickColor}f=a.extend(true,{},s,y);u.shapeRenderer.draw(A,[[m,g],[m,Q]],f);y={};y.fillRect=false;y.strokeRect=false;O=[m-n/2,q];M=[m+n/2,t];n=null;F=null;C=[O,M]}}f=a.extend(true,{},s,y);u.shapeRenderer.draw(A,C,f)}else{E=s.color;if(u.openColor){s.color=u.openColor}if(!u.hlc){u.shapeRenderer.draw(A,[[m-u._tickLength,q],[m,q]],s)}s.color=E;if(u.wickColor){s.color=u.wickColor}u.shapeRenderer.draw(A,[[m,g],[m,Q]],s);s.color=E;if(u.closeColor){s.color=u.closeColor}u.shapeRenderer.draw(A,[[m,t],[m+u._tickLength,t]],s);s.color=E}}}A.restore()};a.jqplot.OHLCRenderer.prototype.drawShadow=function(b,d,c){};a.jqplot.OHLCRenderer.checkOptions=function(d,c,b){if(!b.highlighter){b.highlighter={showMarker:false,tooltipAxes:"y",yvalues:4,formatString:'<table class="jqplot-highlighter"><tr><td>date:</td><td>%s</td></tr><tr><td>open:</td><td>%s</td></tr><tr><td>hi:</td><td>%s</td></tr><tr><td>low:</td><td>%s</td></tr><tr><td>close:</td><td>%s</td></tr></table>'}}}})(jQuery); |
@@ -1,27 +1,70 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* The author would appreciate an email letting him know of any substantial | |||||
* use of jqPlot. You can reach the author at: chris dot leonello at gmail | |||||
* dot com or see http://www.jqplot.com/info.php . This is, of course, | |||||
* not required. | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * making a donation at: http://www.jqplot.com/donate.php . | ||||
* | * | ||||
* Thanks for using jqPlot! | |||||
* 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($) { | (function($) { | ||||
/** | /** | ||||
* Class: $.jqplot.PieRenderer | * Class: $.jqplot.PieRenderer | ||||
* Plugin renderer to draw a pie chart. | * Plugin renderer to draw a pie chart. | ||||
* Pie charts will draw only the first series. Other series are ignored. | |||||
* x values, if present, will be used as slice labels. | * x values, if present, will be used as slice labels. | ||||
* y values give slice size. | * y values give slice size. | ||||
* | |||||
* To use this renderer, you need to include the | |||||
* pie renderer plugin, for example: | |||||
* | |||||
* > <script type="text/javascript" src="plugins/jqplot.pieRenderer.js"></script> | |||||
* | |||||
* Properties described here are passed into the $.jqplot function | |||||
* as options on the series renderer. For example: | |||||
* | |||||
* > plot2 = $.jqplot('chart2', [s1, s2], { | |||||
* > seriesDefaults: { | |||||
* > renderer:$.jqplot.PieRenderer, | |||||
* > rendererOptions:{ | |||||
* > sliceMargin: 2, | |||||
* > startAngle: -90 | |||||
* > } | |||||
* > } | |||||
* > }); | |||||
* | |||||
* A pie plot will trigger events on the plot target | |||||
* according to user interaction. All events return the event object, | |||||
* the series index, the point (slice) index, and the point data for | |||||
* the appropriate slice. | |||||
* | |||||
* 'jqplotDataMouseOver' - triggered when user mouseing over a slice. | |||||
* 'jqplotDataHighlight' - triggered the first time user mouses over a slice, | |||||
* if highlighting is enabled. | |||||
* 'jqplotDataUnhighlight' - triggered when a user moves the mouse out of | |||||
* a highlighted slice. | |||||
* 'jqplotDataClick' - triggered when the user clicks on a slice. | |||||
* 'jqplotDataRightClick' - tiggered when the user right clicks on a slice if | |||||
* the "captureRightClick" option is set to true on the plot. | |||||
*/ | */ | ||||
$.jqplot.PieRenderer = function(){ | $.jqplot.PieRenderer = function(){ | ||||
$.jqplot.LineRenderer.call(this); | $.jqplot.LineRenderer.call(this); | ||||
@@ -31,17 +74,17 @@ | |||||
$.jqplot.PieRenderer.prototype.constructor = $.jqplot.PieRenderer; | $.jqplot.PieRenderer.prototype.constructor = $.jqplot.PieRenderer; | ||||
// called with scope of a series | // called with scope of a series | ||||
$.jqplot.PieRenderer.prototype.init = function(options) { | |||||
$.jqplot.PieRenderer.prototype.init = function(options, plot) { | |||||
// Group: Properties | // Group: Properties | ||||
// | // | ||||
// prop: diameter | // prop: diameter | ||||
// diameter of the pie, auto computed by default | |||||
// Outer diameter of the pie, auto computed by default | |||||
this.diameter = null; | this.diameter = null; | ||||
// prop: padding | // prop: padding | ||||
// padding between the pie and plot edges, legend, etc. | // padding between the pie and plot edges, legend, etc. | ||||
this.padding = 20; | this.padding = 20; | ||||
// prop: sliceMargin | // prop: sliceMargin | ||||
// pixels spacing between pie slices. | |||||
// angular spacing between pie slices in degrees. | |||||
this.sliceMargin = 0; | this.sliceMargin = 0; | ||||
// prop: fill | // prop: fill | ||||
// true or false, wether to fil the slices. | // true or false, wether to fil the slices. | ||||
@@ -57,71 +100,244 @@ | |||||
// number of strokes to apply to the shadow, | // number of strokes to apply to the shadow, | ||||
// each stroke offset shadowOffset from the last. | // each stroke offset shadowOffset from the last. | ||||
this.shadowDepth = 5; | this.shadowDepth = 5; | ||||
// prop: highlightMouseOver | |||||
// True to highlight slice when moused over. | |||||
// This must be false to enable highlightMouseDown to highlight when clicking on a slice. | |||||
this.highlightMouseOver = true; | |||||
// prop: highlightMouseDown | |||||
// True to highlight when a mouse button is pressed over a slice. | |||||
// This will be disabled if highlightMouseOver is true. | |||||
this.highlightMouseDown = false; | |||||
// prop: highlightColors | |||||
// an array of colors to use when highlighting a slice. | |||||
this.highlightColors = []; | |||||
// prop: dataLabels | |||||
// Either 'label', 'value', 'percent' or an array of labels to place on the pie slices. | |||||
// Defaults to percentage of each pie slice. | |||||
this.dataLabels = 'percent'; | |||||
// prop: showDataLabels | |||||
// true to show data labels on slices. | |||||
this.showDataLabels = false; | |||||
// prop: dataLabelFormatString | |||||
// Format string for data labels. If none, '%s' is used for "label" and for arrays, '%d' for value and '%d%%' for percentage. | |||||
this.dataLabelFormatString = null; | |||||
// prop: dataLabelThreshold | |||||
// Threshhold in percentage (0-100) of pie area, below which no label will be displayed. | |||||
// This applies to all label types, not just to percentage labels. | |||||
this.dataLabelThreshold = 3; | |||||
// prop: dataLabelPositionFactor | |||||
// A Multiplier (0-1) of the pie radius which controls position of label on slice. | |||||
// Increasing will slide label toward edge of pie, decreasing will slide label toward center of pie. | |||||
this.dataLabelPositionFactor = 0.52; | |||||
// prop: dataLabelNudge | |||||
// Number of pixels to slide the label away from (+) or toward (-) the center of the pie. | |||||
this.dataLabelNudge = 2; | |||||
// prop: dataLabelCenterOn | |||||
// True to center the data label at its position. | |||||
// False to set the inside facing edge of the label at its position. | |||||
this.dataLabelCenterOn = true; | |||||
// prop: startAngle | |||||
// Angle to start drawing pie in degrees. | |||||
// According to orientation of canvas coordinate system: | |||||
// 0 = on the positive x axis | |||||
// -90 = on the positive y axis. | |||||
// 90 = on the negaive y axis. | |||||
// 180 or - 180 = on the negative x axis. | |||||
this.startAngle = 0; | |||||
this.tickRenderer = $.jqplot.PieTickRenderer; | this.tickRenderer = $.jqplot.PieTickRenderer; | ||||
// Used as check for conditions where pie shouldn't be drawn. | |||||
this._drawData = true; | |||||
this._type = 'pie'; | |||||
// if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver | |||||
if (options.highlightMouseDown && options.highlightMouseOver == null) { | |||||
options.highlightMouseOver = false; | |||||
} | |||||
$.extend(true, this, options); | $.extend(true, this, options); | ||||
if (this.diameter != null) { | |||||
this.diameter = this.diameter - this.sliceMargin; | |||||
if (this.sliceMargin < 0) { | |||||
this.sliceMargin = 0; | |||||
} | } | ||||
this._diameter = null; | this._diameter = null; | ||||
this._radius = null; | |||||
// array of [start,end] angles arrays, one for each slice. In radians. | |||||
this._sliceAngles = []; | |||||
// index of the currenty highlighted point, if any | |||||
this._highlightedPoint = null; | |||||
// set highlight colors if none provided | |||||
if (this.highlightColors.length == 0) { | |||||
for (var i=0; i<this.seriesColors.length; i++){ | |||||
var rgba = $.jqplot.getColorComponents(this.seriesColors[i]); | |||||
var newrgb = [rgba[0], rgba[1], rgba[2]]; | |||||
var sum = newrgb[0] + newrgb[1] + newrgb[2]; | |||||
for (var j=0; j<3; j++) { | |||||
// when darkening, lowest color component can be is 60. | |||||
newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]); | |||||
newrgb[j] = parseInt(newrgb[j], 10); | |||||
} | |||||
this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')'); | |||||
} | |||||
} | |||||
this.highlightColorGenerator = new $.jqplot.ColorGenerator(this.highlightColors); | |||||
plot.postParseOptionsHooks.addOnce(postParseOptions); | |||||
plot.postInitHooks.addOnce(postInit); | |||||
plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); | |||||
plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown); | |||||
plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp); | |||||
plot.eventListenerHooks.addOnce('jqplotClick', handleClick); | |||||
plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); | |||||
plot.postDrawHooks.addOnce(postPlotDraw); | |||||
}; | }; | ||||
$.jqplot.PieRenderer.prototype.setGridData = function(plot) { | $.jqplot.PieRenderer.prototype.setGridData = function(plot) { | ||||
// this is a no-op | |||||
// set gridData property. This will hold angle in radians of each data point. | |||||
var stack = []; | |||||
var td = []; | |||||
var sa = this.startAngle/180*Math.PI; | |||||
var tot = 0; | |||||
// don't know if we have any valid data yet, so set plot to not draw. | |||||
this._drawData = false; | |||||
for (var i=0; i<this.data.length; i++){ | |||||
if (this.data[i][1] != 0) { | |||||
// we have data, O.K. to draw. | |||||
this._drawData = true; | |||||
} | |||||
stack.push(this.data[i][1]); | |||||
td.push([this.data[i][0]]); | |||||
if (i>0) { | |||||
stack[i] += stack[i-1]; | |||||
} | |||||
tot += this.data[i][1]; | |||||
} | |||||
var fact = Math.PI*2/stack[stack.length - 1]; | |||||
for (var i=0; i<stack.length; i++) { | |||||
td[i][1] = stack[i] * fact; | |||||
td[i][2] = this.data[i][1]/tot; | |||||
} | |||||
this.gridData = td; | |||||
}; | }; | ||||
$.jqplot.PieRenderer.prototype.makeGridData = function(data, plot) { | $.jqplot.PieRenderer.prototype.makeGridData = function(data, plot) { | ||||
var stack = []; | var stack = []; | ||||
var td = []; | var td = []; | ||||
var tot = 0; | |||||
var sa = this.startAngle/180*Math.PI; | |||||
// don't know if we have any valid data yet, so set plot to not draw. | |||||
this._drawData = false; | |||||
for (var i=0; i<data.length; i++){ | for (var i=0; i<data.length; i++){ | ||||
if (this.data[i][1] != 0) { | |||||
// we have data, O.K. to draw. | |||||
this._drawData = true; | |||||
} | |||||
stack.push(data[i][1]); | stack.push(data[i][1]); | ||||
td.push([data[i][0]]); | td.push([data[i][0]]); | ||||
if (i>0) { | if (i>0) { | ||||
stack[i] += stack[i-1]; | stack[i] += stack[i-1]; | ||||
} | } | ||||
tot += data[i][1]; | |||||
} | } | ||||
var fact = Math.PI*2/stack[stack.length - 1]; | var fact = Math.PI*2/stack[stack.length - 1]; | ||||
for (var i=0; i<stack.length; i++) { | for (var i=0; i<stack.length; i++) { | ||||
td[i][1] = stack[i] * fact; | td[i][1] = stack[i] * fact; | ||||
td[i][2] = data[i][1]/tot; | |||||
} | } | ||||
return td; | return td; | ||||
}; | }; | ||||
function calcRadiusAdjustment(ang) { | |||||
return Math.sin((ang - (ang-Math.PI) / 8 / Math.PI )/2.0); | |||||
} | |||||
function calcRPrime(ang1, ang2, sliceMargin, fill, lineWidth) { | |||||
var rprime = 0; | |||||
var ang = ang2 - ang1; | |||||
var absang = Math.abs(ang); | |||||
var sm = sliceMargin; | |||||
if (fill == false) { | |||||
sm += lineWidth; | |||||
} | |||||
if (sm > 0 && absang > 0.01 && absang < 6.282) { | |||||
rprime = parseFloat(sm) / 2.0 / calcRadiusAdjustment(ang); | |||||
} | |||||
return rprime; | |||||
} | |||||
$.jqplot.PieRenderer.prototype.drawSlice = function (ctx, ang1, ang2, color, isShadow) { | $.jqplot.PieRenderer.prototype.drawSlice = function (ctx, ang1, ang2, color, isShadow) { | ||||
var r = this._diameter / 2; | |||||
var fill = this.fill; | |||||
var lineWidth = this.lineWidth; | |||||
ctx.save(); | |||||
ctx.translate(this.sliceMargin*Math.cos((ang1+ang2)/2), this.sliceMargin*Math.sin((ang1+ang2)/2)); | |||||
if (isShadow) { | |||||
for (var i=0; i<this.shadowDepth; i++) { | |||||
ctx.save(); | |||||
ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI)); | |||||
doDraw(); | |||||
if (this._drawData) { | |||||
var r = this._radius; | |||||
var fill = this.fill; | |||||
var lineWidth = this.lineWidth; | |||||
var sm = this.sliceMargin; | |||||
if (this.fill == false) { | |||||
sm += this.lineWidth; | |||||
} | } | ||||
ctx.save(); | |||||
ctx.translate(this._center[0], this._center[1]); | |||||
var rprime = calcRPrime(ang1, ang2, this.sliceMargin, this.fill, this.lineWidth); | |||||
var transx = rprime * Math.cos((ang1 + ang2) / 2.0); | |||||
var transy = rprime * Math.sin((ang1 + ang2) / 2.0); | |||||
if ((ang2 - ang1) <= Math.PI) { | |||||
r -= rprime; | |||||
} | |||||
else { | |||||
r += rprime; | |||||
} | |||||
ctx.translate(transx, transy); | |||||
if (isShadow) { | |||||
for (var i=0, l=this.shadowDepth; i<l; i++) { | |||||
ctx.save(); | |||||
ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI)); | |||||
doDraw(r); | |||||
} | |||||
for (var i=0, l=this.shadowDepth; i<l; i++) { | |||||
ctx.restore(); | |||||
} | |||||
} | |||||
else { | |||||
doDraw(r); | |||||
} | |||||
ctx.restore(); | |||||
} | } | ||||
else { | |||||
doDraw(); | |||||
} | |||||
function doDraw () { | |||||
function doDraw (rad) { | |||||
// Fix for IE and Chrome that can't seem to draw circles correctly. | // Fix for IE and Chrome that can't seem to draw circles correctly. | ||||
// ang2 should always be <= 2 pi since that is the way the data is converted. | // ang2 should always be <= 2 pi since that is the way the data is converted. | ||||
if (ang2 > 6.282) { | |||||
ang2 = 6.282; | |||||
// 2Pi = 6.2831853, Pi = 3.1415927 | |||||
if (ang2 > 6.282 + this.startAngle) { | |||||
ang2 = 6.282 + this.startAngle; | |||||
if (ang1 > ang2) { | if (ang1 > ang2) { | ||||
ang1 = 6.281; | |||||
ang1 = 6.281 + this.startAngle; | |||||
} | } | ||||
} | } | ||||
// Fix for IE, where it can't seem to handle 0 degree angles. Also avoids | |||||
// ugly line on unfilled pies. | |||||
if (ang1 >= ang2) { | |||||
return; | |||||
} | |||||
ctx.beginPath(); | ctx.beginPath(); | ||||
ctx.moveTo(0, 0); | |||||
ctx.fillStyle = color; | ctx.fillStyle = color; | ||||
ctx.strokeStyle = color; | ctx.strokeStyle = color; | ||||
ctx.lineWidth = lineWidth; | ctx.lineWidth = lineWidth; | ||||
ctx.arc(0, 0, r, ang1, ang2, false); | |||||
ctx.arc(0, 0, rad, ang1, ang2, false); | |||||
ctx.lineTo(0,0); | |||||
ctx.closePath(); | ctx.closePath(); | ||||
if (fill) { | if (fill) { | ||||
ctx.fill(); | ctx.fill(); | ||||
} | } | ||||
@@ -129,26 +345,18 @@ | |||||
ctx.stroke(); | ctx.stroke(); | ||||
} | } | ||||
} | } | ||||
if (isShadow) { | |||||
for (var i=0; i<this.shadowDepth; i++) { | |||||
ctx.restore(); | |||||
} | |||||
} | |||||
ctx.restore(); | |||||
}; | }; | ||||
// called with scope of series | // called with scope of series | ||||
$.jqplot.PieRenderer.prototype.draw = function (ctx, gd, options) { | |||||
$.jqplot.PieRenderer.prototype.draw = function (ctx, gd, options, plot) { | |||||
var i; | var i; | ||||
var opts = (options != undefined) ? options : {}; | var opts = (options != undefined) ? options : {}; | ||||
// offset and direction of offset due to legend placement | // offset and direction of offset due to legend placement | ||||
var offx = 0; | var offx = 0; | ||||
var offy = 0; | var offy = 0; | ||||
var trans = 1; | var trans = 1; | ||||
var colorGenerator = new this.colorGenerator(this.seriesColors); | |||||
if (options.legendInfo) { | |||||
var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors); | |||||
if (options.legendInfo && options.legendInfo.placement == 'insideGrid') { | |||||
var li = options.legendInfo; | var li = options.legendInfo; | ||||
switch (li.location) { | switch (li.location) { | ||||
case 'nw': | case 'nw': | ||||
@@ -185,33 +393,111 @@ | |||||
} | } | ||||
var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; | var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; | ||||
var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; | |||||
var fill = (opts.fill != undefined) ? opts.fill : this.fill; | var fill = (opts.fill != undefined) ? opts.fill : this.fill; | ||||
var cw = ctx.canvas.width; | var cw = ctx.canvas.width; | ||||
var ch = ctx.canvas.height; | var ch = ctx.canvas.height; | ||||
var w = cw - offx - 2 * this.padding; | var w = cw - offx - 2 * this.padding; | ||||
var h = ch - offy - 2 * this.padding; | var h = ch - offy - 2 * this.padding; | ||||
var d = Math.min(w,h); | |||||
this._diameter = this.diameter || d - this.sliceMargin; | |||||
// this.diameter -= this.sliceMargin; | |||||
var r = this._diameter/2; | |||||
ctx.save(); | |||||
ctx.translate((cw - trans * offx)/2 + trans * offx, (ch - trans*offy)/2 + trans * offy); | |||||
var mindim = Math.min(w,h); | |||||
var d = mindim; | |||||
// Fixes issue #272. Thanks hugwijst! | |||||
// reset slice angles array. | |||||
this._sliceAngles = []; | |||||
var sm = this.sliceMargin; | |||||
if (this.fill == false) { | |||||
sm += this.lineWidth; | |||||
} | |||||
var rprime; | |||||
var maxrprime = 0; | |||||
var ang, ang1, ang2, shadowColor; | |||||
var sa = this.startAngle / 180 * Math.PI; | |||||
// have to pre-draw shadows, so loop throgh here and calculate some values also. | |||||
for (var i=0, l=gd.length; i<l; i++) { | |||||
ang1 = (i == 0) ? sa : gd[i-1][1] + sa; | |||||
ang2 = gd[i][1] + sa; | |||||
this._sliceAngles.push([ang1, ang2]); | |||||
rprime = calcRPrime(ang1, ang2, this.sliceMargin, this.fill, this.lineWidth); | |||||
if (Math.abs(ang2-ang1) > Math.PI) { | |||||
maxrprime = Math.max(rprime, maxrprime); | |||||
} | |||||
} | |||||
if (this.diameter != null && this.diameter > 0) { | |||||
this._diameter = this.diameter - 2*maxrprime; | |||||
} | |||||
else { | |||||
this._diameter = d - 2*maxrprime; | |||||
} | |||||
// Need to check for undersized pie. This can happen if | |||||
// plot area too small and legend is too big. | |||||
if (this._diameter < 6) { | |||||
$.jqplot.log('Diameter of pie too small, not rendering.'); | |||||
return; | |||||
} | |||||
var r = this._radius = this._diameter/2; | |||||
this._center = [(cw - trans * offx)/2 + trans * offx + maxrprime * Math.cos(sa), (ch - trans*offy)/2 + trans * offy + maxrprime * Math.sin(sa)]; | |||||
if (this.shadow) { | if (this.shadow) { | ||||
var shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')'; | |||||
for (var i=0; i<gd.length; i++) { | |||||
var ang1 = (i == 0) ? 0 : gd[i-1][1]; | |||||
this.renderer.drawSlice.call (this, ctx, ang1, gd[i][1], shadowColor, true); | |||||
for (var i=0, l=gd.length; i<l; i++) { | |||||
shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')'; | |||||
this.renderer.drawSlice.call (this, ctx, this._sliceAngles[i][0], this._sliceAngles[i][1], shadowColor, true); | |||||
} | } | ||||
} | } | ||||
for (var i=0; i<gd.length; i++) { | for (var i=0; i<gd.length; i++) { | ||||
var ang1 = (i == 0) ? 0 : gd[i-1][1]; | |||||
this.renderer.drawSlice.call (this, ctx, ang1, gd[i][1], colorGenerator.next()); | |||||
} | |||||
this.renderer.drawSlice.call (this, ctx, this._sliceAngles[i][0], this._sliceAngles[i][1], colorGenerator.next(), false); | |||||
ctx.restore(); | |||||
if (this.showDataLabels && gd[i][2]*100 >= this.dataLabelThreshold) { | |||||
var fstr, avgang = (this._sliceAngles[i][0] + this._sliceAngles[i][1])/2, label; | |||||
if (this.dataLabels == 'label') { | |||||
fstr = this.dataLabelFormatString || '%s'; | |||||
label = $.jqplot.sprintf(fstr, gd[i][0]); | |||||
} | |||||
else if (this.dataLabels == 'value') { | |||||
fstr = this.dataLabelFormatString || '%d'; | |||||
label = $.jqplot.sprintf(fstr, this.data[i][1]); | |||||
} | |||||
else if (this.dataLabels == 'percent') { | |||||
fstr = this.dataLabelFormatString || '%d%%'; | |||||
label = $.jqplot.sprintf(fstr, gd[i][2]*100); | |||||
} | |||||
else if (this.dataLabels.constructor == Array) { | |||||
fstr = this.dataLabelFormatString || '%s'; | |||||
label = $.jqplot.sprintf(fstr, this.dataLabels[i]); | |||||
} | |||||
var fact = (this._radius ) * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge; | |||||
var x = this._center[0] + Math.cos(avgang) * fact + this.canvas._offsets.left; | |||||
var y = this._center[1] + Math.sin(avgang) * fact + this.canvas._offsets.top; | |||||
var labelelem = $('<div class="jqplot-pie-series jqplot-data-label" style="position:absolute;">' + label + '</div>').insertBefore(plot.eventCanvas._elem); | |||||
if (this.dataLabelCenterOn) { | |||||
x -= labelelem.width()/2; | |||||
y -= labelelem.height()/2; | |||||
} | |||||
else { | |||||
x -= labelelem.width() * Math.sin(avgang/2); | |||||
y -= labelelem.height()/2; | |||||
} | |||||
x = Math.round(x); | |||||
y = Math.round(y); | |||||
labelelem.css({left: x, top: y}); | |||||
} | |||||
} | |||||
}; | }; | ||||
$.jqplot.PieAxisRenderer = function() { | $.jqplot.PieAxisRenderer = function() { | ||||
@@ -243,44 +529,201 @@ | |||||
this.show = false; | this.show = false; | ||||
}; | }; | ||||
$.jqplot.PieLegendRenderer = function() { | |||||
$.jqplot.PieLegendRenderer = function(){ | |||||
$.jqplot.TableLegendRenderer.call(this); | $.jqplot.TableLegendRenderer.call(this); | ||||
}; | }; | ||||
$.jqplot.PieLegendRenderer.prototype = new $.jqplot.TableLegendRenderer(); | $.jqplot.PieLegendRenderer.prototype = new $.jqplot.TableLegendRenderer(); | ||||
$.jqplot.PieLegendRenderer.prototype.constructor = $.jqplot.PieLegendRenderer; | $.jqplot.PieLegendRenderer.prototype.constructor = $.jqplot.PieLegendRenderer; | ||||
/** | |||||
* Class: $.jqplot.PieLegendRenderer | |||||
* Legend Renderer specific to pie plots. Set by default | |||||
* when user creates a pie plot. | |||||
*/ | |||||
$.jqplot.PieLegendRenderer.prototype.init = function(options) { | |||||
// Group: Properties | |||||
// | |||||
// 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; | |||||
$.extend(true, this, options); | |||||
}; | |||||
// called with context of legend | // called with context of legend | ||||
$.jqplot.PieLegendRenderer.prototype.draw = function() { | $.jqplot.PieLegendRenderer.prototype.draw = function() { | ||||
var legend = this; | var legend = this; | ||||
if (this.show) { | if (this.show) { | ||||
var series = this._series; | var series = this._series; | ||||
// make a table. one line label per row. | |||||
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>'); | |||||
var pad = false; | |||||
this._elem = $(document.createElement('table')); | |||||
this._elem.addClass('jqplot-table-legend'); | |||||
var ss = {position:'absolute'}; | |||||
if (this.background) { | |||||
ss['background'] = this.background; | |||||
} | |||||
if (this.border) { | |||||
ss['border'] = this.border; | |||||
} | |||||
if (this.fontSize) { | |||||
ss['fontSize'] = this.fontSize; | |||||
} | |||||
if (this.fontFamily) { | |||||
ss['fontFamily'] = this.fontFamily; | |||||
} | |||||
if (this.textColor) { | |||||
ss['textColor'] = this.textColor; | |||||
} | |||||
if (this.marginTop != null) { | |||||
ss['marginTop'] = this.marginTop; | |||||
} | |||||
if (this.marginBottom != null) { | |||||
ss['marginBottom'] = this.marginBottom; | |||||
} | |||||
if (this.marginLeft != null) { | |||||
ss['marginLeft'] = this.marginLeft; | |||||
} | |||||
if (this.marginRight != null) { | |||||
ss['marginRight'] = this.marginRight; | |||||
} | |||||
this._elem.css(ss); | |||||
// Pie 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 = false, | |||||
nr, | |||||
nc; | |||||
var s = series[0]; | var s = series[0]; | ||||
var colorGenerator = new s.colorGenerator(s.seriesColors); | |||||
var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors); | |||||
if (s.show) { | if (s.show) { | ||||
var pd = s.data; | var pd = s.data; | ||||
for (var i=0; i<pd.length; i++){ | |||||
var lt = pd[i][0].toString(); | |||||
if (lt) { | |||||
this.renderer.addrow.call(this, lt, colorGenerator.next(), pad); | |||||
pad = true; | |||||
} | |||||
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; | |||||
var tr, td1, td2; | |||||
var lt, rs, color; | |||||
var idx = 0; | |||||
var div0, div1; | |||||
for (i=0; i<nr; i++) { | |||||
tr = $(document.createElement('tr')); | |||||
tr.addClass('jqplot-table-legend'); | |||||
if (reverse){ | |||||
tr.prependTo(this._elem); | |||||
} | |||||
else{ | |||||
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 = $(document.createElement('td')); | |||||
td1.addClass('jqplot-table-legend'); | |||||
td1.css({textAlign: 'center', paddingTop: rs}); | |||||
div0 = $(document.createElement('div')); | |||||
div1 = $(document.createElement('div')); | |||||
div1.addClass('jqplot-table-legend-swatch'); | |||||
div1.css({backgroundColor: color, borderColor: color}); | |||||
td1.append(div0.append(div1)); | |||||
td2 = $(document.createElement('td')); | |||||
td2.addClass('jqplot-table-legend'); | |||||
td2.css('paddingTop', rs); | |||||
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++; | |||||
} | |||||
} | } | ||||
} | } | ||||
} | |||||
return this._elem; | |||||
} | |||||
return this._elem; | |||||
}; | }; | ||||
$.jqplot.PieRenderer.prototype.handleMove = function(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; | |||||
plot.target.trigger('jqplotDataMouseOver', ins); | |||||
if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { | |||||
plot.target.trigger('jqplotDataHighlight', ins); | |||||
highlight (plot, ins[0], ins[1]); | |||||
} | |||||
} | |||||
else if (neighbor == null) { | |||||
unhighlight (plot); | |||||
} | |||||
}; | |||||
// this.eventCanvas._elem.bind($.jqplot.eventListenerHooks[i][0], {plot:this}, $.jqplot.eventListenerHooks[i][1]); | |||||
// setup default renderers for axes and legend so user doesn't have to | // setup default renderers for axes and legend so user doesn't have to | ||||
// called with scope of plot | // called with scope of plot | ||||
function preInit(target, data, options) { | function preInit(target, data, options) { | ||||
@@ -305,11 +748,22 @@ | |||||
options.axesDefaults.renderer = $.jqplot.PieAxisRenderer; | options.axesDefaults.renderer = $.jqplot.PieAxisRenderer; | ||||
options.legend.renderer = $.jqplot.PieLegendRenderer; | options.legend.renderer = $.jqplot.PieLegendRenderer; | ||||
options.legend.preDraw = true; | options.legend.preDraw = true; | ||||
// options.seriesDefaults.colorGenerator = this.colorGenerator; | |||||
// options.seriesDefaults.seriesColors = this.seriesColors; | |||||
options.seriesDefaults.pointLabels = {show: false}; | |||||
} | } | ||||
} | } | ||||
function postInit(target, data, options) { | |||||
for (var i=0; i<this.series.length; i++) { | |||||
if (this.series[i].renderer.constructor == $.jqplot.PieRenderer) { | |||||
// don't allow mouseover and mousedown at same time. | |||||
if (this.series[i].highlightMouseOver) { | |||||
this.series[i].highlightMouseDown = false; | |||||
} | |||||
} | |||||
} | |||||
this.target.bind('mouseout', {plot:this}, function (ev) { unhighlight(ev.data.plot); }); | |||||
} | |||||
// called with scope of plot | // called with scope of plot | ||||
function postParseOptions(options) { | function postParseOptions(options) { | ||||
for (var i=0; i<this.series.length; i++) { | for (var i=0; i<this.series.length; i++) { | ||||
@@ -318,8 +772,119 @@ | |||||
} | } | ||||
} | } | ||||
function highlight (plot, sidx, pidx) { | |||||
var s = plot.series[sidx]; | |||||
var canvas = plot.plugins.pieRenderer.highlightCanvas; | |||||
canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height); | |||||
s._highlightedPoint = pidx; | |||||
plot.plugins.pieRenderer.highlightedSeriesIndex = sidx; | |||||
s.renderer.drawSlice.call(s, canvas._ctx, s._sliceAngles[pidx][0], s._sliceAngles[pidx][1], s.highlightColorGenerator.get(pidx), false); | |||||
} | |||||
function unhighlight (plot) { | |||||
var canvas = plot.plugins.pieRenderer.highlightCanvas; | |||||
canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); | |||||
for (var i=0; i<plot.series.length; i++) { | |||||
plot.series[i]._highlightedPoint = null; | |||||
} | |||||
plot.plugins.pieRenderer.highlightedSeriesIndex = null; | |||||
plot.target.trigger('jqplotDataUnhighlight'); | |||||
} | |||||
function handleMove(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; | |||||
var evt1 = jQuery.Event('jqplotDataMouseOver'); | |||||
evt1.pageX = ev.pageX; | |||||
evt1.pageY = ev.pageY; | |||||
plot.target.trigger(evt1, ins); | |||||
if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { | |||||
var evt = jQuery.Event('jqplotDataHighlight'); | |||||
evt.pageX = ev.pageX; | |||||
evt.pageY = ev.pageY; | |||||
plot.target.trigger(evt, ins); | |||||
highlight (plot, ins[0], ins[1]); | |||||
} | |||||
} | |||||
else if (neighbor == null) { | |||||
unhighlight (plot); | |||||
} | |||||
} | |||||
function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; | |||||
if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { | |||||
var evt = jQuery.Event('jqplotDataHighlight'); | |||||
evt.pageX = ev.pageX; | |||||
evt.pageY = ev.pageY; | |||||
plot.target.trigger(evt, ins); | |||||
highlight (plot, ins[0], ins[1]); | |||||
} | |||||
} | |||||
else if (neighbor == null) { | |||||
unhighlight (plot); | |||||
} | |||||
} | |||||
function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { | |||||
var idx = plot.plugins.pieRenderer.highlightedSeriesIndex; | |||||
if (idx != null && plot.series[idx].highlightMouseDown) { | |||||
unhighlight(plot); | |||||
} | |||||
} | |||||
function handleClick(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; | |||||
var evt = jQuery.Event('jqplotDataClick'); | |||||
evt.pageX = ev.pageX; | |||||
evt.pageY = ev.pageY; | |||||
plot.target.trigger(evt, ins); | |||||
} | |||||
} | |||||
function handleRightClick(ev, gridpos, datapos, neighbor, plot) { | |||||
if (neighbor) { | |||||
var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; | |||||
var idx = plot.plugins.pieRenderer.highlightedSeriesIndex; | |||||
if (idx != null && plot.series[idx].highlightMouseDown) { | |||||
unhighlight(plot); | |||||
} | |||||
var evt = jQuery.Event('jqplotDataRightClick'); | |||||
evt.pageX = ev.pageX; | |||||
evt.pageY = ev.pageY; | |||||
plot.target.trigger(evt, ins); | |||||
} | |||||
} | |||||
// called within context of plot | |||||
// create a canvas which we can draw on. | |||||
// insert it before the eventCanvas, so eventCanvas will still capture events. | |||||
function postPlotDraw() { | |||||
// Memory Leaks patch | |||||
if (this.plugins.pieRenderer && this.plugins.pieRenderer.highlightCanvas) { | |||||
this.plugins.pieRenderer.highlightCanvas.resetCanvas(); | |||||
this.plugins.pieRenderer.highlightCanvas = null; | |||||
} | |||||
this.plugins.pieRenderer = {highlightedSeriesIndex:null}; | |||||
this.plugins.pieRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); | |||||
// do we have any data labels? if so, put highlight canvas before those | |||||
var labels = $(this.targetId+' .jqplot-data-label'); | |||||
if (labels.length) { | |||||
$(labels[0]).before(this.plugins.pieRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pieRenderer-highlight-canvas', this._plotDimensions, this)); | |||||
} | |||||
// else put highlight canvas before event canvas. | |||||
else { | |||||
this.eventCanvas._elem.before(this.plugins.pieRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pieRenderer-highlight-canvas', this._plotDimensions, this)); | |||||
} | |||||
var hctx = this.plugins.pieRenderer.highlightCanvas.setContext(); | |||||
} | |||||
$.jqplot.preInitHooks.push(preInit); | $.jqplot.preInitHooks.push(preInit); | ||||
$.jqplot.postParseOptionsHooks.push(postParseOptions); | |||||
$.jqplot.PieTickRenderer = function() { | $.jqplot.PieTickRenderer = function() { | ||||
$.jqplot.AxisTickRenderer.call(this); | $.jqplot.AxisTickRenderer.call(this); | ||||
@@ -1,18 +1,30 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* The author would appreciate an email letting him know of any substantial | |||||
* use of jqPlot. You can reach the author at: chris dot leonello at gmail | |||||
* dot com or see http://www.jqplot.com/info.php . This is, of course, | |||||
* not required. | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * making a donation at: http://www.jqplot.com/donate.php . | ||||
* | * | ||||
* Thanks for using jqPlot! | |||||
* 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($) { | (function($) { | ||||
@@ -77,11 +89,14 @@ | |||||
this.labelsFromSeries = false; | this.labelsFromSeries = false; | ||||
// prop: seriesLabelIndex | // prop: seriesLabelIndex | ||||
// array index for location of labels within data point arrays. | // array index for location of labels within data point arrays. | ||||
// if null, will use the last element of teh data point array. | |||||
// if null, will use the last element of the data point array. | |||||
this.seriesLabelIndex = null; | this.seriesLabelIndex = null; | ||||
// prop: labels | // prop: labels | ||||
// array of arrays of labels, one array for each series. | // array of arrays of labels, one array for each series. | ||||
this.labels = []; | this.labels = []; | ||||
// actual labels that will get displayed. | |||||
// needed to preserve user specified labels in labels array. | |||||
this._labels = []; | |||||
// prop: stackedValue | // prop: stackedValue | ||||
// true to display value as stacked in a stacked plot. | // true to display value as stacked in a stacked plot. | ||||
// no effect if labels is specified. | // no effect if labels is specified. | ||||
@@ -100,10 +115,17 @@ | |||||
// Number of pixels that the label must be away from an axis | // Number of pixels that the label must be away from an axis | ||||
// boundary in order to be drawn. Negative values will allow overlap | // boundary in order to be drawn. Negative values will allow overlap | ||||
// with the grid boundaries. | // with the grid boundaries. | ||||
this.edgeTolerance = 0; | |||||
this.edgeTolerance = -5; | |||||
// prop: formatter | |||||
// A class of a formatter for the tick text. sprintf by default. | |||||
this.formatter = $.jqplot.DefaultTickFormatter; | |||||
// prop: formatString | |||||
// string passed to the formatter. | |||||
this.formatString = ''; | |||||
// prop: hideZeros | // prop: hideZeros | ||||
// true to not show a label for a value which is 0. | // true to not show a label for a value which is 0. | ||||
this.hideZeros = false; | this.hideZeros = false; | ||||
this._elems = []; | |||||
$.extend(true, this, options); | $.extend(true, this, options); | ||||
}; | }; | ||||
@@ -115,31 +137,55 @@ | |||||
// called with scope of a series | // called with scope of a series | ||||
$.jqplot.PointLabels.init = function (target, data, seriesDefaults, opts){ | $.jqplot.PointLabels.init = function (target, data, seriesDefaults, opts){ | ||||
var options = $.extend(true, {}, seriesDefaults, opts); | var options = $.extend(true, {}, seriesDefaults, opts); | ||||
options.pointLabels = options.pointLabels || {}; | |||||
if (this.renderer.constructor == $.jqplot.BarRenderer && this.barDirection == 'horizontal' && !options.pointLabels.location) { | |||||
options.pointLabels.location = 'e'; | |||||
} | |||||
// add a pointLabels attribute to the series plugins | // add a pointLabels attribute to the series plugins | ||||
this.plugins.pointLabels = new $.jqplot.PointLabels(options.pointLabels); | this.plugins.pointLabels = new $.jqplot.PointLabels(options.pointLabels); | ||||
var p = this.plugins.pointLabels; | |||||
if (p.labels.length == 0 || p.labelsFromSeries) { | |||||
this.plugins.pointLabels.setLabels.call(this); | |||||
}; | |||||
// called with scope of series | |||||
$.jqplot.PointLabels.prototype.setLabels = function() { | |||||
var p = this.plugins.pointLabels; | |||||
var labelIdx; | |||||
if (p.seriesLabelIndex != null) { | |||||
labelIdx = p.seriesLabelIndex; | |||||
} | |||||
else if (this.renderer.constructor == $.jqplot.BarRenderer && this.barDirection == 'horizontal') { | |||||
labelIdx = 0; | |||||
} | |||||
else { | |||||
labelIdx = this._plotData[0].length -1; | |||||
} | |||||
p._labels = []; | |||||
if (p.labels.length == 0 || p.labelsFromSeries) { | |||||
if (p.stackedValue) { | if (p.stackedValue) { | ||||
if (this._plotData.length && this._plotData[0].length){ | if (this._plotData.length && this._plotData[0].length){ | ||||
var idx = p.seriesLabelIndex || this._plotData[0].length -1; | |||||
// var idx = p.seriesLabelIndex || this._plotData[0].length -1; | |||||
for (var i=0; i<this._plotData.length; i++) { | for (var i=0; i<this._plotData.length; i++) { | ||||
p.labels.push(this._plotData[i][idx]); | |||||
p._labels.push(this._plotData[i][labelIdx]); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
else { | else { | ||||
var d = this.data; | |||||
var d = this._plotData; | |||||
if (this.renderer.constructor == $.jqplot.BarRenderer && this.waterfall) { | if (this.renderer.constructor == $.jqplot.BarRenderer && this.waterfall) { | ||||
d = this._data; | d = this._data; | ||||
} | } | ||||
if (d.length && d[0].length) { | if (d.length && d[0].length) { | ||||
var idx = p.seriesLabelIndex || d[0].length -1; | |||||
// var idx = p.seriesLabelIndex || d[0].length -1; | |||||
for (var i=0; i<d.length; i++) { | for (var i=0; i<d.length; i++) { | ||||
p.labels.push(d[i][idx]); | |||||
p._labels.push(d[i][labelIdx]); | |||||
} | } | ||||
} | } | ||||
d = null; | |||||
} | } | ||||
} | } | ||||
else if (p.labels.length){ | |||||
p._labels = p.labels; | |||||
} | |||||
}; | }; | ||||
$.jqplot.PointLabels.prototype.xOffset = function(elem, location, padding) { | $.jqplot.PointLabels.prototype.xOffset = function(elem, location, padding) { | ||||
@@ -219,60 +265,50 @@ | |||||
// called with scope of series | // called with scope of series | ||||
$.jqplot.PointLabels.draw = function (sctx, options) { | $.jqplot.PointLabels.draw = function (sctx, options) { | ||||
var p = this.plugins.pointLabels; | var p = this.plugins.pointLabels; | ||||
// set labels again in case they have changed. | |||||
p.setLabels.call(this); | |||||
// remove any previous labels | |||||
for (var i=0; i<p._elems.length; i++) { | |||||
// Memory Leaks patch | |||||
// p._elems[i].remove(); | |||||
p._elems[i].emptyForce(); | |||||
} | |||||
p._elems.splice(0, p._elems.length); | |||||
if (p.show) { | if (p.show) { | ||||
// var xoffset, yoffset; | |||||
// | |||||
// switch (p.location) { | |||||
// case 'nw': | |||||
// xoffset = function(elem) { return -elem.outerWidth(true) - p.xpadding; }; | |||||
// yoffset = function(elem) { return -elem.outerHeight(true) - p.ypadding; }; | |||||
// break; | |||||
// case 'n': | |||||
// xoffset = function(elem) { return -elem.outerWidth(true)/2; }; | |||||
// yoffset = function(elem) { return -elem.outerHeight(true) - p.ypadding; }; | |||||
// break; | |||||
// case 'ne': | |||||
// xoffset = function(elem) { return p.xpadding; }; | |||||
// yoffset = function(elem) { return -elem.outerHeight(true) - p.ypadding; }; | |||||
// break; | |||||
// case 'e': | |||||
// xoffset = function(elem) { return p.xpadding; }; | |||||
// yoffset = function(elem) { return -elem.outerHeight(true)/2; }; | |||||
// break; | |||||
// case 'se': | |||||
// xoffset = function(elem) { return p.xpadding; }; | |||||
// yoffset = function(elem) { return p.ypadding; }; | |||||
// break; | |||||
// case 's': | |||||
// xoffset = function(elem) { return -elem.outerWidth(true)/2; }; | |||||
// yoffset = function(elem) { return p.ypadding; }; | |||||
// break; | |||||
// case 'sw': | |||||
// xoffset = function(elem) { return -elem.outerWidth(true) - p.xpadding; }; | |||||
// yoffset = function(elem) { return p.ypadding; }; | |||||
// break; | |||||
// case 'w': | |||||
// xoffset = function(elem) { return -elem.outerWidth(true) - p.xpadding; }; | |||||
// yoffset = function(elem) { return -elem.outerHeight(true)/2; }; | |||||
// break; | |||||
// default: // same as 'nw' | |||||
// xoffset = function(elem) { return -elem.outerWidth(true) - p.xpadding; }; | |||||
// yoffset = function(elem) { return -elem.outerHeight(true) - p.ypadding; }; | |||||
// break; | |||||
// } | |||||
var ax = '_'+this._stackAxis+'axis'; | |||||
for (var i=0; i<p.labels.length; i++) { | |||||
var pd = this._plotData; | |||||
var xax = this._xaxis; | |||||
var yax = this._yaxis; | |||||
var label = p.labels[i]; | |||||
if (!p.formatString) { | |||||
p.formatString = this[ax]._ticks[0].formatString; | |||||
p.formatter = this[ax]._ticks[0].formatter; | |||||
} | |||||
var pd = this._plotData; | |||||
var xax = this._xaxis; | |||||
var yax = this._yaxis; | |||||
var elem, helem; | |||||
for (var i=0, l=p._labels.length; i < l; i++) { | |||||
var label = p._labels[i]; | |||||
if (p.hideZeros && parseInt(p.labels[i], 10) == 0) { | |||||
if (p.hideZeros && parseInt(p._labels[i], 10) == 0) { | |||||
label = ''; | label = ''; | ||||
} | } | ||||
var elem = $('<div class="jqplot-point-label" style="position:absolute"></div>'); | |||||
if (label != null) { | |||||
label = p.formatter(p.formatString, label); | |||||
} | |||||
helem = document.createElement('div'); | |||||
p._elems[i] = $(helem); | |||||
elem = p._elems[i]; | |||||
elem.addClass('jqplot-point-label jqplot-series-'+this.index+' jqplot-point-'+i); | |||||
elem.css('position', 'absolute'); | |||||
elem.insertAfter(sctx.canvas); | elem.insertAfter(sctx.canvas); | ||||
if (p.escapeHTML) { | if (p.escapeHTML) { | ||||
elem.text(label); | elem.text(label); | ||||
} | } | ||||
@@ -280,15 +316,23 @@ | |||||
elem.html(label); | elem.html(label); | ||||
} | } | ||||
var location = p.location; | var location = p.location; | ||||
if (this.waterfall && parseInt(label, 10) < 0) { | |||||
if ((this.fillToZero && pd[i][1] < 0) || (this.waterfall && parseInt(label, 10)) < 0) { | |||||
location = oppositeLocations[locationIndicies[location]]; | location = oppositeLocations[locationIndicies[location]]; | ||||
} | } | ||||
var ell = xax.u2p(pd[i][0]) + p.xOffset(elem, location); | var ell = xax.u2p(pd[i][0]) + p.xOffset(elem, location); | ||||
var elt = yax.u2p(pd[i][1]) + p.yOffset(elem, location); | var elt = yax.u2p(pd[i][1]) + p.yOffset(elem, location); | ||||
if (this.renderer.constructor == $.jqplot.BarRenderer) { | |||||
if (this.barDirection == "vertical") { | |||||
ell += this._barNudge; | |||||
} | |||||
else { | |||||
elt -= this._barNudge; | |||||
} | |||||
} | |||||
elem.css('left', ell); | elem.css('left', ell); | ||||
elem.css('top', elt); | elem.css('top', elt); | ||||
var elr = ell + $(elem).width(); | |||||
var elb = elt + $(elem).height(); | |||||
var elr = ell + elem.width(); | |||||
var elb = elt + elem.height(); | |||||
var et = p.edgeTolerance; | var et = p.edgeTolerance; | ||||
var scl = $(sctx.canvas).position().left; | var scl = $(sctx.canvas).position().left; | ||||
var sct = $(sctx.canvas).position().top; | var sct = $(sctx.canvas).position().top; | ||||
@@ -296,8 +340,10 @@ | |||||
var scb = sctx.canvas.height + sct; | var scb = sctx.canvas.height + sct; | ||||
// if label is outside of allowed area, remove it | // if label is outside of allowed area, remove it | ||||
if (ell - et < scl || elt - et < sct || elr + et > scr || elb + et > scb) { | if (ell - et < scl || elt - et < sct || elr + et > scr || elb + et > scb) { | ||||
$(elem).remove(); | |||||
elem.remove(); | |||||
} | } | ||||
elem = null; | |||||
helem = null; | |||||
} | } | ||||
} | } | ||||
}; | }; | ||||
@@ -1,273 +0,0 @@ | |||||
/** | |||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | |||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* choose the license that best suits your project and use it accordingly. | |||||
* | |||||
* The author would appreciate an email letting him know of any substantial | |||||
* use of jqPlot. You can reach the author at: chris dot leonello at gmail | |||||
* dot com or see http://www.jqplot.com/info.php . This is, of course, | |||||
* not required. | |||||
* | |||||
* If you are feeling kind and generous, consider supporting the project by | |||||
* making a donation at: http://www.jqplot.com/donate.php . | |||||
* | |||||
* Thanks for using jqPlot! | |||||
* | |||||
*/ | |||||
(function($) { | |||||
/** | |||||
* Class: $.jqplot.PointLabels | |||||
* Plugin for putting labels at the data points. | |||||
* | |||||
* To use this plugin, include the js | |||||
* file in your source: | |||||
* | |||||
* > <script type="text/javascript" src="plugins/jqplot.pointLabels.js"></script> | |||||
* | |||||
* By default, the last value in the data ponit array in the data series is used | |||||
* for the label. For most series renderers, extra data can be added to the | |||||
* data point arrays and the last value will be used as the label. | |||||
* | |||||
* For instance, | |||||
* this series: | |||||
* | |||||
* > [[1,4], [3,5], [7,2]] | |||||
* | |||||
* Would, by default, use the y values in the labels. | |||||
* Extra data can be added to the series like so: | |||||
* | |||||
* > [[1,4,'mid'], [3 5,'hi'], [7,2,'low']] | |||||
* | |||||
* And now the point labels would be 'mid', 'low', and 'hi'. | |||||
* | |||||
* Options to the point labels and a custom labels array can be passed into the | |||||
* "pointLabels" option on the series option like so: | |||||
* | |||||
* > series:[{pointLabels:{ | |||||
* > labels:['mid', 'hi', 'low'], | |||||
* > location:'se', | |||||
* > ypadding: 12 | |||||
* > } | |||||
* > }] | |||||
* | |||||
* A custom labels array in the options takes precendence over any labels | |||||
* in the series data. If you have a custom labels array in the options, | |||||
* but still want to use values from the series array as labels, set the | |||||
* "labelsFromSeries" option to true. | |||||
* | |||||
* By default, html entities (<, >, etc.) are escaped in point labels. | |||||
* If you want to include actual html markup in the labels, | |||||
* set the "escapeHTML" option to false. | |||||
* | |||||
*/ | |||||
$.jqplot.PointLabels = function(options) { | |||||
// Group: Properties | |||||
// | |||||
// prop: show | |||||
// show the labels or not. | |||||
this.show = $.jqplot.config.enablePlugins; | |||||
// prop: location | |||||
// compass location where to position the label around the point. | |||||
// 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw' | |||||
this.location = 'n'; | |||||
// prop: labelsFromSeries | |||||
// true to use labels within data point arrays. | |||||
this.labelsFromSeries = false; | |||||
// prop: seriesLabelIndex | |||||
// array index for location of labels within data point arrays. | |||||
// if null, will use the last element of teh data point array. | |||||
this.seriesLabelIndex = null; | |||||
// prop: labels | |||||
// array of arrays of labels, one array for each series. | |||||
this.labels = []; | |||||
// prop: stackedValue | |||||
// true to display value as stacked in a stacked plot. | |||||
// no effect if labels is specified. | |||||
this.stackedValue = false; | |||||
// prop: ypadding | |||||
// vertical padding in pixels between point and label | |||||
this.ypadding = 6; | |||||
// prop: xpadding | |||||
// horizontal padding in pixels between point and label | |||||
this.xpadding = 6; | |||||
// prop: escapeHTML | |||||
// true to escape html entities in the labels. | |||||
// If you want to include markup in the labels, set to false. | |||||
this.escapeHTML = true; | |||||
// prop: edgeTolerance | |||||
// Number of pixels that the label must be away from an axis | |||||
// boundary in order to be drawn. Negative values will allow overlap | |||||
// with the grid boundaries. | |||||
this.edgeTolerance = 0; | |||||
// prop: hideZeros | |||||
// true to not show a label for a value which is 0. | |||||
this.hideZeros = false; | |||||
// prop: formatString | |||||
// format string to format label value | |||||
this.formatString = ''; | |||||
// prop: formatter | |||||
// formatter function to use with format string. | |||||
this.formatter = $.jqplot.sprintf; | |||||
$.extend(true, this, options); | |||||
}; | |||||
var locations = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w']; | |||||
var locationIndicies = {'nw':0, 'n':1, 'ne':2, 'e':3, 'se':4, 's':5, 'sw':6, 'w':7}; | |||||
var oppositeLocations = ['se', 's', 'sw', 'w', 'nw', 'n', 'ne', 'e']; | |||||
// called with scope of a series | |||||
$.jqplot.PointLabels.init = function (target, data, seriesDefaults, opts){ | |||||
var options = $.extend(true, {}, seriesDefaults, opts); | |||||
// add a pointLabels attribute to the series plugins | |||||
this.plugins.pointLabels = new $.jqplot.PointLabels(options.pointLabels); | |||||
var p = this.plugins.pointLabels; | |||||
if (p.labels.length == 0 || p.labelsFromSeries) { | |||||
if (p.stackedValue) { | |||||
if (this._plotData.length && this._plotData[0].length){ | |||||
var idx = p.seriesLabelIndex || this._plotData[0].length -1; | |||||
for (var i=0; i<this._plotData.length; i++) { | |||||
p.labels.push(this._plotData[i][idx]); | |||||
} | |||||
} | |||||
} | |||||
else { | |||||
var d = this.data; | |||||
if (this.renderer.constructor == $.jqplot.BarRenderer && this.waterfall) { | |||||
d = this._data; | |||||
} | |||||
if (d.length && d[0].length) { | |||||
var idx = p.seriesLabelIndex || d[0].length -1; | |||||
for (var i=0; i<d.length; i++) { | |||||
p.labels.push(d[i][idx]); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
}; | |||||
$.jqplot.PointLabels.prototype.xOffset = function(elem, location, padding) { | |||||
location = location || this.location; | |||||
padding = padding || this.xpadding; | |||||
var offset; | |||||
switch (location) { | |||||
case 'nw': | |||||
offset = -elem.outerWidth(true) - this.xpadding; | |||||
break; | |||||
case 'n': | |||||
offset = -elem.outerWidth(true)/2; | |||||
break; | |||||
case 'ne': | |||||
offset = this.xpadding; | |||||
break; | |||||
case 'e': | |||||
offset = this.xpadding; | |||||
break; | |||||
case 'se': | |||||
offset = this.xpadding; | |||||
break; | |||||
case 's': | |||||
offset = -elem.outerWidth(true)/2; | |||||
break; | |||||
case 'sw': | |||||
offset = -elem.outerWidth(true) - this.xpadding; | |||||
break; | |||||
case 'w': | |||||
offset = -elem.outerWidth(true) - this.xpadding; | |||||
break; | |||||
default: // same as 'nw' | |||||
offset = -elem.outerWidth(true) - this.xpadding; | |||||
break; | |||||
} | |||||
return offset; | |||||
}; | |||||
$.jqplot.PointLabels.prototype.yOffset = function(elem, location, padding) { | |||||
location = location || this.location; | |||||
padding = padding || this.xpadding; | |||||
var offset; | |||||
switch (location) { | |||||
case 'nw': | |||||
offset = -elem.outerHeight(true) - this.ypadding; | |||||
break; | |||||
case 'n': | |||||
offset = -elem.outerHeight(true) - this.ypadding; | |||||
break; | |||||
case 'ne': | |||||
offset = -elem.outerHeight(true) - this.ypadding; | |||||
break; | |||||
case 'e': | |||||
offset = -elem.outerHeight(true)/2; | |||||
break; | |||||
case 'se': | |||||
offset = this.ypadding; | |||||
break; | |||||
case 's': | |||||
offset = this.ypadding; | |||||
break; | |||||
case 'sw': | |||||
offset = this.ypadding; | |||||
break; | |||||
case 'w': | |||||
offset = -elem.outerHeight(true)/2; | |||||
break; | |||||
default: // same as 'nw' | |||||
offset = -elem.outerHeight(true) - this.ypadding; | |||||
break; | |||||
} | |||||
return offset; | |||||
}; | |||||
// called with scope of series | |||||
$.jqplot.PointLabels.draw = function (sctx, options) { | |||||
var p = this.plugins.pointLabels; | |||||
if (p.show) { | |||||
for (var i=0; i<p.labels.length; i++) { | |||||
var pd = this._plotData; | |||||
var xax = this._xaxis; | |||||
var yax = this._yaxis; | |||||
var label = p.labels[i]; | |||||
if (p.hideZeros && parseInt(p.labels[i], 10) == 0) { | |||||
label = ''; | |||||
} | |||||
var elem = $('<div class="jqplot-point-label" style="position:absolute"></div>'); | |||||
elem.insertAfter(sctx.canvas); | |||||
if (p.escapeHTML) { | |||||
elem.text(label); | |||||
} | |||||
else { | |||||
elem.html(label); | |||||
} | |||||
var location = p.location; | |||||
if (this.waterfall && parseInt(label, 10) < 0) { | |||||
location = oppositeLocations[locationIndicies[location]]; | |||||
} | |||||
var ell = xax.u2p(pd[i][0]) + p.xOffset(elem, location); | |||||
var elt = yax.u2p(pd[i][1]) + p.yOffset(elem, location); | |||||
elem.css('left', ell); | |||||
elem.css('top', elt); | |||||
var elr = ell + $(elem).width(); | |||||
var elb = elt + $(elem).height(); | |||||
var et = p.edgeTolerance; | |||||
var scl = $(sctx.canvas).position().left; | |||||
var sct = $(sctx.canvas).position().top; | |||||
var scr = sctx.canvas.width + scl; | |||||
var scb = sctx.canvas.height + sct; | |||||
// if label is outside of allowed area, remove it | |||||
if (ell - et < scl || elt - et < sct || elr + et > scr || elb + et > scb) { | |||||
$(elem).remove(); | |||||
} | |||||
} | |||||
} | |||||
}; | |||||
$.jqplot.postSeriesInitHooks.push($.jqplot.PointLabels.init); | |||||
$.jqplot.postDrawSeriesHooks.push($.jqplot.PointLabels.draw); | |||||
})(jQuery); |
@@ -1,14 +1,30 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* Although not required, the author would appreciate an email letting him | * Although not required, the author would appreciate an email letting him | ||||
* know of any substantial use of jqPlot. You can reach the author at: | * know of any substantial use of jqPlot. You can reach the author at: | ||||
* chris dot leonello at gmail dot com or see http://www.jqplot.com/info.php . | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * 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(c){c.jqplot.PointLabels=function(e){this.show=c.jqplot.config.enablePlugins;this.location="n";this.labelsFromSeries=false;this.seriesLabelIndex=null;this.labels=[];this.stackedValue=false;this.ypadding=6;this.xpadding=6;this.escapeHTML=true;this.edgeTolerance=0;this.hideZeros=false;c.extend(true,this,e)};var a=["nw","n","ne","e","se","s","sw","w"];var d={nw:0,n:1,ne:2,e:3,se:4,s:5,sw:6,w:7};var b=["se","s","sw","w","nw","n","ne","e"];c.jqplot.PointLabels.init=function(k,h,g,e){var n=c.extend(true,{},g,e);this.plugins.pointLabels=new c.jqplot.PointLabels(n.pointLabels);var f=this.plugins.pointLabels;if(f.labels.length==0||f.labelsFromSeries){if(f.stackedValue){if(this._plotData.length&&this._plotData[0].length){var m=f.seriesLabelIndex||this._plotData[0].length-1;for(var j=0;j<this._plotData.length;j++){f.labels.push(this._plotData[j][m])}}}else{var l=this.data;if(this.renderer.constructor==c.jqplot.BarRenderer&&this.waterfall){l=this._data}if(l.length&&l[0].length){var m=f.seriesLabelIndex||l[0].length-1;for(var j=0;j<l.length;j++){f.labels.push(l[j][m])}}}}};c.jqplot.PointLabels.prototype.xOffset=function(f,e,g){e=e||this.location;g=g||this.xpadding;var h;switch(e){case"nw":h=-f.outerWidth(true)-this.xpadding;break;case"n":h=-f.outerWidth(true)/2;break;case"ne":h=this.xpadding;break;case"e":h=this.xpadding;break;case"se":h=this.xpadding;break;case"s":h=-f.outerWidth(true)/2;break;case"sw":h=-f.outerWidth(true)-this.xpadding;break;case"w":h=-f.outerWidth(true)-this.xpadding;break;default:h=-f.outerWidth(true)-this.xpadding;break}return h};c.jqplot.PointLabels.prototype.yOffset=function(f,e,g){e=e||this.location;g=g||this.xpadding;var h;switch(e){case"nw":h=-f.outerHeight(true)-this.ypadding;break;case"n":h=-f.outerHeight(true)-this.ypadding;break;case"ne":h=-f.outerHeight(true)-this.ypadding;break;case"e":h=-f.outerHeight(true)/2;break;case"se":h=this.ypadding;break;case"s":h=this.ypadding;break;case"sw":h=this.ypadding;break;case"w":h=-f.outerHeight(true)/2;break;default:h=-f.outerHeight(true)-this.ypadding;break}return h};c.jqplot.PointLabels.draw=function(s,h){var q=this.plugins.pointLabels;if(q.show){for(var r=0;r<q.labels.length;r++){var y=this._plotData;var v=this._xaxis;var n=this._yaxis;var m=q.labels[r];if(q.hideZeros&&parseInt(q.labels[r],10)==0){m=""}var u=c('<div class="jqplot-point-label" style="position:absolute"></div>');u.insertAfter(s.canvas);if(q.escapeHTML){u.text(m)}else{u.html(m)}var f=q.location;if(this.waterfall&&parseInt(m,10)<0){f=b[d[f]]}var l=v.u2p(y[r][0])+q.xOffset(u,f);var g=n.u2p(y[r][1])+q.yOffset(u,f);u.css("left",l);u.css("top",g);var j=l+c(u).width();var o=g+c(u).height();var x=q.edgeTolerance;var e=c(s.canvas).position().left;var t=c(s.canvas).position().top;var w=s.canvas.width+e;var k=s.canvas.height+t;if(l-x<e||g-x<t||j+x>w||o+x>k){c(u).remove()}}}};c.jqplot.postSeriesInitHooks.push(c.jqplot.PointLabels.init);c.jqplot.postDrawSeriesHooks.push(c.jqplot.PointLabels.draw)})(jQuery); | |||||
(function(c){c.jqplot.PointLabels=function(e){this.show=c.jqplot.config.enablePlugins;this.location="n";this.labelsFromSeries=false;this.seriesLabelIndex=null;this.labels=[];this._labels=[];this.stackedValue=false;this.ypadding=6;this.xpadding=6;this.escapeHTML=true;this.edgeTolerance=-5;this.formatter=c.jqplot.DefaultTickFormatter;this.formatString="";this.hideZeros=false;this._elems=[];c.extend(true,this,e)};var a=["nw","n","ne","e","se","s","sw","w"];var d={nw:0,n:1,ne:2,e:3,se:4,s:5,sw:6,w:7};var b=["se","s","sw","w","nw","n","ne","e"];c.jqplot.PointLabels.init=function(i,h,f,g){var e=c.extend(true,{},f,g);e.pointLabels=e.pointLabels||{};if(this.renderer.constructor==c.jqplot.BarRenderer&&this.barDirection=="horizontal"&&!e.pointLabels.location){e.pointLabels.location="e"}this.plugins.pointLabels=new c.jqplot.PointLabels(e.pointLabels);this.plugins.pointLabels.setLabels.call(this)};c.jqplot.PointLabels.prototype.setLabels=function(){var f=this.plugins.pointLabels;var h;if(f.seriesLabelIndex!=null){h=f.seriesLabelIndex}else{if(this.renderer.constructor==c.jqplot.BarRenderer&&this.barDirection=="horizontal"){h=0}else{h=this._plotData[0].length-1}}f._labels=[];if(f.labels.length==0||f.labelsFromSeries){if(f.stackedValue){if(this._plotData.length&&this._plotData[0].length){for(var e=0;e<this._plotData.length;e++){f._labels.push(this._plotData[e][h])}}}else{var g=this._plotData;if(this.renderer.constructor==c.jqplot.BarRenderer&&this.waterfall){g=this._data}if(g.length&&g[0].length){for(var e=0;e<g.length;e++){f._labels.push(g[e][h])}}g=null}}else{if(f.labels.length){f._labels=f.labels}}};c.jqplot.PointLabels.prototype.xOffset=function(f,e,g){e=e||this.location;g=g||this.xpadding;var h;switch(e){case"nw":h=-f.outerWidth(true)-this.xpadding;break;case"n":h=-f.outerWidth(true)/2;break;case"ne":h=this.xpadding;break;case"e":h=this.xpadding;break;case"se":h=this.xpadding;break;case"s":h=-f.outerWidth(true)/2;break;case"sw":h=-f.outerWidth(true)-this.xpadding;break;case"w":h=-f.outerWidth(true)-this.xpadding;break;default:h=-f.outerWidth(true)-this.xpadding;break}return h};c.jqplot.PointLabels.prototype.yOffset=function(f,e,g){e=e||this.location;g=g||this.xpadding;var h;switch(e){case"nw":h=-f.outerHeight(true)-this.ypadding;break;case"n":h=-f.outerHeight(true)-this.ypadding;break;case"ne":h=-f.outerHeight(true)-this.ypadding;break;case"e":h=-f.outerHeight(true)/2;break;case"se":h=this.ypadding;break;case"s":h=this.ypadding;break;case"sw":h=this.ypadding;break;case"w":h=-f.outerHeight(true)/2;break;default:h=-f.outerHeight(true)-this.ypadding;break}return h};c.jqplot.PointLabels.draw=function(w,j){var t=this.plugins.pointLabels;t.setLabels.call(this);for(var v=0;v<t._elems.length;v++){t._elems[v].emptyForce()}t._elems.splice(0,t._elems.length);if(t.show){var r="_"+this._stackAxis+"axis";if(!t.formatString){t.formatString=this[r]._ticks[0].formatString;t.formatter=this[r]._ticks[0].formatter}var C=this._plotData;var z=this._xaxis;var q=this._yaxis;var y,f;for(var v=0,u=t._labels.length;v<u;v++){var o=t._labels[v];if(t.hideZeros&&parseInt(t._labels[v],10)==0){o=""}if(o!=null){o=t.formatter(t.formatString,o)}f=document.createElement("div");t._elems[v]=c(f);y=t._elems[v];y.addClass("jqplot-point-label jqplot-series-"+this.index+" jqplot-point-"+v);y.css("position","absolute");y.insertAfter(w.canvas);if(t.escapeHTML){y.text(o)}else{y.html(o)}var g=t.location;if((this.fillToZero&&C[v][1]<0)||(this.waterfall&&parseInt(o,10))<0){g=b[d[g]]}var n=z.u2p(C[v][0])+t.xOffset(y,g);var h=q.u2p(C[v][1])+t.yOffset(y,g);if(this.renderer.constructor==c.jqplot.BarRenderer){if(this.barDirection=="vertical"){n+=this._barNudge}else{h-=this._barNudge}}y.css("left",n);y.css("top",h);var k=n+y.width();var s=h+y.height();var B=t.edgeTolerance;var e=c(w.canvas).position().left;var x=c(w.canvas).position().top;var A=w.canvas.width+e;var m=w.canvas.height+x;if(n-B<e||h-B<x||k+B>A||s+B>m){y.remove()}y=null;f=null}}};c.jqplot.postSeriesInitHooks.push(c.jqplot.PointLabels.init);c.jqplot.postDrawSeriesHooks.push(c.jqplot.PointLabels.draw)})(jQuery); |
@@ -1,18 +1,30 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* The author would appreciate an email letting him know of any substantial | |||||
* use of jqPlot. You can reach the author at: chris dot leonello at gmail | |||||
* dot com or see http://www.jqplot.com/info.php . This is, of course, | |||||
* not required. | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * making a donation at: http://www.jqplot.com/donate.php . | ||||
* | * | ||||
* Thanks for using jqPlot! | |||||
* 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($) { | (function($) { | ||||
@@ -68,10 +80,11 @@ | |||||
// prop: shadowDepth | // prop: shadowDepth | ||||
// number of strokes to make of the shadow. | // number of strokes to make of the shadow. | ||||
this.shadowDepth = 3; | this.shadowDepth = 3; | ||||
this.isTrendline = true; | |||||
}; | }; | ||||
$.jqplot.postParseSeriesOptionsHooks.push(parseTrendLineOptions); | |||||
$.jqplot.postSeriesInitHooks.push(parseTrendLineOptions); | |||||
$.jqplot.postDrawSeriesHooks.push(drawTrendline); | $.jqplot.postDrawSeriesHooks.push(drawTrendline); | ||||
$.jqplot.addLegendRowHooks.push(addTrendlineLegend); | $.jqplot.addLegendRowHooks.push(addTrendlineLegend); | ||||
@@ -88,8 +101,8 @@ | |||||
} | } | ||||
// called within scope of a series | // called within scope of a series | ||||
function parseTrendLineOptions (seriesDefaults, options) { | |||||
if (this.renderer.constructor != $.jqplot.PieRenderer) { | |||||
function parseTrendLineOptions (target, data, seriesDefaults, options, plot) { | |||||
if (this.renderer.constructor == $.jqplot.LineRenderer) { | |||||
this.trendline = new $.jqplot.Trendline(); | this.trendline = new $.jqplot.Trendline(); | ||||
options = options || {}; | options = options || {}; | ||||
$.extend(true, this.trendline, {color:this.color}, seriesDefaults.trendline, options.trendline); | $.extend(true, this.trendline, {color:this.color}, seriesDefaults.trendline, options.trendline); | ||||
@@ -108,7 +121,6 @@ | |||||
var data = options.data || this.data; | var data = options.data || this.data; | ||||
fit = fitData(data, this.trendline.type); | fit = fitData(data, this.trendline.type); | ||||
var gridData = options.gridData || this.renderer.makeGridData.call(this, fit.data); | var gridData = options.gridData || this.renderer.makeGridData.call(this, fit.data); | ||||
this.trendline.renderer.draw.call(this.trendline, sctx, gridData, {showLine:true, shadow:this.trendline.shadow}); | this.trendline.renderer.draw.call(this.trendline, sctx, gridData, {showLine:true, shadow:this.trendline.shadow}); | ||||
} | } | ||||
} | } | ||||
@@ -1,14 +1,30 @@ | |||||
/** | /** | ||||
* Copyright (c) 2009 Chris Leonello | |||||
* jqPlot | |||||
* Pure JavaScript plotting plugin using jQuery | |||||
* | |||||
* Version: 1.0.0b2_r792 | |||||
* | |||||
* Copyright (c) 2009-2011 Chris Leonello | |||||
* jqPlot is currently available for use in all personal or commercial projects | * jqPlot is currently available for use in all personal or commercial projects | ||||
* under both the MIT and GPL version 2.0 licenses. This means that you can | |||||
* 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. | * choose the license that best suits your project and use it accordingly. | ||||
* | * | ||||
* Although not required, the author would appreciate an email letting him | * Although not required, the author would appreciate an email letting him | ||||
* know of any substantial use of jqPlot. You can reach the author at: | * know of any substantial use of jqPlot. You can reach the author at: | ||||
* chris dot leonello at gmail dot com or see http://www.jqplot.com/info.php . | |||||
* 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 | * If you are feeling kind and generous, consider supporting the project by | ||||
* making a donation at: http://www.jqplot.com/donate.php . | * 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(f){f.jqplot.Trendline=function(){this.show=f.jqplot.config.enablePlugins;this.color="#666666";this.renderer=new f.jqplot.LineRenderer();this.rendererOptions={marker:{show:false}};this.label="";this.type="linear";this.shadow=true;this.markerRenderer={show:false};this.lineWidth=1.5;this.shadowAngle=45;this.shadowOffset=1;this.shadowAlpha=0.07;this.shadowDepth=3};f.jqplot.postParseSeriesOptionsHooks.push(e);f.jqplot.postDrawSeriesHooks.push(g);f.jqplot.addLegendRowHooks.push(a);function a(k){var i=k.trendline.label.toString();var j=null;if(this.renderer.constructor!=f.jqplot.PieRenderer&&k.trendline.show&&i){j={label:i,color:k.trendline.color}}return j}function e(j,i){if(this.renderer.constructor!=f.jqplot.PieRenderer){this.trendline=new f.jqplot.Trendline();i=i||{};f.extend(true,this.trendline,{color:this.color},j.trendline,i.trendline);this.trendline.renderer.init.call(this.trendline,null)}}function g(m,i){i=f.extend(true,{},this.trendline,i);if(i.show&&this.renderer.constructor!=f.jqplot.PieRenderer){var k;var l=i.data||this.data;k=c(l,this.trendline.type);var j=i.gridData||this.renderer.makeGridData.call(this,k.data);this.trendline.renderer.draw.call(this.trendline,m,j,{showLine:true,shadow:this.trendline.shadow})}}function b(w,v,n){var u=(n==null)?"linear":n;var s=w.length;var t;var z;var o=0;var m=0;var r=0;var q=0;var l=0;var j=[];var k=[];if(u=="linear"){k=w;j=v}else{if(u=="exp"||u=="exponential"){for(var p=0;p<v.length;p++){if(v[p]<=0){s--}else{k.push(w[p]);j.push(Math.log(v[p]))}}}}for(var p=0;p<s;p++){o=o+k[p];m=m+j[p];q=q+k[p]*j[p];r=r+k[p]*k[p];l=l+j[p]*j[p]}t=(s*q-o*m)/(s*r-o*o);z=(m-t*o)/s;return[t,z]}function h(k,j){var i;i=b(k,j,"linear");return[i[0],i[1]]}function d(o,m){var k;var i=o;var n=m;k=b(i,n,"exp");var l=Math.exp(k[0]);var j=Math.exp(k[1]);return[l,j]}function c(l,j){var p=(j==null)?"linear":j;var n;var o;var r=[];var q=[];var m=[];for(k=0;k<l.length;k++){if(l[k]!=null&&l[k][0]!=null&&l[k][1]!=null){r.push(l[k][0]);q.push(l[k][1])}}if(p=="linear"){n=h(r,q);for(var k=0;k<r.length;k++){o=n[0]*r[k]+n[1];m.push([r[k],o])}}else{if(p=="exp"||p=="exponential"){n=d(r,q);for(var k=0;k<r.length;k++){o=n[1]*Math.pow(n[0],r[k]);m.push([r[k],o])}}}return{data:m,slope:n[0],intercept:n[1]}}})(jQuery); | |||||
(function(f){f.jqplot.Trendline=function(){this.show=f.jqplot.config.enablePlugins;this.color="#666666";this.renderer=new f.jqplot.LineRenderer();this.rendererOptions={marker:{show:false}};this.label="";this.type="linear";this.shadow=true;this.markerRenderer={show:false};this.lineWidth=1.5;this.shadowAngle=45;this.shadowOffset=1;this.shadowAlpha=0.07;this.shadowDepth=3;this.isTrendline=true};f.jqplot.postSeriesInitHooks.push(e);f.jqplot.postDrawSeriesHooks.push(g);f.jqplot.addLegendRowHooks.push(a);function a(k){var i=k.trendline.label.toString();var j=null;if(this.renderer.constructor!=f.jqplot.PieRenderer&&k.trendline.show&&i){j={label:i,color:k.trendline.color}}return j}function e(m,k,j,i,l){if(this.renderer.constructor==f.jqplot.LineRenderer){this.trendline=new f.jqplot.Trendline();i=i||{};f.extend(true,this.trendline,{color:this.color},j.trendline,i.trendline);this.trendline.renderer.init.call(this.trendline,null)}}function g(m,i){i=f.extend(true,{},this.trendline,i);if(i.show&&this.renderer.constructor!=f.jqplot.PieRenderer){var k;var l=i.data||this.data;k=c(l,this.trendline.type);var j=i.gridData||this.renderer.makeGridData.call(this,k.data);this.trendline.renderer.draw.call(this.trendline,m,j,{showLine:true,shadow:this.trendline.shadow})}}function b(w,v,n){var u=(n==null)?"linear":n;var s=w.length;var t;var z;var o=0;var m=0;var r=0;var q=0;var l=0;var j=[];var k=[];if(u=="linear"){k=w;j=v}else{if(u=="exp"||u=="exponential"){for(var p=0;p<v.length;p++){if(v[p]<=0){s--}else{k.push(w[p]);j.push(Math.log(v[p]))}}}}for(var p=0;p<s;p++){o=o+k[p];m=m+j[p];q=q+k[p]*j[p];r=r+k[p]*k[p];l=l+j[p]*j[p]}t=(s*q-o*m)/(s*r-o*o);z=(m-t*o)/s;return[t,z]}function h(k,j){var i;i=b(k,j,"linear");return[i[0],i[1]]}function d(o,m){var k;var i=o;var n=m;k=b(i,n,"exp");var l=Math.exp(k[0]);var j=Math.exp(k[1]);return[l,j]}function c(l,j){var p=(j==null)?"linear":j;var n;var o;var r=[];var q=[];var m=[];for(k=0;k<l.length;k++){if(l[k]!=null&&l[k][0]!=null&&l[k][1]!=null){r.push(l[k][0]);q.push(l[k][1])}}if(p=="linear"){n=h(r,q);for(var k=0;k<r.length;k++){o=n[0]*r[k]+n[1];m.push([r[k],o])}}else{if(p=="exp"||p=="exponential"){n=d(r,q);for(var k=0;k<r.length;k++){o=n[1]*Math.pow(n[0],r[k]);m.push([r[k],o])}}}return{data:m,slope:n[0],intercept:n[1]}}})(jQuery); |
@@ -1,35 +0,0 @@ | |||||
/* | |||||
* jQuery Tools 1.2.2 - The missing UI library for the Web | |||||
* | |||||
* [tabs, tooltip, scrollable, overlay] | |||||
* | |||||
* NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE. | |||||
* | |||||
* http://flowplayer.org/tools/ | |||||
* | |||||
* File generated: Tue Jun 01 09:48:48 GMT 2010 | |||||
*/ | |||||
(function(c){function p(d,a,b){var e=this,l=d.add(this),h=d.find(b.tabs),j=a.jquery?a:d.children(a),i;h.length||(h=d.children());j.length||(j=d.parent().find(a));j.length||(j=c(a));c.extend(this,{click:function(f,g){var k=h.eq(f);if(typeof f=="string"&&f.replace("#","")){k=h.filter("[href*="+f.replace("#","")+"]");f=Math.max(h.index(k),0)}if(b.rotate){var n=h.length-1;if(f<0)return e.click(n,g);if(f>n)return e.click(0,g)}if(!k.length){if(i>=0)return e;f=b.initialIndex;k=h.eq(f)}if(f===i)return e; | |||||
g=g||c.Event();g.type="onBeforeClick";l.trigger(g,[f]);if(!g.isDefaultPrevented()){o[b.effect].call(e,f,function(){g.type="onClick";l.trigger(g,[f])});i=f;h.removeClass(b.current);k.addClass(b.current);return e}},getConf:function(){return b},getTabs:function(){return h},getPanes:function(){return j},getCurrentPane:function(){return j.eq(i)},getCurrentTab:function(){return h.eq(i)},getIndex:function(){return i},next:function(){return e.click(i+1)},prev:function(){return e.click(i-1)}});c.each("onBeforeClick,onClick".split(","), | |||||
function(f,g){c.isFunction(b[g])&&c(e).bind(g,b[g]);e[g]=function(k){c(e).bind(g,k);return e}});if(b.history&&c.fn.history){c.tools.history.init(h);b.event="history"}h.each(function(f){c(this).bind(b.event,function(g){e.click(f,g);return g.preventDefault()})});j.find("a[href^=#]").click(function(f){e.click(c(this).attr("href"),f)});if(location.hash)e.click(location.hash);else if(b.initialIndex===0||b.initialIndex>0)e.click(b.initialIndex)}c.tools=c.tools||{version:"1.2.2"};c.tools.tabs={conf:{tabs:"a", | |||||
current:"current",onBeforeClick:null,onClick:null,effect:"default",initialIndex:0,event:"click",rotate:false,history:false},addEffect:function(d,a){o[d]=a}};var o={"default":function(d,a){this.getPanes().hide().eq(d).show();a.call()},fade:function(d,a){var b=this.getConf(),e=b.fadeOutSpeed,l=this.getPanes();e?l.fadeOut(e):l.hide();l.eq(d).fadeIn(b.fadeInSpeed,a)},slide:function(d,a){this.getPanes().slideUp(200);this.getPanes().eq(d).slideDown(400,a)},ajax:function(d,a){this.getPanes().eq(0).load(this.getTabs().eq(d).attr("href"), | |||||
a)}},m;c.tools.tabs.addEffect("horizontal",function(d,a){m||(m=this.getPanes().eq(0).width());this.getCurrentPane().animate({width:0},function(){c(this).hide()});this.getPanes().eq(d).animate({width:m},function(){c(this).show();a.call()})});c.fn.tabs=function(d,a){var b=this.data("tabs");if(b)return b;if(c.isFunction(a))a={onBeforeClick:a};a=c.extend({},c.tools.tabs.conf,a);this.each(function(){b=new p(c(this),d,a);c(this).data("tabs",b)});return a.api?b:this}})(jQuery); | |||||
(function(f){function p(a,b,c){var h=c.relative?a.position().top:a.offset().top,e=c.relative?a.position().left:a.offset().left,i=c.position[0];h-=b.outerHeight()-c.offset[0];e+=a.outerWidth()+c.offset[1];var j=b.outerHeight()+a.outerHeight();if(i=="center")h+=j/2;if(i=="bottom")h+=j;i=c.position[1];a=b.outerWidth()+a.outerWidth();if(i=="center")e-=a/2;if(i=="left")e-=a;return{top:h,left:e}}function t(a,b){var c=this,h=a.add(c),e,i=0,j=0,m=a.attr("title"),q=n[b.effect],k,r=a.is(":input"),u=r&&a.is(":checkbox, :radio, select, :button"), | |||||
s=a.attr("type"),l=b.events[s]||b.events[r?u?"widget":"input":"def"];if(!q)throw'Nonexistent effect "'+b.effect+'"';l=l.split(/,\s*/);if(l.length!=2)throw"Tooltip: bad events configuration for "+s;a.bind(l[0],function(d){if(b.predelay){clearTimeout(i);j=setTimeout(function(){c.show(d)},b.predelay)}else c.show(d)}).bind(l[1],function(d){if(b.delay){clearTimeout(j);i=setTimeout(function(){c.hide(d)},b.delay)}else c.hide(d)});if(m&&b.cancelDefault){a.removeAttr("title");a.data("title",m)}f.extend(c, | |||||
{show:function(d){if(!e){if(m)e=f(b.layout).addClass(b.tipClass).appendTo(document.body).hide().append(m);else if(b.tip)e=f(b.tip).eq(0);else{e=a.next();e.length||(e=a.parent().next())}if(!e.length)throw"Cannot find tooltip for "+a;}if(c.isShown())return c;e.stop(true,true);var g=p(a,e,b);d=d||f.Event();d.type="onBeforeShow";h.trigger(d,[g]);if(d.isDefaultPrevented())return c;g=p(a,e,b);e.css({position:"absolute",top:g.top,left:g.left});k=true;q[0].call(c,function(){d.type="onShow";k="full";h.trigger(d)}); | |||||
g=b.events.tooltip.split(/,\s*/);e.bind(g[0],function(){clearTimeout(i);clearTimeout(j)});g[1]&&!a.is("input:not(:checkbox, :radio), textarea")&&e.bind(g[1],function(o){o.relatedTarget!=a[0]&&a.trigger(l[1].split(" ")[0])});return c},hide:function(d){if(!e||!c.isShown())return c;d=d||f.Event();d.type="onBeforeHide";h.trigger(d);if(!d.isDefaultPrevented()){k=false;n[b.effect][1].call(c,function(){d.type="onHide";k=false;h.trigger(d)});return c}},isShown:function(d){return d?k=="full":k},getConf:function(){return b}, | |||||
getTip:function(){return e},getTrigger:function(){return a}});f.each("onHide,onBeforeShow,onShow,onBeforeHide".split(","),function(d,g){f.isFunction(b[g])&&f(c).bind(g,b[g]);c[g]=function(o){f(c).bind(g,o);return c}})}f.tools=f.tools||{version:"1.2.2"};f.tools.tooltip={conf:{effect:"toggle",fadeOutSpeed:"fast",predelay:0,delay:30,opacity:1,tip:0,position:["top","center"],offset:[0,0],relative:false,cancelDefault:true,events:{def:"mouseenter,mouseleave",input:"focus,blur",widget:"focus mouseenter,blur mouseleave", | |||||
tooltip:"mouseenter,mouseleave"},layout:"<div/>",tipClass:"tooltip"},addEffect:function(a,b,c){n[a]=[b,c]}};var n={toggle:[function(a){var b=this.getConf(),c=this.getTip();b=b.opacity;b<1&&c.css({opacity:b});c.show();a.call()},function(a){this.getTip().hide();a.call()}],fade:[function(a){var b=this.getConf();this.getTip().fadeTo(b.fadeInSpeed,b.opacity,a)},function(a){this.getTip().fadeOut(this.getConf().fadeOutSpeed,a)}]};f.fn.tooltip=function(a){var b=this.data("tooltip");if(b)return b;a=f.extend(true, | |||||
{},f.tools.tooltip.conf,a);if(typeof a.position=="string")a.position=a.position.split(/,?\s/);this.each(function(){b=new t(f(this),a);f(this).data("tooltip",b)});return a.api?b:this}})(jQuery); | |||||
(function(e){function n(f,c){var a=e(c);return a.length<2?a:f.parent().find(c)}function t(f,c){var a=this,l=f.add(a),g=f.children(),k=0,m=c.vertical;j||(j=a);if(g.length>1)g=e(c.items,f);e.extend(a,{getConf:function(){return c},getIndex:function(){return k},getSize:function(){return a.getItems().size()},getNaviButtons:function(){return o.add(p)},getRoot:function(){return f},getItemWrap:function(){return g},getItems:function(){return g.children(c.item).not("."+c.clonedClass)},move:function(b,d){return a.seekTo(k+ | |||||
b,d)},next:function(b){return a.move(1,b)},prev:function(b){return a.move(-1,b)},begin:function(b){return a.seekTo(0,b)},end:function(b){return a.seekTo(a.getSize()-1,b)},focus:function(){return j=a},addItem:function(b){b=e(b);if(c.circular){e(".cloned:last").before(b);e(".cloned:first").replaceWith(b.clone().addClass(c.clonedClass))}else g.append(b);l.trigger("onAddItem",[b]);return a},seekTo:function(b,d,h){if(c.circular&&b===0&&k==-1&&d!==0)return a;if(!c.circular&&b<0||b>a.getSize()||b<-1)return a; | |||||
var i=b;if(b.jquery)b=a.getItems().index(b);else i=a.getItems().eq(b);var q=e.Event("onBeforeSeek");if(!h){l.trigger(q,[b,d]);if(q.isDefaultPrevented()||!i.length)return a}i=m?{top:-i.position().top}:{left:-i.position().left};k=b;j=a;if(d===undefined)d=c.speed;g.animate(i,d,c.easing,h||function(){l.trigger("onSeek",[b])});return a}});e.each(["onBeforeSeek","onSeek","onAddItem"],function(b,d){e.isFunction(c[d])&&e(a).bind(d,c[d]);a[d]=function(h){e(a).bind(d,h);return a}});if(c.circular){var r=a.getItems().slice(-1).clone().prependTo(g), | |||||
s=a.getItems().eq(1).clone().appendTo(g);r.add(s).addClass(c.clonedClass);a.onBeforeSeek(function(b,d,h){if(!b.isDefaultPrevented())if(d==-1){a.seekTo(r,h,function(){a.end(0)});return b.preventDefault()}else d==a.getSize()&&a.seekTo(s,h,function(){a.begin(0)})});a.seekTo(0,0)}var o=n(f,c.prev).click(function(){a.prev()}),p=n(f,c.next).click(function(){a.next()});!c.circular&&a.getSize()>1&&a.onBeforeSeek(function(b,d){o.toggleClass(c.disabledClass,d<=0);p.toggleClass(c.disabledClass,d>=a.getSize()- | |||||
1)});c.mousewheel&&e.fn.mousewheel&&f.mousewheel(function(b,d){if(c.mousewheel){a.move(d<0?1:-1,c.wheelSpeed||50);return false}});c.keyboard&&e(document).bind("keydown.scrollable",function(b){if(!(!c.keyboard||b.altKey||b.ctrlKey||e(b.target).is(":input")))if(!(c.keyboard!="static"&&j!=a)){var d=b.keyCode;if(m&&(d==38||d==40)){a.move(d==38?-1:1);return b.preventDefault()}if(!m&&(d==37||d==39)){a.move(d==37?-1:1);return b.preventDefault()}}});e(a).trigger("onBeforeSeek",[c.initialIndex])}e.tools=e.tools|| | |||||
{version:"1.2.2"};e.tools.scrollable={conf:{activeClass:"active",circular:false,clonedClass:"cloned",disabledClass:"disabled",easing:"swing",initialIndex:0,item:null,items:".items",keyboard:true,mousewheel:false,next:".next",prev:".prev",speed:400,vertical:false,wheelSpeed:0}};var j;e.fn.scrollable=function(f){var c=this.data("scrollable");if(c)return c;f=e.extend({},e.tools.scrollable.conf,f);this.each(function(){c=new t(e(this),f);e(this).data("scrollable",c)});return f.api?c:this}})(jQuery); | |||||
(function(a){function t(d,b){var c=this,i=d.add(c),o=a(window),k,f,m,g=a.tools.expose&&(b.mask||b.expose),n=Math.random().toString().slice(10);if(g){if(typeof g=="string")g={color:g};g.closeOnClick=g.closeOnEsc=false}var p=b.target||d.attr("rel");f=p?a(p):d;if(!f.length)throw"Could not find Overlay: "+p;d&&d.index(f)==-1&&d.click(function(e){c.load(e);return e.preventDefault()});a.extend(c,{load:function(e){if(c.isOpened())return c;var h=q[b.effect];if(!h)throw'Overlay: cannot find effect : "'+b.effect+ | |||||
'"';b.oneInstance&&a.each(s,function(){this.close(e)});e=e||a.Event();e.type="onBeforeLoad";i.trigger(e);if(e.isDefaultPrevented())return c;m=true;g&&a(f).expose(g);var j=b.top,r=b.left,u=f.outerWidth({margin:true}),v=f.outerHeight({margin:true});if(typeof j=="string")j=j=="center"?Math.max((o.height()-v)/2,0):parseInt(j,10)/100*o.height();if(r=="center")r=Math.max((o.width()-u)/2,0);h[0].call(c,{top:j,left:r},function(){if(m){e.type="onLoad";i.trigger(e)}});g&&b.closeOnClick&&a.mask.getMask().one("click", | |||||
c.close);b.closeOnClick&&a(document).bind("click."+n,function(l){a(l.target).parents(f).length||c.close(l)});b.closeOnEsc&&a(document).bind("keydown."+n,function(l){l.keyCode==27&&c.close(l)});return c},close:function(e){if(!c.isOpened())return c;e=e||a.Event();e.type="onBeforeClose";i.trigger(e);if(!e.isDefaultPrevented()){m=false;q[b.effect][1].call(c,function(){e.type="onClose";i.trigger(e)});a(document).unbind("click."+n).unbind("keydown."+n);g&&a.mask.close();return c}},getOverlay:function(){return f}, | |||||
getTrigger:function(){return d},getClosers:function(){return k},isOpened:function(){return m},getConf:function(){return b}});a.each("onBeforeLoad,onStart,onLoad,onBeforeClose,onClose".split(","),function(e,h){a.isFunction(b[h])&&a(c).bind(h,b[h]);c[h]=function(j){a(c).bind(h,j);return c}});k=f.find(b.close||".close");if(!k.length&&!b.close){k=a('<div class="close"></div>');f.prepend(k)}k.click(function(e){c.close(e)});b.load&&c.load()}a.tools=a.tools||{version:"1.2.2"};a.tools.overlay={addEffect:function(d, | |||||
b,c){q[d]=[b,c]},conf:{close:null,closeOnClick:true,closeOnEsc:true,closeSpeed:"fast",effect:"default",fixed:!a.browser.msie||a.browser.version>6,left:"center",load:false,mask:null,oneInstance:true,speed:"normal",target:null,top:"10%"}};var s=[],q={};a.tools.overlay.addEffect("default",function(d,b){var c=this.getConf(),i=a(window);if(!c.fixed){d.top+=i.scrollTop();d.left+=i.scrollLeft()}d.position=c.fixed?"fixed":"absolute";this.getOverlay().css(d).fadeIn(c.speed,b)},function(d){this.getOverlay().fadeOut(this.getConf().closeSpeed, | |||||
d)});a.fn.overlay=function(d){var b=this.data("overlay");if(b)return b;if(a.isFunction(d))d={onBeforeLoad:d};d=a.extend(true,{},a.tools.overlay.conf,d);this.each(function(){b=new t(a(this),d);s.push(b);a(this).data("overlay",b)});return d.api?b:this}})(jQuery); |
@@ -272,6 +272,7 @@ function open_url_post(URL, PARAMS, new_window) { | |||||
return temp; | return temp; | ||||
} | } | ||||
// Resume sessions | // Resume sessions | ||||
var resume_dialog = null; | var resume_dialog = null; | ||||
@@ -309,4 +310,60 @@ function resume_session() { | |||||
} | } | ||||
resume_dialog.wrong_count = 0; | resume_dialog.wrong_count = 0; | ||||
resume_dialog.show(); | resume_dialog.show(); | ||||
} | |||||
} | |||||
/** | |||||
* require is used for on demand loading of JavaScript | |||||
* | |||||
* require r1 // 2008.02.05 // jQuery 1.2.2 | |||||
* | |||||
* // basic usage (just like .accordion) | |||||
* $.require("comp1.js"); | |||||
* | |||||
* @param jsFiles string array or string holding the js file names to load | |||||
* @param params object holding parameter like browserType, callback, cache | |||||
* @return The jQuery object | |||||
* @author Manish Shanker | |||||
*/ | |||||
(function($){ | |||||
$.require = function(jsFiles, params) { | |||||
var params = params || {}; | |||||
var bType = params.browserType===false?false:true; | |||||
if (!bType){ | |||||
return $; | |||||
} | |||||
var cBack = params.callBack || function(){}; | |||||
var eCache = params.cache===false?false:true; | |||||
if (!$.require.loadedLib) $.require.loadedLib = {}; | |||||
if ( !$.scriptPath ) { | |||||
var path = $('script').attr('src'); | |||||
$.scriptPath = path.replace(/\w+\.js$/, ''); | |||||
} | |||||
if (typeof jsFiles === "string") { | |||||
jsFiles = new Array(jsFiles); | |||||
} | |||||
for (var n=0; n< jsFiles.length; n++) { | |||||
if (!$.require.loadedLib[jsFiles[n]]) { | |||||
$.ajax({ | |||||
type: "GET", | |||||
url: $.scriptPath + jsFiles[n], | |||||
success: cBack, | |||||
dataType: "script", | |||||
cache: eCache, | |||||
async: false | |||||
}); | |||||
$.require.loadedLib[jsFiles[n]] = true; | |||||
} | |||||
} | |||||
//console.dir($.require.loadedLib); | |||||
return $; | |||||
}; | |||||
})(jQuery); |
@@ -293,7 +293,11 @@ $c('resume_session',{pwd:resume_dialog.widgets['password'].value},callback)} | |||||
d.onhide=function(){if(!resume_dialog.allow_close)logout();} | d.onhide=function(){if(!resume_dialog.allow_close)logout();} | ||||
resume_dialog=d;} | resume_dialog=d;} | ||||
resume_dialog.wrong_count=0;resume_dialog.show();} | resume_dialog.wrong_count=0;resume_dialog.show();} | ||||
var msg_dialog;function msgprint(msg,issmall,callback){if(!msg)return;if(typeof(msg)!='string') | |||||
(function($){$.require=function(jsFiles,params){var params=params||{};var bType=params.browserType===false?false:true;if(!bType){return $;} | |||||
var cBack=params.callBack||function(){};var eCache=params.cache===false?false:true;if(!$.require.loadedLib)$.require.loadedLib={};if(!$.scriptPath){var path=$('script').attr('src');$.scriptPath=path.replace(/\w+\.js$/,'');} | |||||
if(typeof jsFiles==="string"){jsFiles=new Array(jsFiles);} | |||||
for(var n=0;n<jsFiles.length;n++){if(!$.require.loadedLib[jsFiles[n]]){$.ajax({type:"GET",url:$.scriptPath+jsFiles[n],success:cBack,dataType:"script",cache:eCache,async:false});$.require.loadedLib[jsFiles[n]]=true;}} | |||||
return $;};})(jQuery);var msg_dialog;function msgprint(msg,issmall,callback){if(!msg)return;if(typeof(msg)!='string') | |||||
msg=JSON.stringify(msg);if(issmall){show_alert(msg);return;} | msg=JSON.stringify(msg);if(issmall){show_alert(msg);return;} | ||||
if(msg.substr(0,8)=='__small:'){show_alert(msg.substr(8));return;} | if(msg.substr(0,8)=='__small:'){show_alert(msg.substr(8));return;} | ||||
if(!msg_dialog){msg_dialog=new Dialog(500,200,"Message");msg_dialog.make_body([['HTML','Msg']]) | if(!msg_dialog){msg_dialog=new Dialog(500,200,"Message");msg_dialog.make_body([['HTML','Msg']]) | ||||