Leitgedanken/msd2/tracking/piwik/libs/jqplot/jqplot.linearAxisRenderer.js
2023-01-23 11:03:31 +01:00

1007 lines
44 KiB
JavaScript

/**
* jqPlot
* Pure JavaScript plotting plugin using jQuery
*
* Version: @VERSION
* Revision: @REVISION
*
* Copyright (c) 2009-2013 Chris Leonello
* jqPlot is currently available for use in all personal or commercial projects
* under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
* version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
* choose the license that best suits your project and use it accordingly.
*
* Although not required, the author would appreciate an email letting him
* know of any substantial use of jqPlot. You can reach the author at:
* chris at jqplot dot com or see http://www.jqplot.com/info.php .
*
* If you are feeling kind and generous, consider supporting the project by
* making a donation at: http://www.jqplot.com/donate.php .
*
* sprintf functions contained in jqplot.sprintf.js by Ash Searle:
*
* version 2007.04.27
* author Ash Searle
* http://hexmen.com/blog/2007/03/printf-sprintf/
* http://hexmen.com/js/sprintf.js
* The author (Ash Searle) has placed this code in the public domain:
* "This code is unrestricted: you are free to use it however you like."
*
*/
(function($) {
// class: $.jqplot.LinearAxisRenderer
// The default jqPlot axis renderer, creating a numeric axis.
$.jqplot.LinearAxisRenderer = function() {
};
// called with scope of axis object.
$.jqplot.LinearAxisRenderer.prototype.init = function(options){
// prop: breakPoints
// EXPERIMENTAL!! Use at your own risk!
// Works only with linear axes and the default tick renderer.
// Array of [start, stop] points to create a broken axis.
// Broken axes have a "jump" in them, which is an immediate
// transition from a smaller value to a larger value.
// Currently, axis ticks MUST be manually assigned if using breakPoints
// by using the axis ticks array option.
this.breakPoints = null;
// prop: breakTickLabel
// Label to use at the axis break if breakPoints are specified.
this.breakTickLabel = "≈";
// 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;
// prop: forceTickAt0
// This will ensure that there is always a tick mark at 0.
// If data range is strictly positive or negative,
// this will force 0 to be inside the axis bounds unless
// the appropriate axis pad (pad, padMin or padMax) is set
// to 0, then this will force an axis min or max value at 0.
// This has know effect when any of the following options
// are set: autoscale, min, max, numberTicks or tickInterval.
this.forceTickAt0 = false;
// prop: forceTickAt100
// This will ensure that there is always a tick mark at 100.
// If data range is strictly above or below 100,
// this will force 100 to be inside the axis bounds unless
// the appropriate axis pad (pad, padMin or padMax) is set
// to 0, then this will force an axis min or max value at 100.
// This has know effect when any of the following options
// are set: autoscale, min, max, numberTicks or tickInterval.
this.forceTickAt100 = false;
// 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: 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 = 0;
// prop: alignTicks
// true to align tick marks across opposed axes
// such as from the y2axis to yaxis.
this.alignTicks = false;
this._autoFormatString = '';
this._overrideFormatString = false;
this._scalefact = 1.0;
$.extend(true, this, options);
if (this.breakPoints) {
if (!$.isArray(this.breakPoints)) {
this.breakPoints = null;
}
else if (this.breakPoints.length < 2 || this.breakPoints[1] <= this.breakPoints[0]) {
this.breakPoints = null;
}
}
if (this.numberTicks != null && this.numberTicks < 2) {
this.numberTicks = 2;
}
this.resetDataBounds();
};
// called with scope of axis
$.jqplot.LinearAxisRenderer.prototype.draw = function(ctx, plot) {
if (this.show) {
// populate the axis label and value properties.
// createTicks is a method on the renderer, but
// call it within the scope of the axis.
this.renderer.createTicks.call(this, plot);
// fill a div with axes labels in the right direction.
// Need to pregenerate each axis to get its bounds and
// position it and the labels correctly on the plot.
var dim=0;
var temp;
// Added for theming.
if (this._elem) {
// Memory Leaks patch
//this._elem.empty();
this._elem.emptyForce();
this._elem = null;
}
this._elem = $(document.createElement('div'));
this._elem.addClass('jqplot-axis jqplot-'+this.name);
this._elem.css('position', 'absolute');
if (this.name == 'xaxis' || this.name == 'x2axis') {
this._elem.width(this._plotDimensions.width);
}
else {
this._elem.height(this._plotDimensions.height);
}
// create a _label object.
this.labelOptions.axis = this.name;
this._label = new this.labelRenderer(this.labelOptions);
if (this._label.show) {
var elem = this._label.draw(ctx, plot);
elem.appendTo(this._elem);
elem = null;
}
var t = this._ticks;
var tick;
for (var i=0; i<t.length; i++) {
tick = t[i];
if (tick.show && tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
this._elem.append(tick.draw(ctx, plot));
}
}
tick = null;
t = null;
}
return this._elem;
};
// called with scope of an axis
$.jqplot.LinearAxisRenderer.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._ticks = this.__ticks;
};
// called with scope of axis
$.jqplot.LinearAxisRenderer.prototype.set = function() {
var dim = 0;
var temp;
var w = 0;
var h = 0;
var lshow = (this._label == null) ? false : this._label.show;
if (this.show) {
var t = this._ticks;
var tick;
for (var i=0; i<t.length; i++) {
tick = t[i];
if (!tick._breakTick && tick.show && tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
if (this.name == 'xaxis' || this.name == 'x2axis') {
temp = tick._elem.outerHeight(true);
}
else {
temp = tick._elem.outerWidth(true);
}
if (temp > dim) {
dim = temp;
}
}
}
tick = null;
t = null;
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.LinearAxisRenderer.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 dim = (this.name.charAt(0) === 'x') ? this._plotDimensions.width : this._plotDimensions.height;
var interval;
var min, max;
var pos1, pos2;
var tt, i;
// get a copy of user's settings for min/max.
var userMin = this.min;
var userMax = this.max;
var userNT = this.numberTicks;
var userTI = this.tickInterval;
var threshold = 30;
this._scalefact = (Math.max(dim, threshold+1) - threshold)/300.0;
// 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<userTicks.length; i++){
var ut = userTicks[i];
var t = new this.tickRenderer(this.tickOptions);
if ($.isArray(ut)) {
t.value = ut[0];
if (this.breakPoints) {
if (ut[0] == this.breakPoints[0]) {
t.label = this.breakTickLabel;
t._breakTick = true;
t.showGridline = false;
t.showMark = false;
}
else if (ut[0] > this.breakPoints[0] && ut[0] <= this.breakPoints[1]) {
t.show = false;
t.showGridline = false;
t.label = ut[1];
}
else {
t.label = ut[1];
}
}
else {
t.label = ut[1];
}
t.setTick(ut[0], this.name);
this._ticks.push(t);
}
else if ($.isPlainObject(ut)) {
$.extend(true, t, ut);
t.axis = this.name;
this._ticks.push(t);
}
else {
t.value = ut;
if (this.breakPoints) {
if (ut == this.breakPoints[0]) {
t.label = this.breakTickLabel;
t._breakTick = true;
t.showGridline = false;
t.showMark = false;
}
else if (ut > this.breakPoints[0] && ut <= this.breakPoints[1]) {
t.show = false;
t.showGridline = false;
}
}
t.setTick(ut, this.name);
this._ticks.push(t);
}
}
this.numberTicks = userTicks.length;
this.min = this._ticks[0].value;
this.max = this._ticks[this.numberTicks-1].value;
this.tickInterval = (this.max - this.min) / (this.numberTicks - 1);
}
// we don't have any ticks yet, let's make some!
else {
if (name == 'xaxis' || name == 'x2axis') {
dim = this._plotDimensions.width;
}
else {
dim = this._plotDimensions.height;
}
var _numberTicks = this.numberTicks;
// if aligning this axis, use number of ticks from previous axis.
// Do I need to reset somehow if alignTicks is changed and then graph is replotted??
if (this.alignTicks) {
if (this.name === 'x2axis' && plot.axes.xaxis.show) {
_numberTicks = plot.axes.xaxis.numberTicks;
}
else if (this.name.charAt(0) === 'y' && this.name !== 'yaxis' && this.name !== 'yMidAxis' && plot.axes.yaxis.show) {
_numberTicks = plot.axes.yaxis.numberTicks;
}
}
min = ((this.min != null) ? this.min : db.min);
max = ((this.max != null) ? this.max : db.max);
var range = max - min;
var rmin, rmax;
var temp;
if (this.tickOptions == null || !this.tickOptions.formatString) {
this._overrideFormatString = true;
}
// Doing complete autoscaling
if (this.min == null || this.max == null && this.tickInterval == null && !this.autoscale) {
// Check if user must have tick at 0 or 100 and ensure they are in range.
// The autoscaling algorithm will always place ticks at 0 and 100 if they are in range.
if (this.forceTickAt0) {
if (min > 0) {
min = 0;
}
if (max < 0) {
max = 0;
}
}
if (this.forceTickAt100) {
if (min > 100) {
min = 100;
}
if (max < 100) {
max = 100;
}
}
var keepMin = false,
keepMax = false;
if (this.min != null) {
keepMin = true;
}
else if (this.max != null) {
keepMax = true;
}
// var threshold = 30;
// var tdim = Math.max(dim, threshold+1);
// this._scalefact = (tdim-threshold)/300.0;
var ret = $.jqplot.LinearTickGenerator(min, max, this._scalefact, _numberTicks, keepMin, keepMax);
// calculate a padded max and min, points should be less than these
// so that they aren't too close to the edges of the plot.
// User can adjust how much padding is allowed with pad, padMin and PadMax options.
// If min or max is set, don't pad that end of axis.
var tumin = (this.min != null) ? min : min + range*(this.padMin - 1);
var tumax = (this.max != null) ? max : max - range*(this.padMax - 1);
// if they're equal, we shouldn't have to do anything, right?
// if (min <=tumin || max >= tumax) {
if (min <tumin || max > tumax) {
tumin = (this.min != null) ? min : min - range*(this.padMin - 1);
tumax = (this.max != null) ? max : max + range*(this.padMax - 1);
ret = $.jqplot.LinearTickGenerator(tumin, tumax, this._scalefact, _numberTicks, keepMin, keepMax);
}
this.min = ret[0];
this.max = ret[1];
// if numberTicks specified, it should return the same.
this.numberTicks = ret[2];
this._autoFormatString = ret[3];
this.tickInterval = ret[4];
}
// User has specified some axis scale related option, can use auto algorithm
else {
// if min and max are same, space them out a bit
if (min == max) {
var adj = 0.05;
if (min > 0) {
adj = Math.max(Math.log(min)/Math.LN10, 0.05);
}
min -= adj;
max += adj;
}
// autoscale. Can't autoscale if min or max is supplied.
// Will use numberTicks and tickInterval if supplied. Ticks
// across multiple axes may not line up depending on how
// bars are to be plotted.
if (this.autoscale && this.min == null && this.max == null) {
var rrange, ti, margin;
var forceMinZero = false;
var forceZeroLine = false;
var intervals = {min:null, max:null, average:null, stddev:null};
// if any series are bars, or if any are fill to zero, and if this
// is the axis to fill toward, check to see if we can start axis at zero.
for (var i=0; i<this._series.length; i++) {
var s = this._series[i];
var faname = (s.fillAxis == 'x') ? s._xaxis.name : s._yaxis.name;
// check to see if this is the fill axis
if (this.name == faname) {
var vals = s._plotValues[s.fillAxis];
var vmin = vals[0];
var vmax = vals[0];
for (var j=1; j<vals.length; j++) {
if (vals[j] < vmin) {
vmin = vals[j];
}
else if (vals[j] > vmax) {
vmax = vals[j];
}
}
var dp = (vmax - vmin) / vmax;
// is this sries a bar?
if (s.renderer.constructor == $.jqplot.BarRenderer) {
// if no negative values and could also check range.
if (vmin >= 0 && (s.fillToZero || dp > 0.1)) {
forceMinZero = true;
}
else {
forceMinZero = false;
if (s.fill && s.fillToZero && vmin < 0 && vmax > 0) {
forceZeroLine = true;
}
else {
forceZeroLine = false;
}
}
}
// if not a bar and filling, use appropriate method.
else if (s.fill) {
if (vmin >= 0 && (s.fillToZero || dp > 0.1)) {
forceMinZero = true;
}
else if (vmin < 0 && vmax > 0 && s.fillToZero) {
forceMinZero = false;
forceZeroLine = true;
}
else {
forceMinZero = false;
forceZeroLine = false;
}
}
// if not a bar and not filling, only change existing state
// if it doesn't make sense
else if (vmin < 0) {
forceMinZero = false;
}
}
}
// check if we need make axis min at 0.
if (forceMinZero) {
// compute number of ticks
this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
this.min = 0;
userMin = 0;
// what order is this range?
// what tick interval does that give us?
ti = max/(this.numberTicks-1);
temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10)));
if (ti/temp == parseInt(ti/temp, 10)) {
ti += temp;
}
this.tickInterval = Math.ceil(ti/temp) * temp;
this.max = this.tickInterval * (this.numberTicks - 1);
}
// check if we need to make sure there is a tick at 0.
else if (forceZeroLine) {
// compute number of ticks
this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
var ntmin = Math.ceil(Math.abs(min)/range*(this.numberTicks-1));
var ntmax = this.numberTicks - 1 - ntmin;
ti = Math.max(Math.abs(min/ntmin), Math.abs(max/ntmax));
temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10)));
this.tickInterval = Math.ceil(ti/temp) * temp;
this.max = this.tickInterval * ntmax;
this.min = -this.tickInterval * ntmin;
}
// if nothing else, do autoscaling which will try to line up ticks across axes.
else {
if (this.numberTicks == null){
if (this.tickInterval) {
this.numberTicks = 3 + Math.ceil(range / this.tickInterval);
}
else {
this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
}
}
if (this.tickInterval == null) {
// get a tick interval
ti = range/(this.numberTicks - 1);
if (ti < 1) {
temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10)));
}
else {
temp = 1;
}
this.tickInterval = Math.ceil(ti*temp*this.pad)/temp;
}
else {
temp = 1 / this.tickInterval;
}
// try to compute a nicer, more even tick interval
// temp = Math.pow(10, Math.floor(Math.log(ti)/Math.LN10));
// this.tickInterval = Math.ceil(ti/temp) * temp;
rrange = this.tickInterval * (this.numberTicks - 1);
margin = (rrange - range)/2;
if (this.min == null) {
this.min = Math.floor(temp*(min-margin))/temp;
}
if (this.max == null) {
this.max = this.min + rrange;
}
}
// Compute a somewhat decent format string if it is needed.
// get precision of interval and determine a format string.
var sf = $.jqplot.getSignificantFigures(this.tickInterval);
var fstr;
// if we have only a whole number, use integer formatting
if (sf.digitsLeft >= sf.significantDigits) {
fstr = '%d';
}
else {
var temp = Math.max(0, 5 - sf.digitsLeft);
temp = Math.min(temp, sf.digitsRight);
fstr = '%.'+ temp + 'f';
}
this._autoFormatString = fstr;
}
// Use the default algorithm which pads each axis to make the chart
// centered nicely on the grid.
else {
rmin = (this.min != null) ? this.min : min - range*(this.padMin - 1);
rmax = (this.max != null) ? this.max : max + range*(this.padMax - 1);
range = rmax - rmin;
if (this.numberTicks == null){
// if tickInterval is specified by user, we will ignore computed maximum.
// max will be equal or greater to fit even # of ticks.
if (this.tickInterval != null) {
this.numberTicks = Math.ceil((rmax - rmin)/this.tickInterval)+1;
}
else if (dim > 100) {
this.numberTicks = parseInt(3+(dim-100)/75, 10);
}
else {
this.numberTicks = 2;
}
}
if (this.tickInterval == null) {
this.tickInterval = range / (this.numberTicks-1);
}
if (this.max == null) {
rmax = rmin + this.tickInterval*(this.numberTicks - 1);
}
if (this.min == null) {
rmin = rmax - this.tickInterval*(this.numberTicks - 1);
}
// get precision of interval and determine a format string.
var sf = $.jqplot.getSignificantFigures(this.tickInterval);
var fstr;
// if we have only a whole number, use integer formatting
if (sf.digitsLeft >= sf.significantDigits) {
fstr = '%d';
}
else {
var temp = Math.max(0, 5 - sf.digitsLeft);
temp = Math.min(temp, sf.digitsRight);
fstr = '%.'+ temp + 'f';
}
this._autoFormatString = fstr;
this.min = rmin;
this.max = rmax;
}
if (this.renderer.constructor == $.jqplot.LinearAxisRenderer && this._autoFormatString == '') {
// fix for misleading tick display with small range and low precision.
range = this.max - this.min;
// figure out precision
var temptick = new this.tickRenderer(this.tickOptions);
// use the tick formatString or, the default.
var fs = temptick.formatString || $.jqplot.config.defaultTickFormatString;
var fs = fs.match($.jqplot.sprintf.regex)[0];
var precision = 0;
if (fs) {
if (fs.search(/[fFeEgGpP]/) > -1) {
var m = fs.match(/\%\.(\d{0,})?[eEfFgGpP]/);
if (m) {
precision = parseInt(m[1], 10);
}
else {
precision = 6;
}
}
else if (fs.search(/[di]/) > -1) {
precision = 0;
}
// fact will be <= 1;
var fact = Math.pow(10, -precision);
if (this.tickInterval < fact) {
// need to correct underrange
if (userNT == null && userTI == null) {
this.tickInterval = fact;
if (userMax == null && userMin == null) {
// this.min = Math.floor((this._dataBounds.min - this.tickInterval)/fact) * fact;
this.min = Math.floor(this._dataBounds.min/fact) * fact;
if (this.min == this._dataBounds.min) {
this.min = this._dataBounds.min - this.tickInterval;
}
// this.max = Math.ceil((this._dataBounds.max + this.tickInterval)/fact) * fact;
this.max = Math.ceil(this._dataBounds.max/fact) * fact;
if (this.max == this._dataBounds.max) {
this.max = this._dataBounds.max + this.tickInterval;
}
var n = (this.max - this.min)/this.tickInterval;
n = n.toFixed(11);
n = Math.ceil(n);
this.numberTicks = n + 1;
}
else if (userMax == null) {
// add one tick for top of range.
var n = (this._dataBounds.max - this.min) / this.tickInterval;
n = n.toFixed(11);
this.numberTicks = Math.ceil(n) + 2;
this.max = this.min + this.tickInterval * (this.numberTicks-1);
}
else if (userMin == null) {
// add one tick for bottom of range.
var n = (this.max - this._dataBounds.min) / this.tickInterval;
n = n.toFixed(11);
this.numberTicks = Math.ceil(n) + 2;
this.min = this.max - this.tickInterval * (this.numberTicks-1);
}
else {
// calculate a number of ticks so max is within axis scale
this.numberTicks = Math.ceil((userMax - userMin)/this.tickInterval) + 1;
// if user's min and max don't fit evenly in ticks, adjust.
// This takes care of cases such as user min set to 0, max set to 3.5 but tick
// format string set to %d (integer ticks)
this.min = Math.floor(userMin*Math.pow(10, precision))/Math.pow(10, precision);
this.max = Math.ceil(userMax*Math.pow(10, precision))/Math.pow(10, precision);
// this.max = this.min + this.tickInterval*(this.numberTicks-1);
this.numberTicks = Math.ceil((this.max - this.min)/this.tickInterval) + 1;
}
}
}
}
}
}
if (this._overrideFormatString && this._autoFormatString != '') {
this.tickOptions = this.tickOptions || {};
this.tickOptions.formatString = this._autoFormatString;
}
var t, to;
for (var i=0; i<this.numberTicks; i++){
tt = this.min + i * this.tickInterval;
t = new this.tickRenderer(this.tickOptions);
// var t = new $.jqplot.AxisTickRenderer(this.tickOptions);
t.setTick(tt, this.name);
this._ticks.push(t);
if (i < this.numberTicks - 1) {
for (var j=0; j<this.minorTicks; j++) {
tt += this.tickInterval/(this.minorTicks+1);
to = $.extend(true, {}, this.tickOptions, {name:this.name, value:tt, label:'', isMinorTick:true});
t = new this.tickRenderer(to);
this._ticks.push(t);
}
}
t = null;
}
}
if (this.tickInset) {
this.min = this.min - this.tickInset * this.tickInterval;
this.max = this.max + this.tickInset * this.tickInterval;
}
ticks = null;
};
// Used to reset just the values of the ticks and then repack, which will
// recalculate the positioning functions. It is assuemd that the
// number of ticks is the same and the values of the new array are at the
// proper interval.
// This method needs to be called with the scope of an axis object, like:
//
// > plot.axes.yaxis.renderer.resetTickValues.call(plot.axes.yaxis, yarr);
//
$.jqplot.LinearAxisRenderer.prototype.resetTickValues = function(opts) {
if ($.isArray(opts) && opts.length == this._ticks.length) {
var t;
for (var i=0; i<opts.length; i++) {
t = this._ticks[i];
t.value = opts[i];
t.label = t.formatter(t.formatString, opts[i]);
t.label = t.prefix + t.label;
t._elem.html(t.label);
}
t = null;
this.min = $.jqplot.arrayMin(opts);
this.max = $.jqplot.arrayMax(opts);
this.pack();
}
// Not implemented yet.
// else if ($.isPlainObject(opts)) {
//
// }
};
// called with scope of axis
$.jqplot.LinearAxisRenderer.prototype.pack = function(pos, offsets) {
// Add defaults for repacking from resetTickValues function.
pos = pos || {};
offsets = offsets || this._offsets;
var ticks = this._ticks;
var max = this.max;
var min = this.min;
var offmax = offsets.max;
var offmin = offsets.min;
var lshow = (this._label == null) ? false : this._label.show;
for (var p in pos) {
this._elem.css(p, pos[p]);
}
this._offsets = offsets;
// pixellength will be + for x axes and - for y axes becasue pixels always measured from top left.
var pixellength = offmax - offmin;
var unitlength = max - min;
// point to unit and unit to point conversions references to Plot DOM element top left corner.
if (this.breakPoints) {
unitlength = unitlength - this.breakPoints[1] + this.breakPoints[0];
this.p2u = function(p){
return (p - offmin) * unitlength / pixellength + min;
};
this.u2p = function(u){
if (u > this.breakPoints[0] && u < this.breakPoints[1]){
u = this.breakPoints[0];
}
if (u <= this.breakPoints[0]) {
return (u - min) * pixellength / unitlength + offmin;
}
else {
return (u - this.breakPoints[1] + this.breakPoints[0] - min) * pixellength / unitlength + offmin;
}
};
if (this.name.charAt(0) == 'x'){
this.series_u2p = function(u){
if (u > this.breakPoints[0] && u < this.breakPoints[1]){
u = this.breakPoints[0];
}
if (u <= this.breakPoints[0]) {
return (u - min) * pixellength / unitlength;
}
else {
return (u - this.breakPoints[1] + this.breakPoints[0] - min) * pixellength / unitlength;
}
};
this.series_p2u = function(p){
return p * unitlength / pixellength + min;
};
}
else {
this.series_u2p = function(u){
if (u > this.breakPoints[0] && u < this.breakPoints[1]){
u = this.breakPoints[0];
}
if (u >= this.breakPoints[1]) {
return (u - max) * pixellength / unitlength;
}
else {
return (u + this.breakPoints[1] - this.breakPoints[0] - max) * pixellength / unitlength;
}
};
this.series_p2u = function(p){
return p * unitlength / pixellength + max;
};
}
}
else {
this.p2u = function(p){
return (p - offmin) * unitlength / pixellength + min;
};
this.u2p = function(u){
return (u - min) * pixellength / unitlength + offmin;
};
if (this.name == 'xaxis' || this.name == 'x2axis'){
this.series_u2p = function(u){
return (u - min) * pixellength / unitlength;
};
this.series_p2u = function(p){
return p * unitlength / pixellength + min;
};
}
else {
this.series_u2p = function(u){
return (u - max) * pixellength / unitlength;
};
this.series_p2u = function(p){
return p * unitlength / pixellength + max;
};
}
}
if (this.show) {
if (this.name == 'xaxis' || this.name == 'x2axis') {
for (var i=0; i<ticks.length; i++) {
var t = ticks[i];
if (t.show && t.showLabel) {
var shim;
if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
// will need to adjust auto positioning based on which axis this is.
var temp = (this.name == 'xaxis') ? 1 : -1;
switch (t.labelPosition) {
case 'auto':
// position at end
if (temp * t.angle < 0) {
shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
}
// position at start
else {
shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
}
break;
case 'end':
shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
break;
case 'start':
shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
break;
case 'middle':
shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
break;
default:
shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
break;
}
}
else {
shim = -t.getWidth()/2;
}
var val = this.u2p(t.value) + shim + 'px';
t._elem.css('left', val);
t.pack();
}
}
if (lshow) {
var w = this._label._elem.outerWidth(true);
this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px');
if (this.name == 'xaxis') {
this._label._elem.css('bottom', '0px');
}
else {
this._label._elem.css('top', '0px');
}
this._label.pack();
}
}
else {
for (var i=0; i<ticks.length; i++) {
var t = ticks[i];
if (t.show && t.showLabel) {
var shim;
if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
// will need to adjust auto positioning based on which axis this is.
var temp = (this.name == 'yaxis') ? 1 : -1;
switch (t.labelPosition) {
case 'auto':
// position at end
case 'end':
if (temp * t.angle < 0) {
shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
}
else {
shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
}
break;
case 'start':
if (t.angle > 0) {
shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
}
else {
shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
}
break;
case 'middle':
// if (t.angle > 0) {
// shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
// }
// else {
// shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
// }
shim = -t.getHeight()/2;
break;
default:
shim = -t.getHeight()/2;
break;
}
}
else {
shim = -t.getHeight()/2;
}
var val = this.u2p(t.value) + shim + 'px';
t._elem.css('top', val);
t.pack();
}
}
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();
}
}
}
ticks = null;
};
})(jQuery);