RGraph = window.RGraph || {isRGraph:
true
};
RGraph.Line =
function
(conf)
{
if
(
typeof
conf === 'object
'
&& typeof conf.data === '
object
'
&& typeof conf.id === '
string
') {
var id = conf.id;
var canvas = document.getElementById(id);
var data = conf.data;
var parseConfObjectForOptions = true; // Set this so the config is parsed (at the end of the constructor)
} else {
var id = conf;
var canvas = document.getElementById(id);
var data = arguments[1];
}
this.id = id;
this.canvas = canvas;
this.context = this.canvas.getContext('
2d
');
this.canvas.__object__ = this;
this.type = '
line
';
this.max = 0;
this.coords = [];
this.coords2 = [];
this.coords.key = [];
this.coordsText = [];
this.coordsSpline = [];
this.coordsAxes = {xaxis: [], yaxis: []};
this.hasnegativevalues = false;
this.isRGraph = true;
this.uid = RGraph.CreateUID();
this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID();
this.colorsParsed = false;
this.original_colors = [];
this.firstDraw = true; // After the first draw this will be false
/**
* Compatibility with older browsers
*/
//RGraph.OldBrowserCompat(this.context);
// Various config type stuff
this.properties =
{
'
chart.background.barcolor1
': '
rgba(0,0,0,0)
',
'
chart.background.barcolor2
': '
rgba(0,0,0,0)
',
'
chart.background.grid
': 1,
'
chart.background.grid.width
': 1,
'
chart.background.grid.hsize
': 25,
'
chart.background.grid.vsize
': 25,
'
chart.background.grid.color
': '
#ddd',
'chart.background.grid.vlines
': true,
'
chart.background.grid.hlines
': true,
'
chart.background.grid.border
': true,
'
chart.background.grid.autofit
': true,
'
chart.background.grid.autofit.align
': true,
'
chart.background.grid.autofit.numhlines
': 5,
'
chart.background.grid.autofit.numvlines
': null,
'
chart.background.grid.dashed
': false,
'
chart.background.grid.dotted
': false,
'
chart.background.hbars
': null,
'
chart.background.image
': null,
'
chart.background.image.stretch
': true,
'
chart.background.image.x
': null,
'
chart.background.image.y
': null,
'
chart.background.image.w
': null,
'
chart.background.image.h
': null,
'
chart.background.image.align
': null,
'
chart.background.color
': null,
'
chart.labels
': null,
'
chart.labels.bold
': false,
'
chart.labels.color
': null,
'
chart.labels.ingraph
': null,
'
chart.labels.above
': false, // Working
'
chart.labels.above.size
': 8, // Working
'
chart.labels.above.decimals
': null, // Working
'
chart.labels.above.color
': null,
'
chart.labels.above.background
': '
white
',
'
chart.labels.above.font
': null,
'
chart.labels.above.border
': true,
'
chart.labels.above.offsety
': 5,
'
chart.labels.above.units.pre
': '
',
'
chart.labels.above.units.post
': '
',
'
chart.labels.above.specific
': null,
'
chart.labels.offsetx
': 0,
'
chart.labels.offsety
': 0,
'
chart.xtickgap
': 20,
'
chart.smallxticks
': 3,
'
chart.largexticks
': 5,
'
chart.ytickgap
': 20,
'
chart.smallyticks
': 3,
'
chart.largeyticks
': 5,
'
chart.numyticks
': 10,
'
chart.linewidth
': 2.01,
'
chart.colors
': ['
red
', '
#0f0', '#00f', '#f0f', '#ff0', '#0ff','green','pink','blue','black'],
'chart.hmargin
': 0,
'
chart.tickmarks.dot.stroke
': '
white
',
'
chart.tickmarks.dot.fill
': null,
'
chart.tickmarks.dot.linewidth
': 3,
'
chart.tickmarks
': '
endcircle
',
'
chart.tickmarks.linewidth
': null,
'
chart.tickmarks.image
': null,
'
chart.tickmarks.image.halign
': '
center
',
'
chart.tickmarks.image.valign
': '
center
',
'
chart.tickmarks.image.offsetx
':0,
'
chart.tickmarks.image.offsety
':0,
'
chart.ticksize
': 3,
'
chart.gutter.left
': 25,
'
chart.gutter.right
': 25,
'
chart.gutter.top
': 25,
'
chart.gutter.bottom
': 30,
'
chart.tickdirection
': -1,
'
chart.yaxispoints
': 5,
'
chart.fillstyle
': null,
'
chart.xaxispos
': '
bottom
',
'
chart.xaxispos.value
': 0,
'
chart.yaxispos
': '
left
',
'
chart.xticks
': null,
'
chart.text.size
': 12,
'
chart.text.angle
': 0,
'
chart.text.color
': '
black
',
'
chart.text.font
': '
Segoe UI, Arial, Verdana, sans-serif
',
'
chart.text.accessible
': true,
'
chart.text.accessible.overflow
': '
visible
',
'
chart.text.accessible.pointerevents
': true,
'
chart.ymin
': 0,
'
chart.ymax
': null,
'
chart.title
': '
',
'
chart.title.background
': null,
'
chart.title.hpos
': null,
'
chart.title.vpos
': null,
'
chart.title.bold
': true,
'
chart.title.font
': null,
'
chart.title.xaxis
': '
',
'
chart.title.xaxis.bold
': true,
'
chart.title.xaxis.size
': null,
'
chart.title.xaxis.font
': null,
'
chart.title.xaxis.color
': null,
'
chart.title.yaxis
': '
',
'
chart.title.yaxis.bold
': true,
'
chart.title.yaxis.size
': null,
'
chart.title.yaxis.font
': null,
'
chart.title.yaxis.color
': null,
'
chart.title.xaxis.pos
': null,
'
chart.title.yaxis.pos
': null,
'
chart.title.yaxis.x
': null,
'
chart.title.yaxis.y
': null,
'
chart.title.xaxis.x
': null,
'
chart.title.xaxis.y
': null,
'
chart.title.x
': null,
'
chart.title.y
': null,
'
chart.title.halign
': null,
'
chart.title.valign
': null,
'
chart.shadow
': true,
'
chart.shadow.offsetx
': 2,
'
chart.shadow.offsety
': 2,
'
chart.shadow.blur
': 3,
'
chart.shadow.color
': '
rgba(128,128,128,0.5)
',
'
chart.tooltips
': null,
'
chart.tooltips.hotspot.xonly
': false,
'
chart.tooltips.hotspot.size
': 5,
'
chart.tooltips.effect
': '
fade
',
'
chart.tooltips.css.
class
': '
RGraph_tooltip
',
'
chart.tooltips.event
': '
onmousemove
',
'
chart.tooltips.highlight
': true,
'
chart.tooltips.coords.page
': false,
'
chart.highlight.style
': null,
'
chart.highlight.stroke
': '
gray
',
'
chart.highlight.fill
': '
white
',
'
chart.stepped
': false,
'
chart.key
': null,
'
chart.key.background
': '
white
',
'
chart.key.position
': '
graph
',
'
chart.key.halign
': null,
'
chart.key.shadow
': false,
'
chart.key.shadow.color
': '
#666',
'chart.key.shadow.blur
': 3,
'
chart.key.shadow.offsetx
': 2,
'
chart.key.shadow.offsety
': 2,
'
chart.key.position.gutter.boxed
': false,
'
chart.key.position.x
': null,
'
chart.key.position.y
': null,
'
chart.key.color.shape
': '
square
',
'
chart.key.rounded
': true,
'
chart.key.linewidth
': 1,
'
chart.key.colors
': null,
'
chart.key.interactive
': false,
'
chart.key.interactive.highlight.chart.stroke
': '
rgba(255,0,0,0.3)
',
'
chart.key.interactive.highlight.label
': '
rgba(255,0,0,0.2)
',
'
chart.key.text.color
': '
black
',
'
chart.contextmenu
': null,
'
chart.ylabels
': true,
'
chart.ylabels.count
': 5,
'
chart.ylabels.inside
': false,
'
chart.ylabels.offsetx
': 0,
'
chart.ylabels.offsety
': 0,
'
chart.scale.invert
': false,
'
chart.xlabels.inside
': false,
'
chart.xlabels.inside.color
': '
rgba(255,255,255,0.5)
',
'
chart.noaxes
': false,
'
chart.noyaxis
': false,
'
chart.noxaxis
': false,
'
chart.noendxtick
': false,
'
chart.noendytick
': false,
'
chart.units.post
': '
',
'
chart.units.pre
': '
',
'
chart.scale.zerostart
': true,
'
chart.scale.decimals
': null,
'
chart.scale.point
': '
.
',
'
chart.scale.thousand
': '
,
',
'
chart.crosshairs
': false,
'
chart.crosshairs.color
': '
#333',
'chart.crosshairs.hline
': true,
'
chart.crosshairs.vline
': true,
'
chart.annotatable
': false,
'
chart.annotate.color
': '
black
',
'
chart.axesontop
': false,
'
chart.filled
': false,
'
chart.filled.range
': false,
'
chart.filled.range.threshold
': null,
'
chart.filled.range.threshold.colors
': ['
red
', '
green
'],
'
chart.filled.accumulative
': true,
'
chart.variant
': null,
'
chart.axis.color
': '
black
',
'
chart.axis.linewidth
': 1,
'
chart.numxticks
': (data && typeof(data[0]) == '
number
' ? data.length - 1 : (typeof data[0] === '
object
' && data[0] && typeof data[0][0] === '
number
' ? data[0].length - 1 : 20)),
'
chart.numyticks
': 10,
'
chart.zoom.factor
': 1.5,
'
chart.zoom.fade.
in
': true,
'
chart.zoom.fade.out
': true,
'
chart.zoom.hdir
': '
right
',
'
chart.zoom.vdir
': '
down
',
'
chart.zoom.frames
': 25,
'
chart.zoom.delay
': 16.666,
'
chart.zoom.shadow
': true,
'
chart.zoom.background
': true,
'
chart.zoom.action
': '
zoom
',
'
chart.backdrop
': false,
'
chart.backdrop.size
': 30,
'
chart.backdrop.alpha
': 0.2,
'
chart.resizable
': false,
'
chart.resize.handle.adjust
': [0,0],
'
chart.resize.handle.background
': null,
'
chart.adjustable
': false,
'
chart.adjustable.only
': null,
'
chart.noredraw
': false,
'
chart.outofbounds
': false,
'
chart.outofbounds.clip
': false,
'
chart.chromefix
': true,
'
chart.animation.factor
': 1,
'
chart.animation.unfold.x
': false,
'
chart.animation.unfold.y
': true,
'
chart.animation.unfold.initial
': 2,
'
chart.animation.trace.clip
': 1,
'
chart.curvy
': false,
'
chart.line.visible
': [],
'
chart.events.click
': null,
'
chart.events.mousemove
': null,
'
chart.errorbars
': false,
'
chart.errorbars.color
': '
black
',
'
chart.errorbars.capped
': true,
'
chart.errorbars.capped.width
': 12,
'
chart.errorbars.linewidth
': 1,
'
chart.combinedchart.effect
': null,
'
chart.combinedchart.effect.options
': null,
'
chart.combinedchart.effect.callback
': null,
'
chart.clearto
': '
rgba(0,0,0,0)
',
'
chart.dotted
': false,
'
chart.dashed
': false
}
/**
* Change null arguments to empty arrays
*/
for (var i=1; i<arguments.length; ++i) {
if (typeof(arguments[i]) == '
null
' || !arguments[i]) {
arguments[i] = [];
}
}
/**
* Store the original data. This also allows for giving arguments as one big array.
*/
this.original_data = [];
// This allows for the new object based configuration style
if (typeof conf === '
object
' && conf.data) {
if (typeof conf.data[0] === '
number
' || RGraph.isNull(conf.data[0])) {
this.original_data[0] = RGraph.arrayClone(conf.data);
//} else if (typeof conf.data[0] === '
object
' && !RGraph.isNull(conf.data[0])) {
} else {
for (var i=0; i<conf.data.length; ++i) {
this.original_data[i] = RGraph.arrayClone(conf.data[i]);
}
}
// Allow for the older configuration style
} else {
for (var i=1; i<arguments.length; ++i) {
if ( arguments[1]
&& typeof(arguments[1]) == '
object
'
&& arguments[1][0]
&& typeof(arguments[1][0]) == '
object
'
&& arguments[1][0].length) {
var tmp = [];
for (var i=0; i<arguments[1].length; ++i) {
tmp[i] = RGraph.array_clone(arguments[1][i]);
}
for (var j=0; j<tmp.length; ++j) {
this.original_data[j] = RGraph.array_clone(tmp[j]);
}
} else {
this.original_data[i - 1] = RGraph.array_clone(arguments[i]);
}
}
}
// Check for support
if (!this.canvas) {
alert('
[LINE] Fatal error: no canvas support
');
return;
}
// Convert strings to numbers
for (var i=0; i<this.original_data.length; ++i) {
for (var j=0; j<this.original_data[i].length; ++j) {
if (typeof this.original_data[i][j] === '
string
') {
this.original_data[i][j] = parseFloat(this.original_data[i][j]);
}
}
}
/**
* Store the data here as one big array
*/
this.data_arr = RGraph.arrayLinearize(this.original_data);
for (var i=0; i<this.data_arr.length; ++i) {
this['
$
' + i] = {};
}
/**
* Translate half a pixel for antialiasing purposes - but only if it hasn'
t beeen
* done already
*/
if
(!
this
.canvas.__rgraph_aa_translated__) {
this
.context.translate(0.5,0.5);
this
.canvas.__rgraph_aa_translated__ =
true
;
}
var
RG = RGraph,
ca =
this
.canvas,
co = ca.getContext(
'2d'
),
prop =
this
.properties,
pa2 = RG.path2,
win = window,
doc = document,
ma = Math
if
(RG.Effects &&
typeof
RG.Effects.decorate ===
'function'
) {
RG.Effects.decorate(
this
);
}
this
.set =
this
.Set =
function
(name)
{
var
value =
typeof
arguments[1] ===
'undefined'
?
null
: arguments[1];
if
(arguments.length === 1 &&
typeof
name === 'object
') {
RG.parseObjectStyleConfig(this, name);
return this;
}
/**
* This should be done first - prepend the propertyy name with "chart." if necessary
*/
if (name.substr(0,6) != '
chart.
') {
name = '
chart.
' + name;
}
// Convert uppercase letters to dot+lower case letter
while(name.match(/([A-Z])/)) {
name = name.replace(/([A-Z])/, '
.
' + RegExp.$1.toLowerCase());
}
// Consolidate the tooltips
if (name == '
chart.tooltips
' && typeof value == '
object
' && value) {
var tooltips = [];
for (var i=1; i<arguments.length; i++) {
if (typeof(arguments[i]) == '
object
' && arguments[i][0]) {
for (var j=0; j<arguments[i].length; j++) {
tooltips.push(arguments[i][j]);
}
} else if (typeof(arguments[i]) == '
function
') {
tooltips = arguments[i];
} else {
tooltips.push(arguments[i]);
}
}
// Because "value" is used further down at the end of this function, set it to the expanded array os tooltips
value = tooltips;
}
/**
* If (buggy) Chrome and the linewidth is 1, change it to 1.01
*/
if (name == '
chart.linewidth
' && navigator.userAgent.match(/Chrome/)) {
if (value == 1) {
value = 1.01;
} else if (RGraph.is_array(value)) {
for (var i=0; i<value.length; ++i) {
if (typeof(value[i]) == '
number
' && value[i] == 1) {
value[i] = 1.01;
}
}
}
}
/**
* Check for xaxispos
*/
if (name == '
chart.xaxispos
' ) {
if (value != '
bottom
' && value != '
center
' && value != '
top
') {
alert('
[LINE] (
' + this.id + '
) chart.xaxispos should be top, center or bottom. Tried to set it to:
' + value + '
Changing it to center
');
value = '
center
';
}
}
/**
* chart.xticks is now called chart.numxticks
*/
if (name == '
chart.xticks
') {
name = '
chart.numxticks
';
}
/**
* Change the new chart.spline option to chart.curvy
*/
if (name == '
chart.spline
') {
name = '
chart.curvy
';
}
/**
* Chnge chart.ylabels.invert to chart.scale.invert
*/
if (name == '
chart.ylabels.invert
') {
name = '
chart.scale.invert
';
}
this.properties[name] = value;
return this;
};
/**
* An all encompassing accessor
*
* @param string name The name of the property
*/
this.get =
this.Get = function (name)
{
/**
* This should be done first - prepend the property name with "chart." if necessary
*/
if (name.substr(0,6) != '
chart.
') {
name = '
chart.
' + name;
}
// Convert uppercase letters to dot+lower case letter
while(name.match(/([A-Z])/)) {
name = name.replace(/([A-Z])/, '
.
' + RegExp.$1.toLowerCase());
}
/**
* If requested property is chart.spline - change it to chart.curvy
*/
if (name == '
chart.spline
') {
name = '
chart.curvy
';
}
return prop[name];
};
/**
* The function you call to draw the line chart
*
* @param bool An optional bool used internally to ditinguish whether the
* line chart is being called by the bar chart
*
* Draw()
* |
* +--Draw()
* | |
* | +-DrawLine()
* |
* +-RedrawLine()
* |
* +-DrawCurvyLine()
* |
* +-DrawSpline()
*/
this.draw =
this.Draw = function ()
{
// MUST be the first thing done!
if (typeof(prop['
chart.background.image
']) == '
string
') {
RG.DrawBackgroundImage(this);
}
/**
* Fire the onbeforedraw event
*/
RG.FireCustomEvent(this, '
onbeforedraw
');
/**
* Parse the colors. This allows for simple gradient syntax
*/
if (!this.colorsParsed) {
this.parseColors();
// Don'
t want to
do
this
again
this
.colorsParsed =
true
;
}
this
.gutterLeft = prop[
'chart.gutter.left'
];
this
.gutterRight = prop[
'chart.gutter.right'
];
this
.gutterTop = prop[
'chart.gutter.top'
];
this
.gutterBottom = prop[
'chart.gutter.bottom'
];
this
.data = RG.array_clone(
this
.original_data);
this
.max = 0;
if
(prop[
'chart.filled'
] && !prop[
'chart.filled.range'
] &&
this
.data.length > 1 && prop[
'chart.filled.accumulative'
]) {
var
accumulation = [];
for
(
var
set=0; set<
this
.data.length; ++set) {
for
(
var
point=0; point<
this
.data[set].length; ++point) {
this
.data[set][point] = Number(accumulation[point] ? accumulation[point] : 0) +
this
.data[set][point];
accumulation[point] =
this
.data[set][point];
}
}
}
if
(prop[
'chart.ymax'
]) {
this
.max = prop[
'chart.ymax'
];
this
.min = prop[
'chart.ymin'
] ? prop[
'chart.ymin'
] : 0;
this
.scale2 = RG.getScale2(
this
, {
'max'
:
this
.max,
'min'
:prop[
'chart.ymin'
],
'strict'
:
true
,
'scale.thousand'
:prop[
'chart.scale.thousand'
],
'scale.point'
:prop[
'chart.scale.point'
],
'scale.decimals'
:prop[
'chart.scale.decimals'
],
'ylabels.count'
:prop[
'chart.ylabels.count'
],
'scale.round'
:prop[
'chart.scale.round'
],
'units.pre'
: prop[
'chart.units.pre'
],
'units.post'
: prop[
'chart.units.post'
]
});
this
.max =
this
.scale2.max ?
this
.scale2.max : 0;
if
(!prop[
'chart.outofbounds'
]) {
for
(dataset=0; dataset<
this
.data.length; ++dataset) {
if
(RGraph.isArray(
this
.data[dataset])) {
for
(
var
datapoint=0; datapoint<
this
.data[dataset].length; datapoint++) {
this
.hasnegativevalues = (
this
.data[dataset][datapoint] < 0) ||
this
.hasnegativevalues;
}
}
}
}
}
else
{
this
.min = prop[
'chart.ymin'
] ? prop[
'chart.ymin'
] : 0;
for
(dataset=0; dataset<
this
.data.length; ++dataset) {
for
(
var
datapoint=0; datapoint<
this
.data[dataset].length; datapoint++) {
this
.max = Math.max(
this
.max,
this
.data[dataset][datapoint] ? Math.abs(parseFloat(
this
.data[dataset][datapoint])) : 0);
if
(!prop[
'chart.outofbounds'
]) {
this
.hasnegativevalues = (
this
.data[dataset][datapoint] < 0) ||
this
.hasnegativevalues;
}
}
}
this
.scale2 = RG.getScale2(
this
, {
'max'
:
this
.max,
'min'
:prop[
'chart.ymin'
],
'scale.thousand'
:prop[
'chart.scale.thousand'
],
'scale.point'
:prop[
'chart.scale.point'
],
'scale.decimals'
:prop[
'chart.scale.decimals'
],
'ylabels.count'
:prop[
'chart.ylabels.count'
],
'scale.round'
:prop[
'chart.scale.round'
],
'units.pre'
: prop[
'chart.units.pre'
],
'units.post'
: prop[
'chart.units.post'
]
});
this
.max =
this
.scale2.max ?
this
.scale2.max : 0;
}
if
(prop[
'chart.contextmenu'
]) {
RG.ShowContext(
this
);
}
this
.coords = [];
this
.coordsText = [];
this
.grapharea = ca.height -
this
.gutterTop -
this
.gutterBottom;
this
.halfgrapharea =
this
.grapharea / 2;
this
.halfTextHeight = prop[
'chart.text.size'
] / 2;
if
(prop[
'chart.variant'
] ==
'3d'
) {
RG.Draw3DAxes(
this
);
}
RG.background.Draw(
this
);
if
(prop[
'chart.background.hbars'
] && prop[
'chart.background.hbars'
].length > 0) {
RG.DrawBars(
this
);
}
if
(prop[
'chart.axesontop'
] ==
false
) {
this
.DrawAxes();
}
co.save()
co.beginPath();
co.rect(0, 0, ca.width * prop[
'chart.animation.trace.clip'
], ca.height);
co.clip();
for
(
var
i=0, j=0, len=
this
.data.length; i<len; i++, j++) {
co.beginPath();
if
(!prop[
'chart.filled'
]) {
this
.SetShadow(i);
}
if
(prop[
'chart.fillstyle'
]) {
if
(
typeof
(prop[
'chart.fillstyle'
]) ==
'object'
&& prop[
'chart.fillstyle'
][j]) {
var
fill = prop[
'chart.fillstyle'
][j];
}
else
if
(
typeof
(prop[
'chart.fillstyle'
]) ==
'object'
&& prop[
'chart.fillstyle'
].toString().indexOf(
'Gradient'
) > 0) {
var
fill = prop[
'chart.fillstyle'
];
}
else
if
(
typeof
(prop[
'chart.fillstyle'
]) ==
'string'
) {
var
fill = prop[
'chart.fillstyle'
];
}
}
else
if
(prop[
'chart.filled'
]) {
var
fill = prop[
'chart.colors'
][j];
}
else
{
var
fill =
null
;
}
if
(prop[
'chart.tickmarks'
] &&
typeof
(prop[
'chart.tickmarks'
]) ==
'object'
) {
var
tickmarks = prop[
'chart.tickmarks'
][i];
}
else
if
(prop[
'chart.tickmarks'
] &&
typeof
(prop[
'chart.tickmarks'
]) ==
'string'
) {
var
tickmarks = prop[
'chart.tickmarks'
];
}
else
if
(prop[
'chart.tickmarks'
] &&
typeof
(prop[
'chart.tickmarks'
]) ==
'function'
) {
var
tickmarks = prop[
'chart.tickmarks'
];
}
else
{
var
tickmarks =
null
;
}
if
(prop[
'chart.outofbounds.clip'
]) {
pa2(
co,
'sa b r % % % % cl b'
,
0,
this
.gutterTop,
ca.width,
ca.height -
this
.gutterTop -
this
.gutterBottom
);
}
this
.drawLine(
this
.data[i],
prop[
'chart.colors'
][j],
fill,
this
.getLineWidth(j),
tickmarks,
i
);
if
(prop[
'chart.outofbounds.clip'
]) {
co.restore();
}
co.stroke();
}
if
(prop[
'chart.outofbounds.clip'
]) {
pa2(
co,
'sa b r % % % % cl b'
,
0,
this
.gutterTop,
ca.width,
ca.height -
this
.gutterTop -
this
.gutterBottom
);
}
if
(prop[
'chart.filled'
] && prop[
'chart.filled.accumulative'
] && !prop[
'chart.curvy'
]) {
for
(
var
i=0; i<
this
.coords2.length; ++i) {
co.beginPath();
co.lineWidth =
this
.GetLineWidth(i);
co.strokeStyle = !
this
.hidden(i) ? prop[
'chart.colors'
][i] :
'rgba(0,0,0,0)'
;
for
(
var
j=0,len=
this
.coords2[i].length; j<len; ++j) {
if
(j == 0 ||
this
.coords2[i][j][1] ==
null
|| (
this
.coords2[i][j - 1] &&
this
.coords2[i][j - 1][1] ==
null
)) {
co.moveTo(
this
.coords2[i][j][0],
this
.coords2[i][j][1]);
}
else
{
if
(prop[
'chart.stepped'
]) {
co.lineTo(
this
.coords2[i][j][0],
this
.coords2[i][j - 1][1]);
}
co.lineTo(
this
.coords2[i][j][0],
this
.coords2[i][j][1]);
}
}
co.stroke();
}
if
(prop[
'chart.tickmarks'
]) {
co.beginPath();
co.fillStyle =
'white'
;
for
(
var
i=0,len=
this
.coords2.length; i<len; ++i) {
co.beginPath();
co.strokeStyle = prop[
'chart.colors'
][i];
for
(
var
j=0; j<
this
.coords2[i].length; ++j) {
if
(
typeof
(
this
.coords2[i][j]) ==
'object'
&&
typeof
(
this
.coords2[i][j][0]) ==
'number'
&&
typeof
(
this
.coords2[i][j][1]) ==
'number'
) {
var
tickmarks =
typeof
(prop[
'chart.tickmarks'
]) ==
'object'
? prop[
'chart.tickmarks'
][i] : prop[
'chart.tickmarks'
];
this
.DrawTick(
this
.coords2[i],
this
.coords2[i][j][0],
this
.coords2[i][j][1],
co.strokeStyle,
false
,
j == 0 ? 0 :
this
.coords2[i][j - 1][0],
j == 0 ? 0 :
this
.coords2[i][j - 1][1],
tickmarks,
j,
i
);
}
}
}
co.stroke();
co.fill();
}
}
else
if
(prop[
'chart.filled'
] && prop[
'chart.filled.accumulative'
] && prop[
'chart.curvy'
]) {
for
(
var
i=0; i<
this
.coordsSpline.length; i+=1) {
co.beginPath();
co.strokeStyle = prop[
'chart.colors'
][i];
co.lineWidth =
this
.GetLineWidth(i);
for
(
var
j=0,len=
this
.coordsSpline[i].length; j<len; j+=1) {
var
point =
this
.coordsSpline[i][j];
j == 0 ? co.moveTo(point[0], point[1]) : co.lineTo(point[0], point[1]);
}
co.stroke();
}
for
(
var
i=0,len=
this
.coords2.length; i<len; i+=1) {
for
(
var
j=0,len2=
this
.coords2[i].length; j<len2; ++j) {
if
(
typeof
(
this
.coords2[i][j]) ==
'object'
&&
typeof
(
this
.coords2[i][j][0]) ==
'number'
&&
typeof
(
this
.coords2[i][j][1]) ==
'number'
) {
var
tickmarks =
typeof
prop[
'chart.tickmarks'
] ==
'object'
&& !RGraph.is_null(prop[
'chart.tickmarks'
]) ? prop[
'chart.tickmarks'
][i] : prop[
'chart.tickmarks'
];
co.strokeStyle = prop[
'chart.colors'
][i];
this
.DrawTick(
this
.coords2[i],
this
.coords2[i][j][0],
this
.coords2[i][j][1],
prop[
'chart.colors'
][i],
false
,
j == 0 ? 0 :
this
.coords2[i][j - 1][0],
j == 0 ? 0 :
this
.coords2[i][j - 1][1],
tickmarks,
j,
i
);
}
}
}
}
if
(prop[
'chart.outofbounds.clip'
]) {
co.restore();
}
co.restore();
co.beginPath();
if
(prop[
'chart.axesontop'
]) {
this
.DrawAxes();
}
this
.DrawLabels();
this
.DrawRange();
if
(prop[
'chart.key'
] && prop[
'chart.key'
].length && RG.DrawKey) {
RG.DrawKey(
this
, prop[
'chart.key'
], prop[
'chart.colors'
]);
}
if
(prop[
'chart.labels.above'
]) {
this
.drawAboveLabels();
}
RG.DrawInGraphLabels(
this
);
if
(prop[
'chart.filled'
] && prop[
'chart.filled.range'
] &&
this
.data.length == 2) {
co.beginPath();
var
len =
this
.coords.length / 2;
co.lineWidth = prop[
'chart.linewidth'
];
co.strokeStyle =
this
.hidden(0) ?
'rgba(0,0,0,0)'
: prop[
'chart.colors'
][0];
for
(
var
i=0; i<len; ++i) {
if
(!RG.isNull(
this
.coords[i][1])) {
if
(i == 0) {
co.moveTo(
this
.coords[i][0],
this
.coords[i][1]);
}
else
{
co.lineTo(
this
.coords[i][0],
this
.coords[i][1]);
}
}
}
co.stroke();
co.beginPath();
if
(prop[
'chart.colors'
][1]) {
co.strokeStyle =
this
.hidden(1) ?
'rgba(0,0,0,0)'
: prop[
'chart.colors'
][1];
}
for
(
var
i=
this
.coords.length - 1; i>=len; --i) {
if
(!RG.is_null(
this
.coords[i][1])) {
if
(i == (
this
.coords.length - 1)) {
co.moveTo(
this
.coords[i][0],
this
.coords[i][1]);
}
else
{
co.lineTo(
this
.coords[i][0],
this
.coords[i][1]);
}
}
}
co.stroke();
}
else
if
(prop[
'chart.filled'
] && prop[
'chart.filled.range'
]) {
alert(
'[LINE] You must have only two sets of data for a filled range chart'
);
}
if
(prop[
'chart.resizable'
]) {
RG.AllowResizing(
this
);
}
RG.InstallEventListeners(
this
);
if
(
this
.firstDraw) {
this
.firstDraw =
false
;
RG.fireCustomEvent(
this
,
'onfirstdraw'
);
this
.firstDrawFunc();
}
RG.FireCustomEvent(
this
,
'ondraw'
);
return
this
;
};
this
.exec =
function
(func)
{
func(
this
);
return
this
;
};
this
.drawAxes =
this
.DrawAxes =
function
()
{
if
(prop['chart.noaxes
']) {
return;
}
// Turn any shadow off
RG.noShadow(this);
co.lineWidth = prop['
chart.axis.linewidth
'] + 0.001;
co.lineCap = '
square
';
co.lineJoin = '
miter
';
co.strokeStyle = prop['
chart.axis.color
'];
coords = {
xaxis: {},
yaxis: {}
};
co.beginPath();
// Draw the X axis
if (prop['
chart.noxaxis
'] == false) {
if (prop['
chart.xaxispos
'] == '
center
') {
coords.xaxis = [
this.gutterLeft,
ma.round((this.grapharea / 2) + this.gutterTop),
ca.width - this.gutterRight,
ma.round((this.grapharea / 2) + this.gutterTop)
];
} else if (prop['
chart.xaxispos
'] === '
top
') {
coords.xaxis = [
this.gutterLeft,
this.gutterTop,
ca.width - this.gutterRight,
this.gutterTop
];
} else {
var y = ma.round(this.getYCoord(prop['
chart.ymin
'] != 0 ? prop['
chart.ymin
'] : 0));
if (prop['
chart.scale.invert
'] && prop['
chart.ymin
'] === 0) {
y = this.getYCoord(this.scale2.max);
} else if (prop['
chart.scale.invert
'] || prop['
chart.ymin
'] < 0) {
y = this.getYCoord(0);
}
coords.xaxis = [
this.gutterLeft,
y,
ca.width - this.gutterRight,
y
];
}
co.moveTo(coords.xaxis[0], coords.xaxis[1]);
co.lineTo(coords.xaxis[2], coords.xaxis[3]);
// Save the coords so that they can
// be referenced at a later time
this.coordsAxes = coords;
}
// Draw the Y axis
if (prop['
chart.noyaxis
'] == false) {
if (prop['
chart.yaxispos
'] == '
left
') {
co.moveTo(this.gutterLeft, this.gutterTop);
co.lineTo(this.gutterLeft, ca.height - this.gutterBottom);
} else {
co.moveTo(ca.width - this.gutterRight, this.gutterTop);
co.lineTo(ca.width - this.gutterRight, ca.height - this.gutterBottom);
}
}
/**
* Draw the X tickmarks
*/
if (prop['
chart.noxaxis
'] == false && prop['
chart.numxticks
'] > 0) {
var xTickInterval = (ca.width - this.gutterLeft - this.gutterRight) / prop['
chart.numxticks
'];
if (!xTickInterval || xTickInterval <= 0) {
xTickInterval = (ca.width - this.gutterLeft - this.gutterRight) / (prop['
chart.labels
'] && prop['
chart.labels
'].length ? prop['
chart.labels
'].length - 1 : 10);
}
for (x=this.gutterLeft + (prop['
chart.yaxispos
'] == '
left
' ? xTickInterval : 0); x<=(ca.width - this.gutterRight + 1 ); x+=xTickInterval) {
if (prop['
chart.yaxispos
'] == '
right
' && x >= (ca.width - this.gutterRight - 1) ) {
break;
}
// If the last tick is not desired...
if (prop['
chart.noendxtick
']) {
if (prop['
chart.yaxispos
'] == '
left
' && x >= (ca.width - this.gutterRight - 1)) {
break;
} else if (prop['
chart.yaxispos
'] == '
right
' && x == this.gutterLeft) {
continue;
}
}
var yStart = prop['
chart.xaxispos
'] === '
center
' ? (this.gutterTop + (this.grapharea / 2)) - 3 : ca.height - this.gutterBottom;
var yEnd = prop['
chart.xaxispos
'] === '
center
' ? yStart + 6 : ca.height - this.gutterBottom - (x % 60 == 0 ? prop['
chart.largexticks
'] * prop['
chart.tickdirection
'] : prop['
chart.smallxticks
'] * prop['
chart.tickdirection
']);
// Draw the tick
if (prop['
chart.ymin
'] >= 0 && prop['
chart.xaxispos
'] === '
bottom
') {
var yStart = this.getYCoord(prop['
chart.ymin
']) - (prop['
chart.ymin
'] >= 0 ? 0 : 3),
yEnd = this.getYCoord(prop['
chart.ymin
']) + 3;
if (prop['
chart.scale.invert
']) {
yStart = ca.height - prop['
chart.gutter.bottom
'];
yEnd = yStart + 3;
}
} else if (prop['
chart.xaxispos
'] == '
center
') {
var yStart = Math.round((this.gutterTop + (this.grapharea / 2))) - 3,
yEnd = yStart + 6;
} else if (prop['
chart.xaxispos
'] == '
bottom
') {
var yStart = this.getYCoord(0) - (prop['
chart.ymin
'] !== 0 ? 3 : 0),
yEnd = this.getYCoord(0) - (x % 60 == 0 ? prop['
chart.largexticks
'] * prop['
chart.tickdirection
'] : prop['
chart.smallxticks
'] * prop['
chart.tickdirection
']);
yEnd += 0;
} else if (prop['
chart.xaxispos
'] == '
top
') {
yStart = this.gutterTop - 3;
yEnd = this.gutterTop;
}
co.moveTo(ma.round(x), yStart);
co.lineTo(ma.round(x), yEnd);
}
// Draw an extra tickmark if there is no X axis, but there IS a Y axis
// OR if there is an offset X axis
} else if (prop['
chart.noyaxis
'] == false && prop['
chart.numyticks
'] > 0) {
if (!prop['
chart.noendytick
']) {
if (prop['
chart.yaxispos
'] == '
left
') {
co.moveTo(this.gutterLeft, Math.round(ca.height - this.gutterBottom));
co.lineTo(this.gutterLeft - prop['
chart.smallyticks
'], Math.round(ca.height - this.gutterBottom));
} else {
co.moveTo(ca.width - this.gutterRight, Math.round(ca.height - this.gutterBottom));
co.lineTo(ca.width - this.gutterRight + prop['
chart.smallyticks
'], Math.round(ca.height - this.gutterBottom));
}
}
}
/**
* Draw the Y tickmarks
*/
var numyticks = prop['
chart.numyticks
'];
if (prop['
chart.noyaxis
'] == false && numyticks > 0) {
var counter = 0,
adjustment = 0;
if (prop['
chart.yaxispos
'] == '
right
') {
adjustment = (ca.width - this.gutterLeft - this.gutterRight);
}
// X axis at the center
if (prop['
chart.xaxispos
'] == '
center
') {
var interval = (this.grapharea / numyticks);
var lineto = (prop['
chart.yaxispos
'] == '
left
' ? this.gutterLeft : ca.width - this.gutterRight + prop['
chart.smallyticks
']);
// Draw the upper halves Y tick marks
for (y=this.gutterTop; y<(this.grapharea / 2) + this.gutterTop; y+=interval) {
if (y < (this.grapharea / 2) + this.gutterTop) {
co.moveTo((prop['
chart.yaxispos
'] == '
left
' ? this.gutterLeft - prop['
chart.smallyticks
'] : ca.width - this.gutterRight), Math.round(y));
co.lineTo(lineto, Math.round(y));
}
}
// Draw the lower halves Y tick marks
for (y=this.gutterTop + (this.halfgrapharea) + interval; y <= this.grapharea + this.gutterTop; y+=interval) {
co.moveTo((prop['
chart.yaxispos
'] == '
left
' ? this.gutterLeft - prop['
chart.smallyticks
'] : ca.width - this.gutterRight), Math.round(y));
co.lineTo(lineto, Math.round(y));
}
// X axis at the top
} else if (prop['
chart.xaxispos
'] == '
top
') {
var interval = (this.grapharea / numyticks);
var lineto = (prop['
chart.yaxispos
'] == '
left
' ? this.gutterLeft : ca.width - this.gutterRight + prop['
chart.smallyticks
']);
// Draw the Y tick marks
for (y=this.gutterTop + interval; y <= this.grapharea + this.gutterBottom; y+=interval) {
co.moveTo((prop['
chart.yaxispos
'] == '
left
' ? this.gutterLeft - prop['
chart.smallyticks
'] : ca.width - this.gutterRight), Math.round(y));
co.lineTo(lineto, Math.round(y));
}
// If there'
s no X axis draw an extra tick
if
(prop[
'chart.noxaxis'
] && prop[
'chart.noendytick'
] ==
false
) {
co.moveTo((prop[
'chart.yaxispos'
] ==
'left'
?
this
.gutterLeft - prop[
'chart.smallyticks'
] : ca.width -
this
.gutterRight),
this
.gutterTop);
co.lineTo(lineto,
this
.gutterTop);
}
}
else
{
var
lineto = (prop[
'chart.yaxispos'
] ==
'left'
?
this
.gutterLeft - prop[
'chart.smallyticks'
] : ca.width -
this
.gutterRight + prop[
'chart.smallyticks'
]);
for
(y=
this
.gutterTop;
y<(ca.height -
this
.gutterBottom) && counter < numyticks;
y+=( (ca.height -
this
.gutterTop -
this
.gutterBottom) / numyticks)
) {
if
(ma.round(y) !== ma.round(
this
.coordsAxes.xaxis[1])) {
co.moveTo(
this
.gutterLeft + adjustment, ma.round(y));
co.lineTo(lineto, ma.round(y));
}
var
counter = counter + 1;
}
if
(prop[
'chart.ymin'
] < 0) {
co.moveTo(
(prop[
'chart.yaxispos'
] ==
'left'
?
this
.gutterLeft : ca.width -
this
.gutterRight),
ma.round(y)
);
co.lineTo(
lineto,
ma.round(y)
);
}
}
}
else
if
(prop[
'chart.noxaxis'
] ==
false
&& prop[
'chart.numxticks'
] > 0) {
if
(prop[
'chart.yaxispos'
] ==
'left'
) {
co.moveTo(
this
.gutterLeft, prop[
'chart.xaxispos'
] ==
'top'
?
this
.gutterTop : ca.height -
this
.gutterBottom);
co.lineTo(
this
.gutterLeft, prop[
'chart.xaxispos'
] ==
'top'
?
this
.gutterTop - prop[
'chart.smallxticks'
] : ca.height -
this
.gutterBottom + prop[
'chart.smallxticks'
]);
}
else
{
co.moveTo(ca.width -
this
.gutterRight, ca.height -
this
.gutterBottom);
co.lineTo(ca.width -
this
.gutterRight, ca.height -
this
.gutterBottom + prop[
'chart.smallxticks'
]);
}
}
co.stroke();
co.beginPath();
};
this
.drawLabels =
this
.DrawLabels =
function
()
{
co.strokeStyle = 'black
';
co.fillStyle = prop['
chart.text.color
'];
co.lineWidth = 1;
// Turn off any shadow
RG.NoShadow(this);
// This needs to be here
var font = prop['
chart.text.font
'];
var text_size = prop['
chart.text.size
'];
var decimals = prop['
chart.scale.decimals
'];
var context = co;
var canvas = ca;
var ymin = prop['
chart.ymin
'];
// Draw the Y axis labels
if (prop['
chart.ylabels
'] && prop['
chart.ylabels.specific
'] == null) {
var units_pre = prop['
chart.units.pre
'];
var units_post = prop['
chart.units.post
'];
var xpos = prop['
chart.yaxispos
'] == '
left
' ? this.gutterLeft - 5 : ca.width - this.gutterRight + 5;
var align = prop['
chart.yaxispos
'] == '
left
' ? '
right
' : '
left
';
var numYLabels = this.scale2.labels.length;
var bounding = false;
var bgcolor = prop['
chart.ylabels.inside
'] ? prop['
chart.ylabels.inside.color
'] : null;
var offsetx = prop['
chart.ylabels.offsetx
'];
var offsety = prop['
chart.ylabels.offsety
'];
/**
* If the Y labels are inside the Y axis, invert the alignment
*/
if (prop['
chart.ylabels.inside
'] == true && align == '
left
') {
xpos -= 10;
align = '
right
';
bounding = true;
} else if (prop['
chart.ylabels.inside
'] == true && align == '
right
') {
xpos += 10;
align = '
left
';
bounding = true;
}
/**
* X axis in the center
*/
if (prop['
chart.xaxispos
'] == '
center
') {
var half = this.grapharea / 2;
/**
* Draw the top half
*/
for (var i=0; i<this.scale2.labels.length; ++i) {
RG.text2(this, {
'
font
': font,
'
size
': text_size,
'
x
': xpos + offsetx,
'
y
': this.gutterTop + half - (((i+1)/numYLabels) * half) + offsety,
'
valign
': '
center
',
'
halign
':align,
'
bounding
': bounding,
'
boundingFill
': bgcolor,
'
text
': this.scale2.labels[i],
'
tag
': '
scale
'
});
}
/**
* Draw the bottom half
*/
for (var i=0; i<this.scale2.labels.length; ++i) {
RG.text2(this, {
'
font
': font,
'
size
': text_size,
'
x
': xpos + offsetx,
'
y
': this.gutterTop + half + (((i+1)/numYLabels) * half) + offsety,
'
valign
': '
center
',
'
halign
':align,
'
bounding
': bounding,
'
boundingFill
': bgcolor,
'
text
': '
-
' + this.scale2.labels[i],
'
tag
': '
scale
'
});
}
// No X axis - so draw 0
if (prop['
chart.noxaxis
'] == true || ymin != 0 || prop['
chart.scale.zerostart
']) {
RG.text2(this,{
'
font
':font,
'
size
':text_size,
'
x
':xpos + offsetx,
'
y
':this.gutterTop + half + offsety,
'
text
':prop['
chart.units.pre
'] + ymin.toFixed(ymin === 0 ? 0 : decimals) + prop['
chart.units.post
'],
'
bounding
':bounding,
'
boundingFill
':bgcolor,
'
valign
':'
center
',
'
halign
':align,
'
tag
': '
scale
'
});
}
/**
* X axis at the top
*/
} else if (prop['
chart.xaxispos
'] == '
top
') {
var half = this.grapharea / 2;
if (prop['
chart.scale.invert
']) {
for (var i=0; i<this.scale2.labels.length; ++i) {
RG.text2(this, {
'
font
': font,
'
size
': text_size,
'
x
': xpos + offsetx,
'
y
': this.gutterTop + ((i/this.scale2.labels.length) * this.grapharea) + offsety,
'
valign
': '
center
',
'
halign
':align,
'
bounding
': bounding,
'
boundingFill
': bgcolor,
'
text
': '
-
' + this.scale2.labels[this.scale2.labels.length - (i+1)],
'
tag
': '
scale
'
});
}
} else {
for (var i=0; i<this.scale2.labels.length; ++i) {
RG.text2(this, {
'
font
': font,
'
size
': text_size,
'
x
': xpos + offsetx,
'
y
': this.gutterTop + (((i+1)/numYLabels) * this.grapharea) + offsety,
'
valign
': '
center
',
'
halign
':align,
'
bounding
': bounding,
'
boundingFill
': bgcolor,
'
text
': '
-
' + this.scale2.labels[i],
'
tag
': '
scale
'
});
}
}
// Draw the lower limit if chart.ymin is specified
if ((prop['
chart.ymin
'] != 0 || prop['
chart.noxaxis
']) || prop['
chart.scale.invert
'] || prop['
chart.scale.zerostart
']) {
RG.text2(this, {
'
font
':font,
'
size
':text_size,
'
x
':xpos + offsetx,
'
y
': prop['
chart.scale.invert
'] ? ca.height - this.gutterBottom + offsety : this.gutterTop + offsety,
'
text
': (prop['
chart.ymin
'] != 0 ? '
-
' : '
') + RG.numberFormat(this, prop['
chart.ymin
'].toFixed(ymin === 0 ? 0 : decimals), units_pre, units_post),
'
valign
':'
center
',
'
halign
': align,
'
bounding
':bounding,
'
boundingFill
':bgcolor,
'
tag
': '
scale
'
});
}
/**
* X axis labels at the bottom
*/
} else {
if (prop['
chart.scale.invert
']) {
// Draw the minimum value
RG.text2(this, {
'
font
': font,
'
size
': text_size,
'
x
': xpos + offsetx,
'
y
': this.gutterTop + offsety,
'
valign
': '
center
',
'
halign
':align,
'
bounding
': bounding,
'
boundingFill
': bgcolor,
'
text
': RG.numberFormat(this, this.min.toFixed(prop['
chart.ymin
'] === 0 ? 0 : prop['
chart.scale.decimals
']), units_pre, units_post),
'
tag
': '
scale
'
});
for (var i=0,len=this.scale2.labels.length; i<len; ++i) {
RG.Text2(this, {
'
font
': font,
'
size
': text_size,
'
x
': xpos + offsetx,
'
y
': this.gutterTop + (((i+1)/this.scale2.labels.length) * this.grapharea) + offsety,
'
valign
': '
center
',
'
halign
':align,
'
bounding
': bounding,
'
boundingFill
': bgcolor,
'
text
': this.scale2.labels[i],
'
tag
': '
scale
'
});
}
} else {
for (var i=0,len=this.scale2.labels.length; i<len; ++i) {
RG.text2(this, {
'
font
': font,
'
size
': text_size,
'
x
': xpos + offsetx,
'
y
': this.gutterTop + ((i/this.scale2.labels.length) * this.grapharea) + offsety,
'
valign
': '
center
',
'
halign
':align,
'
bounding
': bounding,
'
boundingFill
': bgcolor,
'
text
': this.scale2.labels[this.scale2.labels.length - (i + 1)],
'
tag
': '
scale
'
});
}
}
// Draw the lower limit if chart.ymin is specified
if ( (prop['
chart.ymin
']!= 0 && !prop['
chart.scale.invert
'] || prop['
chart.scale.zerostart
'])
|| prop['
chart.noxaxis
']
) {
RG.text2(this, {
'
font
':font,
'
size
':text_size,
'
x
':xpos + offsetx,
'
y
':prop['
chart.scale.invert
'] ? this.gutterTop + offsety : ca.height - this.gutterBottom + offsety,
'
text
':RG.numberFormat(this, prop['
chart.ymin
'].toFixed(prop['
chart.ymin
'] === 0 ? 0 : prop['
chart.scale.decimals
']), units_pre, units_post),
'
valign
':'
center
',
'
halign
':align,
'
bounding
':bounding,
'
boundingFill
':bgcolor,
'
tag
': '
scale
'
});
}
}
// No X axis - so draw 0 - but not if the X axis is in the center
if ( prop['
chart.noxaxis
'] == true
&& prop['
chart.ymin
'] == null
&& prop['
chart.xaxispos
'] != '
center
'
&& prop['
chart.noendytick
'] == false
) {
RG.text2(this, {
'
font
':font,
'
size
':text_size,
'
x
':xpos + offsetx,
'
y
':prop['
chart.xaxispos
'] == '
top
' ? this.gutterTop + offsety : (ca.height - this.gutterBottom),'
text
': prop['
chart.units.pre
'] + Number(0).toFixed(prop['
chart.scale.decimals
']) + prop['
chart.units.post
'] + offsety,
'
valign
':'
center
',
'
halign
':align,
'
bounding
':bounding,
'
boundingFill
':bgcolor,
'
tag
':'
scale
'
});
}
} else if (prop['
chart.ylabels
'] && typeof(prop['
chart.ylabels.specific
']) == '
object
') {
// A few things
var gap = this.grapharea / prop['
chart.ylabels.specific
'].length;
var halign = prop['
chart.yaxispos
'] == '
left
' ? '
right
' : '
left
';
var bounding = false;
var bgcolor = null;
var ymin = prop['
chart.ymin
'] != null && prop['
chart.ymin
'];
// Figure out the X coord based on the position of the axis
if (prop['
chart.yaxispos
'] == '
left
') {
var x = this.gutterLeft - 5;
if (prop['
chart.ylabels.inside
']) {
x += 10;
halign = '
left
';
bounding = true;
bgcolor = '
rgba(255,255,255,0.5)
';
}
} else if (prop['
chart.yaxispos
'] == '
right
') {
var x = ca.width - this.gutterRight + 5;
if (prop['
chart.ylabels.inside
']) {
x -= 10;
halign = '
right
';
bounding = true;
bgcolor = '
rgba(255,255,255,0.5)
';
}
}
var offsetx = prop['
chart.ylabels.offsetx
'];
var offsety = prop['
chart.ylabels.offsety
'];
// Draw the labels
if (prop['
chart.xaxispos
'] == '
center
') {
// Draw the top halfs labels
for (var i=0; i<prop['
chart.ylabels.specific
'].length; ++i) {
var y = this.gutterTop + (this.grapharea / (((prop['
chart.ylabels.specific
'].length - 1)) * 2) * i);
if (ymin && ymin > 0) {
var y = ((this.grapharea / 2) / (prop['
chart.ylabels.specific
'].length - (ymin ? 1 : 0)) ) * i;
y += this.gutterTop;
}
RG.text2(this, {
'
font
':font,
'
size
':text_size,
'
x
':x + offsetx,
'
y
':y + offsety,
'
text
':String(prop['
chart.ylabels.specific
'][i]),
'
valign
': '
center
',
'
halign
':halign,
'
bounding
':bounding,
'
boundingFill
':bgcolor,
'
tag
': '
ylabels.specific
'
});
}
// Now reverse the labels and draw the bottom half
var reversed_labels = RG.array_reverse(prop['
chart.ylabels.specific
']);
// Draw the bottom halfs labels
for (var i=0; i<reversed_labels.length; ++i) {
var y = (this.grapharea / 2) + this.gutterTop + ((this.grapharea / ((reversed_labels.length - 1) * 2) ) * i);
RG.text2(this, {
'
font
':font,
'
size
':text_size,
'
x
':x + offsetx,
'
y
':y + offsety,
'
text
':i == 0 ? '
' : String(reversed_labels[i]),
'
valign
': '
center
',
'
halign
':halign,
'
bounding
':bounding,
'
boundingFill
':bgcolor,
'
tag
': '
ylabels.specific
'
});
}
} else if (prop['
chart.xaxispos
'] == '
top
') {
// Reverse the labels and draw
var reversed_labels = RG.array_reverse(prop['
chart.ylabels.specific
']);
// Draw the bottom halfs labels
for (var i=0; i<reversed_labels.length; ++i) {
var y = (this.grapharea / (reversed_labels.length - 1)) * i;
y = y + this.gutterTop;
RG.Text2(this, {
'
font
':font,
'
size
':text_size,
'
x
':x + offsetx,
'
y
':y + offsety,
'
text
':String(reversed_labels[i]),
'
valign
': '
center
',
'
halign
':halign,
'
bounding
':bounding,
'
boundingFill
':bgcolor,
'
tag
': '
ylabels.specific
'
});
}
} else {
for (var i=0; i<prop['
chart.ylabels.specific
'].length; ++i) {
var y = this.gutterTop + ((this.grapharea / (prop['
chart.ylabels.specific
'].length - 1)) * i);
RG.text2(this, {
'
font
':font,
'
size
':text_size,
'
x
':x + offsetx,
'
y
':y + offsety,
'
text
':String(prop['
chart.ylabels.specific
'][i]),
'
valign
':'
center
',
'
halign
':halign,
'
bounding
':bounding,
'
boundingFill
':bgcolor,
'
tag
': '
ylabels.specific
'
});
}
}
}
// Draw the X axis labels
if (prop['
chart.labels
'] && prop['
chart.labels
'].length > 0) {
var yOffset = 5,
bordered = false,
bgcolor = null
co.fillStyle = prop['
chart.labels.color
'] || prop['
chart.text.color
'];
/**
* Text angle
*/
var angle = 0,
valign = '
top
',
halign = '
center
',
bold = prop['
chart.labels.bold
']
if (prop['
chart.xlabels.inside
']) {
yOffset = -5;
bordered = true;
bgcolor = prop['
chart.xlabels.inside.color
'];
valign = '
bottom
';
}
if (prop['
chart.xaxispos
'] == '
top
') {
valign = '
bottom
';
yOffset += 2;
}
if (typeof(prop['
chart.text.angle
']) == '
number
' && prop['
chart.text.angle
'] > 0) {
angle = -1 * prop['
chart.text.angle
'];
valign = '
center
';
halign = '
right
';
yOffset = 10;
if (prop['
chart.xaxispos
'] == '
top
') {
yOffset = 10;
}
}
var numLabels = prop['
chart.labels
'].length,
offsetx = prop['
chart.labels.offsetx
'],
offsety = prop['
chart.labels.offsety
'];
for (i=0; i<numLabels; ++i) {
// Changed 8th Nov 2010 to be not reliant on the coords
//if (this.properties['
chart.labels
'][i] && this.coords && this.coords[i] && this.coords[i][0]) {
if (prop['
chart.labels
'][i]) {
var labelX = ((ca.width - this.gutterLeft - this.gutterRight - (2 * prop['
chart.hmargin
'])) / (numLabels - 1) ) * i;
labelX += this.gutterLeft + prop['
chart.hmargin
'];
/**
* Account for an unrelated number of labels
*/
if (this.data.length === 0 || !this.data[0] || prop['
chart.labels
'].length != this.data[0].length) {
labelX = this.gutterLeft + prop['
chart.hmargin
'] + ((ca.width - this.gutterLeft - this.gutterRight - (2 * prop['
chart.hmargin
'])) * (i / (prop['
chart.labels
'].length - 1)));
}
// This accounts for there only being one point on the chart
if (!labelX) {
labelX = this.gutterLeft + prop['
chart.hmargin
'];
}
if (prop['
chart.xaxispos
'] == '
top
' && prop['
chart.text.angle
'] > 0) {
halign = '
left
';
}
if (prop['
chart.text.angle
'] != 0) {
halign = '
right
';
}
RG.Text2(this, {
'
font
':font,
'
size
':text_size,
'
bold
': bold,
'
x
':labelX + offsetx,
'
y
':(prop['
chart.xaxispos
'] == '
top
') ? this.gutterTop - yOffset - (prop['
chart.xlabels.inside
'] ? -22 : 0) + offsety : (ca.height - this.gutterBottom) + yOffset + offsety,
'
text
':String(prop['
chart.labels
'][i]),
'
valign
':valign,
'
halign
':halign,
'
bounding
':bordered,
'
boundingFill
':bgcolor,
'
angle
':angle,
'
tag
': '
labels
'
});
}
}
}
co.stroke();
co.fill();
}
/**
* Draws the line
*/
this.drawLine =
this.DrawLine = function (lineData, color, fill, linewidth, tickmarks, index)
{
// This facilitates the Rise animation (the Y value only)
if (prop['
chart.animation.unfold.y
'] && prop['
chart.animation.factor
'] != 1) {
for (var i=0; i<lineData.length; ++i) {
lineData[i] *= prop['
chart.animation.factor
'];
}
}
var penUp = false;
var yPos = null;
var xPos = 0;
co.lineWidth = 1;
var lineCoords = [];
/**
* Get the previous line data
*/
if (index > 0) {
var prevLineCoords = this.coords2[index - 1];
}
// Work out the X interval
var xInterval = (ca.width - (2 * prop['
chart.hmargin
']) - this.gutterLeft - this.gutterRight) / (lineData.length - 1);
// Loop thru each value given, plotting the line
// (FORMERLY FIRST)
for (i=0,len=lineData.length; i<len; i+=1) {
var data_point = lineData[i];
/**
* Get the yPos for the given data point
*/
var yPos = this.getYCoord(data_point);
if ( lineData[i] == null
|| (prop['
chart.xaxispos
'] == '
bottom
' && lineData[i] < this.min && !prop['
chart.outofbounds
'])
|| (prop['
chart.xaxispos
'] == '
center
' && lineData[i] < (-1 * this.max) && !prop['
chart.outofbounds
'])
|| (((lineData[i] < this.min && prop['
chart.xaxispos
'] !== '
center
') || lineData[i] > this.max) && !prop['
chart.outofbounds
'])) {
yPos = null;
}
// Not always very noticeable, but it does have an effect
// with thick lines
co.lineCap = '
round
';
co.lineJoin = '
round
';
// Plot the line if we'
re at least on the second iteration
if
(i > 0) {
xPos = xPos + xInterval;
}
else
{
xPos = prop[
'chart.hmargin'
] +
this
.gutterLeft;
}
if
(prop[
'chart.animation.unfold.x'
]) {
xPos *= prop[
'chart.animation.factor'
];
if
(xPos < prop[
'chart.gutter.left'
]) {
xPos = prop[
'chart.gutter.left'
];
}
}
this
.coords.push([xPos, yPos]);
lineCoords.push([xPos, yPos]);
}
co.stroke();
this
.coords2[index] = lineCoords;
if
(RG.ISOLD && prop['chart.shadow
']) {
this.DrawIEShadow(lineCoords, co.shadowColor);
}
/**
* Now draw the actual line [FORMERLY SECOND]
*/
co.beginPath();
// Transparent now as of 11/19/2011
co.strokeStyle = '
rgba(0,0,0,0)
';
//co.strokeStyle = fill;
if (fill) {
co.fillStyle = fill;
}
var isStepped = prop['
chart.stepped
'];
var isFilled = prop['
chart.filled
'];
if (prop['
chart.xaxispos
'] == '
top
') {
var xAxisPos = this.gutterTop;
} else if (prop['
chart.xaxispos
'] == '
center
') {
var xAxisPos = this.gutterTop + (this.grapharea / 2);
} else if (prop['
chart.xaxispos
'] == '
bottom
') {
var xAxisPos = this.getYCoord(prop['
chart.ymin
'])
}
for (var i=0,len=lineCoords.length; i<len; i+=1) {
xPos = lineCoords[i][0];
yPos = lineCoords[i][1];
var set = index;
var prevY = (lineCoords[i - 1] ? lineCoords[i - 1][1] : null);
var isLast = (i + 1) == lineCoords.length;
/**
* This nullifys values which are out-of-range
*/
if (!prop['
chart.outofbounds
'] && (prevY < this.gutterTop || prevY > (ca.height - this.gutterBottom) ) ) {
penUp = true;
}
if (i == 0 || penUp || !yPos || !prevY || prevY < this.gutterTop) {
if (prop['
chart.filled
'] && !prop['
chart.filled.range
']) {
if (!prop['
chart.outofbounds
'] || prevY === null || yPos === null) {
co.moveTo(xPos + 1, xAxisPos);
}
// This facilitates the X axis being at the top
// NOTE: Also done below
if (prop['
chart.xaxispos
'] == '
top
') {
co.moveTo(xPos + 1, xAxisPos);
}
if (isStepped && i > 0) {
co.lineTo(xPos, lineCoords[i - 1][1]);
}
co.lineTo(xPos, yPos);
} else {
if (RG.ISOLD && yPos == null) {
// Nada
} else {
co.moveTo(xPos + 1, yPos);
}
}
if (yPos == null) {
penUp = true;
} else {
penUp = false;
}
} else {
// Draw the stepped part of stepped lines
if (isStepped) {
co.lineTo(xPos, lineCoords[i - 1][1]);
}
if ((yPos >= this.gutterTop && yPos <= (ca.height - this.gutterBottom)) || prop['
chart.outofbounds
'] ) {
if (isLast && prop['
chart.filled
'] && !prop['
chart.filled.range
'] && prop['
chart.yaxispos
'] == '
right
') {
xPos -= 1;
}
// Added 8th September 2009
if (!isStepped || !isLast) {
co.lineTo(xPos, yPos);
if (isFilled && lineCoords[i+1] && lineCoords[i+1][1] == null) {
co.lineTo(xPos, xAxisPos);
}
// Added August 2010
} else if (isStepped && isLast) {
co.lineTo(xPos,yPos);
}
penUp = false;
} else {
penUp = true;
}
}
}
/**
* Draw a line to the X axis if the chart is filled
*/
if (prop['
chart.filled
'] && !prop['
chart.filled.range
'] && !prop['
chart.curvy
']) {
// Is this needed ??
var fillStyle = prop['
chart.fillstyle
'];
/**
* Draw the bottom edge of the filled bit using either the X axis or the prevlinedata,
* depending on the index of the line. The first line uses the X axis, and subsequent
* lines use the prevLineCoords array
*/
if (index > 0 && prop['
chart.filled.accumulative
']) {
co.lineTo(xPos, prevLineCoords ? prevLineCoords[i - 1][1] : (ca.height - this.gutterBottom - 1 + (prop['
chart.xaxispos
'] == '
center
' ? (ca.height - this.gutterTop - this.gutterBottom) / 2 : 0)));
for (var k=(i - 1); k>=0; --k) {
co.lineTo(k == 0 ? prevLineCoords[k][0] + 1: prevLineCoords[k][0], prevLineCoords[k][1]);
}
} else {
// Draw a line down to the X axis
if (prop['
chart.xaxispos
'] == '
top
') {
co.lineTo(xPos, prop['
chart.gutter.top
'] + 1);
co.lineTo(lineCoords[0][0],prop['
chart.gutter.top
'] + 1);
} else if (typeof(lineCoords[i - 1][1]) == '
number
') {
// var yPosition = prop['
chart.xaxispos
'] == '
center
' ? ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop : this.getYCoord(0);//ca.height - this.gutterBottom;
var yPosition = this.getYCoord(0);
co.lineTo(xPos,yPosition);
co.lineTo(lineCoords[0][0],yPosition);
}
}
co.fillStyle = !this.hidden(index) ? fill : '
rgba(0,0,0,0)
';
co.fill();
co.beginPath();
}
/**
* FIXME this may need removing when Chrome is fixed
* SEARCH TAGS: CHROME SHADOW BUG
*/
//if (false && RGraph.ISCHROME && prop['
chart.shadow
'] && prop['
chart.chromefix
'] && prop['
chart.shadow.blur
'] > 0) {
//
// for (var i=lineCoords.length - 1; i>=0; --i) {
// if (
// typeof(lineCoords[i][1]) != '
number
'
// || (typeof(lineCoords[i+1]) == '
object
' && typeof(lineCoords[i+1][1]) != '
number
')
// ) {
// co.moveTo(lineCoords[i][0],lineCoords[i][1]);
// } else {
// co.lineTo(lineCoords[i][0],lineCoords[i][1]);
// }
// }
//}
co.stroke();
if (prop['
chart.backdrop
']) {
this.DrawBackdrop(lineCoords, color);
}
/**
* TODO CLIP TRACE
* By using the clip() method the Trace animation can be updated.
* NOTE: Needs to be done for the filled part as well
*/
co.save();
co.beginPath();
co.rect(0,0,ca.width * prop['
chart.animation.trace.clip
'],ca.height);
co.clip();
//
// Draw errorbars
//
if (typeof prop['
chart.errorbars
'] !== '
null
') {
this.drawErrorbars();
}
// Now redraw the lines with the correct line width
this.SetShadow(index);
this.redrawLine(lineCoords, color, linewidth, index);
co.stroke();
RG.NoShadow(this);
// Draw the tickmarks
for (var i=0; i<lineCoords.length; ++i) {
i = Number(i);
/**
* Set the color
*/
co.strokeStyle = color;
if (isStepped && i == (lineCoords.length - 1)) {
co.beginPath();
//continue;
}
if (
(
tickmarks != '
endcircle
'
&& tickmarks != '
endsquare
'
&& tickmarks != '
filledendsquare
'
&& tickmarks != '
endtick
'
&& tickmarks != '
endtriangle
'
&& tickmarks != '
arrow
'
&& tickmarks != '
filledarrow
'
)
|| (i == 0 && tickmarks != '
arrow
' && tickmarks != '
filledarrow
')
|| i == (lineCoords.length - 1)
) {
var prevX = (i <= 0 ? null : lineCoords[i - 1][0]);
var prevY = (i <= 0 ? null : lineCoords[i - 1][1]);
this.DrawTick(
lineData,
lineCoords[i][0],
lineCoords[i][1],
color,
false,
prevX,
prevY,
tickmarks,
i,
index
);
// Draws tickmarks on the stepped bits of stepped charts. Takend out 14th July 2010
//
//if (this.properties['
chart.stepped
'] && lineCoords[i + 1] && this.properties['
chart.tickmarks
'] != '
endsquare
' && this.properties['
chart.tickmarks
'] != '
endcircle
' && this.properties['
chart.tickmarks
'] != '
endtick
') {
// this.DrawTick(lineCoords[i + 1][0], lineCoords[i][1], color);
//}
}
}
co.restore();
// Draw something off canvas to skirt an annoying bug
co.beginPath();
co.arc(ca.width + 50000, ca.height + 50000, 2, 0, 6.38, 1);
};
/**
* This functions draws a tick mark on the line
*/
this.drawTick =
this.DrawTick = function (lineData, xPos, yPos, color, isShadow, prevX, prevY, tickmarks, index, dataset)
{
// Various conditions mean no tick
if (this.hidden(dataset)) {
return;
} else if (RG.is_null(yPos)) {
return false;
} else if ((yPos > (ca.height - this.gutterBottom)) && !prop['
chart.outofbounds
']) {
return;
} else if ((yPos < this.gutterTop) && !prop['
chart.outofbounds
']) {
return;
}
co.beginPath();
var offset = 0;
// Reset the stroke and lineWidth back to the same as what they were when the line was drawm
// UPDATE 28th July 2011 - the line width is now set to 1
co.lineWidth = prop['
chart.tickmarks.linewidth
'] ? prop['
chart.tickmarks.linewidth
'] : prop['
chart.linewidth
'];
co.strokeStyle = isShadow ? prop['
chart.shadow.color
'] : co.strokeStyle;
co.fillStyle = isShadow ? prop['
chart.shadow.color
'] : co.strokeStyle;
// Cicular tick marks
if ( tickmarks == '
circle
'
|| tickmarks == '
filledcircle
'
|| tickmarks == '
endcircle
') {
if (tickmarks == '
circle
'|| tickmarks == '
filledcircle
' || (tickmarks == '
endcircle
' && (index == 0 || index == (lineData.length - 1)))) {
co.beginPath();
co.arc(xPos + offset, yPos + offset, prop['
chart.ticksize
'], 0, 360 / (180 / RG.PI), false);
if (tickmarks == '
filledcircle
') {
co.fillStyle = isShadow ? prop['
chart.shadow.color
'] : co.strokeStyle;
} else {
co.fillStyle = isShadow ? prop['
chart.shadow.color
'] : '
white
';
}
co.stroke();
co.fill();
}
// Halfheight "Line" style tick marks
} else if (tickmarks == '
halftick
') {
co.beginPath();
co.moveTo(Math.round(xPos), yPos);
co.lineTo(Math.round(xPos), yPos + prop['
chart.ticksize
']);
co.stroke();
// Tick style tickmarks
} else if (tickmarks == '
tick
') {
co.beginPath();
co.moveTo(Math.round(xPos), yPos - prop['
chart.ticksize
']);
co.lineTo(Math.round(xPos), yPos + prop['
chart.ticksize
']);
co.stroke();
// Endtick style tickmarks
} else if (tickmarks == '
endtick
' && (index == 0 || index == (lineData.length - 1))) {
co.beginPath();
co.moveTo(Math.round(xPos), yPos - prop['
chart.ticksize
']);
co.lineTo(Math.round(xPos), yPos + prop['
chart.ticksize
']);
co.stroke();
// "Cross" style tick marks
} else if (tickmarks == '
cross
') {
co.beginPath();
var ticksize = prop['
chart.ticksize
'];
co.moveTo(xPos - ticksize, yPos - ticksize);
co.lineTo(xPos + ticksize, yPos + ticksize);
co.moveTo(xPos + ticksize, yPos - ticksize);
co.lineTo(xPos - ticksize, yPos + ticksize);
co.stroke();
// Triangle style tick marks
} else if (tickmarks == '
triangle
' || tickmarks == '
filledtriangle
' || (tickmarks == '
endtriangle
' && (index == 0 || index == (lineData.length - 1)))) {
co.beginPath();
if (tickmarks == '
filledtriangle
') {
co.fillStyle = isShadow ? prop['
chart.shadow.color
'] : co.strokeStyle;
} else {
co.fillStyle = '
white
';
}
co.moveTo(ma.round(xPos - prop['
chart.ticksize
']), yPos + prop['
chart.ticksize
']);
co.lineTo(ma.round(xPos), yPos - prop['
chart.ticksize
']);
co.lineTo(ma.round(xPos + prop['
chart.ticksize
']), yPos + prop['
chart.ticksize
']);
co.closePath();
co.stroke();
co.fill();
//
// A white bordered circle
//
} else if (tickmarks == '
borderedcircle
' || tickmarks == '
dot
') {
co.lineWidth = prop['
chart.tickmarks.dot.linewidth
'] || 0.00000001;
pa2(co, [
'
b
',
'
a
',xPos, yPos, prop['
chart.ticksize
'], 0, 360 / (180 / RG.PI), false,
'
c
',
'
f
',prop['
chart.tickmarks.dot.fill
'] || color,
'
s
',prop['
chart.tickmarks.dot.stroke
'] || color
]);
} else if ( tickmarks == '
square
'
|| tickmarks == '
filledsquare
'
|| (tickmarks == '
endsquare
' && (index == 0 || index == (lineData.length - 1)))
|| (tickmarks == '
filledendsquare
' && (index == 0 || index == (lineData.length - 1))) ) {
co.fillStyle = '
white
';
co.strokeStyle = co.strokeStyle;
co.beginPath();
co.rect(Math.round(xPos - prop['
chart.ticksize
']), Math.round(yPos - prop['
chart.ticksize
']), prop['
chart.ticksize
'] * 2, prop['
chart.ticksize
'] * 2);
// Fillrect
if (tickmarks == '
filledsquare
' || tickmarks == '
filledendsquare
') {
co.fillStyle = isShadow ? prop['
chart.shadow.color
'] : co.strokeStyle;
co.rect(Math.round(xPos - prop['
chart.ticksize
']), Math.round(yPos - prop['
chart.ticksize
']), prop['
chart.ticksize
'] * 2, prop['
chart.ticksize
'] * 2);
} else if (tickmarks == '
square
' || tickmarks == '
endsquare
') {
co.fillStyle = isShadow ? prop['
chart.shadow.color
'] : '
white
';
co.rect(Math.round((xPos - prop['
chart.ticksize
']) + 1), Math.round((yPos - prop['
chart.ticksize
']) + 1), (prop['
chart.ticksize
'] * 2) - 2, (prop['
chart.ticksize
'] * 2) - 2);
}
co.stroke();
co.fill();
/**
* FILLED arrowhead
*/
} else if (tickmarks == '
filledarrow
') {
var x = Math.abs(xPos - prevX);
var y = Math.abs(yPos - prevY);
if (yPos < prevY) {
var a = Math.atan(x / y) + 1.57;
} else {
var a = Math.atan(y / x) + 3.14;
}
co.beginPath();
co.moveTo(Math.round(xPos), Math.round(yPos));
co.arc(Math.round(xPos), Math.round(yPos), 7, a - 0.5, a + 0.5, false);
co.closePath();
co.stroke();
co.fill();
/**
* Arrow head, NOT filled
*/
} else if (tickmarks == '
arrow
') {
var orig_linewidth = co.lineWidth;
var x = Math.abs(xPos - prevX);
var y = Math.abs(yPos - prevY);
co.lineWidth;
if (yPos < prevY) {
var a = Math.atan(x / y) + 1.57;
} else {
var a = Math.atan(y / x) + 3.14;
}
co.beginPath();
co.moveTo(Math.round(xPos), Math.round(yPos));
co.arc(Math.round(xPos), Math.round(yPos), 7, a - 0.5 - (doc.all ? 0.1 : 0.01), a - 0.4, false);
co.moveTo(Math.round(xPos), Math.round(yPos));
co.arc(Math.round(xPos), Math.round(yPos), 7, a + 0.5 + (doc.all ? 0.1 : 0.01), a + 0.5, true);
co.stroke();
co.fill();
// Revert to original lineWidth
co.lineWidth = orig_linewidth;
/**
* Image based tickmark
*/
// lineData, xPos, yPos, color, isShadow, prevX, prevY, tickmarks, index
} else if (
typeof tickmarks === '
string
' &&
(
tickmarks.substr(0, 6) === '
image:
' ||
tickmarks.substr(0, 5) === '
data:
' ||
tickmarks.substr(0, 1) === '
/
' ||
tickmarks.substr(0, 3) === '
../
' ||
tickmarks.substr(0, 7) === '
images/
'
)
) {
var img = new Image();
if (tickmarks.substr(0, 6) === '
image:
') {
img.src = tickmarks.substr(6);
} else {
img.src = tickmarks;
}
img.onload = function ()
{
if (prop['
chart.tickmarks.image.halign
'] === '
center
') xPos -= (this.width / 2);
if (prop['
chart.tickmarks.image.halign
'] === '
right
') xPos -= this.width;
if (prop['
chart.tickmarks.image.valign
'] === '
center
') yPos -= (this.height / 2);
if (prop['
chart.tickmarks.image.valign
'] === '
bottom
') yPos -= this.height;
xPos += prop['
chart.tickmarks.image.offsetx
'];
yPos += prop['
chart.tickmarks.image.offsety
'];
co.drawImage(this, xPos, yPos);
};
/**
* Custom tick drawing function
*/
} else if (typeof(tickmarks) == '
function
') {
tickmarks(this, lineData, lineData[index], index, xPos, yPos, color, prevX, prevY);
}
};
/**
* Draws a filled range if necessary
*/
this.drawRange =
this.DrawRange = function ()
{
/**
* Fill the range if necessary
*/
if (prop['
chart.filled.range
'] && prop['
chart.filled
']) {
if (RG.isNull(prop['
chart.filled.range.threshold
'])) {
prop['
chart.filled.range.threshold
'] = this.ymin
prop['
chart.filled.range.threshold.colors
'] = [prop['
chart.fillstyle
'], prop['
chart.fillstyle
']]
}
for (var idx=0; idx<2; ++idx) {
var threshold_colors = prop['
chart.filled.range.threshold.colors
'];
var y = this.getYCoord(prop['
chart.filled.range.threshold
'])
co.save();
if (idx == 0) {
co.beginPath();
co.rect(0,0,ca.width,y);
co.clip();
} else {
co.beginPath();
co.rect(0,y,ca.width, ca.height);
co.clip();
}
co.beginPath();
co.fillStyle = (idx == 1 ? prop['
chart.filled.range.threshold.colors
'][1] : prop['
chart.filled.range.threshold.colors
'][0]);
//co.strokeStyle = prop['
chart.fillstyle
']; // Strokestyle not used now (10th October 2012)
co.lineWidth = !this.hidden(idx) ? 1 : 0;
var len = (this.coords.length / 2);
for (var i=0; i<len; ++i) {
if (!RG.is_null(this.coords[i][1])) {
if (i == 0) {
co.moveTo(this.coords[i][0], this.coords[i][1])
} else {
co.lineTo(this.coords[i][0], this.coords[i][1])
}
}
}
for (var i=this.coords.length - 1; i>=len; --i) {
if (RG.is_null(this.coords[i][1])) {
co.moveTo(this.coords[i][0], this.coords[i][1])
} else {
co.lineTo(this.coords[i][0], this.coords[i][1])
}
//co.lineTo(this.coords[i][0], this.coords[i][1])
}
// Taken out - 10th Oct 2012
//co.stroke();
co.fill();
co.restore();
}
}
};
/**
* Redraws the line with the correct line width etc
*
* @param array coords The coordinates of the line
*/
this.redrawLine =
this.RedrawLine = function (coords, color, linewidth, index)
{
if (prop['
chart.noredraw
'] || prop['
chart.filled.range
']) {
return;
}
co.strokeStyle = (typeof(color) == '
object
' && color && color.toString().indexOf('
CanvasGradient
') == -1 ? color[0] : color);
co.lineWidth = linewidth;
// Added this on 1/1/17 to facilitate dotted and dashed lines
if (prop['
chart.dashed
']) {
co.setLineDash([2,6])
} else if (prop['
chart.dotted
']) {
co.setLineDash([1,5])
}
if (this.hidden(index)) {
co.strokeStyle = '
rgba(0,0,0,0)
';
}
if (!RG.ISOLD && (prop['
chart.curvy
'] || prop['
chart.spline
'])) {
this.DrawCurvyLine(coords, this.hidden(index) ? '
rgba(0,0,0,0)
' : color, linewidth, index);
return;
}
co.beginPath();
var len = coords.length;
var width = ca.width
var height = ca.height;
var penUp = false;
for (var i=0; i<len; ++i) {
var xPos = coords[i][0];
var yPos = coords[i][1];
if (i > 0) {
var prevX = coords[i - 1][0];
var prevY = coords[i - 1][1];
}
if ((
(i == 0 && coords[i])
|| (yPos < this.gutterTop)
|| (prevY < this.gutterTop)
|| (yPos > (height - this.gutterBottom))
|| (i > 0 && prevX > (width - this.gutterRight))
|| (i > 0 && prevY > (height - this.gutterBottom))
|| prevY == null
|| penUp == true
) && (!prop['
chart.outofbounds
'] || yPos == null || prevY == null) ) {
if (RG.ISOLD && yPos == null) {
// ...?
} else {
co.moveTo(coords[i][0], coords[i][1]);
}
penUp = false;
} else {
if (prop['
chart.stepped
'] && i > 0) {
co.lineTo(coords[i][0], coords[i - 1][1]);
}
// Don'
t draw the last bit of a stepped chart. Now DO
co.lineTo(coords[i][0], coords[i][1]);
penUp =
false
;
}
}
if
(prop[
'chart.colors.alternate'
] &&
typeof
(color) ==
'object'
&& color[0] && color[1]) {
for
(
var
i=1; i<len; ++i) {
var
prevX = coords[i - 1][0];
var
prevY = coords[i - 1][1];
if
(prevY !=
null
&& coords[i][1] !=
null
) {
co.beginPath();
co.strokeStyle = color[coords[i][1] < prevY ? 0 : 1];
co.lineWidth = prop[
'chart.linewidth'
];
co.moveTo(prevX, prevY);
co.lineTo(coords[i][0], coords[i][1]);
co.stroke();
}
}
}
if
(prop[
'chart.dashed'
] || prop[
'chart.dotted'
]) {
co.setLineDash([1,0]);
}
};
this
.drawIEShadow =
this
.DrawIEShadow =
function
(coords, color)
{
var
offsetx = prop[
'chart.shadow.offsetx'
];
var
offsety = prop[
'chart.shadow.offsety'
];
co.lineWidth = prop[
'chart.linewidth'
];
co.strokeStyle = color;
co.beginPath();
for
(
var
i=0; i<coords.length; ++i) {
var
isNull = RG.isNull(coords[i][1]);
var
prevIsNull = RG.isNull(coords[i-1]) || RG.isNull(coords[i-1][1]);
if
(i == 0 || isNull || prevIsNull) {
if
(!isNull) {
co.moveTo(coords[i][0] + offsetx, coords[i][1] + offsety);
}
}
else
{
co.lineTo(coords[i][0] + offsetx, coords[i][1] + offsety);
}
}
co.stroke();
};
this
.drawBackdrop =
this
.DrawBackdrop =
function
(coords, color)
{
var
size = prop[
'chart.backdrop.size'
];
co.lineWidth = size;
co.globalAlpha = prop[
'chart.backdrop.alpha'
];
co.strokeStyle = color;
var
yCoords = [];
co.beginPath();
if
(prop[
'chart.curvy'
] && !RG.ISOLD) {
for
(
var
i=0; i<coords.length; ++i) {
yCoords.push(coords[i][1])
}
this
.DrawSpline(co, yCoords, color,
null
);
}
else
{
co.moveTo(coords[0][0], coords[0][1]);
for
(
var
j=1; j<coords.length; ++j) {
co.lineTo(coords[j][0], coords[j][1]);
}
}
co.stroke();
co.globalAlpha = 1;
RG.NoShadow(
this
);
};
this
.getLineWidth =
this
.GetLineWidth =
function
(i)
{
var
linewidth = prop[
'chart.linewidth'
];
if
(
typeof
(linewidth) ==
'number'
) {
return
linewidth;
}
else
if
(
typeof
(linewidth) ==
'object'
) {
if
(linewidth[i]) {
return
linewidth[i];
}
else
{
return
linewidth[0];
}
alert(
'[LINE] Error! chart.linewidth should be a single number or an array of one or more numbers'
);
}
};
this
.getShape =
this
.getPoint =
function
(e)
{
var
obj =
this
,
mouseXY = RG.getMouseXY(e),
mouseX = mouseXY[0],
mouseY = mouseXY[1];
if
(arguments[1]) {
obj = arguments[1];
}
for
(
var
i=0; i<obj.coords.length; ++i) {
var
x = obj.coords[i][0],
y = obj.coords[i][1];
if
( mouseX <= (x + prop[
'chart.tooltips.hotspot.size'
])
&& mouseX >= (x - prop[
'chart.tooltips.hotspot.size'
])
&& mouseY <= (y + prop[
'chart.tooltips.hotspot.size'
])
&& mouseY >= (y - prop[
'chart.tooltips.hotspot.size'
])
) {
if
(RG.parseTooltipText) {
var
tooltip = RG.parseTooltipText(prop[
'chart.tooltips'
], i);
}
var
dataset = 0,
idx = i;
while
((idx + 1) >
this
.data[dataset].length) {
idx -=
this
.data[dataset].length;
dataset++;
}
if
(
this
.hidden(dataset)) {
continue
;
}
return
{
0: obj, object: obj,
1: x, x: x,
2: y, y: y,
3: i, index: i,
tooltip: tooltip,
dataset: dataset,
index_adjusted: idx
};
}
else
if
( prop['chart.tooltips.hotspot.xonly
'] == true
&& mouseX <= (x + prop['
chart.tooltips.hotspot.size
'])
&& mouseX >= (x - prop['
chart.tooltips.hotspot.size
'])) {
var tooltip = RG.parseTooltipText(prop['
chart.tooltips
'], i);
return {
0: obj, object: obj,
1: x, x: x,
2: y, y: y,
3: i, index: i,
tooltip: tooltip
};
}
}
};
/**
* Draws the above line labels
*/
this.drawAboveLabels =
this.DrawAboveLabels = function ()
{
var size = prop['
chart.labels.above.size
'],
font = prop['
chart.labels.above.font
'] || prop['
chart.text.font
'],
units_pre = prop['
chart.labels.above.units.pre
'],
units_post = prop['
chart.labels.above.units.post
'],
decimals = prop['
chart.labels.above.decimals
'],
color = prop['
chart.labels.above.color
'] || prop['
chart.text.color
'],
bgcolor = prop['
chart.labels.above.background
'] || '
white
',
border = ((
typeof prop['
chart.labels.above.border
'] === '
boolean
'
|| typeof prop['
chart.labels.above.border
'] === '
number
'
) ? prop['
chart.labels.above.border
'] : true),
offsety = prop['
chart.labels.above.offsety
'] + size,
specific = prop['
chart.labels.above.specific
'];
// Use this to '
reset
' the drawing state
co.beginPath();
// Don'
t need to check that chart.labels.above is enabled here, it
's been done already
for (var i=0, len=this.coords.length; i<len; i+=1) {
var coords = this.coords[i];
RG.text2(this, {
color:color,
'
font
':font,
'
size
':size,
'
x
':coords[0],
'
y
':coords[1] - offsety,
'
text
':(specific && specific[i]) ? specific[i] : (specific ? null : RG.numberFormat(this, typeof decimals === '
number
' ? this.data_arr[i].toFixed(decimals) : this.data_arr[i], units_pre, units_post)),
'
valign
':'
center
',
'
halign
':'
center
',
'
bounding
':true,
'
boundingFill
':bgcolor,
'
boundingStroke
':border ? '
black
' : '
rgba(0,0,0,0)
',
'
tag
':'
labels.above
'
});
}
};
/**
* Draw a curvy line.
*/
this.drawCurvyLine =
this.DrawCurvyLine = function (coords, color, linewidth, index)
{
var yCoords = [];
for (var i=0; i<coords.length; ++i) {
yCoords.push(coords[i][1]);
}
if (prop['
chart.filled
']) {
co.beginPath();
// First, work out the xaxispos
//if (prop['
chart.xaxispos
'] === '
center
') {
// var xaxisY = ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop;
//} else {
var xaxisY = this.getYCoord(prop['
chart.ymin
']);
//}
co.moveTo(coords[0][0],xaxisY);
this.drawSpline(co, yCoords, color, index);
if (prop['
chart.filled.accumulative
'] && index > 0) {
for (var i=(this.coordsSpline[index - 1].length - 1); i>=0; i-=1) {
co.lineTo(this.coordsSpline[index - 1][i][0], this.coordsSpline[index - 1][i][1]);
}
} else {
co.lineTo(coords[coords.length - 1][0],xaxisY);
}
co.fill();
}
co.beginPath();
this.DrawSpline(co, yCoords, color, index);
co.stroke();
};
/**
* When you click on the chart, this method can return the Y value at that point. It works for any point on the
* chart (that is inside the gutters) - not just points on the Line.
*
* @param object e The event object
*/
this.getValue = function (arg)
{
if (arg.length == 2) {
var mouseX = arg[0];
var mouseY = arg[1];
} else {
var mouseCoords = RG.getMouseXY(arg);
var mouseX = mouseCoords[0];
var mouseY = mouseCoords[1];
}
var obj = this;
var xaxispos = prop['
chart.xaxispos
'];
if (mouseY < prop['
chart.gutter.top
']) {
return xaxispos == '
bottom
' || xaxispos == '
center
' ? this.max : this.min;
} else if (mouseY > (ca.height - prop['
chart.gutter.bottom
'])) {
return xaxispos == '
bottom
' ? this.min : this.max;
}
if (prop['
chart.xaxispos
'] == '
center
') {
var value = (( (obj.grapharea / 2) - (mouseY - prop['
chart.gutter.top
'])) / obj.grapharea) * (obj.max - obj.min);
value *= 2;
value > 0 ? value += this.min : value -= this.min;
return value;
} else if (prop['
chart.xaxispos
'] == '
top
') {
var value = ((obj.grapharea - (mouseY - prop['
chart.gutter.top
'])) / obj.grapharea) * (obj.max - obj.min);
value = Math.abs(obj.max - value) * -1;
return value;
} else {
var value = ((obj.grapharea - (mouseY - prop['
chart.gutter.top
'])) / obj.grapharea) * (obj.max - obj.min)
value += obj.min;
return value;
}
};
/**
* Each object type has its own Highlight() function which highlights the appropriate shape
*
* @param object shape The shape to highlight
*/
this.highlight =
this.Highlight = function (shape)
{
if (prop['
chart.tooltips.highlight
']) {
if (typeof prop['
chart.highlight.style
'] === '
function
') {
(prop['
chart.highlight.style
'])(shape);
} else if (prop['
chart.highlight.style
'] === '
halo
') {
var obj = shape.object,
color = prop['
chart.colors
'][shape.dataset];
// Clear a space in white first for the tickmark
RG.path2(obj.context, '
b a % % 13 0 6.2830
false
f rgba(255,255,255,0.75)
',
shape.x,
shape.y
);
RG.path2(obj.context, '
ga 0.15 b a % % 13 0 6.2830
false
f % ga 1
',
shape.x,
shape.y,
color
);
RG.path2(obj.context, '
b a % % 7 0 6.2830
false
f white
',
shape.x,
shape.y
);
RG.path2(obj.context, '
b a % % 5 0 6.2830
false
f %
',
shape.x,
shape.y,
color
);
} else {
RG.Highlight.Point(this, shape);
}
}
};
/**
* The getObjectByXY() worker method. Don'
t call
this
call:
*
* RG.ObjectRegistry.getObjectByXY(e)
*
* @param object e The event object
*/
this
.getObjectByXY =
function
(e)
{
var
mouseXY = RG.getMouseXY(e);
if
(
(mouseXY[0] > prop['chart.gutter.left
'] - 5)
&& mouseXY[0] < (ca.width - prop['
chart.gutter.right
'] + 5)
&& mouseXY[1] > (prop['
chart.gutter.top
'] - 5)
&& mouseXY[1] < (ca.height - prop['
chart.gutter.bottom
'] + 5)
) {
return this;
}
};
/**
* This method handles the adjusting calculation for when the mouse is moved
*
* @param object e The event object
*/
this.adjusting_mousemove =
this.Adjusting_mousemove = function (e)
{
/**
* Handle adjusting for the Bar
*/
if (prop['
chart.adjustable
'] && RG.Registry.Get('
chart.adjusting
') && RG.Registry.Get('
chart.adjusting
').uid == this.uid) {
// Rounding the value to the given number of decimals make the chart step
var value = Number(this.getValue(e));//.toFixed(this.properties['
chart.scale.decimals
']);
var shape = RG.Registry.Get('
chart.adjusting.shape
');
if (shape) {
RG.Registry.Set('
chart.adjusting.shape
', shape);
this.original_data[shape['
dataset
']][shape['
index_adjusted
']] = Number(value);
RG.redrawCanvas(e.target);
RG.fireCustomEvent(this, '
onadjust
');
}
}
};
/**
* This function can be used when the canvas is clicked on (or similar - depending on the event)
* to retrieve the relevant Y coordinate for a particular value.
*
* @param int value The value to get the Y coordinate for
*/
this.getYCoord = function (value)
{
if (typeof(value) != '
number
') {
return null;
}
var y;
var xaxispos = prop['
chart.xaxispos
'];
// Higher than max
// Commented out on March 7th 2013 because the tan curve was not showing correctly
//if (value > this.max) {
// value = this.max;
//}
if (xaxispos == '
top
') {
// Account for negative numbers
//if (value < 0) {
// value = Math.abs(value);
//}
y = ((value - this.min) / (this.max - this.min)) * this.grapharea;
// Inverted Y labels
if (prop['
chart.scale.invert
']) {
y = this.grapharea - y;
}
y = y + this.gutterTop
} else if (xaxispos == '
center
') {
y = ((value - this.min) / (this.max - this.min)) * (this.grapharea / 2);
y = (this.grapharea / 2) - y;
y += this.gutterTop;
} else {
if ((value < this.min || value > this.max) && prop['
chart.outofbounds
'] == false) {
return null;
}
y = ((value - this.min) / (this.max - this.min)) * this.grapharea;
// Inverted Y labels
if (prop['
chart.scale.invert
']) {
y = this.grapharea - y;
}
y = ca.height - this.gutterBottom - y;
}
return y;
};
/**
* This function positions a tooltip when it is displayed
*
* @param obj object The chart object
* @param int x The X coordinate specified for the tooltip
* @param int y The Y coordinate specified for the tooltip
* @param objec tooltip The tooltips DIV element
*
this.positionTooltip = function (obj, x, y, tooltip, idx)
{
var coordX = obj.coords[tooltip.__index__][0];
var coordY = obj.coords[tooltip.__index__][1];
var canvasXY = RG.getCanvasXY(obj.canvas);
var gutterLeft = prop['
chart.gutter.left
'];
var gutterTop = prop['
chart.gutter.top
'];
var width = tooltip.offsetWidth;
var height = tooltip.offsetHeight;
var mouseXY = RG.getMouseXY(window.event);
// Set the top position
tooltip.style.left = 0;
tooltip.style.top = window.event.pageY - height - 20 + '
px
';
// By default any overflow is hidden
tooltip.style.overflow = '
';
// Reposition the tooltip if at the edges:
// LEFT edge
if (canvasXY[0] + mouseXY[0] - (width / 2) < 0) {
tooltip.style.left = canvasXY[0] + mouseXY[0] - (width * 0.1) + '
px
';
// RIGHT edge
} else if (canvasXY[0] + mouseXY[0] + (width / 2) > doc.body.offsetWidth) {
tooltip.style.left = canvasXY[0] + mouseXY[0] - (width * 0.9) + '
px
';
// Default positioning - CENTERED
} else {
tooltip.style.left = canvasXY[0] + mouseXY[0] - (width / 2) + '
px
';
}
};*/
/**
* This function draws a curvy line
*
* @param object context The 2D context
* @param array coords The coordinates
*/
this.drawSpline =
this.DrawSpline = function (context, coords, color, index)
{
this.coordsSpline[index] = [];
var xCoords = [];
var gutterLeft = prop['
chart.gutter.left
'];
var gutterRight = prop['
chart.gutter.right
'];
var hmargin = prop['
chart.hmargin
'];
var interval = (ca.width - (gutterLeft + gutterRight) - (2 * hmargin)) / (coords.length - 1);
co.strokeStyle = color;
/**
* The drawSpline function takes an array of JUST Y coords - not X/Y coords. So the line coords need converting
* if we'
ve been given X/Y pairs
*/
for
(
var
i=0,len=coords.length; i<len;i+=1) {
if
(
typeof
coords[i] ==
'object'
&& coords[i] && coords[i].length == 2) {
coords[i] = Number(coords[i][1]);
}
}
var
P = [coords[0]];
for
(
var
i=0; i<coords.length; ++i) {
P.push(coords[i]);
}
P.push(coords[coords.length - 1] + (coords[coords.length - 1] - coords[coords.length - 2]));
for
(
var
j=1; j<P.length-2; ++j) {
for
(
var
t=0; t<10; ++t) {
var
yCoord = Spline( t/10, P[j-1], P[j], P[j+1], P[j+2] );
xCoords.push(((j-1) * interval) + (t * (interval / 10)) + gutterLeft + hmargin);
co.lineTo(xCoords[xCoords.length - 1], yCoord);
if
(
typeof
index ==
'number'
) {
this
.coordsSpline[index].push([xCoords[xCoords.length - 1], yCoord]);
}
}
}
co.lineTo(((j-1) * interval) + gutterLeft + hmargin, P[j]);
if
(
typeof
index ==
'number'
) {
this
.coordsSpline[index].push([((j-1) * interval) + gutterLeft + hmargin, P[j]]);
}
function
Spline (t, P0, P1, P2, P3)
{
return
0.5 * ((2 * P1) +
((0-P0) + P2) * t +
((2*P0 - (5*P1) + (4*P2) - P3) * (t*t) +
((0-P0) + (3*P1)- (3*P2) + P3) * (t*t*t)));
}
};
this
.parseColors =
function
()
{
if
(
this
.original_colors.length === 0) {
this
.original_colors[
'chart.colors'
] = RGraph.array_clone(prop[
'chart.colors'
]);
this
.original_colors[
'chart.fillstyle'
] = RGraph.array_clone(prop[
'chart.fillstyle'
]);
this
.original_colors[
'chart.key.colors'
] = RGraph.array_clone(prop[
'chart.key.colors'
]);
this
.original_colors[
'chart.background.barcolor1'
] = prop[
'chart.background.barcolor1'
];
this
.original_colors[
'chart.background.barcolor2'
] = prop[
'chart.background.barcolor2'
];
this
.original_colors[
'chart.background.grid.color'
] = prop[
'chart.background.grid.color'
];
this
.original_colors[
'chart.background.color'
] = prop[
'chart.background.color'
];
this
.original_colors[
'chart.text.color'
] = prop[
'chart.text.color'
];
this
.original_colors[
'chart.crosshairs.color'
] = prop[
'chart.crosshairs.color'
];
this
.original_colors[
'chart.annotate.color'
] = prop[
'chart.annotate.color'
];
this
.original_colors[
'chart.title.color'
] = prop[
'chart.title.color'
];
this
.original_colors[
'chart.title.yaxis.color'
] = prop[
'chart.title.yaxis.color'
];
this
.original_colors[
'chart.key.background'
] = prop[
'chart.key.background'
];
this
.original_colors[
'chart.axis.color'
] = prop[
'chart.axis.color'
];
this
.original_colors[
'chart.highlight.fill'
] = prop[
'chart.highlight.fill'
];
}
for
(
var
i=0; i<prop[
'chart.colors'
].length; ++i) {
if
(
typeof
(prop[
'chart.colors'
][i]) ==
'object'
&& prop[
'chart.colors'
][i][0] && prop[
'chart.colors'
][i][1]) {
prop[
'chart.colors'
][i][0] =
this
.parseSingleColorForGradient(prop[
'chart.colors'
][i][0]);
prop[
'chart.colors'
][i][1] =
this
.parseSingleColorForGradient(prop[
'chart.colors'
][i][1]);
}
else
{
prop[
'chart.colors'
][i] =
this
.parseSingleColorForGradient(prop[
'chart.colors'
][i]);
}
}
if
(prop[
'chart.fillstyle'
]) {
if
(
typeof
(prop[
'chart.fillstyle'
]) ==
'string'
) {
prop[
'chart.fillstyle'
] =
this
.parseSingleColorForGradient(prop[
'chart.fillstyle'
],
'vertical'
);
}
else
{
for
(
var
i=0; i<prop[
'chart.fillstyle'
].length; ++i) {
prop[
'chart.fillstyle'
][i] =
this
.parseSingleColorForGradient(prop[
'chart.fillstyle'
][i],
'vertical'
);
}
}
}
if
(!RG.is_null(prop[
'chart.key.colors'
])) {
for
(
var
i=0; i<prop[
'chart.key.colors'
].length; ++i) {
prop[
'chart.key.colors'
][i] =
this
.parseSingleColorForGradient(prop[
'chart.key.colors'
][i]);
}
}
var
properties = [
'chart.background.barcolor1'
,
'chart.background.barcolor2'
,
'chart.background.grid.color'
,
'chart.background.color'
,
'chart.text.color'
,
'chart.crosshairs.color'
,
'chart.annotate.color'
,
'chart.title.color'
,
'chart.title.yaxis.color'
,
'chart.key.background'
,
'chart.axis.color'
,
'chart.highlight.fill'
];
for
(
var
i=0; i<properties.length; ++i) {
prop[properties[i]] =
this
.parseSingleColorForGradient(prop[properties[i]]);
}
};
this
.reset =
function
()
{
};
this
.parseSingleColorForGradient =
function
(color)
{
if
(!color ||
typeof
(color) !=
'string'
) {
return
color;
}
var
dir =
typeof
(arguments[1]) ==
'string'
? arguments[1] :
'vertical'
;
if
(
typeof
color ===
'string'
&& color.match(/^gradient\((.*)\)$/i)) {
var
parts = RegExp.$1.split(
':'
);
if
(dir ==
'horizontal'
) {
var
grad = co.createLinearGradient(0,0,ca.width,0);
}
else
{
var
grad = co.createLinearGradient(0,ca.height - prop[
'chart.gutter.bottom'
],0,prop[
'chart.gutter.top'
]);
}
var
diff = 1 / (parts.length - 1);
grad.addColorStop(0, RG.trim(parts[0]));
for
(
var
j=1; j<parts.length; ++j) {
grad.addColorStop(
j * diff,
RG.trim(parts[j])
);
}
}
return
grad ? grad : color;
};
this
.setShadow =
this
.SetShadow =
function
(i)
{
if
(prop[
'chart.shadow'
]) {
var
shadowColor = prop[
'chart.shadow.color'
];
if
(
typeof
(shadowColor) ==
'object'
&& shadowColor[i - 1]) {
co.shadowColor = shadowColor[i];
}
else
if
(
typeof
(shadowColor) ==
'object'
) {
co.shadowColor = shadowColor[0];
}
else
if
(
typeof
(shadowColor) ==
'string'
) {
co.shadowColor = shadowColor;
}
co.shadowBlur = prop[
'chart.shadow.blur'
];
co.shadowOffsetX = prop[
'chart.shadow.offsetx'
];
co.shadowOffsetY = prop[
'chart.shadow.offsety'
];
}
};
this
.interactiveKeyHighlight =
function
(index)
{
var
coords =
this
.coords2[index];
if
(coords) {
var
pre_linewidth = co.lineWidth;
var
pre_linecap = co.lineCap;
co.lineWidth = prop[
'chart.linewidth'
] + 10;
co.lineCap =
'round'
;
co.strokeStyle = prop[
'chart.key.interactive.highlight.chart.stroke'
];
co.beginPath();
if
(prop[
'chart.curvy'
]) {
this
.DrawSpline(co, coords, prop[
'chart.key.interactive.highlight.chart'
],
null
);
}
else
{
for
(
var
i=0,len=coords.length; i<len; i+=1) {
if
( i == 0
|| RG.is_null(coords[i][1])
|| (
typeof
coords[i - 1][1] != undefined && RG.is_null(coords[i - 1][1]))) {
co.moveTo(coords[i][0], coords[i][1]);
}
else
{
co.lineTo(coords[i][0], coords[i][1]);
}
}
}
co.stroke();
co.lineWidth = pre_linewidth;
co.lineCap = pre_linecap;
}
};
this
.on =
function
(type, func)
{
if
(type.substr(0,2) !==
'on'
) {
type =
'on'
+ type;
}
if
(
typeof
this
[type] !==
'function'
) {
this
[type] = func;
}
else
{
RG.addCustomEventListener(
this
, type, func);
}
return
this
;
};
this
.firstDrawFunc =
function
()
{
};
this
.drawErrorbars =
function
()
{
co.save();
RG.noShadow(
this
);
var
coords =
this
.coords,
x = 0,
errorbars = prop[
'chart.errorbars'
],
length = 0;
if
(!prop[
'chart.errorbars.capped'
]) {
prop[
'chart.errorbars.capped.width'
] = 0.001;
halfwidth = 0.0005;
}
co.lineWidth = prop[
'chart.errorbars.linewidth'
];
for
(
var
i=0; i<coords.length; ++i) {
var
halfwidth = prop[
'chart.errorbars.capped.width'
] / 2 || 5,
color = prop[
'chart.errorbars.color'
] ||
'black'
;
if
(errorbars[i] &&
typeof
errorbars[i][3] ===
'number'
) {
co.lineWidth = errorbars[i][3];
}
else
if
(
typeof
prop[
'chart.errorbars.linewidth'
] ===
'number'
) {
co.lineWidth = prop[
'chart.errorbars.linewidth'
];
}
else
{
co.lineWidth = 1;
}
if
(
typeof
errorbars ===
'number'
||
typeof
errorbars[i] ===
'number'
) {
if
(
typeof
errorbars ===
'number'
) {
var
positiveLength =
this
.getYCoord(
this
.min) -
this
.getYCoord(
this
.min + errorbars),
negativeLength = positiveLength;
}
else
{
var
positiveLength =
this
.getYCoord(
this
.min) -
this
.getYCoord(
this
.min + errorbars[i]),
negativeLength = positiveLength;
}
if
(positiveLength || negativeLength) {
pa2(
co,
'lj miter lc square b m % % l % % m % % l % % l % % m % % l % % s %'
,
coords[i][0] - halfwidth,
coords[i][1] + negativeLength,
coords[i][0] + halfwidth,
coords[i][1] + negativeLength,
coords[i][0],
coords[i][1] + negativeLength,
coords[i][0],
coords[i][1] - positiveLength,
coords[i][0] - halfwidth,
coords[i][1] - positiveLength,
coords[i][0],
coords[i][1] - positiveLength,
coords[i][0] + halfwidth,
coords[i][1] - positiveLength,
color
);
pa2(
co,
'lj miter lc square b m % % l % % s %'
,
coords[i][0] - halfwidth,
coords[i][1] + negativeLength,
coords[i][0] + halfwidth,
coords[i][1] + negativeLength,
color
);
}
}
else
if
(
typeof
errorbars[i] ===
'object'
&& !RG.isNull(errorbars[i])) {
var
positiveLength =
this
.getYCoord(
this
.min) -
this
.getYCoord(
this
.min + errorbars[i][0]),
negativeLength =
this
.getYCoord(
this
.min) -
this
.getYCoord(
this
.min + errorbars[i][1]);
if
(
typeof
errorbars[i][2] ===
'string'
) {
color = errorbars[i][2];
}
halfwidth =
typeof
errorbars[i][4] ===
'number'
? errorbars[i][4] / 2 : halfwidth;
if
(
typeof
errorbars[i] ===
'object'
&&
typeof
errorbars[i][3] ===
'number'
) {
co.lineWidth = errorbars[i][3];
}
else
if
(
typeof
prop[
'chart.errorbars.linewidth'
] ===
'number'
) {
co.lineWidth = prop[
'chart.errorbars.linewidth'
];
}
else
{
co.lineWidth = 1;
}
if
(!RG.isNull(errorbars[i][0])) {
pa2(
co,
'lc square b m % % l % % l % % m % % l % % s %'
,
coords[i][0],
coords[i][1],
coords[i][0],
coords[i][1] - positiveLength,
coords[i][0] - halfwidth,
ma.round(coords[i][1] - positiveLength),
coords[i][0],
ma.round(coords[i][1] - positiveLength),
coords[i][0] + halfwidth,
ma.round(coords[i][1] - positiveLength),
color
);
}
if
(
typeof
errorbars[i][1] ===
'number'
) {
var
negativeLength = ma.abs(
this
.getYCoord(errorbars[i][1]) -
this
.getYCoord(0));
pa2(
co,
'b m % % l % % l % % m % % l % % s %'
,
coords[i][0],
coords[i][1],
coords[i][0],
coords[i][1] + negativeLength,
coords[i][0] - halfwidth,
ma.round(coords[i][1] + negativeLength),
coords[i][0],
ma.round(coords[i][1] + negativeLength),
coords[i][0] + halfwidth,
ma.round(coords[i][1] + negativeLength),
color
);
}
}
}
co.restore();
};
this
.hide =
function
()
{
if
(
typeof
arguments[0] ===
'number'
) {
prop[
'chart.line.visible'
][arguments[0]] =
false
;
}
else
if
(
typeof
arguments[0] ===
'object'
) {
for
(
var
i=0; i<arguments[0].length; ++i) {
prop[
'chart.line.visible'
][arguments[0][i]] =
false
;
}
}
else
{
for
(
var
i=0; i<
this
.original_data.length; ++i) {
prop[
'chart.line.visible'
][i] =
false
;
}
}
RG.redraw();
return
this
;
};
this
.show =
function
()
{
if
(
typeof
arguments[0] ===
'number'
) {
prop[
'chart.line.visible'
][arguments[0]] =
true
;
}
else
if
(
typeof
arguments[0] ===
'object'
) {
for
(
var
i=0; i<arguments[0].length; ++i) {
prop[
'chart.line.visible'
][arguments[0][i]] =
true
;
}
}
else
{
for
(
var
i=0; i<
this
.original_data.length; ++i) {
prop[
'chart.line.visible'
][i] =
true
;
}
}
RG.redraw();
return
this
;
};
this
.hidden =
function
(index)
{
return
!prop[
'chart.line.visible'
][index];
};
this
.unfold =
function
()
{
var
obj =
this
;
var
opt = arguments[0] ? arguments[0] : {};
var
frames = opt.frames ? opt.frames : 30;
var
frame = 0;
var
callback = arguments[1] ? arguments[1] :
function
() {};
var
initial = prop[
'chart.animation.unfold.initial'
];
prop[
'chart.animation.factor'
] = prop[
'chart.animation.unfold.initial'
];
function
iterator ()
{
prop[
'chart.animation.factor'
] = ((1 - initial) * (frame / frames)) + initial;
RG.clear(obj.canvas);
RG.redrawCanvas(obj.canvas);
if
(frame < frames) {
frame++;
RG.Effects.updateCanvas(iterator);
}
else
{
callback(obj);
}
}
iterator();
return
this
;
};
this
.trace =
this
.trace2 =
function
()
{
var
obj =
this
;
var
callback = arguments[2];
var
opt = arguments[0] || {};
var
frames = opt.frames || 30;
var
frame = 0;
var
callback = arguments[1] ||
function
() {};
obj.Set(
'animation.trace.clip'
, 0);
function
iterator ()
{
RG.clear(obj.canvas);
RG.redrawCanvas(obj.canvas);
if
(frame++ < frames) {
obj.Set(
'animation.trace.clip'
, frame / frames);
RG.Effects.updateCanvas(iterator);
}
else
{
callback(obj);
}
}
iterator();
return
this
;
};
this
.foldtocenter =
this
.foldToCenter =
function
()
{
var
obj =
this
;
var
opt = arguments[0] || {};
var
frames = opt.frames || 30;
var
frame = 0;
var
callback = arguments[1] ||
function
() {};
var
center_value = obj.scale2.max / 2;
obj.Set(
'chart.ymax'
, obj.scale2.max);
var
original_data = RG.array_clone(obj.original_data);
function
iterator ()
{
for
(
var
i=0,len=obj.data.length; i<len; ++i) {
if
(obj.data[i].length) {
for
(
var
j=0,len2=obj.data[i].length; j<len2; ++j) {
var
dataset = obj.original_data[i];
if
(dataset[j] > center_value) {
dataset[j] = original_data[i][j] - ((original_data[i][j] - center_value) * (frame / frames));
}
else
{
dataset[j] = original_data[i][j] + (((center_value - original_data[i][j]) / frames) * frame);
}
}
}
}
RG.clear(obj.canvas);
RG.redrawCanvas(obj.canvas)
if
(frame++ < frames) {
RG.Effects.updateCanvas(iterator);
}
else
{
callback(obj);
}
}
iterator();
return
this
;
};
this
.unfoldFromCenterTrace =
this
.unfoldFromCenterTrace2 =
function
()
{
var
obj =
this
,
opt = arguments[0] || {},
frames = opt.frames || 30,
frame = 0,
data = RG.arrayClone(obj.original_data),
callback = arguments[1] ||
function
() {};
obj.canvas.style.visibility =
'hidden'
;
obj.draw();
var
max = obj.scale2.max;
RG.clear(obj.canvas);
obj.canvas.style.visibility =
'visible'
;
var
unfoldCallback =
function
()
{
obj.original_data = data;
obj.unfoldFromCenter({frames: frames / 2}, callback);
};
var
half = obj.Get(
'chart.xaxispos'
) ==
'center'
? obj.min : ((obj.max - obj.min) / 2) + obj.min;
obj.Set(
'chart.ymax'
, obj.max);
for
(
var
i=0,len=obj.original_data.length; i<len; ++i) {
for
(
var
j=0; j<obj.original_data[i].length; ++j) {
obj.original_data[i][j] = (obj.Get(
'chart.filled'
) && obj.Get(
'chart.filled.accumulative'
) && i > 0) ? 0 : half;
}
}
RG.clear(obj.canvas);
obj.trace2({frames: frames / 2}, unfoldCallback);
return
obj;
};
this
.unfoldFromCenter =
function
()
{
var
obj =
this
;
var
opt = arguments[0] || {};
var
frames = opt.frames || 30;
var
frame = 0;
var
callback = arguments[1] ||
function
() {};
obj.canvas.style.visibility =
'hidden'
;
obj.Draw();
var
max = obj.scale2.max;
RG.clear(obj.canvas);
obj.canvas.style.visibility =
'visible'
;
var
center_value = obj.Get(
'chart.xaxispos'
) ===
'center'
? prop[
'chart.ymin'
] : ((obj.max - obj.min) / 2) + obj.min;
var
original_data = RG.array_clone(obj.original_data);
var
steps =
null
;
obj.Set(
'chart.ymax'
, max);
if
(!steps) {
steps = [];
for
(
var
dataset=0,len=original_data.length; dataset<len; ++dataset) {
steps[dataset] = []
for
(
var
i=0,len2=original_data[dataset].length; i<len2; ++i) {
if
(prop[
'chart.filled'
] && prop[
'chart.filled.accumulative'
] && dataset > 0) {
steps[dataset][i] = original_data[dataset][i] / frames;
obj.original_data[dataset][i] = center_value;
}
else
{
steps[dataset][i] = (original_data[dataset][i] - center_value) / frames;
obj.original_data[dataset][i] = center_value;
}
}
}
}
function
unfoldFromCenter ()
{
for
(
var
dataset=0; dataset<original_data.length; ++dataset) {
for
(
var
i=0; i<original_data[dataset].length; ++i) {
obj.original_data[dataset][i] += steps[dataset][i];
}
}
RG.clear(obj.canvas);
RG.redrawCanvas(obj.canvas);
if
(--frames > 0) {
RG.Effects.updateCanvas(unfoldFromCenter);
}
else
{
obj.original_data = RG.array_clone(original_data);
RG.clear(obj.canvas);
RG.redrawCanvas(obj.canvas);
callback(obj);
}
}
unfoldFromCenter();
return
this
;
};
RG.att(ca);
this
.isAdjustable =
function
(shape)
{
if
(RG.isNull(prop[
'chart.adjustable.only'
])) {
return
true
;
}
if
(RG.isArray(prop[
'chart.adjustable.only'
]) && prop[
'chart.adjustable.only'
][shape.index]) {
return
true
;
}
return
false
;
};
RG.Register(
this
);
if
(parseConfObjectForOptions) {
RG.parseObjectStyleConfig(
this
, conf.options);
}
for
(
var
i=0; i<
this
.original_data.length; ++i) {
prop[
'chart.line.visible'
][i] =
true
;
}
};