');G.appendTo(E);G.data("seriesIndex",s);if(this.escapeHtml){G.text(D)}else{G.html(D)}E=null;G=null}return this._elem}})(jQuery);
\ No newline at end of file
diff --git a/public/site_assets/test/js/plugins/jqplot.dateAxisRenderer.js b/public/site_assets/test/js/plugins/jqplot.dateAxisRenderer.js
new file mode 100644
index 00000000..e371a28d
--- /dev/null
+++ b/public/site_assets/test/js/plugins/jqplot.dateAxisRenderer.js
@@ -0,0 +1,741 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.8
+ * Revision: 1250
+ *
+ * Copyright (c) 2009-2013 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, the author would appreciate an email letting him
+ * know of any substantial use of jqPlot. You can reach the author at:
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * version 2007.04.27
+ * author Ash Searle
+ * http://hexmen.com/blog/2007/03/printf-sprintf/
+ * http://hexmen.com/js/sprintf.js
+ * The author (Ash Searle) has placed this code in the public domain:
+ * "This code is unrestricted: you are free to use it however you like."
+ *
+ */
+(function($) {
+ /**
+ * Class: $.jqplot.DateAxisRenderer
+ * A plugin for a jqPlot to render an axis as a series of date values.
+ * This renderer has no options beyond those supplied by the class.
+ * It supplies its own tick formatter, so the tickOptions.formatter option
+ * should not be overridden.
+ *
+ * Thanks to Ken Synder for his enhanced Date instance methods which are
+ * included with this code .
+ *
+ * To use this renderer, include the plugin in your source
+ * >
+ *
+ * and supply the appropriate options to your plot
+ *
+ * > {axes:{xaxis:{renderer:$.jqplot.DateAxisRenderer}}}
+ *
+ * Dates can be passed into the axis in almost any recognizable value and
+ * will be parsed. They will be rendered on the axis in the format
+ * specified by tickOptions.formatString. e.g. tickOptions.formatString = '%Y-%m-%d'.
+ *
+ * Accecptable format codes
+ * are:
+ *
+ * > Code Result Description
+ * > == Years ==
+ * > %Y 2008 Four-digit year
+ * > %y 08 Two-digit year
+ * > == Months ==
+ * > %m 09 Two-digit month
+ * > %#m 9 One or two-digit month
+ * > %B September Full month name
+ * > %b Sep Abbreviated month name
+ * > == Days ==
+ * > %d 05 Two-digit day of month
+ * > %#d 5 One or two-digit day of month
+ * > %e 5 One or two-digit day of month
+ * > %A Sunday Full name of the day of the week
+ * > %a Sun Abbreviated name of the day of the week
+ * > %w 0 Number of the day of the week (0 = Sunday, 6 = Saturday)
+ * > %o th The ordinal suffix string following the day of the month
+ * > == Hours ==
+ * > %H 23 Hours in 24-hour format (two digits)
+ * > %#H 3 Hours in 24-hour integer format (one or two digits)
+ * > %I 11 Hours in 12-hour format (two digits)
+ * > %#I 3 Hours in 12-hour integer format (one or two digits)
+ * > %p PM AM or PM
+ * > == Minutes ==
+ * > %M 09 Minutes (two digits)
+ * > %#M 9 Minutes (one or two digits)
+ * > == Seconds ==
+ * > %S 02 Seconds (two digits)
+ * > %#S 2 Seconds (one or two digits)
+ * > %s 1206567625723 Unix timestamp (Seconds past 1970-01-01 00:00:00)
+ * > == Milliseconds ==
+ * > %N 008 Milliseconds (three digits)
+ * > %#N 8 Milliseconds (one to three digits)
+ * > == Timezone ==
+ * > %O 360 difference in minutes between local time and GMT
+ * > %Z Mountain Standard Time Name of timezone as reported by browser
+ * > %G -06:00 Hours and minutes between GMT
+ * > == Shortcuts ==
+ * > %F 2008-03-26 %Y-%m-%d
+ * > %T 05:06:30 %H:%M:%S
+ * > %X 05:06:30 %H:%M:%S
+ * > %x 03/26/08 %m/%d/%y
+ * > %D 03/26/08 %m/%d/%y
+ * > %#c Wed Mar 26 15:31:00 2008 %a %b %e %H:%M:%S %Y
+ * > %v 3-Sep-2008 %e-%b-%Y
+ * > %R 15:31 %H:%M
+ * > %r 3:31:00 PM %I:%M:%S %p
+ * > == Characters ==
+ * > %n \n Newline
+ * > %t \t Tab
+ * > %% % Percent Symbol
+ */
+ $.jqplot.DateAxisRenderer = function() {
+ $.jqplot.LinearAxisRenderer.call(this);
+ this.date = new $.jsDate();
+ };
+
+ var second = 1000;
+ var minute = 60 * second;
+ var hour = 60 * minute;
+ var day = 24 * hour;
+ var week = 7 * day;
+
+ // these are less definitive
+ var month = 30.4368499 * day;
+ var year = 365.242199 * day;
+
+ var daysInMonths = [31,28,31,30,31,30,31,30,31,30,31,30];
+ // array of consistent nice intervals. Longer intervals
+ // will depend on days in month, days in year, etc.
+ var niceFormatStrings = ['%M:%S.%#N', '%M:%S.%#N', '%M:%S.%#N', '%M:%S', '%M:%S', '%M:%S', '%M:%S', '%H:%M:%S', '%H:%M:%S', '%H:%M', '%H:%M', '%H:%M', '%H:%M', '%H:%M', '%H:%M', '%a %H:%M', '%a %H:%M', '%b %e %H:%M', '%b %e %H:%M', '%b %e %H:%M', '%b %e %H:%M', '%v', '%v', '%v', '%v', '%v', '%v', '%v'];
+ var niceIntervals = [0.1*second, 0.2*second, 0.5*second, second, 2*second, 5*second, 10*second, 15*second, 30*second, minute, 2*minute, 5*minute, 10*minute, 15*minute, 30*minute, hour, 2*hour, 4*hour, 6*hour, 8*hour, 12*hour, day, 2*day, 3*day, 4*day, 5*day, week, 2*week];
+
+ var niceMonthlyIntervals = [];
+
+ function bestDateInterval(min, max, titarget) {
+ // iterate through niceIntervals to find one closest to titarget
+ var badness = Number.MAX_VALUE;
+ var temp, bestTi, bestfmt;
+ for (var i=0, l=niceIntervals.length; i < l; i++) {
+ temp = Math.abs(titarget - niceIntervals[i]);
+ if (temp < badness) {
+ badness = temp;
+ bestTi = niceIntervals[i];
+ bestfmt = niceFormatStrings[i];
+ }
+ }
+
+ return [bestTi, bestfmt];
+ }
+
+ $.jqplot.DateAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
+ $.jqplot.DateAxisRenderer.prototype.constructor = $.jqplot.DateAxisRenderer;
+
+ $.jqplot.DateTickFormatter = function(format, val) {
+ if (!format) {
+ format = '%Y/%m/%d';
+ }
+ return $.jsDate.strftime(val, format);
+ };
+
+ $.jqplot.DateAxisRenderer.prototype.init = function(options){
+ // prop: tickRenderer
+ // A class of a rendering engine for creating the ticks labels displayed on the plot,
+ // See <$.jqplot.AxisTickRenderer>.
+ // this.tickRenderer = $.jqplot.AxisTickRenderer;
+ // this.labelRenderer = $.jqplot.AxisLabelRenderer;
+ this.tickOptions.formatter = $.jqplot.DateTickFormatter;
+ // prop: tickInset
+ // Controls the amount to inset the first and last ticks from
+ // the edges of the grid, in multiples of the tick interval.
+ // 0 is no inset, 0.5 is one half a tick interval, 1 is a full
+ // tick interval, etc.
+ this.tickInset = 0;
+ // prop: drawBaseline
+ // True to draw the axis baseline.
+ this.drawBaseline = true;
+ // prop: baselineWidth
+ // width of the baseline in pixels.
+ this.baselineWidth = null;
+ // prop: baselineColor
+ // CSS color spec for the baseline.
+ this.baselineColor = null;
+ this.daTickInterval = null;
+ this._daTickInterval = null;
+
+ $.extend(true, this, options);
+
+ var db = this._dataBounds,
+ stats,
+ sum,
+ s,
+ d,
+ pd,
+ sd,
+ intv;
+
+ // Go through all the series attached to this axis and find
+ // the min/max bounds for this axis.
+ for (var i=0; i db.max) || db.max == null) {
+ 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 {
+ 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];
+ }
+ if ((d[j][1] != null && d[j][1] > db.max) || db.max == null) {
+ 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;
+ }
+
+ if (s.renderer.bands) {
+ if (s.renderer.bands.hiData.length) {
+ var bd = s.renderer.bands.hiData;
+ for (var j=0, l=bd.length; j < l; j++) {
+ if (this.name === 'xaxis' || this.name === 'x2axis') {
+ bd[j][0] = new $.jsDate(bd[j][0]).getTime();
+ if ((bd[j][0] != null && bd[j][0] > db.max) || db.max == null) {
+ db.max = bd[j][0];
+ }
+ }
+ else {
+ bd[j][1] = new $.jsDate(bd[j][1]).getTime();
+ if ((bd[j][1] != null && bd[j][1] > db.max) || db.max == null) {
+ db.max = bd[j][1];
+ }
+ }
+ }
+ }
+ if (s.renderer.bands.lowData.length) {
+ var bd = s.renderer.bands.lowData;
+ for (var j=0, l=bd.length; j < l; j++) {
+ if (this.name === 'xaxis' || this.name === 'x2axis') {
+ bd[j][0] = new $.jsDate(bd[j][0]).getTime();
+ if ((bd[j][0] != null && bd[j][0] < db.min) || db.min == null) {
+ db.min = bd[j][0];
+ }
+ }
+ else {
+ bd[j][1] = new $.jsDate(bd[j][1]).getTime();
+ if ((bd[j][1] != null && bd[j][1] < db.min) || db.min == null) {
+ db.min = bd[j][1];
+ }
+ }
+ }
+ }
+ }
+
+ 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
+ $.jqplot.DateAxisRenderer.prototype.reset = function() {
+ this.min = this._options.min;
+ this.max = this._options.max;
+ this.tickInterval = this._options.tickInterval;
+ this.numberTicks = this._options.numberTicks;
+ this._autoFormatString = '';
+ if (this._overrideFormatString && this.tickOptions && this.tickOptions.formatString) {
+ this.tickOptions.formatString = '';
+ }
+ this.daTickInterval = this._daTickInterval;
+ // this._ticks = this.__ticks;
+ };
+
+ $.jqplot.DateAxisRenderer.prototype.createTicks = function(plot) {
+ // we're are operating on an axis here
+ var ticks = this._ticks;
+ var userTicks = this.ticks;
+ var name = this.name;
+ // databounds were set on axis initialization.
+ var db = this._dataBounds;
+ var iv = this._intervalStats;
+ var dim = (this.name.charAt(0) === 'x') ? this._plotDimensions.width : this._plotDimensions.height;
+ var interval;
+ var min, max;
+ var pos1, pos2;
+ var tt, i;
+ var threshold = 30;
+ var insetMult = 1;
+ var daTickInterval = null;
+
+ // if user specified a tick interval, convert to usable.
+ if (this.tickInterval != null)
+ {
+ // if interval is a number or can be converted to one, use it.
+ // Assume it is in SECONDS!!!
+ if (Number(this.tickInterval)) {
+ daTickInterval = [Number(this.tickInterval), 'seconds'];
+ }
+ // else, parse out something we can build from.
+ else if (typeof this.tickInterval == "string") {
+ var parts = this.tickInterval.split(' ');
+ if (parts.length == 1) {
+ daTickInterval = [1, parts[0]];
+ }
+ else if (parts.length == 2) {
+ daTickInterval = [parts[0], parts[1]];
+ }
+ }
+ }
+
+ var tickInterval = this.tickInterval;
+
+ // if we already have ticks, use them.
+ // ticks must be in order of increasing value.
+
+ min = new $.jsDate((this.min != null) ? this.min : db.min).getTime();
+ max = new $.jsDate((this.max != null) ? this.max : db.max).getTime();
+
+ // see if we're zooming. if we are, don't use the min and max we're given,
+ // but compute some nice ones. They will be reset later.
+
+ var cursor = plot.plugins.cursor;
+
+ if (cursor && cursor._zoom && cursor._zoom.zooming) {
+ this.min = null;
+ this.max = null;
+ }
+
+ var range = max - min;
+
+ if (this.tickOptions == null || !this.tickOptions.formatString) {
+ this._overrideFormatString = true;
+ }
+
+ if (userTicks.length) {
+ // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed
+ for (i=0; i 6) {
+ intv = 6;
+ }
+
+ // figure out the starting month and ending month.
+ var mstart = new $.jsDate(min).setDate(1).setHours(0,0,0,0);
+
+ // See if max ends exactly on a month
+ var tempmend = new $.jsDate(max);
+ var mend = new $.jsDate(max).setDate(1).setHours(0,0,0,0);
+
+ if (tempmend.getTime() !== mend.getTime()) {
+ mend = mend.add(1, 'month');
+ }
+
+ var nmonths = mend.diff(mstart, 'month');
+
+ nttarget = Math.ceil(nmonths/intv) + 1;
+
+ this.min = mstart.getTime();
+ this.max = mstart.clone().add((nttarget - 1) * intv, 'month').getTime();
+ this.numberTicks = nttarget;
+
+ for (var i=0; i 200) {
+ this.numberTicks = parseInt(3+(dim-200)/100, 10);
+ }
+ else {
+ this.numberTicks = 2;
+ }
+ }
+
+ insetMult = range / (this.numberTicks-1)/1000;
+
+ if (this.daTickInterval == null) {
+ this.daTickInterval = [insetMult, 'seconds'];
+ }
+
+
+ for (var i=0; iC.max)||C.max==null){C.max=y[r][0]}if(r>0){o=Math.abs(y[r][0]-y[r-1][0]);u.intervals.push(o);if(u.frequencies.hasOwnProperty(o)){u.frequencies[o]+=1}else{u.frequencies[o]=1}}x+=o}else{y[r][1]=new h.jsDate(y[r][1]).getTime();A[r][1]=new h.jsDate(y[r][1]).getTime();z[r][1]=new h.jsDate(y[r][1]).getTime();if((y[r][1]!=null&&y[r][1]C.max)||C.max==null){C.max=y[r][1]}if(r>0){o=Math.abs(y[r][1]-y[r-1][1]);u.intervals.push(o);if(u.frequencies.hasOwnProperty(o)){u.frequencies[o]+=1}else{u.frequencies[o]=1}}}x+=o}if(D.renderer.bands){if(D.renderer.bands.hiData.length){var w=D.renderer.bands.hiData;for(var r=0,q=w.length;rC.max)||C.max==null){C.max=w[r][0]}}else{w[r][1]=new h.jsDate(w[r][1]).getTime();if((w[r][1]!=null&&w[r][1]>C.max)||C.max==null){C.max=w[r][1]}}}}if(D.renderer.bands.lowData.length){var w=D.renderer.bands.lowData;for(var r=0,q=w.length;r6){D=6}}var V=new h.jsDate(ae).setDate(1).setHours(0,0,0,0);var q=new h.jsDate(J);var z=new h.jsDate(J).setDate(1).setHours(0,0,0,0);if(q.getTime()!==z.getTime()){z=z.add(1,"month")}var S=z.diff(V,"month");ab=Math.ceil(S/D)+1;this.min=V.getTime();this.max=V.clone().add((ab-1)*D,"month").getTime();this.numberTicks=ab;for(var aa=0;aa200){this.numberTicks=parseInt(3+(n-200)/100,10)}else{this.numberTicks=2}}}O=B/(this.numberTicks-1)/1000;if(this.daTickInterval==null){this.daTickInterval=[O,"seconds"]}for(var aa=0;aa
+ *
+ * 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, whether 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 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; i0) {
+ stack[i] += stack[i-1];
+ }
+ tot += this.data[i][1];
+ }
+ var fact = Math.PI*2/stack[stack.length - 1];
+
+ for (var i=0; i0) {
+ stack[i] += stack[i-1];
+ }
+ tot += data[i][1];
+ }
+ var fact = Math.PI*2/stack[stack.length - 1];
+
+ for (var i=0; i 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 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= 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 = $('' + label + '').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 = $('
');
+ // 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').prependTo(this._elem);
+ }
+ else{
+ tr = $('
').appendTo(this._elem);
+ }
+ for (j=0; j0){
+ pad = true;
+ }
+ else{
+ pad = false;
+ }
+ }
+ else{
+ if (i == nr -1){
+ pad = false;
+ }
+ else{
+ pad = true;
+ }
+ }
+ rs = (pad) ? this.rowSpacing : '0';
+
+ td1 = $('
'+
+ '
'+
+ '
');
+ td2 = $('
');
+ 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;
+ };
+
+ // 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; i570)?n[o]*0.8:n[o]+0.3*(255-n[o]);n[o]=parseInt(n[o],10)}this.highlightColors.push("rgb("+n[0]+","+n[1]+","+n[2]+")")}}t.postParseOptionsHooks.addOnce(l);t.postInitHooks.addOnce(g);t.eventListenerHooks.addOnce("jqplotMouseMove",b);t.eventListenerHooks.addOnce("jqplotMouseDown",a);t.eventListenerHooks.addOnce("jqplotMouseUp",j);t.eventListenerHooks.addOnce("jqplotClick",f);t.eventListenerHooks.addOnce("jqplotRightClick",m);t.postDrawHooks.addOnce(h)};e.jqplot.DonutRenderer.prototype.setGridData=function(s){var o=[];var t=[];var n=this.startAngle/180*Math.PI;var r=0;this._drawData=false;for(var q=0;q0){o[q]+=o[q-1]}r+=this.data[q][1]}var p=Math.PI*2/o[o.length-1];for(var q=0;q0){o[q]+=o[q-1]}r+=s[q][1]}var p=Math.PI*2/o[o.length-1];for(var q=0;q6.282+this.startAngle){t=6.282+this.startAngle;if(u>t){u=6.281+this.startAngle}}if(u>=t){return}x.beginPath();x.fillStyle=p;x.strokeStyle=p;x.arc(0,0,n,u,t,false);x.lineTo(v*Math.cos(t),v*Math.sin(t));x.arc(0,0,v,t,u,true);x.closePath();if(w){x.fill()}else{x.stroke()}}if(s){for(var q=0;q1&&this.index>0)?this._previousSeries[0]._diameter:this._diameter;this._thickness=this.thickness||(M-this.innerDiameter-2*X*this._numberSeries)/this._numberSeries/2}else{this._thickness=this.thickness||v/2/(this._numberSeries+1)*0.85}var K=this._radius=this._diameter/2;this._innerRadius=this._radius-this._thickness;var o=this.startAngle/180*Math.PI;this._center=[(s-u*q)/2+u*q,(H-u*p)/2+u*p];if(this.shadow){var L="rgba(0,0,0,"+this.shadowAlpha+")";for(var Q=0;Q=this.dataLabelThreshold){var S,U=(A+z)/2,C;if(this.dataLabels=="label"){S=this.dataLabelFormatString||"%s";C=e.jqplot.sprintf(S,V[Q][0])}else{if(this.dataLabels=="value"){S=this.dataLabelFormatString||"%d";C=e.jqplot.sprintf(S,this.data[Q][1])}else{if(this.dataLabels=="percent"){S=this.dataLabelFormatString||"%d%%";C=e.jqplot.sprintf(S,V[Q][2]*100)}else{if(this.dataLabels.constructor==Array){S=this.dataLabelFormatString||"%s";C=e.jqplot.sprintf(S,this.dataLabels[Q])}}}}var n=this._innerRadius+this._thickness*this.dataLabelPositionFactor+this.sliceMargin+this.dataLabelNudge;var F=this._center[0]+Math.cos(U)*n+this.canvas._offsets.left;var E=this._center[1]+Math.sin(U)*n+this.canvas._offsets.top;var D=e(''+C+"").insertBefore(P.eventCanvas._elem);F-=D.width()/2;E-=D.height()/2;F=Math.round(F);E=Math.round(E);D.css({left:F,top:E})}}};e.jqplot.DonutAxisRenderer=function(){e.jqplot.LinearAxisRenderer.call(this)};e.jqplot.DonutAxisRenderer.prototype=new e.jqplot.LinearAxisRenderer();e.jqplot.DonutAxisRenderer.prototype.constructor=e.jqplot.DonutAxisRenderer;e.jqplot.DonutAxisRenderer.prototype.init=function(n){this.tickRenderer=e.jqplot.DonutTickRenderer;e.extend(true,this,n);this._dataBounds={min:0,max:100};this.min=0;this.max=100;this.showTicks=false;this.ticks=[];this.showMark=false;this.show=false};e.jqplot.DonutLegendRenderer=function(){e.jqplot.TableLegendRenderer.call(this)};e.jqplot.DonutLegendRenderer.prototype=new e.jqplot.TableLegendRenderer();e.jqplot.DonutLegendRenderer.prototype.constructor=e.jqplot.DonutLegendRenderer;e.jqplot.DonutLegendRenderer.prototype.init=function(n){this.numberRows=null;this.numberColumns=null;e.extend(true,this,n)};e.jqplot.DonutLegendRenderer.prototype.draw=function(){var q=this;if(this.show){var y=this._series;var B="position:absolute;";B+=(this.background)?"background:"+this.background+";":"";B+=(this.border)?"border:"+this.border+";":"";B+=(this.fontSize)?"font-size:"+this.fontSize+";":"";B+=(this.fontFamily)?"font-family:"+this.fontFamily+";":"";B+=(this.textColor)?"color:"+this.textColor+";":"";B+=(this.marginTop!=null)?"margin-top:"+this.marginTop+";":"";B+=(this.marginBottom!=null)?"margin-bottom:"+this.marginBottom+";":"";B+=(this.marginLeft!=null)?"margin-left:"+this.marginLeft+";":"";B+=(this.marginRight!=null)?"margin-right:"+this.marginRight+";":"";this._elem=e('
');if(this.escapeHtml){r.text(u)}else{r.html(u)}if(x){r.prependTo(p);t.prependTo(p)}else{t.appendTo(p);r.appendTo(p)}F=true}A++}}}}return this._elem};function c(r,q,o){o=o||{};o.axesDefaults=o.axesDefaults||{};o.legend=o.legend||{};o.seriesDefaults=o.seriesDefaults||{};var n=false;if(o.seriesDefaults.renderer==e.jqplot.DonutRenderer){n=true}else{if(o.series){for(var p=0;p= 0.6) ? rgba[3]*0.6 : rgba[3]*(2-rgba[3]);
+ drag.color = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+alpha+')';
+ }
+ mr.color = drag.color;
+ mr.init();
+
+ var start = (neighbor.pointIndex > 0) ? neighbor.pointIndex - 1 : 0;
+ var end = neighbor.pointIndex+2;
+ drag._gridData = s.gridData.slice(start, end);
+ }
+
+ function handleMove(ev, gridpos, datapos, neighbor, plot) {
+ if (plot.plugins.dragable.dragCanvas.isDragging) {
+ var dc = plot.plugins.dragable.dragCanvas;
+ var dp = dc._neighbor;
+ var s = plot.series[dp.seriesIndex];
+ var drag = s.plugins.dragable;
+ var gd = s.gridData;
+
+ // compute the new grid position with any constraints.
+ var x = (drag.constrainTo == 'y') ? dp.gridData[0] : gridpos.x;
+ var y = (drag.constrainTo == 'x') ? dp.gridData[1] : gridpos.y;
+
+ // compute data values for any listeners.
+ var xu = s._xaxis.series_p2u(x);
+ var yu = s._yaxis.series_p2u(y);
+
+ // clear the canvas then redraw effect at new position.
+ var ctx = dc._ctx;
+ ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+
+ // adjust our gridData for the new mouse position
+ if (dp.pointIndex > 0) {
+ drag._gridData[1] = [x, y];
+ }
+ else {
+ drag._gridData[0] = [x, y];
+ }
+ plot.series[dp.seriesIndex].draw(dc._ctx, {gridData:drag._gridData, shadow:false, preventJqPlotSeriesDrawTrigger:true, color:drag.color, markerOptions:{color:drag.color, shadow:false}, trendline:{show:false}});
+ plot.target.trigger('jqplotSeriesPointChange', [dp.seriesIndex, dp.pointIndex, [xu,yu], [x,y]]);
+ }
+ else if (neighbor != null) {
+ var series = plot.series[neighbor.seriesIndex];
+ if (series.isDragable) {
+ var dc = plot.plugins.dragable.dragCanvas;
+ if (!dc.isOver) {
+ dc._cursors.push(ev.target.style.cursor);
+ ev.target.style.cursor = "pointer";
+ }
+ dc.isOver = true;
+ }
+ }
+ else if (neighbor == null) {
+ var dc = plot.plugins.dragable.dragCanvas;
+ if (dc.isOver) {
+ ev.target.style.cursor = dc._cursors.pop();
+ dc.isOver = false;
+ }
+ }
+ }
+
+ function handleDown(ev, gridpos, datapos, neighbor, plot) {
+ var dc = plot.plugins.dragable.dragCanvas;
+ dc._cursors.push(ev.target.style.cursor);
+ if (neighbor != null) {
+ var s = plot.series[neighbor.seriesIndex];
+ var drag = s.plugins.dragable;
+ if (s.isDragable && !dc.isDragging) {
+ dc._neighbor = neighbor;
+ dc.isDragging = true;
+ initDragPoint(plot, neighbor);
+ drag.markerRenderer.draw(s.gridData[neighbor.pointIndex][0], s.gridData[neighbor.pointIndex][1], dc._ctx);
+ 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.
+ else {
+ var ctx = dc._ctx;
+ ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+ dc.isDragging = false;
+ }
+ }
+
+ function handleUp(ev, gridpos, datapos, neighbor, plot) {
+ if (plot.plugins.dragable.dragCanvas.isDragging) {
+ var dc = plot.plugins.dragable.dragCanvas;
+ // clear the canvas
+ var ctx = dc._ctx;
+ ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+ dc.isDragging = false;
+ // redraw the series canvas at the new point.
+ var dp = dc._neighbor;
+ var s = plot.series[dp.seriesIndex];
+ var drag = s.plugins.dragable;
+ // compute the new grid position with any constraints.
+ var x = (drag.constrainTo == 'y') ? dp.data[0] : datapos[s.xaxis];
+ var y = (drag.constrainTo == 'x') ? dp.data[1] : datapos[s.yaxis];
+ // var x = datapos[s.xaxis];
+ // var y = datapos[s.yaxis];
+ s.data[dp.pointIndex][0] = x;
+ s.data[dp.pointIndex][1] = y;
+ plot.drawSeries({preventJqPlotSeriesDrawTrigger:true}, dp.seriesIndex);
+ dc._neighbor = null;
+ ev.target.style.cursor = dc._cursors.pop();
+ plot.target.trigger('jqplotDragStop', [gridpos, datapos]);
+ }
+ }
+})(jQuery);
\ No newline at end of file
diff --git a/public/site_assets/test/js/plugins/jqplot.dragable.min.js b/public/site_assets/test/js/plugins/jqplot.dragable.min.js
new file mode 100644
index 00000000..83c8e65c
--- /dev/null
+++ b/public/site_assets/test/js/plugins/jqplot.dragable.min.js
@@ -0,0 +1,3 @@
+/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com
+ jsDate | (c) 2010-2013 Chris Leonello
+ */(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);
\ No newline at end of file
diff --git a/public/site_assets/test/js/plugins/jqplot.enhancedLegendRenderer.js b/public/site_assets/test/js/plugins/jqplot.enhancedLegendRenderer.js
new file mode 100644
index 00000000..5a2cbc61
--- /dev/null
+++ b/public/site_assets/test/js/plugins/jqplot.enhancedLegendRenderer.js
@@ -0,0 +1,305 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.8
+ * Revision: 1250
+ *
+ * Copyright (c) 2009-2013 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, the author would appreciate an email letting him
+ * know of any substantial use of jqPlot. You can reach the author at:
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * version 2007.04.27
+ * author Ash Searle
+ * http://hexmen.com/blog/2007/03/printf-sprintf/
+ * http://hexmen.com/js/sprintf.js
+ * The author (Ash Searle) has placed this code in the public domain:
+ * "This code is unrestricted: you are free to use it however you like."
+ *
+ */
+(function($) {
+ // class $.jqplot.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: seriesToggleReplot
+ // True to replot the chart after toggling series on/off.
+ // This will set the series show property to false.
+ // This allows for rescaling or other maniplation of chart.
+ // Set to an options object (e.g. {resetAxes: true}) for replot options.
+ this.seriesToggleReplot = false;
+ // 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(offsets, plot) {
+ 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 = $('
');
+ 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, div, div0, div1;
+ var idx = 0;
+ // check to see if we need to reverse
+ for (i=series.length-1; i>=0; i--) {
+ if (nc == 1 && series[i]._stack || series[i].renderer.constructor == $.jqplot.BezierCurveRenderer){
+ reverse = true;
+ }
+ }
+
+ for (i=0; i0){
+ 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 jqplot-table-legend-swatch');
+ td1.css({textAlign: 'center', paddingTop: rs});
+
+ div0 = $(document.createElement('div'));
+ div0.addClass('jqplot-table-legend-swatch-outline');
+ 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 jqplot-table-legend-label');
+ td2.css('paddingTop', rs);
+
+ // td1 = $('
'+
+ // '
'+
+ // '
');
+ // td2 = $('
');
+ 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) {
+
+ // add an overlay for clicking series on/off
+ // div0 = $(document.createElement('div'));
+ // div0.addClass('jqplot-table-legend-overlay');
+ // div0.css({position:'relative', left:0, top:0, height:'100%', width:'100%'});
+ // tr.append(div0);
+
+ 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, plot: plot, replot:this.seriesToggleReplot}, handleToggle);
+ td1.addClass('jqplot-seriesToggle');
+ }
+ if (this.showLabels) {
+ td2.bind('click', {series:s, speed:speed, plot: plot, replot:this.seriesToggleReplot}, handleToggle);
+ td2.addClass('jqplot-seriesToggle');
+ }
+
+ // for series that are already hidden, add the hidden class
+ if (!s.show && s.showLabel) {
+ td1.addClass('jqplot-series-hidden');
+ td2.addClass('jqplot-series-hidden');
+ }
+ }
+
+ pad = true;
+ }
+ }
+ idx++;
+ }
+
+ td1 = td2 = div0 = div1 = null;
+ }
+ }
+ return this._elem;
+ };
+
+ var handleToggle = function (ev) {
+ var d = ev.data,
+ s = d.series,
+ replot = d.replot,
+ plot = d.plot,
+ speed = d.speed,
+ sidx = s.index,
+ showing = false;
+
+ if (s.canvas._elem.is(':hidden') || !s.show) {
+ showing = true;
+ }
+
+ var doLegendToggle = function() {
+
+ if (replot) {
+ var opts = {};
+
+ if ($.isPlainObject(replot)) {
+ $.extend(true, opts, replot);
+ }
+
+ plot.replot(opts);
+ // if showing, there was no canvas element to fade in, so hide here
+ // and then do a fade in.
+ if (showing && speed) {
+ var s = plot.series[sidx];
+
+ if (s.shadowCanvas._elem) {
+ s.shadowCanvas._elem.hide().fadeIn(speed);
+ }
+ s.canvas._elem.hide().fadeIn(speed);
+ s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).hide().fadeIn(speed);
+ }
+
+ }
+
+ else {
+ var s = plot.series[sidx];
+
+ if (s.canvas._elem.is(':hidden') || !s.show) {
+ // Not sure if there is a better way to check for showSwatches and showLabels === true.
+ // Test for "undefined" since default values for both showSwatches and showLables is true.
+ if (typeof plot.options.legend.showSwatches === 'undefined' || plot.options.legend.showSwatches === true) {
+ plot.legend._elem.find('td').eq(sidx * 2).addClass('jqplot-series-hidden');
+ }
+ if (typeof plot.options.legend.showLabels === 'undefined' || plot.options.legend.showLabels === true) {
+ plot.legend._elem.find('td').eq((sidx * 2) + 1).addClass('jqplot-series-hidden');
+ }
+ }
+ else {
+ if (typeof plot.options.legend.showSwatches === 'undefined' || plot.options.legend.showSwatches === true) {
+ plot.legend._elem.find('td').eq(sidx * 2).removeClass('jqplot-series-hidden');
+ }
+ if (typeof plot.options.legend.showLabels === 'undefined' || plot.options.legend.showLabels === true) {
+ plot.legend._elem.find('td').eq((sidx * 2) + 1).removeClass('jqplot-series-hidden');
+ }
+ }
+
+ }
+
+ };
+
+ s.toggleDisplay(ev, doLegendToggle);
+ };
+
+ // 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);
diff --git a/public/site_assets/test/js/plugins/jqplot.enhancedLegendRenderer.min.js b/public/site_assets/test/js/plugins/jqplot.enhancedLegendRenderer.min.js
new file mode 100644
index 00000000..968e77cd
--- /dev/null
+++ b/public/site_assets/test/js/plugins/jqplot.enhancedLegendRenderer.min.js
@@ -0,0 +1,3 @@
+/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com
+ jsDate | (c) 2010-2013 Chris Leonello
+ */(function(c){c.jqplot.EnhancedLegendRenderer=function(){c.jqplot.TableLegendRenderer.call(this)};c.jqplot.EnhancedLegendRenderer.prototype=new c.jqplot.TableLegendRenderer();c.jqplot.EnhancedLegendRenderer.prototype.constructor=c.jqplot.EnhancedLegendRenderer;c.jqplot.EnhancedLegendRenderer.prototype.init=function(d){this.numberRows=null;this.numberColumns=null;this.seriesToggle="normal";this.seriesToggleReplot=false;this.disableIEFading=true;c.extend(true,this,d);if(this.seriesToggle){c.jqplot.postDrawHooks.push(b)}};c.jqplot.EnhancedLegendRenderer.prototype.draw=function(m,y){var f=this;if(this.show){var r=this._series;var u;var w="position:absolute;";w+=(this.background)?"background:"+this.background+";":"";w+=(this.border)?"border:"+this.border+";":"";w+=(this.fontSize)?"font-size:"+this.fontSize+";":"";w+=(this.fontFamily)?"font-family:"+this.fontFamily+";":"";w+=(this.textColor)?"color:"+this.textColor+";":"";w+=(this.marginTop!=null)?"margin-top:"+this.marginTop+";":"";w+=(this.marginBottom!=null)?"margin-bottom:"+this.marginBottom+";":"";w+=(this.marginLeft!=null)?"margin-left:"+this.marginLeft+";":"";w+=(this.marginRight!=null)?"margin-right:"+this.marginRight+";":"";this._elem=c('
');if(this.seriesToggle){this._elem.css("z-index","3")}var C=false,q=false,d,o;if(this.numberRows){d=this.numberRows;if(!this.numberColumns){o=Math.ceil(r.length/d)}else{o=this.numberColumns}}else{if(this.numberColumns){o=this.numberColumns;d=Math.ceil(r.length/this.numberColumns)}else{d=r.length;o=1}}var B,z,e,l,k,n,p,t,h,g;var v=0;for(B=r.length-1;B>=0;B--){if(o==1&&r[B]._stack||r[B].renderer.constructor==c.jqplot.BezierCurveRenderer){q=true}}for(B=0;B0){C=true}else{C=false}}else{if(B==d-1){C=false}else{C=true}}p=(C)?this.rowSpacing:"0";l=c(document.createElement("td"));l.addClass("jqplot-table-legend jqplot-table-legend-swatch");l.css({textAlign:"center",paddingTop:p});h=c(document.createElement("div"));h.addClass("jqplot-table-legend-swatch-outline");g=c(document.createElement("div"));g.addClass("jqplot-table-legend-swatch");g.css({backgroundColor:x,borderColor:x});l.append(h.append(g));k=c(document.createElement("td"));k.addClass("jqplot-table-legend jqplot-table-legend-label");k.css("paddingTop",p);if(this.escapeHtml){k.text(n)}else{k.html(n)}if(q){if(this.showLabels){k.prependTo(e)}if(this.showSwatches){l.prependTo(e)}}else{if(this.showSwatches){l.appendTo(e)}if(this.showLabels){k.appendTo(e)}}if(this.seriesToggle){var A;if(typeof(this.seriesToggle)==="string"||typeof(this.seriesToggle)==="number"){if(!c.jqplot.use_excanvas||!this.disableIEFading){A=this.seriesToggle}}if(this.showSwatches){l.bind("click",{series:u,speed:A,plot:y,replot:this.seriesToggleReplot},a);l.addClass("jqplot-seriesToggle")}if(this.showLabels){k.bind("click",{series:u,speed:A,plot:y,replot:this.seriesToggleReplot},a);k.addClass("jqplot-seriesToggle")}if(!u.show&&u.showLabel){l.addClass("jqplot-series-hidden");k.addClass("jqplot-series-hidden")}}C=true}}v++}l=k=h=g=null}}return this._elem};var a=function(j){var i=j.data,m=i.series,k=i.replot,h=i.plot,f=i.speed,l=m.index,g=false;if(m.canvas._elem.is(":hidden")||!m.show){g=true}var e=function(){if(k){var n={};if(c.isPlainObject(k)){c.extend(true,n,k)}h.replot(n);if(g&&f){var d=h.series[l];if(d.shadowCanvas._elem){d.shadowCanvas._elem.hide().fadeIn(f)}d.canvas._elem.hide().fadeIn(f);d.canvas._elem.nextAll(".jqplot-point-label.jqplot-series-"+d.index).hide().fadeIn(f)}}else{var d=h.series[l];if(d.canvas._elem.is(":hidden")||!d.show){if(typeof h.options.legend.showSwatches==="undefined"||h.options.legend.showSwatches===true){h.legend._elem.find("td").eq(l*2).addClass("jqplot-series-hidden")}if(typeof h.options.legend.showLabels==="undefined"||h.options.legend.showLabels===true){h.legend._elem.find("td").eq((l*2)+1).addClass("jqplot-series-hidden")}}else{if(typeof h.options.legend.showSwatches==="undefined"||h.options.legend.showSwatches===true){h.legend._elem.find("td").eq(l*2).removeClass("jqplot-series-hidden")}if(typeof h.options.legend.showLabels==="undefined"||h.options.legend.showLabels===true){h.legend._elem.find("td").eq((l*2)+1).removeClass("jqplot-series-hidden")}}}};m.toggleDisplay(j,e)};var b=function(){if(this.legend.renderer.constructor==c.jqplot.EnhancedLegendRenderer&&this.legend.seriesToggle){var d=this.legend._elem.detach();this.eventCanvas._elem.after(d)}}})(jQuery);
\ No newline at end of file
diff --git a/public/site_assets/test/js/plugins/jqplot.funnelRenderer.js b/public/site_assets/test/js/plugins/jqplot.funnelRenderer.js
new file mode 100644
index 00000000..2cbc9c2c
--- /dev/null
+++ b/public/site_assets/test/js/plugins/jqplot.funnelRenderer.js
@@ -0,0 +1,943 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.8
+ * Revision: 1250
+ *
+ * Copyright (c) 2009-2013 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, the author would appreciate an email letting him
+ * know of any substantial use of jqPlot. You can reach the author at:
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * version 2007.04.27
+ * author Ash Searle
+ * http://hexmen.com/blog/2007/03/printf-sprintf/
+ * http://hexmen.com/js/sprintf.js
+ * The author (Ash Searle) has placed this code in the public domain:
+ * "This code is unrestricted: you are free to use it however you like."
+ *
+ */
+(function($) {
+ /**
+ * Class: $.jqplot.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:
+ *
+ * >
+ *
+ * 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, whether 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 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._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 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= 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 = $('' + label + '').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 = $('
');
+ // 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').prependTo(this._elem);
+ }
+ else{
+ tr = $('
').appendTo(this._elem);
+ }
+ for (j=0; j0){
+ pad = true;
+ }
+ else{
+ pad = false;
+ }
+ }
+ else{
+ if (i == nr -1){
+ pad = false;
+ }
+ else{
+ pad = true;
+ }
+ }
+ rs = (pad) ? this.rowSpacing : '0';
+
+ td1 = $('
'+
+ '
'+
+ '
');
+ td2 = $('
');
+ 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; i570)?m[n]*0.8:m[n]+0.4*(255-m[n]);m[n]=parseInt(m[n],10)}this.highlightColors.push("rgb("+m[0]+","+m[1]+","+m[2]+")")}}t.postParseOptionsHooks.addOnce(k);t.postInitHooks.addOnce(g);t.eventListenerHooks.addOnce("jqplotMouseMove",a);t.eventListenerHooks.addOnce("jqplotMouseDown",b);t.eventListenerHooks.addOnce("jqplotMouseUp",j);t.eventListenerHooks.addOnce("jqplotClick",f);t.eventListenerHooks.addOnce("jqplotRightClick",l);t.postDrawHooks.addOnce(h)};e.jqplot.FunnelRenderer.prototype.setGridData=function(o){var n=0;var p=[];for(var m=0;mthis._lengths[Y]*n&&W<100){this._lengths[Y]=this._areas[Y]/(this._bases[Y]-this._lengths[Y]*Math.tan(this._angle));aa=Math.abs(this._lengths[Y]-E);this._bases[Y+1]=this._bases[Y]-(2*this._lengths[Y]*Math.tan(this._angle));E=this._lengths[Y];W++}Q+=this._lengths[Y]}this._vertices=new Array(B.length);var ae=[t,F],ad=[t+this._bases[0],F],ac=[t+(this._bases[0]-this._bases[this._bases.length-1])/2,F+this._length],ab=[ac[0]+this._bases[this._bases.length-1],ac[1]];function V(ag){var x=(ae[1]-ac[1])/(ae[0]-ac[0]);var v=ae[1]-x*ae[0];var ah=ag+ae[1];return[(ah-v)/x,ah]}function D(ag){var x=(ad[1]-ab[1])/(ad[0]-ab[0]);var v=ad[1]-x*ad[0];var ah=ag+ad[1];return[(ah-v)/x,ah]}var T=w,S=u;var Z=0,m=0;for(Y=0;Y0&&Y0&&Y=this.dataLabelThreshold){var K,X;if(this.dataLabels=="label"){K=this.dataLabelFormatString||"%s";X=e.jqplot.sprintf(K,B[Y][0])}else{if(this.dataLabels=="value"){K=this.dataLabelFormatString||"%d";X=e.jqplot.sprintf(K,this.data[Y][1])}else{if(this.dataLabels=="percent"){K=this.dataLabelFormatString||"%d%%";X=e.jqplot.sprintf(K,B[Y][1]*100)}else{if(this.dataLabels.constructor==Array){K=this.dataLabelFormatString||"%s";X=e.jqplot.sprintf(K,this.dataLabels[this._dataIndices[Y]])}}}}var s=(this._radius)*this.dataLabelPositionFactor+this.sliceMargin+this.dataLabelNudge;var T=(U[0][0]+U[1][0])/2+this.canvas._offsets.left;var S=(U[1][1]+U[2][1])/2+this.canvas._offsets.top;var z=e(''+X+"").insertBefore(p.eventCanvas._elem);T-=z.width()/2;S-=z.height()/2;T=Math.round(T);S=Math.round(S);z.css({left:T,top:S})}}};e.jqplot.FunnelAxisRenderer=function(){e.jqplot.LinearAxisRenderer.call(this)};e.jqplot.FunnelAxisRenderer.prototype=new e.jqplot.LinearAxisRenderer();e.jqplot.FunnelAxisRenderer.prototype.constructor=e.jqplot.FunnelAxisRenderer;e.jqplot.FunnelAxisRenderer.prototype.init=function(m){this.tickRenderer=e.jqplot.FunnelTickRenderer;e.extend(true,this,m);this._dataBounds={min:0,max:100};this.min=0;this.max=100;this.showTicks=false;this.ticks=[];this.showMark=false;this.show=false};e.jqplot.FunnelLegendRenderer=function(){e.jqplot.TableLegendRenderer.call(this)};e.jqplot.FunnelLegendRenderer.prototype=new e.jqplot.TableLegendRenderer();e.jqplot.FunnelLegendRenderer.prototype.constructor=e.jqplot.FunnelLegendRenderer;e.jqplot.FunnelLegendRenderer.prototype.init=function(m){this.numberRows=null;this.numberColumns=null;e.extend(true,this,m)};e.jqplot.FunnelLegendRenderer.prototype.draw=function(){var p=this;if(this.show){var x=this._series;var A="position:absolute;";A+=(this.background)?"background:"+this.background+";":"";A+=(this.border)?"border:"+this.border+";":"";A+=(this.fontSize)?"font-size:"+this.fontSize+";":"";A+=(this.fontFamily)?"font-family:"+this.fontFamily+";":"";A+=(this.textColor)?"color:"+this.textColor+";":"";A+=(this.marginTop!=null)?"margin-top:"+this.marginTop+";":"";A+=(this.marginBottom!=null)?"margin-bottom:"+this.marginBottom+";":"";A+=(this.marginLeft!=null)?"margin-left:"+this.marginLeft+";":"";A+=(this.marginRight!=null)?"margin-right:"+this.marginRight+";":"";this._elem=e('
');if(this.escapeHtml){q.text(t)}else{q.html(t)}if(w){q.prependTo(o);r.prependTo(o)}else{r.appendTo(o);q.appendTo(o)}E=true}z++}}}}return this._elem};function c(q,p,n){n=n||{};n.axesDefaults=n.axesDefaults||{};n.legend=n.legend||{};n.seriesDefaults=n.seriesDefaults||{};var m=false;if(n.seriesDefaults.renderer==e.jqplot.FunnelRenderer){m=true}else{if(n.series){for(var o=0;o
+ *
+ * A tooltip providing information about the data point is enabled by default.
+ * To disable the tooltip, set "showTooltip" to false.
+ *
+ * You can control what data is displayed in the tooltip with various
+ * options. The "tooltipAxes" option controls whether the x, y or both
+ * data values are displayed.
+ *
+ * Some chart types (e.g. hi-low-close) have more than one y value per
+ * data point. To display the additional values in the tooltip, set the
+ * "yvalues" option to the desired number of y values present (3 for a hlc chart).
+ *
+ * By default, data values will be formatted with the same formatting
+ * specifiers as used to format the axis ticks. A custom format code
+ * can be supplied with the tooltipFormatString option. This will apply
+ * to all values in the tooltip.
+ *
+ * For more complete control, the "formatString" option can be set. This
+ * Allows conplete control over tooltip formatting. Values are passed to
+ * the format string in an order determined by the "tooltipAxes" and "yvalues"
+ * options. So, if you have a hi-low-close chart and you just want to display
+ * the hi-low-close values in the tooltip, you could set a formatString like:
+ *
+ * > highlighter: {
+ * > tooltipAxes: 'y',
+ * > yvalues: 3,
+ * > formatString:'
+ * >
hi:
%s
+ * >
low:
%s
+ * >
close:
%s
'
+ * > }
+ *
+ */
+ $.jqplot.Highlighter = function(options) {
+ // Group: Properties
+ //
+ //prop: show
+ // true to show the highlight.
+ this.show = $.jqplot.config.enablePlugins;
+ // prop: markerRenderer
+ // Renderer used to draw the marker of the highlighted point.
+ // Renderer will assimilate attributes from the data point being highlighted,
+ // so no attributes need set on the renderer directly.
+ // Default is to turn off shadow drawing on the highlighted point.
+ this.markerRenderer = new $.jqplot.MarkerRenderer({shadow:false});
+ // prop: showMarker
+ // true to show the marker
+ this.showMarker = true;
+ // prop: lineWidthAdjust
+ // Pixels to add to the lineWidth of the highlight.
+ this.lineWidthAdjust = 2.5;
+ // prop: sizeAdjust
+ // Pixels to add to the overall size of the highlight.
+ this.sizeAdjust = 5;
+ // prop: showTooltip
+ // Show a tooltip with data point values.
+ this.showTooltip = true;
+ // prop: tooltipLocation
+ // Where to position tooltip, 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'
+ this.tooltipLocation = 'nw';
+ // prop: fadeTooltip
+ // true = fade in/out tooltip, flase = show/hide tooltip
+ this.fadeTooltip = true;
+ // prop: tooltipFadeSpeed
+ // 'slow', 'def', 'fast', or number of milliseconds.
+ this.tooltipFadeSpeed = "fast";
+ // prop: tooltipOffset
+ // Pixel offset of tooltip from the highlight.
+ this.tooltipOffset = 2;
+ // prop: tooltipAxes
+ // Which axes to display in tooltip, 'x', 'y' or 'both', 'xy' or 'yx'
+ // 'both' and 'xy' are equivalent, 'yx' reverses order of labels.
+ this.tooltipAxes = 'both';
+ // prop; tooltipSeparator
+ // String to use to separate x and y axes in tooltip.
+ 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
+ // Use the x and y axes formatters to format the text in the tooltip.
+ this.useAxesFormatters = true;
+ // prop: tooltipFormatString
+ // sprintf format string for the tooltip.
+ // Uses Ash Searle's javascript sprintf implementation
+ // found here: http://hexmen.com/blog/2007/03/printf-sprintf/
+ // See http://perldoc.perl.org/functions/sprintf.html for reference.
+ // Additional "p" and "P" format specifiers added by Chris Leonello.
+ this.tooltipFormatString = '%.5P';
+ // prop: formatString
+ // alternative to tooltipFormatString
+ // will format the whole tooltip text, populating with x, y values as
+ // indicated by tooltipAxes option. So, you could have a tooltip like:
+ // 'Date: %s, number of cats: %d' to format the whole tooltip at one go.
+ // If useAxesFormatters is true, values will be formatted according to
+ // Axes formatters and you can populate your tooltip string with
+ // %s placeholders.
+ this.formatString = null;
+ // prop: yvalues
+ // Number of y values to expect in the data point array.
+ // Typically this is 1. Certain plots, like OHLC, will
+ // have more y values in each data point array.
+ 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.isHighlighting = false;
+ this.currentNeighbor = null;
+
+ $.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
+
+ // called with scope of plot
+ $.jqplot.Highlighter.init = function (target, data, opts){
+ var options = opts || {};
+ // add a highlighter attribute to the plot
+ this.plugins.highlighter = new $.jqplot.Highlighter(options.highlighter);
+ };
+
+ // called within scope of series
+ $.jqplot.Highlighter.parseOptions = function (defaults, options) {
+ // Add a showHighlight option to the series
+ // and set it to true by default.
+ this.showHighlight = true;
+ };
+
+ // 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.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.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'});
+
+ this.eventCanvas._elem.before(this.plugins.highlighter._tooltipElem);
+ };
+
+ $.jqplot.preInitHooks.push($.jqplot.Highlighter.init);
+ $.jqplot.preParseSeriesOptionsHooks.push($.jqplot.Highlighter.parseOptions);
+ $.jqplot.postDrawHooks.push($.jqplot.Highlighter.postPlotDraw);
+
+ function draw(plot, neighbor) {
+ var hl = plot.plugins.highlighter;
+ var s = plot.series[neighbor.seriesIndex];
+ var smr = s.markerRenderer;
+ var mr = hl.markerRenderer;
+ mr.style = smr.style;
+ mr.lineWidth = smr.lineWidth + hl.lineWidthAdjust;
+ mr.size = smr.size + hl.sizeAdjust;
+ var rgba = $.jqplot.getColorComponents(smr.color);
+ var newrgb = [rgba[0], rgba[1], rgba[2]];
+ var alpha = (rgba[3] >= 0.6) ? rgba[3]*0.6 : rgba[3]*(2-rgba[3]);
+ mr.color = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+alpha+')';
+ mr.init();
+ mr.draw(s.gridData[neighbor.pointIndex][0], s.gridData[neighbor.pointIndex][1], hl.highlightCanvas._ctx);
+ }
+
+ function showTooltip(plot, series, neighbor) {
+ // neighbor looks like: {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}
+ // gridData should be x,y pixel coords on the grid.
+ // add the plot._gridPadding to that to get x,y in the target.
+ var hl = plot.plugins.highlighter;
+ var elem = hl._tooltipElem;
+ var serieshl = series.highlighter || {};
+
+ var opts = $.extend(true, {}, hl, serieshl);
+
+ if (opts.useAxesFormatters) {
+ var xf = series._xaxis._ticks[0].formatter;
+ var yf = series._yaxis._ticks[0].formatter;
+ var xfstr = series._xaxis._ticks[0].formatString;
+ var yfstr = series._yaxis._ticks[0].formatString;
+ var str;
+ var xstr = xf(xfstr, neighbor.data[0]);
+ var ystrs = [];
+ for (var i=1; i=0.6)?l[3]*0.6:l[3]*(2-l[3]);i.color="rgba("+n[0]+","+n[1]+","+n[2]+","+k+")";i.init();i.draw(p.gridData[o.pointIndex][0],p.gridData[o.pointIndex][1],j.highlightCanvas._ctx)}function g(A,q,m){var k=A.plugins.highlighter;var D=k._tooltipElem;var r=q.highlighter||{};var t=d.extend(true,{},k,r);if(t.useAxesFormatters){var w=q._xaxis._ticks[0].formatter;var h=q._yaxis._ticks[0].formatter;var E=q._xaxis._ticks[0].formatString;var s=q._yaxis._ticks[0].formatString;var z;var u=w(E,m.data[0]);var l=[];for(var B=1;B
+ *
+ * and supply the appropriate options to your plot
+ *
+ * > {axes:{xaxis:{renderer:$.jqplot.LogAxisRenderer}}}
+ **/
+ $.jqplot.LogAxisRenderer = function() {
+ $.jqplot.LinearAxisRenderer.call(this);
+ // prop: axisDefaults
+ // Default properties which will be applied directly to the series.
+ //
+ // Group: Properties
+ //
+ // Properties
+ //
+ // base - the logarithmic base, commonly 2, 10 or Math.E
+ // tickDistribution - Deprecated. "power" distribution of ticks
+ // always used. Option has no effect.
+ this.axisDefaults = {
+ base : 10,
+ tickDistribution :'power'
+ };
+ };
+
+ $.jqplot.LogAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
+ $.jqplot.LogAxisRenderer.prototype.constructor = $.jqplot.LogAxisRenderer;
+
+ $.jqplot.LogAxisRenderer.prototype.init = function(options) {
+ // prop: drawBaseline
+ // True to draw the axis baseline.
+ this.drawBaseline = true;
+ // prop: minorTicks
+ // Number of ticks to add between "major" ticks.
+ // Major ticks are ticks supplied by user or auto computed.
+ // Minor ticks cannot be created by user.
+ this.minorTicks = 'auto';
+ this._scalefact = 1.0;
+
+ $.extend(true, this, options);
+
+ this._autoFormatString = '%d';
+ this._overrideFormatString = false;
+
+ for (var d in this.renderer.axisDefaults) {
+ if (this[d] == null) {
+ this[d] = this.renderer.axisDefaults[d];
+ }
+ }
+
+ this.resetDataBounds();
+ };
+
+ $.jqplot.LogAxisRenderer.prototype.createTicks = function(plot) {
+ // we're are operating on an axis here
+ var ticks = this._ticks;
+ var userTicks = this.ticks;
+ var name = this.name;
+ var db = this._dataBounds;
+ var dim = (this.name.charAt(0) === 'x') ? this._plotDimensions.width : this._plotDimensions.height;
+ var interval;
+ var min, max;
+ var pos1, pos2;
+ var tt, i;
+
+ var threshold = 30;
+ // For some reason scalefactor is screwing up ticks.
+ this._scalefact = (Math.max(dim, threshold+1) - threshold)/300;
+
+ // if we already have ticks, use them.
+ // ticks must be in order of increasing value.
+ if (userTicks.length) {
+ // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed
+ for (i=0; i 140) {
+ numberTicks = Math.round(Math.log(this.max/this.min)/Math.log(this.base) + 1);
+ if (numberTicks < 2) {
+ numberTicks = 2;
+ }
+ if (minorTicks === 0) {
+ var temp = dim/(numberTicks - 1);
+ if (temp < 100) {
+ minorTicks = 0;
+ }
+ else if (temp < 190) {
+ minorTicks = 1;
+ }
+ else if (temp < 250) {
+ minorTicks = 3;
+ }
+ else if (temp < 600) {
+ minorTicks = 4;
+ }
+ else {
+ minorTicks = 9;
+ }
+ }
+ }
+ else {
+ numberTicks = 2;
+ if (minorTicks === 0) {
+ minorTicks = 1;
+ }
+ minorTicks = 0;
+ }
+ }
+ else {
+ numberTicks = this.numberTicks;
+ }
+
+ if (order >= 0 && minorTicks !== 3) {
+ this._autoFormatString = '%d';
+ }
+ // Adjust format string for case with 3 ticks where we'll have like 1, 2.5, 5, 7.5, 10
+ else if (order <= 0 && minorTicks === 3) {
+ var temp = -(order - 1);
+ this._autoFormatString = '%.'+ Math.abs(order-1) + 'f';
+ }
+
+ // Adjust format string for values less than 1.
+ else if (order < 0) {
+ var temp = -order;
+ this._autoFormatString = '%.'+ Math.abs(order) + 'f';
+ }
+
+ else {
+ this._autoFormatString = '%d';
+ }
+
+ var to, t, val, tt1, spread, interval;
+ for (var i=0; i=0; j--) {
+ val = tt1-interval*(j+1);
+ t = new this.tickRenderer(this.tickOptions);
+
+ if (this._overrideFormatString && this._autoFormatString != '') {
+ t.formatString = this._autoFormatString;
+ }
+ if (!this.showTicks) {
+ t.showLabel = false;
+ t.showMark = false;
+ }
+ else if (!this.showTickMarks) {
+ t.showMark = false;
+ }
+ t.setTick(val, this.name);
+ this._ticks.push(t);
+ }
+ }
+ }
+ }
+
+ // min and max are set as would be the case with zooming
+ else if (this.min != null && this.max != null) {
+ var opts = $.extend(true, {}, this.tickOptions, {name: this.name, value: null});
+ var nt, ti;
+ // don't have an interval yet, pick one that gives the most
+ // "round" ticks we can get.
+ if (this.numberTicks == null && this.tickInterval == null) {
+ // var threshold = 30;
+ var tdim = Math.max(dim, threshold+1);
+ var nttarget = Math.ceil((tdim-threshold)/35 + 1);
+
+ var ret = $.jqplot.LinearTickGenerator.bestConstrainedInterval(this.min, this.max, nttarget);
+
+ this._autoFormatString = ret[3];
+ nt = ret[2];
+ ti = ret[4];
+
+ for (var i=0; i 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();
+ }
+ }
+ 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');
+ }
+ else {
+ this._label._elem.css('right', '0px');
+ }
+ this._label.pack();
+ }
+ }
+ }
+ };
+})(jQuery);
\ No newline at end of file
diff --git a/public/site_assets/test/js/plugins/jqplot.logAxisRenderer.min.js b/public/site_assets/test/js/plugins/jqplot.logAxisRenderer.min.js
new file mode 100644
index 00000000..855f9a1a
--- /dev/null
+++ b/public/site_assets/test/js/plugins/jqplot.logAxisRenderer.min.js
@@ -0,0 +1,3 @@
+/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com
+ jsDate | (c) 2010-2013 Chris Leonello
+ */(function(a){a.jqplot.LogAxisRenderer=function(){a.jqplot.LinearAxisRenderer.call(this);this.axisDefaults={base:10,tickDistribution:"power"}};a.jqplot.LogAxisRenderer.prototype=new a.jqplot.LinearAxisRenderer();a.jqplot.LogAxisRenderer.prototype.constructor=a.jqplot.LogAxisRenderer;a.jqplot.LogAxisRenderer.prototype.init=function(b){this.drawBaseline=true;this.minorTicks="auto";this._scalefact=1;a.extend(true,this,b);this._autoFormatString="%d";this._overrideFormatString=false;for(var c in this.renderer.axisDefaults){if(this[c]==null){this[c]=this.renderer.axisDefaults[c]}}this.resetDataBounds()};a.jqplot.LogAxisRenderer.prototype.createTicks=function(d){var G=this._ticks;var w=this.ticks;var s=this.name;var u=this._dataBounds;var b=(this.name.charAt(0)==="x")?this._plotDimensions.width:this._plotDimensions.height;var k;var N,v;var m,l;var M,K;var g=30;this._scalefact=(Math.max(b,g+1)-g)/300;if(w.length){for(K=0;K140){h=Math.round(Math.log(this.max/this.min)/Math.log(this.base)+1);if(h<2){h=2}if(C===0){var o=b/(h-1);if(o<100){C=0}else{if(o<190){C=1}else{if(o<250){C=3}else{if(o<600){C=4}else{C=9}}}}}}else{h=2;if(C===0){C=1}C=0}}else{h=this.numberTicks}if(E>=0&&C!==3){this._autoFormatString="%d"}else{if(E<=0&&C===3){var o=-(E-1);this._autoFormatString="%."+Math.abs(E-1)+"f"}else{if(E<0){var o=-E;this._autoFormatString="%."+Math.abs(E)+"f"}else{this._autoFormatString="%d"}}}var O,H,z,p,n,k;for(var K=0;K=0;J--){z=p-k*(J+1);H=new this.tickRenderer(this.tickOptions);if(this._overrideFormatString&&this._autoFormatString!=""){H.formatString=this._autoFormatString}if(!this.showTicks){H.showLabel=false;H.showMark=false}else{if(!this.showTickMarks){H.showMark=false}}H.setTick(z,this.name);this._ticks.push(H)}}}}else{if(this.min!=null&&this.max!=null){var y=a.extend(true,{},this.tickOptions,{name:this.name,value:null});var I,e;if(this.numberTicks==null&&this.tickInterval==null){var D=Math.max(b,g+1);var L=Math.ceil((D-g)/35+1);var B=a.jqplot.LinearTickGenerator.bestConstrainedInterval(this.min,this.max,L);this._autoFormatString=B[3];I=B[2];e=B[4];for(var K=0;K0){c=-n._textRenderer.height*Math.cos(-n._textRenderer.angle)/2}else{c=-n.getHeight()+n._textRenderer.height*Math.cos(n._textRenderer.angle)/2}break;case"middle":c=-n.getHeight()/2;break;default:c=-n.getHeight()/2;break}}else{c=-n.getHeight()/2}var z=this.u2p(n.value)+c+"px";n._elem.css("top",z);n.pack()}}if(o){var x=this._label._elem.outerHeight(true);this._label._elem.css("top",m-g/2-x/2+"px");if(this.name=="yaxis"){this._label._elem.css("left","0px")}else{this._label._elem.css("right","0px")}this._label.pack()}}}}})(jQuery);
\ No newline at end of file
diff --git a/public/site_assets/test/js/plugins/jqplot.mekkoAxisRenderer.js b/public/site_assets/test/js/plugins/jqplot.mekkoAxisRenderer.js
new file mode 100644
index 00000000..b90c5bc6
--- /dev/null
+++ b/public/site_assets/test/js/plugins/jqplot.mekkoAxisRenderer.js
@@ -0,0 +1,611 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.8
+ * Revision: 1250
+ *
+ * Copyright (c) 2009-2013 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, the author would appreciate an email letting him
+ * know of any substantial use of jqPlot. You can reach the author at:
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * version 2007.04.27
+ * author Ash Searle
+ * http://hexmen.com/blog/2007/03/printf-sprintf/
+ * http://hexmen.com/js/sprintf.js
+ * The author (Ash Searle) has placed this code in the public domain:
+ * "This code is unrestricted: you are free to use it however you like."
+ *
+ */
+(function($) {
+ // class: $.jqplot.MekkoAxisRenderer
+ // An axis renderer for a Mekko chart.
+ // Should be used with a Mekko chart where the mekkoRenderer is used on the series.
+ // Displays the Y axis as a range from 0 to 1 (0 to 100%) and the x axis with a tick
+ // for each series scaled to the sum of all the y values.
+ $.jqplot.MekkoAxisRenderer = function() {
+ };
+
+ // called with scope of axis object.
+ $.jqplot.MekkoAxisRenderer.prototype.init = function(options){
+ // prop: tickMode
+ // How to space the ticks on the axis.
+ // 'bar' will place a tick at the width of each bar.
+ // This is the default for the x axis.
+ // 'even' will place ticks at even intervals. This is
+ // the default for x2 axis and y axis. y axis cannot be changed.
+ this.tickMode;
+ // prop: barLabelRenderer
+ // renderer to use to draw labels under each bar.
+ this.barLabelRenderer = $.jqplot.AxisLabelRenderer;
+ // prop: barLabels
+ // array of labels to put under each bar.
+ this.barLabels = this.barLabels || [];
+ // prop: barLabelOptions
+ // options object to pass to the bar label renderer.
+ this.barLabelOptions = {};
+ this.tickOptions = $.extend(true, {showGridline:false}, this.tickOptions);
+ this._barLabels = [];
+ $.extend(true, this, options);
+ if (this.name == 'yaxis') {
+ this.tickOptions.formatString = this.tickOptions.formatString || "%d\%";
+ }
+ var db = this._dataBounds;
+ db.min = 0;
+ // for y axes, scale always go from 0 to 1 (0 to 100%)
+ if (this.name == 'yaxis' || this.name == 'y2axis') {
+ db.max = 100;
+ this.tickMode = 'even';
+ }
+ // For x axes, scale goes from 0 to sum of all y values.
+ else if (this.name == 'xaxis'){
+ this.tickMode = (this.tickMode == null) ? 'bar' : this.tickMode;
+ for (var i=0; i dim) {
+ dim = temp;
+ }
+ }
+ }
+
+ if (lshow) {
+ w = this._label._elem.outerWidth(true);
+ h = this._label._elem.outerHeight(true);
+ }
+ if (this.name == 'xaxis') {
+ dim = dim + h;
+ this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'});
+ }
+ else if (this.name == 'x2axis') {
+ dim = dim + h;
+ this._elem.css({'height':dim+'px', left:'0px', top:'0px'});
+ }
+ else if (this.name == 'yaxis') {
+ dim = dim + 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 = dim + 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.MekkoAxisRenderer.prototype.createTicks = function() {
+ // we're are operating on an axis here
+ var ticks = this._ticks;
+ var userTicks = this.ticks;
+ var name = this.name;
+ // databounds were set on axis initialization.
+ var db = this._dataBounds;
+ var dim, interval;
+ var min, max;
+ var pos1, pos2;
+ var t, tt, i, j;
+
+ // if we already have ticks, use them.
+ // ticks must be in order of increasing value.
+
+ if (userTicks.length) {
+ // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed
+ for (i=0; i 0) {
+ adj = Math.max(Math.log(min)/Math.LN10, 0.05);
+ }
+ min -= adj;
+ max += adj;
+ }
+
+ var range = max - min;
+ var rmin, rmax;
+ var temp, prev, curr;
+ var ynumticks = [3,5,6,11,21];
+
+ // yaxis divide ticks in nice intervals from 0 to 1.
+ if (this.name == 'yaxis' || this.name == 'y2axis') {
+ this.min = 0;
+ this.max = 100;
+ // user didn't specify number of ticks.
+ if (!this.numberTicks){
+ if (this.tickInterval) {
+ this.numberTicks = 3 + Math.ceil(range / this.tickInterval);
+ }
+ else {
+ temp = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
+ for (i=0; i 1) {
+ prev = curr;
+ continue;
+ }
+ else if (curr < 1) {
+ // was prev or is curr closer to one?
+ if (Math.abs(prev - 1) < Math.abs(curr - 1)) {
+ this.numberTicks = ynumticks[i-1];
+ break;
+ }
+ else {
+ this.numberTicks = ynumticks[i];
+ break;
+ }
+ }
+ else if (i == ynumticks.length -1) {
+ this.numberTicks = ynumticks[i];
+ }
+ }
+ this.tickInterval = range / (this.numberTicks - 1);
+ }
+ }
+
+ // user did specify number of ticks.
+ else {
+ this.tickInterval = range / (this.numberTicks - 1);
+ }
+
+ for (var i=0; i temp) {
+ t = new this.tickRenderer(this.tickOptions);
+ if (!this.showTicks) {
+ t.showLabel = false;
+ t.showMark = false;
+ }
+ else if (!this.showTickMarks) {
+ t.showMark = false;
+ }
+ t.setTick(this.max, this.name);
+ this._ticks.push(t);
+
+ }
+ }
+
+ else if (this.tickMode == 'even') {
+ this.min = 0;
+ this.max = this.max || db.max;
+ // get a desired number of ticks
+ var nt = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
+ range = this.max - this.min;
+ this.numberTicks = nt;
+ this.tickInterval = range / (this.numberTicks - 1);
+
+ for (i=0; i 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':
+ 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();
+ }
+ }
+ 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');
+ }
+ else {
+ this._label._elem.css('right', '0px');
+ }
+ this._label.pack();
+ }
+ }
+ }
+ };
+})(jQuery);
diff --git a/public/site_assets/test/js/plugins/jqplot.mekkoAxisRenderer.min.js b/public/site_assets/test/js/plugins/jqplot.mekkoAxisRenderer.min.js
new file mode 100644
index 00000000..7969de73
--- /dev/null
+++ b/public/site_assets/test/js/plugins/jqplot.mekkoAxisRenderer.min.js
@@ -0,0 +1,3 @@
+/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com
+ jsDate | (c) 2010-2013 Chris Leonello
+ */(function(a){a.jqplot.MekkoAxisRenderer=function(){};a.jqplot.MekkoAxisRenderer.prototype.init=function(c){this.tickMode;this.barLabelRenderer=a.jqplot.AxisLabelRenderer;this.barLabels=this.barLabels||[];this.barLabelOptions={};this.tickOptions=a.extend(true,{showGridline:false},this.tickOptions);this._barLabels=[];a.extend(true,this,c);if(this.name=="yaxis"){this.tickOptions.formatString=this.tickOptions.formatString||"%d%"}var b=this._dataBounds;b.min=0;if(this.name=="yaxis"||this.name=="y2axis"){b.max=100;this.tickMode="even"}else{if(this.name=="xaxis"){this.tickMode=(this.tickMode==null)?"bar":this.tickMode;for(var d=0;dk){k=d}}}if(b){c=this._label._elem.outerWidth(true);j=this._label._elem.outerHeight(true)}if(this.name=="xaxis"){k=k+j;this._elem.css({height:k+"px",left:"0px",bottom:"0px"})}else{if(this.name=="x2axis"){k=k+j;this._elem.css({height:k+"px",left:"0px",top:"0px"})}else{if(this.name=="yaxis"){k=k+c;this._elem.css({width:k+"px",left:"0px",top:"0px"});if(b&&this._label.constructor==a.jqplot.AxisLabelRenderer){this._label._elem.css("width",c+"px")}}else{k=k+c;this._elem.css({width:k+"px",right:"0px",top:"0px"});if(b&&this._label.constructor==a.jqplot.AxisLabelRenderer){this._label._elem.css("width",c+"px")}}}}}};a.jqplot.MekkoAxisRenderer.prototype.createTicks=function(){var z=this._ticks;var w=this.ticks;var B=this.name;var y=this._dataBounds;var p,x;var n,r;var d,c;var h,b,s,q;if(w.length){for(s=0;s0){g=Math.max(Math.log(n)/Math.LN10,0.05)}n-=g;r+=g}var k=r-n;var m,o;var v,l,u;var f=[3,5,6,11,21];if(this.name=="yaxis"||this.name=="y2axis"){this.min=0;this.max=100;if(!this.numberTicks){if(this.tickInterval){this.numberTicks=3+Math.ceil(k/this.tickInterval)}else{v=2+Math.ceil((p-(this.tickSpacing-1))/this.tickSpacing);for(s=0;s1){l=u;continue}else{if(u<1){if(Math.abs(l-1)v){h=new this.tickRenderer(this.tickOptions);if(!this.showTicks){h.showLabel=false;h.showMark=false}else{if(!this.showTickMarks){h.showMark=false}}h.setTick(this.max,this.name);this._ticks.push(h)}}else{if(this.tickMode=="even"){this.min=0;this.max=this.max||y.max;var A=2+Math.ceil((p-(this.tickSpacing-1))/this.tickSpacing);k=this.max-this.min;this.numberTicks=A;this.tickInterval=k/(this.numberTicks-1);for(s=0;s0){c=-n._textRenderer.height*Math.cos(-n._textRenderer.angle)/2}else{c=-n.getHeight()+n._textRenderer.height*Math.cos(n._textRenderer.angle)/2}break;case"middle":c=-n.getHeight()/2;break;default:c=-n.getHeight()/2;break}}else{c=-n.getHeight()/2}var D=this.u2p(n.value)+c+"px";n._elem.css("top",D);n.pack()}}if(o){var z=this._label._elem.outerHeight(true);this._label._elem.css("top",m-f/2-z/2+"px");if(this.name=="yaxis"){this._label._elem.css("left","0px")}else{this._label._elem.css("right","0px")}this._label.pack()}}}}})(jQuery);
\ No newline at end of file
diff --git a/public/site_assets/test/js/plugins/jqplot.mekkoRenderer.js b/public/site_assets/test/js/plugins/jqplot.mekkoRenderer.js
new file mode 100644
index 00000000..05f495fb
--- /dev/null
+++ b/public/site_assets/test/js/plugins/jqplot.mekkoRenderer.js
@@ -0,0 +1,437 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.8
+ * Revision: 1250
+ *
+ * Copyright (c) 2009-2013 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, the author would appreciate an email letting him
+ * know of any substantial use of jqPlot. You can reach the author at:
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * version 2007.04.27
+ * author Ash Searle
+ * http://hexmen.com/blog/2007/03/printf-sprintf/
+ * http://hexmen.com/js/sprintf.js
+ * The author (Ash Searle) has placed this code in the public domain:
+ * "This code is unrestricted: you are free to use it however you like."
+ *
+ */
+(function($) {
+ /**
+ * Class: $.jqplot.MekkoRenderer
+ * Draws a Mekko style chart which shows 3 dimensional data on a 2 dimensional graph.
+ * the <$.jqplot.MekkoAxisRenderer> should be used with mekko charts. The mekko renderer
+ * overrides the default legend renderer with its own $.jqplot.MekkoLegendRenderer
+ * which allows more flexibility to specify number of rows and columns in the legend.
+ *
+ * Data is specified per bar in the chart. You can specify data as an array of y values, or as
+ * an array of [label, value] pairs. Note that labels are used only on the first series.
+ * Labels on subsequent series are ignored:
+ *
+ * > bar1 = [['shirts', 8],['hats', 14],['shoes', 6],['gloves', 16],['dolls', 12]];
+ * > bar2 = [15,6,9,13,6];
+ * > bar3 = [['grumpy',4],['sneezy',2],['happy',7],['sleepy',9],['doc',7]];
+ *
+ * If you want to place labels for each bar under the axis, you use the barLabels option on
+ * the axes. The bar labels can be styled with the ".jqplot-mekko-barLabel" css class.
+ *
+ * > barLabels = ['Mickey Mouse', 'Donald Duck', 'Goofy'];
+ * > axes:{xaxis:{barLabels:barLabels}}
+ *
+ */
+
+
+ $.jqplot.MekkoRenderer = function(){
+ this.shapeRenderer = new $.jqplot.ShapeRenderer();
+ // prop: borderColor
+ // color of the borders between areas on the chart
+ this.borderColor = null;
+ // prop: showBorders
+ // True to draw borders lines between areas on the chart.
+ // False will draw borders lines with the same color as the area.
+ this.showBorders = true;
+ };
+
+ // called with scope of series.
+ $.jqplot.MekkoRenderer.prototype.init = function(options, plot) {
+ this.fill = false;
+ this.fillRect = true;
+ this.strokeRect = true;
+ this.shadow = false;
+ // width of bar on x axis.
+ this._xwidth = 0;
+ this._xstart = 0;
+ $.extend(true, this.renderer, options);
+ // set the shape renderer options
+ var opts = {lineJoin:'miter', lineCap:'butt', isarc:false, fillRect:this.fillRect, strokeRect:this.strokeRect};
+ this.renderer.shapeRenderer.init(opts);
+ plot.axes.x2axis._series.push(this);
+ this._type = 'mekko';
+ };
+
+ // Method: setGridData
+ // converts the user data values to grid coordinates and stores them
+ // in the gridData array. Will convert user data into appropriate
+ // rectangles.
+ // Called with scope of a series.
+ $.jqplot.MekkoRenderer.prototype.setGridData = function(plot) {
+ // recalculate the grid data
+ var xp = this._xaxis.series_u2p;
+ var yp = this._yaxis.series_u2p;
+ var data = this._plotData;
+ this.gridData = [];
+ // figure out width on x axis.
+ // this._xwidth = this._sumy / plot._sumy * this.canvas.getWidth();
+ this._xwidth = xp(this._sumy) - xp(0);
+ if (this.index>0) {
+ this._xstart = plot.series[this.index-1]._xstart + plot.series[this.index-1]._xwidth;
+ }
+ var totheight = this.canvas.getHeight();
+ var sumy = 0;
+ var cury;
+ var curheight;
+ for (var i=0; i');
+ // Mekko charts legends don't go by number of series, but by number of data points
+ // in the series. Refactor things here for that.
+
+ var pad = false,
+ reverse = true, // mekko charts are always stacked, so reverse
+ nr, nc;
+ var s = series[0];
+ var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors);
+
+ if (s.show) {
+ var pd = s.data;
+ if (this.numberRows) {
+ nr = this.numberRows;
+ if (!this.numberColumns){
+ nc = Math.ceil(pd.length/nr);
+ }
+ else{
+ nc = this.numberColumns;
+ }
+ }
+ else if (this.numberColumns) {
+ nc = this.numberColumns;
+ nr = Math.ceil(pd.length/this.numberColumns);
+ }
+ else {
+ nr = pd.length;
+ nc = 1;
+ }
+
+ var i, j, tr, td1, td2, lt, rs, color;
+ var idx = 0;
+
+ for (i=0; i').prependTo(this._elem);
+ }
+ else{
+ tr = $('
').appendTo(this._elem);
+ }
+ for (j=0; j0){
+ pad = true;
+ }
+ else{
+ pad = false;
+ }
+ }
+ else{
+ if (i == nr -1){
+ pad = false;
+ }
+ else{
+ pad = true;
+ }
+ }
+ rs = (pad) ? this.rowSpacing : '0';
+
+ td1 = $('
'+
+ '
'+
+ '
');
+ td2 = $('
');
+ if (this.escapeHtml){
+ td2.text(lt);
+ }
+ else {
+ td2.html(lt);
+ }
+ if (reverse) {
+ td2.prependTo(tr);
+ td1.prependTo(tr);
+ }
+ else {
+ td1.appendTo(tr);
+ td2.appendTo(tr);
+ }
+ pad = true;
+ }
+ idx++;
+ }
+ }
+
+ tr = null;
+ td1 = null;
+ td2 = null;
+ }
+ }
+ return this._elem;
+ };
+
+ $.jqplot.MekkoLegendRenderer.prototype.pack = function(offsets) {
+ if (this.show) {
+ // fake a grid for positioning
+ var grid = {_top:offsets.top, _left:offsets.left, _right:offsets.right, _bottom:this._plotDimensions.height - offsets.bottom};
+ if (this.placement == 'insideGrid') {
+ switch (this.location) {
+ case 'nw':
+ var a = grid._left + this.xoffset;
+ var b = grid._top + this.yoffset;
+ this._elem.css('left', a);
+ this._elem.css('top', b);
+ break;
+ case 'n':
+ var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+ var b = grid._top + this.yoffset;
+ this._elem.css('left', a);
+ this._elem.css('top', b);
+ break;
+ case 'ne':
+ var a = offsets.right + this.xoffset;
+ var b = grid._top + this.yoffset;
+ this._elem.css({right:a, top:b});
+ break;
+ case 'e':
+ var a = offsets.right + this.xoffset;
+ var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+ this._elem.css({right:a, top:b});
+ break;
+ case 'se':
+ var a = offsets.right + this.xoffset;
+ var b = offsets.bottom + this.yoffset;
+ this._elem.css({right:a, bottom:b});
+ break;
+ case 's':
+ var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+ var b = offsets.bottom + this.yoffset;
+ this._elem.css({left:a, bottom:b});
+ break;
+ case 'sw':
+ var a = grid._left + this.xoffset;
+ var b = offsets.bottom + this.yoffset;
+ this._elem.css({left:a, bottom:b});
+ break;
+ case 'w':
+ var a = grid._left + this.xoffset;
+ var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+ this._elem.css({left:a, top:b});
+ break;
+ default: // same as 'se'
+ var a = grid._right - this.xoffset;
+ var b = grid._bottom + this.yoffset;
+ this._elem.css({right:a, bottom:b});
+ break;
+ }
+
+ }
+ else {
+ switch (this.location) {
+ case 'nw':
+ var a = this._plotDimensions.width - grid._left + this.xoffset;
+ var b = grid._top + this.yoffset;
+ this._elem.css('right', a);
+ this._elem.css('top', b);
+ break;
+ case 'n':
+ var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+ var b = this._plotDimensions.height - grid._top + this.yoffset;
+ this._elem.css('left', a);
+ this._elem.css('bottom', b);
+ break;
+ case 'ne':
+ var a = this._plotDimensions.width - offsets.right + this.xoffset;
+ var b = grid._top + this.yoffset;
+ this._elem.css({left:a, top:b});
+ break;
+ case 'e':
+ var a = this._plotDimensions.width - offsets.right + this.xoffset;
+ var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+ this._elem.css({left:a, top:b});
+ break;
+ case 'se':
+ var a = this._plotDimensions.width - offsets.right + this.xoffset;
+ var b = offsets.bottom + this.yoffset;
+ this._elem.css({left:a, bottom:b});
+ break;
+ case 's':
+ var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+ var b = this._plotDimensions.height - offsets.bottom + this.yoffset;
+ this._elem.css({left:a, top:b});
+ break;
+ case 'sw':
+ var a = this._plotDimensions.width - grid._left + this.xoffset;
+ var b = offsets.bottom + this.yoffset;
+ this._elem.css({right:a, bottom:b});
+ break;
+ case 'w':
+ var a = this._plotDimensions.width - grid._left + this.xoffset;
+ var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+ this._elem.css({right:a, top:b});
+ break;
+ default: // same as 'se'
+ var a = grid._right - this.xoffset;
+ var b = grid._bottom + this.yoffset;
+ this._elem.css({right:a, bottom:b});
+ break;
+ }
+ }
+ }
+ };
+
+ // setup default renderers for axes and legend so user doesn't have to
+ // called with scope of plot
+ function preInit(target, data, options) {
+ options = options || {};
+ options.axesDefaults = options.axesDefaults || {};
+ options.legend = options.legend || {};
+ options.seriesDefaults = options.seriesDefaults || {};
+ var setopts = false;
+ if (options.seriesDefaults.renderer == $.jqplot.MekkoRenderer) {
+ setopts = true;
+ }
+ else if (options.series) {
+ for (var i=0; i < options.series.length; i++) {
+ if (options.series[i].renderer == $.jqplot.MekkoRenderer) {
+ setopts = true;
+ }
+ }
+ }
+
+ if (setopts) {
+ options.axesDefaults.renderer = $.jqplot.MekkoAxisRenderer;
+ options.legend.renderer = $.jqplot.MekkoLegendRenderer;
+ options.legend.preDraw = true;
+ }
+ }
+
+ $.jqplot.preInitHooks.push(preInit);
+
+})(jQuery);
diff --git a/public/site_assets/test/js/plugins/jqplot.mekkoRenderer.min.js b/public/site_assets/test/js/plugins/jqplot.mekkoRenderer.min.js
new file mode 100644
index 00000000..18dc3a12
--- /dev/null
+++ b/public/site_assets/test/js/plugins/jqplot.mekkoRenderer.min.js
@@ -0,0 +1,3 @@
+/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com
+ jsDate | (c) 2010-2013 Chris Leonello
+ */(function(b){b.jqplot.MekkoRenderer=function(){this.shapeRenderer=new b.jqplot.ShapeRenderer();this.borderColor=null;this.showBorders=true};b.jqplot.MekkoRenderer.prototype.init=function(c,e){this.fill=false;this.fillRect=true;this.strokeRect=true;this.shadow=false;this._xwidth=0;this._xstart=0;b.extend(true,this.renderer,c);var d={lineJoin:"miter",lineCap:"butt",isarc:false,fillRect:this.fillRect,strokeRect:this.strokeRect};this.renderer.shapeRenderer.init(d);e.axes.x2axis._series.push(this);this._type="mekko"};b.jqplot.MekkoRenderer.prototype.setGridData=function(h){var e=this._xaxis.series_u2p;var c=this._yaxis.series_u2p;var g=this._plotData;this.gridData=[];this._xwidth=e(this._sumy)-e(0);if(this.index>0){this._xstart=h.series[this.index-1]._xstart+h.series[this.index-1]._xwidth}var l=this.canvas.getHeight();var d=0;var k;var j;for(var f=0;f');var w=false,n=true,c,l;var p=o[0];var d=new b.jqplot.ColorGenerator(p.seriesColors);if(p.show){var x=p.data;if(this.numberRows){c=this.numberRows;if(!this.numberColumns){l=Math.ceil(x.length/c)}else{l=this.numberColumns}}else{if(this.numberColumns){l=this.numberColumns;c=Math.ceil(x.length/this.numberColumns)}else{c=x.length;l=1}}var v,u,e,h,g,k,m,t;var q=0;for(v=0;v').prependTo(this._elem)}else{e=b('
');if(this.escapeHtml){g.text(k)}else{g.html(k)}if(n){g.prependTo(e);h.prependTo(e)}else{h.appendTo(e);g.appendTo(e)}w=true}q++}}e=null;h=null;g=null}}return this._elem};b.jqplot.MekkoLegendRenderer.prototype.pack=function(f){if(this.show){var e={_top:f.top,_left:f.left,_right:f.right,_bottom:this._plotDimensions.height-f.bottom};if(this.placement=="insideGrid"){switch(this.location){case"nw":var d=e._left+this.xoffset;var c=e._top+this.yoffset;this._elem.css("left",d);this._elem.css("top",c);break;case"n":var d=(f.left+(this._plotDimensions.width-f.right))/2-this.getWidth()/2;var c=e._top+this.yoffset;this._elem.css("left",d);this._elem.css("top",c);break;case"ne":var d=f.right+this.xoffset;var c=e._top+this.yoffset;this._elem.css({right:d,top:c});break;case"e":var d=f.right+this.xoffset;var c=(f.top+(this._plotDimensions.height-f.bottom))/2-this.getHeight()/2;this._elem.css({right:d,top:c});break;case"se":var d=f.right+this.xoffset;var c=f.bottom+this.yoffset;this._elem.css({right:d,bottom:c});break;case"s":var d=(f.left+(this._plotDimensions.width-f.right))/2-this.getWidth()/2;var c=f.bottom+this.yoffset;this._elem.css({left:d,bottom:c});break;case"sw":var d=e._left+this.xoffset;var c=f.bottom+this.yoffset;this._elem.css({left:d,bottom:c});break;case"w":var d=e._left+this.xoffset;var c=(f.top+(this._plotDimensions.height-f.bottom))/2-this.getHeight()/2;this._elem.css({left:d,top:c});break;default:var d=e._right-this.xoffset;var c=e._bottom+this.yoffset;this._elem.css({right:d,bottom:c});break}}else{switch(this.location){case"nw":var d=this._plotDimensions.width-e._left+this.xoffset;var c=e._top+this.yoffset;this._elem.css("right",d);this._elem.css("top",c);break;case"n":var d=(f.left+(this._plotDimensions.width-f.right))/2-this.getWidth()/2;var c=this._plotDimensions.height-e._top+this.yoffset;this._elem.css("left",d);this._elem.css("bottom",c);break;case"ne":var d=this._plotDimensions.width-f.right+this.xoffset;var c=e._top+this.yoffset;this._elem.css({left:d,top:c});break;case"e":var d=this._plotDimensions.width-f.right+this.xoffset;var c=(f.top+(this._plotDimensions.height-f.bottom))/2-this.getHeight()/2;this._elem.css({left:d,top:c});break;case"se":var d=this._plotDimensions.width-f.right+this.xoffset;var c=f.bottom+this.yoffset;this._elem.css({left:d,bottom:c});break;case"s":var d=(f.left+(this._plotDimensions.width-f.right))/2-this.getWidth()/2;var c=this._plotDimensions.height-f.bottom+this.yoffset;this._elem.css({left:d,top:c});break;case"sw":var d=this._plotDimensions.width-e._left+this.xoffset;var c=f.bottom+this.yoffset;this._elem.css({right:d,bottom:c});break;case"w":var d=this._plotDimensions.width-e._left+this.xoffset;var c=(f.top+(this._plotDimensions.height-f.bottom))/2-this.getHeight()/2;this._elem.css({right:d,top:c});break;default:var d=e._right-this.xoffset;var c=e._bottom+this.yoffset;this._elem.css({right:d,bottom:c});break}}}};function a(g,f,d){d=d||{};d.axesDefaults=d.axesDefaults||{};d.legend=d.legend||{};d.seriesDefaults=d.seriesDefaults||{};var c=false;if(d.seriesDefaults.renderer==b.jqplot.MekkoRenderer){c=true}else{if(d.series){for(var e=0;e
+ *
+ * Properties described here are passed into the $.jqplot function
+ * as options on the series renderer. For example:
+ *
+ * > plot0 = $.jqplot('chart0',[[18]],{
+ * > title: 'Network Speed',
+ * > seriesDefaults: {
+ * > renderer: $.jqplot.MeterGaugeRenderer,
+ * > rendererOptions: {
+ * > label: 'MB/s'
+ * > }
+ * > }
+ * > });
+ *
+ * A meterGauge plot does not support events.
+ */
+ $.jqplot.MeterGaugeRenderer = function(){
+ $.jqplot.LineRenderer.call(this);
+ };
+
+ $.jqplot.MeterGaugeRenderer.prototype = new $.jqplot.LineRenderer();
+ $.jqplot.MeterGaugeRenderer.prototype.constructor = $.jqplot.MeterGaugeRenderer;
+
+ // called with scope of a series
+ $.jqplot.MeterGaugeRenderer.prototype.init = function(options) {
+ // Group: Properties
+ //
+ // prop: diameter
+ // Outer diameter of the meterGauge, auto computed by default
+ this.diameter = null;
+ // prop: padding
+ // padding between the meterGauge and plot edges, auto
+ // calculated by default.
+ this.padding = null;
+ // prop: shadowOffset
+ // offset of the shadow from the gauge ring 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 = 4;
+ // prop: background
+ // background color of the inside of the gauge.
+ this.background = "#efefef";
+ // prop: ringColor
+ // color of the outer ring, hub, and needle of the gauge.
+ this.ringColor = "#BBC6D0";
+ // needle color not implemented yet.
+ this.needleColor = "#C3D3E5";
+ // prop: tickColor
+ // color of the tick marks around the gauge.
+ this.tickColor = "#989898";
+ // prop: ringWidth
+ // width of the ring around the gauge. Auto computed by default.
+ this.ringWidth = null;
+ // prop: min
+ // Minimum value on the gauge. Auto computed by default
+ this.min;
+ // prop: max
+ // Maximum value on the gauge. Auto computed by default
+ this.max;
+ // prop: ticks
+ // Array of tick values. Auto computed by default.
+ this.ticks = [];
+ // prop: showTicks
+ // true to show ticks around gauge.
+ this.showTicks = true;
+ // prop: showTickLabels
+ // true to show tick labels next to ticks.
+ this.showTickLabels = true;
+ // prop: label
+ // A gauge label like 'kph' or 'Volts'
+ this.label = null;
+ // prop: labelHeightAdjust
+ // Number of Pixels to offset the label up (-) or down (+) from its default position.
+ this.labelHeightAdjust = 0;
+ // prop: labelPosition
+ // Where to position the label, either 'inside' or 'bottom'.
+ this.labelPosition = 'inside';
+ // prop: intervals
+ // Array of ranges to be drawn around the gauge.
+ // Array of form:
+ // > [value1, value2, ...]
+ // indicating the values for the first, second, ... intervals.
+ this.intervals = [];
+ // prop: intervalColors
+ // Array of colors to use for the intervals.
+ this.intervalColors = [ "#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"];
+ // prop: intervalInnerRadius
+ // Radius of the inner circle of the interval ring.
+ this.intervalInnerRadius = null;
+ // prop: intervalOuterRadius
+ // Radius of the outer circle of the interval ring.
+ this.intervalOuterRadius = null;
+ this.tickRenderer = $.jqplot.MeterGaugeTickRenderer;
+ // ticks spaced every 1, 2, 2.5, 5, 10, 20, .1, .2, .25, .5, etc.
+ this.tickPositions = [1, 2, 2.5, 5, 10];
+ // prop: tickSpacing
+ // Degrees between ticks. This is a target number, if
+ // incompatible span and ticks are supplied, a suitable
+ // spacing close to this value will be computed.
+ this.tickSpacing = 30;
+ this.numberMinorTicks = null;
+ // prop: hubRadius
+ // Radius of the hub at the bottom center of gauge which the needle attaches to.
+ // Auto computed by default
+ this.hubRadius = null;
+ // prop: tickPadding
+ // padding of the tick marks to the outer ring and the tick labels to marks.
+ // Auto computed by default.
+ this.tickPadding = null;
+ // prop: needleThickness
+ // Maximum thickness the needle. Auto computed by default.
+ this.needleThickness = null;
+ // prop: needlePad
+ // Padding between needle and inner edge of the ring when the needle is at the min or max gauge value.
+ this.needlePad = 6;
+ // prop: pegNeedle
+ // True will stop needle just below/above the min/max values if data is below/above min/max,
+ // as if the meter is "pegged".
+ this.pegNeedle = true;
+ this._type = 'meterGauge';
+
+ $.extend(true, this, options);
+ this.type = null;
+ this.numberTicks = null;
+ this.tickInterval = null;
+ // span, the sweep (in degrees) from min to max. This gauge is
+ // a semi-circle.
+ this.span = 180;
+ // get rid of this nonsense
+ // this.innerSpan = this.span;
+ if (this.type == 'circular') {
+ this.semiCircular = false;
+ }
+ else if (this.type != 'circular') {
+ this.semiCircular = true;
+ }
+ else {
+ this.semiCircular = (this.span <= 180) ? true : false;
+ }
+ this._tickPoints = [];
+ // reference to label element.
+ this._labelElem = null;
+
+ // start the gauge at the beginning of the span
+ this.startAngle = (90 + (360 - this.span)/2) * Math.PI/180;
+ this.endAngle = (90 - (360 - this.span)/2) * Math.PI/180;
+
+ this.setmin = !!(this.min == null);
+ this.setmax = !!(this.max == null);
+
+ // if given intervals and is an array of values, create labels and colors.
+ if (this.intervals.length) {
+ if (this.intervals[0].length == null || this.intervals.length == 1) {
+ for (var i=0; i= this.data[0][1]) {
+ this.max = this.intervals[this.intervals.length-1][0];
+ this.setmax = false;
+ }
+ }
+ else {
+ this.setmax = false;
+ }
+ }
+
+ else {
+ // no ticks and no intervals supplied, put needle in middle
+ this.min = (this.min == null) ? 0 : this.min;
+ this.setmin = false;
+ if (this.max == null) {
+ this.max = this.data[0][1] * 1.25;
+ this.setmax = true;
+ }
+ else {
+ this.setmax = false;
+ }
+ }
+ };
+
+ $.jqplot.MeterGaugeRenderer.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;
+ for (var i=0; i0) {
+ stack[i] += stack[i-1];
+ }
+ }
+ var fact = Math.PI*2/stack[stack.length - 1];
+
+ for (var i=0; i0) {
+ stack[i] += stack[i-1];
+ }
+ }
+ var fact = Math.PI*2/stack[stack.length - 1];
+
+ for (var i=0; i=0; i--) {
+ temp = interval/(pos[i] * Math.pow(10, fact));
+ if (temp == 4 || temp == 5) {
+ return temp - 1;
+ }
+ }
+ return null;
+ }
+
+ // called with scope of series
+ $.jqplot.MeterGaugeRenderer.prototype.draw = function (ctx, gd, options) {
+ 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;
+ if (options.legendInfo && options.legendInfo.placement == 'inside') {
+ 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;
+ }
+ }
+
+
+
+ // pre-draw so can get its dimensions.
+ if (this.label) {
+ this._labelElem = $('
'+this.label+'
');
+ this.canvas._elem.after(this._labelElem);
+ }
+
+ 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;
+ if (this.padding == null) {
+ this.padding = Math.round(Math.min(cw, ch)/30);
+ }
+ var w = cw - offx - 2 * this.padding;
+ var h = ch - offy - 2 * this.padding;
+ if (this.labelPosition == 'bottom' && this.label) {
+ h -= this._labelElem.outerHeight(true);
+ }
+ var mindim = Math.min(w,h);
+ var d = mindim;
+
+ if (!this.diameter) {
+ if (this.semiCircular) {
+ if ( w >= 2*h) {
+ if (!this.ringWidth) {
+ this.ringWidth = 2*h/35;
+ }
+ this.needleThickness = this.needleThickness || 2+Math.pow(this.ringWidth, 0.8);
+ this.innerPad = this.ringWidth/2 + this.needleThickness/2 + this.needlePad;
+ this.diameter = 2 * (h - 2*this.innerPad);
+ }
+ else {
+ if (!this.ringWidth) {
+ this.ringWidth = w/35;
+ }
+ this.needleThickness = this.needleThickness || 2+Math.pow(this.ringWidth, 0.8);
+ this.innerPad = this.ringWidth/2 + this.needleThickness/2 + this.needlePad;
+ this.diameter = w - 2*this.innerPad - this.ringWidth - this.padding;
+ }
+ // center taking into account legend and over draw for gauge bottom below hub.
+ // this will be center of hub.
+ this._center = [(cw - trans * offx)/2 + trans * offx, (ch + trans*offy - this.padding - this.ringWidth - this.innerPad)];
+ }
+ else {
+ if (!this.ringWidth) {
+ this.ringWidth = d/35;
+ }
+ this.needleThickness = this.needleThickness || 2+Math.pow(this.ringWidth, 0.8);
+ this.innerPad = 0;
+ this.diameter = d - this.ringWidth;
+ // center in middle of canvas taking into account legend.
+ // will be center of hub.
+ this._center = [(cw-trans*offx)/2 + trans * offx, (ch-trans*offy)/2 + trans * offy];
+ }
+ if (this._labelElem && this.labelPosition == 'bottom') {
+ this._center[1] -= this._labelElem.outerHeight(true);
+ }
+
+ }
+
+ this._radius = this.diameter/2;
+
+ this.tickSpacing = 6000/this.diameter;
+
+ if (!this.hubRadius) {
+ this.hubRadius = this.diameter/18;
+ }
+
+ this.shadowOffset = 0.5 + this.ringWidth/9;
+ this.shadowWidth = this.ringWidth*1;
+
+ this.tickPadding = 3 + Math.pow(this.diameter/20, 0.7);
+ this.tickOuterRadius = this._radius - this.ringWidth/2 - this.tickPadding;
+ this.tickLength = (this.showTicks) ? this._radius/13 : 0;
+
+ if (this.ticks.length == 0) {
+ // no ticks, lets make some.
+ var max = this.max,
+ min = this.min,
+ setmax = this.setmax,
+ setmin = this.setmin,
+ ti = (max - min) * this.tickSpacing / this.span;
+ var tf = Math.floor(parseFloat((Math.log(ti)/Math.log(10)).toFixed(11)));
+ var tp = (ti/Math.pow(10, tf));
+ (tp > 2 && tp <= 2.5) ? tp = 2.5 : tp = Math.ceil(tp);
+ var t = this.tickPositions;
+ var tpindex, nt;
+
+ for (i=0; i 0) ? min - min % ti : min - min % ti - ti;
+ if (!this.forceZero) {
+ var diff = Math.min(min - tmin, 0.8*ti);
+ var ntp = Math.floor(diff/t[tpindex]);
+ if (ntp > 1) {
+ tmin = tmin + t[tpindex] * (ntp-1);
+ if (parseInt(tmin, 10) != tmin && parseInt(tmin-t[tpindex], 10) == tmin-t[tpindex]) {
+ tmin = tmin - t[tpindex];
+ }
+ }
+ }
+ if (min == tmin) {
+ min -= ti;
+ }
+ else {
+ // tmin should always be lower than dataMin
+ if (min - tmin > 0.23*ti) {
+ min = tmin;
+ }
+ else {
+ min = tmin -ti;
+ nt += 1;
+ }
+ }
+ nt += 1;
+ var tmax = min + (nt - 1) * ti;
+ if (max >= tmax) {
+ tmax += ti;
+ nt += 1;
+ }
+ // now tmax should always be mroe than dataMax
+ if (tmax - max < 0.23*ti) {
+ tmax += ti;
+ nt += 1;
+ }
+ this.max = max = tmax;
+ this.min = min;
+
+ this.tickInterval = ti;
+ this.numberTicks = nt;
+ var it;
+ for (i=0; i= tmax) {
+ max = tmax + ti;
+ nt += 1;
+ }
+ else {
+ max = tmax;
+ }
+
+ this.tickInterval = this.tickInterval || ti;
+ this.numberTicks = this.numberTicks || nt;
+ var it;
+ for (i=0; i 1) {
+ var rstr = String(range);
+ if (rstr.search(/\./) == -1) {
+ var pos = rstr.search(/0+$/);
+ nonSigDigits = (pos > 0) ? rstr.length - pos - 1 : 0;
+ }
+ }
+ sigRange = range/Math.pow(10, nonSigDigits);
+ for (i=0; i'+this.ticks[i][1]+'');
+ this.canvas._elem.after(elem);
+ ew = elem.outerWidth(true);
+ eh = elem.outerHeight(true);
+ l = this._tickPoints[i][0] - ew * (this._tickPoints[i][2]-Math.PI)/Math.PI - tp * Math.cos(this._tickPoints[i][2]);
+ t = this._tickPoints[i][1] - eh/2 + eh/2 * Math.pow(Math.abs((Math.sin(this._tickPoints[i][2]))), 0.5) + tp/3 * Math.pow(Math.abs((Math.sin(this._tickPoints[i][2]))), 0.5) ;
+ // t = this._tickPoints[i][1] - eh/2 - eh/2 * Math.sin(this._tickPoints[i][2]) - tp/2 * Math.sin(this._tickPoints[i][2]);
+ elem.css({left:l, top:t, color: this.tickColor});
+ dim = ew*Math.cos(this._tickPoints[i][2]) + eh*Math.sin(Math.PI/2+this._tickPoints[i][2]/2);
+ maxdim = (dim > maxdim) ? dim : maxdim;
+ }
+ }
+
+ // draw the gauge label
+ if (this.label && this.labelPosition == 'inside') {
+ var l = this._center[0] + this.canvas._offsets.left;
+ var tp = this.tickPadding * (1 - 1/(this.diameter/80+1));
+ var t = 0.5*(this._center[1] + this.canvas._offsets.top - this.hubRadius) + 0.5*(this._center[1] + this.canvas._offsets.top - this.tickOuterRadius + this.tickLength + tp) + this.labelHeightAdjust;
+ // this._labelElem = $('
'+this.label+'
');
+ // this.canvas._elem.after(this._labelElem);
+ l -= this._labelElem.outerWidth(true)/2;
+ t -= this._labelElem.outerHeight(true)/2;
+ this._labelElem.css({left:l, top:t});
+ }
+
+ else if (this.label && this.labelPosition == 'bottom') {
+ var l = this._center[0] + this.canvas._offsets.left - this._labelElem.outerWidth(true)/2;
+ var t = this._center[1] + this.canvas._offsets.top + this.innerPad + this.ringWidth + this.padding + this.labelHeightAdjust;
+ this._labelElem.css({left:l, top:t});
+
+ }
+
+ // draw the intervals
+
+ ctx.save();
+ var inner = this.intervalInnerRadius || this.hubRadius * 1.5;
+ if (this.intervalOuterRadius == null) {
+ if (this.showTickLabels) {
+ var outer = (this.tickOuterRadius - this.tickLength - this.tickPadding - this.diameter/8);
+ }
+ else {
+ var outer = (this.tickOuterRadius - this.tickLength - this.diameter/16);
+ }
+ }
+ else {
+ var outer = this.intervalOuterRadius;
+ }
+ var range = this.max - this.min;
+ var intrange = this.intervals[this.intervals.length-1] - this.min;
+ var start, end, span = this.span*Math.PI/180;
+ for (i=0; i this.max + dataspan*3/this.span) {
+ datapoint = this.max + dataspan*3/this.span;
+ }
+ if (this.data[0][1] < this.min - dataspan*3/this.span) {
+ datapoint = this.min - dataspan*3/this.span;
+ }
+ }
+ var dataang = (datapoint - this.min)/dataspan * this.span * Math.PI/180 + this.startAngle;
+
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.fillStyle = this.ringColor;
+ ctx.strokeStyle = this.ringColor;
+ this.needleLength = (this.tickOuterRadius - this.tickLength) * 0.85;
+ this.needleThickness = (this.needleThickness < 2) ? 2 : this.needleThickness;
+ var endwidth = this.needleThickness * 0.4;
+
+
+ var dl = this.needleLength/10;
+ var dt = (this.needleThickness - endwidth)/10;
+ var templ;
+ for (var i=0; i<10; i++) {
+ templ = this.needleThickness - i*dt;
+ ctx.moveTo(dl*i*Math.cos(dataang), dl*i*Math.sin(dataang));
+ ctx.lineWidth = templ;
+ ctx.lineTo(dl*(i+1)*Math.cos(dataang), dl*(i+1)*Math.sin(dataang));
+ ctx.stroke();
+ }
+
+ ctx.restore();
+ }
+ else {
+ this._center = [(cw - trans * offx)/2 + trans * offx, (ch - trans*offy)/2 + trans * offy];
+ }
+ };
+
+ $.jqplot.MeterGaugeAxisRenderer = function() {
+ $.jqplot.LinearAxisRenderer.call(this);
+ };
+
+ $.jqplot.MeterGaugeAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
+ $.jqplot.MeterGaugeAxisRenderer.prototype.constructor = $.jqplot.MeterGaugeAxisRenderer;
+
+
+ // There are no traditional axes on a gauge chart. We just need to provide
+ // dummy objects with properties so the plot will render.
+ // called with scope of axis object.
+ $.jqplot.MeterGaugeAxisRenderer.prototype.init = function(options){
+ //
+ this.tickRenderer = $.jqplot.MeterGaugeTickRenderer;
+ $.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.MeterGaugeLegendRenderer = function(){
+ $.jqplot.TableLegendRenderer.call(this);
+ };
+
+ $.jqplot.MeterGaugeLegendRenderer.prototype = new $.jqplot.TableLegendRenderer();
+ $.jqplot.MeterGaugeLegendRenderer.prototype.constructor = $.jqplot.MeterGaugeLegendRenderer;
+
+ /**
+ * Class: $.jqplot.MeterGaugeLegendRenderer
+ *Meter gauges don't typically have a legend, this overrides the default legend renderer.
+ */
+ $.jqplot.MeterGaugeLegendRenderer.prototype.init = function(options) {
+ // Maximum number of rows in the legend. 0 or null for unlimited.
+ this.numberRows = null;
+ // 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.MeterGaugeLegendRenderer.prototype.draw = function() {
+ 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 = $('
');
+ // MeterGauge 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];
+
+ 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').prependTo(this._elem);
+ }
+ else{
+ tr = $('
').appendTo(this._elem);
+ }
+ for (j=0; j0){
+ pad = true;
+ }
+ else{
+ pad = false;
+ }
+ }
+ else{
+ if (i == nr -1){
+ pad = false;
+ }
+ else{
+ pad = true;
+ }
+ }
+ rs = (pad) ? this.rowSpacing : '0';
+
+ td1 = $('
'+
+ '
'+
+ '
');
+ td2 = $('
');
+ 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;
+ };
+
+
+ // setup default renderers for axes and legend so user doesn't have to
+ // called with scope of plot
+ function preInit(target, data, options) {
+ // debugger
+ options = options || {};
+ options.axesDefaults = options.axesDefaults || {};
+ options.legend = options.legend || {};
+ options.seriesDefaults = options.seriesDefaults || {};
+ options.grid = options.grid || {};
+
+ // only set these if there is a gauge series
+ var setopts = false;
+ if (options.seriesDefaults.renderer == $.jqplot.MeterGaugeRenderer) {
+ setopts = true;
+ }
+ else if (options.series) {
+ for (var i=0; i < options.series.length; i++) {
+ if (options.series[i].renderer == $.jqplot.MeterGaugeRenderer) {
+ setopts = true;
+ }
+ }
+ }
+
+ if (setopts) {
+ options.axesDefaults.renderer = $.jqplot.MeterGaugeAxisRenderer;
+ options.legend.renderer = $.jqplot.MeterGaugeLegendRenderer;
+ options.legend.preDraw = true;
+ options.grid.background = options.grid.background || 'white';
+ options.grid.drawGridlines = false;
+ options.grid.borderWidth = (options.grid.borderWidth != null) ? options.grid.borderWidth : 0;
+ options.grid.shadow = (options.grid.shadow != null) ? options.grid.shadow : false;
+ }
+ }
+
+ // called with scope of plot
+ function postParseOptions(options) {
+ //
+ }
+
+ $.jqplot.preInitHooks.push(preInit);
+ $.jqplot.postParseOptionsHooks.push(postParseOptions);
+
+ $.jqplot.MeterGaugeTickRenderer = function() {
+ $.jqplot.AxisTickRenderer.call(this);
+ };
+
+ $.jqplot.MeterGaugeTickRenderer.prototype = new $.jqplot.AxisTickRenderer();
+ $.jqplot.MeterGaugeTickRenderer.prototype.constructor = $.jqplot.MeterGaugeTickRenderer;
+
+})(jQuery);
+
+
diff --git a/public/site_assets/test/js/plugins/jqplot.meterGaugeRenderer.min.js b/public/site_assets/test/js/plugins/jqplot.meterGaugeRenderer.min.js
new file mode 100644
index 00000000..e0f1081f
--- /dev/null
+++ b/public/site_assets/test/js/plugins/jqplot.meterGaugeRenderer.min.js
@@ -0,0 +1,3 @@
+/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com
+ jsDate | (c) 2010-2013 Chris Leonello
+ */(function(c){c.jqplot.MeterGaugeRenderer=function(){c.jqplot.LineRenderer.call(this)};c.jqplot.MeterGaugeRenderer.prototype=new c.jqplot.LineRenderer();c.jqplot.MeterGaugeRenderer.prototype.constructor=c.jqplot.MeterGaugeRenderer;c.jqplot.MeterGaugeRenderer.prototype.init=function(e){this.diameter=null;this.padding=null;this.shadowOffset=2;this.shadowAlpha=0.07;this.shadowDepth=4;this.background="#efefef";this.ringColor="#BBC6D0";this.needleColor="#C3D3E5";this.tickColor="#989898";this.ringWidth=null;this.min;this.max;this.ticks=[];this.showTicks=true;this.showTickLabels=true;this.label=null;this.labelHeightAdjust=0;this.labelPosition="inside";this.intervals=[];this.intervalColors=["#4bb2c5","#EAA228","#c5b47f","#579575","#839557","#958c12","#953579","#4b5de4","#d8b83f","#ff5800","#0085cc","#c747a3","#cddf54","#FBD178","#26B4E3","#bd70c7"];this.intervalInnerRadius=null;this.intervalOuterRadius=null;this.tickRenderer=c.jqplot.MeterGaugeTickRenderer;this.tickPositions=[1,2,2.5,5,10];this.tickSpacing=30;this.numberMinorTicks=null;this.hubRadius=null;this.tickPadding=null;this.needleThickness=null;this.needlePad=6;this.pegNeedle=true;this._type="meterGauge";c.extend(true,this,e);this.type=null;this.numberTicks=null;this.tickInterval=null;this.span=180;if(this.type=="circular"){this.semiCircular=false}else{if(this.type!="circular"){this.semiCircular=true}else{this.semiCircular=(this.span<=180)?true:false}}this._tickPoints=[];this._labelElem=null;this.startAngle=(90+(360-this.span)/2)*Math.PI/180;this.endAngle=(90-(360-this.span)/2)*Math.PI/180;this.setmin=!!(this.min==null);this.setmax=!!(this.max==null);if(this.intervals.length){if(this.intervals[0].length==null||this.intervals.length==1){for(var f=0;f=this.data[0][1]){this.max=this.intervals[this.intervals.length-1][0];this.setmax=false}}else{this.setmax=false}}else{this.min=(this.min==null)?0:this.min;this.setmin=false;if(this.max==null){this.max=this.data[0][1]*1.25;this.setmax=true}else{this.setmax=false}}}};c.jqplot.MeterGaugeRenderer.prototype.setGridData=function(j){var f=[];var k=[];var e=this.startAngle;for(var h=0;h0){f[h]+=f[h-1]}}var g=Math.PI*2/f[f.length-1];for(var h=0;h0){f[h]+=f[h-1]}}var g=Math.PI*2/f[f.length-1];for(var h=0;h=0;h--){e=f/(j[h]*Math.pow(10,g));if(e==4||e==5){return e-1}}return null}c.jqplot.MeterGaugeRenderer.prototype.draw=function(X,aC,ap){var aa;var aM=(ap!=undefined)?ap:{};var ai=0;var ah=0;var at=1;if(ap.legendInfo&&ap.legendInfo.placement=="inside"){var aI=ap.legendInfo;switch(aI.location){case"nw":ai=aI.width+aI.xoffset;break;case"w":ai=aI.width+aI.xoffset;break;case"sw":ai=aI.width+aI.xoffset;break;case"ne":ai=aI.width+aI.xoffset;at=-1;break;case"e":ai=aI.width+aI.xoffset;at=-1;break;case"se":ai=aI.width+aI.xoffset;at=-1;break;case"n":ah=aI.height+aI.yoffset;break;case"s":ah=aI.height+aI.yoffset;at=-1;break;default:break}}if(this.label){this._labelElem=c('
');if(this.escapeHtml){e.text(l)}else{e.html(l)}if(q){e.prependTo(r);g.prependTo(r)}else{g.appendTo(r);e.appendTo(r)}f=true}v++}}}}return this._elem};function a(j,h,f){f=f||{};f.axesDefaults=f.axesDefaults||{};f.legend=f.legend||{};f.seriesDefaults=f.seriesDefaults||{};f.grid=f.grid||{};var e=false;if(f.seriesDefaults.renderer==c.jqplot.MeterGaugeRenderer){e=true}else{if(f.series){for(var g=0;g
+ *
+ * You will most likely want to use a date axis renderer
+ * for the x axis also, so include the date axis render js file also:
+ *
+ * >
+ *
+ * Then you set the renderer in the series options on your plot:
+ *
+ * > series: [{renderer:$.jqplot.OHLCRenderer}]
+ *
+ * For OHLC and candlestick charts, data should be specified
+ * like so:
+ *
+ * > dat = [['07/06/2009',138.7,139.68,135.18,135.4], ['06/29/2009',143.46,144.66,139.79,140.02], ...]
+ *
+ * If the data array has only 4 values per point instead of 5,
+ * the renderer will create a Hi Low Close chart instead. In that case,
+ * data should be supplied like:
+ *
+ * > dat = [['07/06/2009',139.68,135.18,135.4], ['06/29/2009',144.66,139.79,140.02], ...]
+ *
+ * To generate a candlestick chart instead of an OHLC chart,
+ * set the "candlestick" option to true:
+ *
+ * > series: [{renderer:$.jqplot.OHLCRenderer, rendererOptions:{candleStick:true}}],
+ *
+ */
+ $.jqplot.OHLCRenderer = function(){
+ // subclass line renderer to make use of some of its methods.
+ $.jqplot.LineRenderer.call(this);
+ // prop: candleStick
+ // true to render chart as candleStick.
+ // Must have an open price, cannot be a hlc chart.
+ this.candleStick = false;
+ // prop: tickLength
+ // length of the line in pixels indicating open and close price.
+ // Default will auto calculate based on plot width and
+ // number of points displayed.
+ this.tickLength = 'auto';
+ // prop: bodyWidth
+ // width of the candlestick body in pixels. Default will auto calculate
+ // based on plot width and number of candlesticks displayed.
+ this.bodyWidth = 'auto';
+ // prop: openColor
+ // color of the open price tick mark. Default is series color.
+ this.openColor = null;
+ // prop: closeColor
+ // color of the close price tick mark. Default is series color.
+ this.closeColor = null;
+ // prop: wickColor
+ // color of the hi-lo line thorugh the candlestick body.
+ // Default is the series color.
+ this.wickColor = null;
+ // prop: fillUpBody
+ // true to render an "up" day (close price greater than open price)
+ // with a filled candlestick body.
+ this.fillUpBody = false;
+ // prop: fillDownBody
+ // true to render a "down" day (close price lower than open price)
+ // with a filled candlestick body.
+ this.fillDownBody = true;
+ // prop: upBodyColor
+ // Color of candlestick body of an "up" day. Default is series color.
+ this.upBodyColor = null;
+ // prop: downBodyColor
+ // Color of candlestick body on a "down" day. Default is series color.
+ this.downBodyColor = null;
+ // prop: hlc
+ // true if is a hi-low-close chart (no open price).
+ // This is determined automatically from the series data.
+ 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._bodyWidth;
+ };
+
+ $.jqplot.OHLCRenderer.prototype = new $.jqplot.LineRenderer();
+ $.jqplot.OHLCRenderer.prototype.constructor = $.jqplot.OHLCRenderer;
+
+ // called with scope of series.
+ $.jqplot.OHLCRenderer.prototype.init = function(options) {
+ 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);
+ this._type = 'ohlc';
+ // set the yaxis data bounds here to account for hi and low values
+ var db = this._yaxis._dataBounds;
+ var d = this._plotData;
+ // if data points have less than 5 values, force a hlc chart.
+ if (d[0].length < 5) {
+ this.renderer.hlc = true;
+
+ for (var j=0; j db.max || db.max == null) {
+ db.max = d[j][1];
+ }
+ }
+ }
+ else {
+ for (var j=0; j db.max || db.max == null) {
+ db.max = d[j][2];
+ }
+ }
+ }
+
+ };
+
+ // called within scope of series.
+ $.jqplot.OHLCRenderer.prototype.draw = function(ctx, gd, options) {
+ var d = this.data;
+ var xmin = this._xaxis.min;
+ var xmax = this._xaxis.max;
+ // index of last value below range of plot.
+ var xminidx = 0;
+ // index of first value above range of plot.
+ var xmaxidx = d.length;
+ var xp = this._xaxis.series_u2p;
+ var yp = this._yaxis.series_u2p;
+ var i, prevColor, ops, b, h, w, a, points;
+ var o;
+ var r = this.renderer;
+ var opts = (options != undefined) ? options : {};
+ var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
+ var fill = (opts.fill != undefined) ? opts.fill : this.fill;
+ var fillAndStroke = (opts.fillAndStroke != undefined) ? opts.fillAndStroke : this.fillAndStroke;
+ r.bodyWidth = (opts.bodyWidth != undefined) ? opts.bodyWidth : r.bodyWidth;
+ r.tickLength = (opts.tickLength != undefined) ? opts.tickLength : r.tickLength;
+ ctx.save();
+ if (this.show) {
+ var x, open, hi, low, close;
+ // need to get widths based on number of points shown,
+ // not on total number of points. Use the results
+ // to speed up drawing in next step.
+ for (var i=0; i open) {
+ // draw wick
+ if (r.wickColor) {
+ o.color = r.wickColor;
+ }
+ else if (r.downBodyColor) {
+ o.color = r.downBodyColor;
+ }
+ ops = $.extend(true, {}, opts, o);
+ r.shapeRenderer.draw(ctx, [[x, hi], [x, open]], ops);
+ r.shapeRenderer.draw(ctx, [[x, close], [x, low]], ops);
+
+ o = {};
+
+ b = open;
+ h = close - open;
+ // if color specified, use it
+ if (r.fillDownBody) {
+ o.fillRect = true;
+ }
+ else {
+ o.strokeRect = true;
+ w = w - this.lineWidth;
+ a = x - w/2;
+ }
+ if (r.downBodyColor) {
+ o.color = r.downBodyColor;
+ o.fillStyle = r.downBodyColor;
+ }
+ points = [a, b, w, h];
+ }
+ // even, open = close
+ else {
+ // draw wick
+ if (r.wickColor) {
+ o.color = r.wickColor;
+ }
+ ops = $.extend(true, {}, opts, o);
+ r.shapeRenderer.draw(ctx, [[x, hi], [x, low]], ops);
+ o = {};
+ o.fillRect = false;
+ o.strokeRect = false;
+ a = [x - w/2, open];
+ b = [x + w/2, close];
+ w = null;
+ h = null;
+ points = [a, b];
+ }
+ ops = $.extend(true, {}, opts, o);
+ r.shapeRenderer.draw(ctx, points, ops);
+ }
+ else {
+ prevColor = opts.color;
+ if (r.openColor) {
+ opts.color = r.openColor;
+ }
+ // draw open tick
+ if (!r.hlc) {
+ r.shapeRenderer.draw(ctx, [[x-r._tickLength, open], [x, open]], opts);
+ }
+ opts.color = prevColor;
+ // draw wick
+ if (r.wickColor) {
+ opts.color = r.wickColor;
+ }
+ r.shapeRenderer.draw(ctx, [[x, hi], [x, low]], opts);
+ opts.color = prevColor;
+ // draw close tick
+ if (r.closeColor) {
+ opts.color = r.closeColor;
+ }
+ r.shapeRenderer.draw(ctx, [[x, close], [x+r._tickLength, close]], opts);
+ opts.color = prevColor;
+ }
+ }
+ }
+
+ ctx.restore();
+ };
+
+ $.jqplot.OHLCRenderer.prototype.drawShadow = function(ctx, gd, options) {
+ // This is a no-op, shadows drawn with lines.
+ };
+
+ // called with scope of plot.
+ $.jqplot.OHLCRenderer.checkOptions = function(target, data, options) {
+ // provide some sensible highlighter options by default
+ // These aren't good for hlc, only for ohlc or candlestick
+ if (!options.highlighter) {
+ options.highlighter = {
+ showMarker:false,
+ tooltipAxes: 'y',
+ yvalues: 4,
+ formatString:'
'}}}})(jQuery);
\ No newline at end of file
diff --git a/public/site_assets/test/js/plugins/jqplot.pieRenderer.js b/public/site_assets/test/js/plugins/jqplot.pieRenderer.js
new file mode 100644
index 00000000..1d508bd6
--- /dev/null
+++ b/public/site_assets/test/js/plugins/jqplot.pieRenderer.js
@@ -0,0 +1,904 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.8
+ * Revision: 1250
+ *
+ * Copyright (c) 2009-2013 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
+ * choose the license that best suits your project and use it accordingly.
+ *
+ * Although not required, the author would appreciate an email letting him
+ * know of any substantial use of jqPlot. You can reach the author at:
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ * version 2007.04.27
+ * author Ash Searle
+ * http://hexmen.com/blog/2007/03/printf-sprintf/
+ * http://hexmen.com/js/sprintf.js
+ * The author (Ash Searle) has placed this code in the public domain:
+ * "This code is unrestricted: you are free to use it however you like."
+ *
+ */
+(function($) {
+ /**
+ * Class: $.jqplot.PieRenderer
+ * Plugin renderer to draw a pie 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
+ * pie renderer plugin, for example:
+ *
+ * >
+ *
+ * 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.LineRenderer.call(this);
+ };
+
+ $.jqplot.PieRenderer.prototype = new $.jqplot.LineRenderer();
+ $.jqplot.PieRenderer.prototype.constructor = $.jqplot.PieRenderer;
+
+ // called with scope of a series
+ $.jqplot.PieRenderer.prototype.init = function(options, plot) {
+ // Group: Properties
+ //
+ // prop: diameter
+ // Outer diameter of the pie, auto computed by default
+ this.diameter = null;
+ // prop: padding
+ // padding between the pie and plot edges, legend, etc.
+ this.padding = 20;
+ // prop: sliceMargin
+ // angular spacing between pie slices in degrees.
+ this.sliceMargin = 0;
+ // prop: fill
+ // true or false, whether 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.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;
+ // 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);
+
+ if (this.sliceMargin < 0) {
+ this.sliceMargin = 0;
+ }
+
+ 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 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) {
+ // 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; i0) {
+ stack[i] += stack[i-1];
+ }
+ tot += this.data[i][1];
+ }
+ var fact = Math.PI*2/stack[stack.length - 1];
+
+ for (var i=0; i0) {
+ stack[i] += stack[i-1];
+ }
+ tot += data[i][1];
+ }
+ var fact = Math.PI*2/stack[stack.length - 1];
+
+ for (var i=0; i 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) {
+ 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 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 pies.
+ if (ang1 >= ang2) {
+ return;
+ }
+
+ ctx.beginPath();
+ ctx.fillStyle = color;
+ ctx.strokeStyle = color;
+ ctx.lineWidth = lineWidth;
+ ctx.arc(0, 0, rad, ang1, ang2, false);
+ ctx.lineTo(0,0);
+ ctx.closePath();
+
+ if (fill) {
+ ctx.fill();
+ }
+ else {
+ ctx.stroke();
+ }
+ }
+ };
+
+ // called with scope of series
+ $.jqplot.PieRenderer.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 $.jqplot.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 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;
+
+ // 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 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) {
+ for (var i=0, l=gd.length; i= 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 = $('
' + label + '
').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.LinearAxisRenderer.call(this);
+ };
+
+ $.jqplot.PieAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
+ $.jqplot.PieAxisRenderer.prototype.constructor = $.jqplot.PieAxisRenderer;
+
+
+ // There are no traditional axes on a pie chart. We just need to provide
+ // dummy objects with properties so the plot will render.
+ // called with scope of axis object.
+ $.jqplot.PieAxisRenderer.prototype.init = function(options){
+ //
+ this.tickRenderer = $.jqplot.PieTickRenderer;
+ $.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.PieLegendRenderer = function(){
+ $.jqplot.TableLegendRenderer.call(this);
+ };
+
+ $.jqplot.PieLegendRenderer.prototype = new $.jqplot.TableLegendRenderer();
+ $.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
+ $.jqplot.PieLegendRenderer.prototype.draw = function() {
+ var legend = this;
+ if (this.show) {
+ var series = this._series;
+
+
+ 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 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;
+ var tr, td1, td2;
+ var lt, rs, color;
+ var idx = 0;
+ var div0, div1;
+
+ for (i=0; i0){
+ 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 jqplot-table-legend-swatch');
+ td1.css({textAlign: 'center', paddingTop: rs});
+
+ div0 = $(document.createElement('div'));
+ div0.addClass('jqplot-table-legend-swatch-outline');
+ 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 jqplot-table-legend-label');
+ 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;
+ };
+
+ $.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
+ // 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 pie series
+ var setopts = false;
+ if (options.seriesDefaults.renderer == $.jqplot.PieRenderer) {
+ setopts = true;
+ }
+ else if (options.series) {
+ for (var i=0; i < options.series.length; i++) {
+ if (options.series[i].renderer == $.jqplot.PieRenderer) {
+ setopts = true;
+ }
+ }
+ }
+
+ if (setopts) {
+ options.axesDefaults.renderer = $.jqplot.PieAxisRenderer;
+ options.legend.renderer = $.jqplot.PieLegendRenderer;
+ options.legend.preDraw = true;
+ options.seriesDefaults.pointLabels = {show: false};
+ }
+ }
+
+ function postInit(target, data, options) {
+ for (var i=0; i570)?o[p]*0.8:o[p]+0.3*(255-o[p]);o[p]=parseInt(o[p],10)}this.highlightColors.push("rgb("+o[0]+","+o[1]+","+o[2]+")")}}this.highlightColorGenerator=new e.jqplot.ColorGenerator(this.highlightColors);u.postParseOptionsHooks.addOnce(m);u.postInitHooks.addOnce(g);u.eventListenerHooks.addOnce("jqplotMouseMove",b);u.eventListenerHooks.addOnce("jqplotMouseDown",a);u.eventListenerHooks.addOnce("jqplotMouseUp",l);u.eventListenerHooks.addOnce("jqplotClick",f);u.eventListenerHooks.addOnce("jqplotRightClick",n);u.postDrawHooks.addOnce(i)};e.jqplot.PieRenderer.prototype.setGridData=function(t){var p=[];var u=[];var o=this.startAngle/180*Math.PI;var s=0;this._drawData=false;for(var r=0;r0){p[r]+=p[r-1]}s+=this.data[r][1]}var q=Math.PI*2/p[p.length-1];for(var r=0;r0){p[r]+=p[r-1]}s+=t[r][1]}var q=Math.PI*2/p[p.length-1];for(var r=0;r0&&s>0.01&&s<6.282){w=parseFloat(p)/2/h(q)}return w}e.jqplot.PieRenderer.prototype.drawSlice=function(B,z,y,u,w){if(this._drawData){var p=this._radius;var A=this.fill;var x=this.lineWidth;var s=this.sliceMargin;if(this.fill==false){s+=this.lineWidth}B.save();B.translate(this._center[0],this._center[1]);var D=j(z,y,this.sliceMargin,this.fill,this.lineWidth);var o=D*Math.cos((z+y)/2);var C=D*Math.sin((z+y)/2);if((y-z)<=Math.PI){p-=D}else{p+=D}B.translate(o,C);if(w){for(var v=0,t=this.shadowDepth;v6.282+this.startAngle){y=6.282+this.startAngle;if(z>y){z=6.281+this.startAngle}}if(z>=y){return}B.beginPath();B.fillStyle=u;B.strokeStyle=u;B.lineWidth=x;B.arc(0,0,r,z,y,false);B.lineTo(0,0);B.closePath();if(A){B.fill()}else{B.stroke()}}};e.jqplot.PieRenderer.prototype.draw=function(B,z,E,o){var W;var H=(E!=undefined)?E:{};var t=0;var s=0;var N=1;var L=new e.jqplot.ColorGenerator(this.seriesColors);if(E.legendInfo&&E.legendInfo.placement=="insideGrid"){var J=E.legendInfo;switch(J.location){case"nw":t=J.width+J.xoffset;break;case"w":t=J.width+J.xoffset;break;case"sw":t=J.width+J.xoffset;break;case"ne":t=J.width+J.xoffset;N=-1;break;case"e":t=J.width+J.xoffset;N=-1;break;case"se":t=J.width+J.xoffset;N=-1;break;case"n":s=J.height+J.yoffset;break;case"s":s=J.height+J.yoffset;N=-1;break;default:break}}var K=(H.shadow!=undefined)?H.shadow:this.shadow;var A=(H.fill!=undefined)?H.fill:this.fill;var C=B.canvas.width;var I=B.canvas.height;var Q=C-t-2*this.padding;var X=I-s-2*this.padding;var M=Math.min(Q,X);var Y=M;this._sliceAngles=[];var v=this.sliceMargin;if(this.fill==false){v+=this.lineWidth}var q;var G=0;var R,aa,Z,ab;var D=this.startAngle/180*Math.PI;for(var W=0,V=z.length;WMath.PI){G=Math.max(q,G)}}if(this.diameter!=null&&this.diameter>0){this._diameter=this.diameter-2*G}else{this._diameter=Y-2*G}if(this._diameter<6){e.jqplot.log("Diameter of pie too small, not rendering.");return}var S=this._radius=this._diameter/2;this._center=[(C-N*t)/2+N*t+G*Math.cos(D),(I-N*s)/2+N*s+G*Math.sin(D)];if(this.shadow){for(var W=0,V=z.length;W=this.dataLabelThreshold){var F,U=(this._sliceAngles[W][0]+this._sliceAngles[W][1])/2,T;if(this.dataLabels=="label"){F=this.dataLabelFormatString||"%s";T=e.jqplot.sprintf(F,z[W][0])}else{if(this.dataLabels=="value"){F=this.dataLabelFormatString||"%d";T=e.jqplot.sprintf(F,this.data[W][1])}else{if(this.dataLabels=="percent"){F=this.dataLabelFormatString||"%d%%";T=e.jqplot.sprintf(F,z[W][2]*100)}else{if(this.dataLabels.constructor==Array){F=this.dataLabelFormatString||"%s";T=e.jqplot.sprintf(F,this.dataLabels[W])}}}}var p=(this._radius)*this.dataLabelPositionFactor+this.sliceMargin+this.dataLabelNudge;var P=this._center[0]+Math.cos(U)*p+this.canvas._offsets.left;var O=this._center[1]+Math.sin(U)*p+this.canvas._offsets.top;var u=e('
First of all if talking about PPLNS it is important to understand how it works. PPLNS is short for .Pay Per Last N Shares.. The current proportional reward system is round based. One round is the time between the first share after the last found block and the share which solves a block. PPLNS however means that we no longer consider valid shares of one round but we consider a number N of shares. No matter if they are part of the round or not. The number N is currently for each blockchain twice the difficulty (actually rounded down to an easy to calculate integer value).
+
Let me illustrate PPLNS using the image below. One round has an arbitrary number of shares which is solely based on sheer luck. On proportional reward system only shares of one round are considered for calculating rewards. However with PPLNS a quite constant number N of shares is considered for calculating rewards. This number N changes only with the difficulty. Please have a look at the image below:
+
+
As you know the number of shares needed to solve a block within a round is different. Round one and three needed (difficulty * 2) shares to be solved. Round two and four are quite short rounds. There were less than (difficulty * 2) shares nessecary to solve them. Round five however is a very long round which means the pool needed more than (difficulty * 2) shares to solve the block. From this follows that:
+
+
Rounds one and three are like proportional rounds. All of your shares from the given round are considered for reward calculations.
+
For rounds two and four shares from the previous rounds are considered for calculations as well (marked green). In other words: regardless of round boundaries we always consider the last (difficulty * 2) shares. Your portion of the amount of shares is used to calculate your reward.
+
Round five however is very long. In this round your lowermost shares (within the marked red part) are silently dropped if they are not within the last (difficulty * 2) shares.
+
+
Why do we need to switch the reward system at all?
+
PPLNS favors constant and/or occasional loyal pool members over pool hoppers. As you might have seen each time we find a LTC block the pools hashrate peaks instantly. This is due to pool hoppers starting to mine. They are betting for a .quick win. (like round two above) with low shares per round. If the round exceeds a certain amount of shares they .hop. to another proportional pool which started a new round more recently trying their luck on the other pool. This assures better rewards for pool hoppers over occasional or constant miners which are loyal to their pool. Pool hopping however implies that pool hoppers need to know when a round is started and how much shares are considered for reward. This is very easy with propotional reward system. Using PPLNS this is no longer true. On long rounds (like round five above) the pool hoppers shares won.t be considered for reward calculations in favor of loyal miners. This is due to the fact that pool hoppers only mine on the beginning of rounds. On short and normal rounds pool hoppers won.t loose their shares. But due to the fact that shares from previous rounds from loyal miners are considered twice (or even more often on extremely short rounds) the pool hopper won.t get the same reward as from proportional reward system.
+
I.m only an occasional miner. Will I loose shares if playing/being offline?
+
It depends. Even if you are a constant miner you.ll loose shares on very long rounds. On the other hand if you for example stop mining shortly before the end of round three from the image above you.ll be rewarded for round three. If you then start mining on the mid of round five you.ll be rewarded a bit for round four (despite the fact you haven.t submitted any shares as some of your shares from round three are considered for round four as well) and you.ll be fully rewarded for round five (you won.t loose any shares as you .skipped. the .red part. of shares which have been cut off due to the length of the round). The point however is no one (and thus the pool hopper as well) knows when a round ends in advance. Thus you deliberately can.t avoid cut offs. Sometimes you are lucky sometimes your are not. This however levels out over the time for non-hoppers. The important point is, that you as loyal pool member have an advantage over pool hoppers. Even if you are mining only occasionally in order to enjoy the silence within your mining room.
+
Isn.t it that you as pool operator earn more from PPLNS?
+
Short answer: No. As a pool operator I. In fact for a pool operator it doesn.t matter at all which reward system is used. I expect rounds to become a bit longer after PPLNS is activated as the pool hoppers will start to avoid the Pool. Thus revenues for me will be lower. But there were some loyal pool members who clearly stated that they want to get rid of the pool hoppers and I respect their wish. Furthermore I.m too an occasional miner on the Pool as I don.t want the miners to be running at night and from time to time I need them to test new Pool versions on development systems. Thus being a miner like you I like PPLNS better than proportional. But speaking as pool operator there is no difference between the reward systems.
After mining in other pools I have decided to setup my own pool, mostly for educational reason. I was curious how pools work, what is needed to get them started and what tools can be used to run them.
+
Lots of digging finally revealed that the following tools were required to run a Litecoin pool:
1. Provides litecoind, used to synchronize blocks and offer the API the pool connects to
+
Pushpool 0.5.1-tenebrix, a modified version of Pushpool supporting changed target difficulties (2^22 in this pool)
+
2. Provides the API each worker (client miner) authenticates and connects to
+
mmcFE modified by Greedi & g2x3k with adjustments for this pool (proper hashrate for 2^22 difficulty)
+
3. The Webinterface merging SQL and API information into a cohesive interface for user and worker management
+
+
+
+The hardest part was finding all the information needed and applying it to a new setup.
+Many tools exist and even those three took a while to get them to work.
+Especially the difficulty adjustment would not have been possible (for me) if it wasn't
+for the pushpool tenebrix branch allowing a custom target bit and reducing difficulty per share.
+More adjustments in the PHP code were necessary to reflect those changes and, at least
+close enough, properly display hashrates on the pool site. It is running well right now but
+please keen in mind that neither the code nor functionaliy are supported.
+I am not responsible for lost coins due to a pool crash or other malfunctions which
+could be caused by by code or the tools used in this implementation.
+
First of all if talking about PPLNS it is important to understand how it works. PPLNS is short for .Pay Per Last N Shares.. The current proportional reward system is round based. One round is the time between the first share after the last found block and the share which solves a block. PPLNS however means that we no longer consider valid shares of one round but we consider a number N of shares. No matter if they are part of the round or not. The number N is currently for each blockchain twice the difficulty (actually rounded down to an easy to calculate integer value).
+
Let me illustrate PPLNS using the image below. One round has an arbitrary number of shares which is solely based on sheer luck. On proportional reward system only shares of one round are considered for calculating rewards. However with PPLNS a quite constant number N of shares is considered for calculating rewards. This number N changes only with the difficulty. Please have a look at the image below:
+
+
As you know the number of shares needed to solve a block within a round is different. Round one and three needed (difficulty * 2) shares to be solved. Round two and four are quite short rounds. There were less than (difficulty * 2) shares nessecary to solve them. Round five however is a very long round which means the pool needed more than (difficulty * 2) shares to solve the block. From this follows that:
+
+
Rounds one and three are like proportional rounds. All of your shares from the given round are considered for reward calculations.
+
For rounds two and four shares from the previous rounds are considered for calculations as well (marked green). In other words: regardless of round boundaries we always consider the last (difficulty * 2) shares. Your portion of the amount of shares is used to calculate your reward.
+
Round five however is very long. In this round your lowermost shares (within the marked red part) are silently dropped if they are not within the last (difficulty * 2) shares.
+
+
Why do we need to switch the reward system at all?
+
PPLNS favors constant and/or occasional loyal pool members over pool hoppers. As you might have seen each time we find a LTC block the pools hashrate peaks instantly. This is due to pool hoppers starting to mine. They are betting for a .quick win. (like round two above) with low shares per round. If the round exceeds a certain amount of shares they .hop. to another proportional pool which started a new round more recently trying their luck on the other pool. This assures better rewards for pool hoppers over occasional or constant miners which are loyal to their pool. Pool hopping however implies that pool hoppers need to know when a round is started and how much shares are considered for reward. This is very easy with propotional reward system. Using PPLNS this is no longer true. On long rounds (like round five above) the pool hoppers shares won.t be considered for reward calculations in favor of loyal miners. This is due to the fact that pool hoppers only mine on the beginning of rounds. On short and normal rounds pool hoppers won.t loose their shares. But due to the fact that shares from previous rounds from loyal miners are considered twice (or even more often on extremely short rounds) the pool hopper won.t get the same reward as from proportional reward system.
+
I.m only an occasional miner. Will I loose shares if playing/being offline?
+
It depends. Even if you are a constant miner you.ll loose shares on very long rounds. On the other hand if you for example stop mining shortly before the end of round three from the image above you.ll be rewarded for round three. If you then start mining on the mid of round five you.ll be rewarded a bit for round four (despite the fact you haven.t submitted any shares as some of your shares from round three are considered for round four as well) and you.ll be fully rewarded for round five (you won.t loose any shares as you .skipped. the .red part. of shares which have been cut off due to the length of the round). The point however is no one (and thus the pool hopper as well) knows when a round ends in advance. Thus you deliberately can.t avoid cut offs. Sometimes you are lucky sometimes your are not. This however levels out over the time for non-hoppers. The important point is, that you as loyal pool member have an advantage over pool hoppers. Even if you are mining only occasionally in order to enjoy the silence within your mining room.
+
Isn.t it that you as pool operator earn more from PPLNS?
+
Short answer: No. As a pool operator I. In fact for a pool operator it doesn.t matter at all which reward system is used. I expect rounds to become a bit longer after PPLNS is activated as the pool hoppers will start to avoid the Pool. Thus revenues for me will be lower. But there were some loyal pool members who clearly stated that they want to get rid of the pool hoppers and I respect their wish. Furthermore I.m too an occasional miner on the Pool as I don.t want the miners to be running at night and from time to time I need them to test new Pool versions on development systems. Thus being a miner like you I like PPLNS better than proportional. But speaking as pool operator there is no difference between the reward systems.
+ If you want, you can create additional workers with usernames and passwords of your choice Here
+
+
4. Create a Litecoin address to recieve payments.
+
+
Downloading the client & block chain: Download the Litecoin client from the here.
+ Generate a new address and input it on your account page to receive payments.
+
+ Don't set intensity too high, I=11 is standard and safest. Higher intensity takes more GPU RAM. Check for hardware errors in cgminer (HW). HW=0 is good, otherwise lower intensity :)
+
+
+ Set shaders according to the readme (or look at your graphic cards specifications). Cgminer uses this value at first run to calculate thread-concurrency.
+ Easiest way to get this optimized is to use same settings as others have used here: here.
+
+
+ There's also an interesting project which gives you a GUI for cgminer. Windows only it seems.
+
+
+ Here's a great guide how to get up and running with Xubuntu.
+
+
+
+
+
+
+
diff --git a/public/templates/test/global/breadcrumbs.tpl b/public/templates/test/global/breadcrumbs.tpl
new file mode 100644
index 00000000..8cda7485
--- /dev/null
+++ b/public/templates/test/global/breadcrumbs.tpl
@@ -0,0 +1,3 @@
+