RGraph = window.RGraph || {isRGraph:
true
};
RGraph.SVG = RGraph.SVG || {};
(
function
(win, doc, undefined)
{
var
RG = RGraph,
ua = navigator.userAgent,
ma = Math,
win = window,
doc = document;
RG.SVG.Scatter =
function
(conf)
{
this
.set =
function
(name, value)
{
if
(arguments.length === 1 &&
typeof
name === 'object
') {
for (i in arguments[0]) {
if (typeof i === '
string
') {
name = ret.name;
value = ret.value;
this.set(name, value);
}
}
} else {
var ret = RG.SVG.commonSetter({
object: this,
name: name,
value: value
});
name = ret.name;
value = ret.value;
// If setting the colors, update the originalColors
// property too
if (name === '
colors
') {
this.originalColors = RG.SVG.arrayClone(value);
this.colorsParsed = false;
}
// BC for labelsAboveSeperator
if (name === '
labelsAboveSeperator
') {
name = labelsAboveSeparator;
}
this.properties[name] = value;
}
return this;
};
/**
* A getter.
*
* @param name string The name of the property to get
*/
this.get = function (name)
{
return this.properties[name];
};
this.id = conf.id;
this.uid = RG.SVG.createUID();
this.container = document.getElementById(this.id);
this.layers = {}; // MUST be before the SVG tag is created!
this.svg = RG.SVG.createSVG({object: this,container: this.container});
this.isRGraph = true;
this.width = Number(this.svg.getAttribute('
width
'));
this.height = Number(this.svg.getAttribute('
height
'));
this.data = conf.data;
this.type = '
scatter
';
this.coords = [];
this.coords2 = [];
this.colorsParsed = false;
this.originalColors = {};
this.gradientCounter = 1;
this.sequential = 0;
this.line_groups = [];
// This is a list of new property names that are used now in place of
// the old names.
//
// *** When adding this list to a new chart library don'
t forget ***
this
.propertyNameAliases = {
};
RG.SVG.OR.add(
this
);
this
.container.style.display =
'inline-block'
;
this
.properties =
{
marginLeft: 35,
marginRight: 35,
marginTop: 35,
marginBottom: 35,
backgroundColor:
null
,
backgroundImage:
null
,
backgroundImageAspect:
'none'
,
backgroundImageStretch:
true
,
backgroundImageOpacity:
null
,
backgroundImageX:
null
,
backgroundImageY:
null
,
backgroundImageW:
null
,
backgroundImageH:
null
,
backgroundGrid:
true
,
backgroundGridColor:
'#ddd'
,
backgroundGridLinewidth: 1,
backgroundGridHlines:
true
,
backgroundGridHlinesCount:
null
,
backgroundGridVlines:
true
,
backgroundGridVlinesCount:
null
,
backgroundGridBorder:
true
,
backgroundGridDashed:
false
,
backgroundGridDotted:
false
,
backgroundGridDashArray:
null
,
xmax: 0,
tickmarksStyle:
'cross'
,
tickmarksSize: 7,
colors: [
'black'
],
line:
false
,
lineColors: 1,
lineLinewidth: 1,
errorbarsColor:
'black'
,
errorbarsLinewidth: 1,
errorbarsCapwidth: 10,
yaxis:
true
,
yaxisTickmarks:
true
,
yaxisTickmarksLength: 3,
yaxisColor:
'black'
,
yaxisScale:
true
,
yaxisLabels:
null
,
yaxisLabelsOffsetx: 0,
yaxisLabelsOffsety: 0,
yaxisLabelsCount: 10,
yaxisScaleUnitsPre:
''
,
yaxisScaleUnitsPost:
''
,
yaxisScaleStrict:
false
,
yaxisScaleDecimals: 0,
yaxisScalePoint:
'.'
,
yaxisScaleThousand:
','
,
yaxisScaleRound:
false
,
yaxisScaleMax:
null
,
yaxisScaleMin: 0,
yaxisScaleFormatter:
null
,
xaxis:
true
,
xaxisTickmarks:
true
,
xaxisTickmarksLength: 3,
xaxisLabels:
null
,
xaxisLabelsPosition:
'section'
,
xaxisLabelsPositionEdgeTickmarksCount: 10,
xaxisColor:
'black'
,
xaxisLabelsOffsetx: 0,
xaxisLabelsOffsety: 0,
xaxisLabelsCount: 10,
xaxisLabelsFont:
null
,
xaxisLabelsSize:
null
,
xaxisLabelsColor:
null
,
xaxisLabelsBold:
null
,
xaxisLabelsItalic:
null
,
xaxisScaleUnitsPre:
''
,
xaxisScaleUnitsPost:
''
,
xaxisScaleMax:
null
,
xaxisScaleMin: 0,
xaxisScalePoint:
'.'
,
xaxisRound:
false
,
xaxisScaleThousand:
','
,
xaxisScaleDecimals: 0,
xaxisScaleFormatter:
null
,
textColor:
'black'
,
textFont:
'Arial, Verdana, sans-serif'
,
textSize: 12,
textBold:
false
,
textItalic:
false
,
labelsAboveFont:
null
,
labelsAboveSize:
null
,
labelsAboveBold:
null
,
labelsAboveItalic:
null
,
labelsAboveColor:
null
,
labelsAboveBackground:
'rgba(255,255,255,0.7)'
,
labelsAboveBackgroundPadding: 2,
labelsAboveXUnitsPre:
null
,
labelsAboveXUnitsPost:
null
,
labelsAboveXPoint:
null
,
labelsAboveXThousand:
null
,
labelsAboveXFormatter:
null
,
labelsAboveXDecimals:
null
,
labelsAboveYUnitsPre:
null
,
labelsAboveYUnitsPost:
null
,
labelsAboveYPoint:
null
,
labelsAboveYThousand:
null
,
labelsAboveYFormatter:
null
,
labelsAboveYDecimals:
null
,
labelsAboveOffsetx: 0,
labelsAboveOffsety: -10,
labelsAboveHalign:
'center'
,
labelsAboveValign:
'bottom'
,
labelsAboveSeparator:
','
,
tooltipsOverride:
null
,
tooltipsEffect:
'fade'
,
tooltipsCssClass:
'RGraph_tooltip'
,
tooltipsEvent:
'mousemove'
,
highlightStroke:
'rgba(0,0,0,0)'
,
highlightFill:
'rgba(255,255,255,0.7)'
,
highlightLinewidth: 1,
title:
''
,
titleX:
null
,
titleY:
null
,
titleHalign:
'center'
,
titleValign:
null
,
titleSize:
null
,
titleColor:
null
,
titleFont:
null
,
titleBold:
null
,
titleItalic:
null
,
titleSubtitle:
null
,
titleSubtitleX:
null
,
titleSubtitleY:
null
,
titleSubtitleHalign:
'center'
,
titleSubtitleValign:
null
,
titleSubtitleSize:
null
,
titleSubtitleColor:
'#aaa'
,
titleSubtitleFont:
null
,
titleSubtitleBold:
null
,
titleSubtitleItalic:
null
,
key:
null
,
keyColors:
null
,
keyOffsetx: 0,
keyOffsety: 0,
keyLabelsOffsetx: 0,
keyLabelsOffsety: -1,
keyLabelsFont:
null
,
keyLabelsSize:
null
,
keyLabelsColor:
null
,
keyLabelsBold:
null
,
keyLabelsItalic:
null
,
bubble:
false
,
bubbleMaxValue:
null
,
bubbleMaxRadius:
null
,
bubbleColorsSolid:
false
,
errorbars:
null
,
errorbarsColor:
'black'
,
errorbarsLinewidth: 1,
errorbarsCapwidth: 10,
};
RG.SVG.getGlobals(
this
);
for
(i
in
conf.options) {
if
(
typeof
i ===
'string'
) {
this
.set(i, conf.options[i]);
}
}
if
(
this
.data[0] && !RG.SVG.isArray(
this
.data[0])) {
this
.data = [];
this
.data[0] = conf.data;
}
if
(RG.SVG.FX &&
typeof
RG.SVG.FX.decorate ===
'function'
) {
RG.SVG.FX.decorate(
this
);
}
var
prop =
this
.properties;
if
(
typeof
prop.xaxisScaleMin ===
'string'
) {
prop.xaxisScaleMin = RG.SVG.parseDate(prop.xaxisScaleMin);
}
if
(
typeof
prop.xaxisScaleMax ===
'string'
) {
prop.xaxisScaleMax = RG.SVG.parseDate(prop.xaxisScaleMax);
}
for
(
var
i=0; i<
this
.data.length; ++i) {
for
(
var
j=0; j<
this
.data[i].length; ++j) {
if
(
typeof
this
.data[i][j].x ===
'string'
) {
this
.data[i][j].x = RG.SVG.parseDate(
this
.data[i][j].x);
}
}
}
this
.draw =
function
()
{
RG.SVG.fireCustomEvent(
this
,
'onbeforedraw'
);
this
.width = Number(
this
.svg.getAttribute('width
'));
this.height = Number(this.svg.getAttribute('
height
'));
// Create the defs tag if necessary
RG.SVG.createDefs(this);
this.graphWidth = this.width - prop.marginLeft - prop.marginRight;
this.graphHeight = this.height - prop.marginTop - prop.marginBottom;
// Prevents these from growing
this.coords = [];
this.coords2 = [];
// Parse the colors for gradients
RG.SVG.resetColorsToOriginalValues({object:this});
this.parseColors();
// Work out the maximum value
for (var ds=0,max=0; ds<this.data.length; ++ds) { // Datasets
for (var dp=0; dp<this.data[ds].length; ++dp) { // Datapoints
max = ma.max(
max,
this.data[ds][dp].y + (this.data[ds][dp].errorbar ? (typeof this.data[ds][dp].errorbar === '
number
' ? this.data[ds][dp].errorbar : this.data[ds][dp].errorbar.max) : 0)
);
}
}
// A custom, user-specified maximum value
if (typeof prop.yaxisScaleMax === '
number
') {
max = prop.yaxisScaleMax;
}
// Set the ymin to zero if it'
s set mirror
if
(prop.yaxisScaleMin ===
'mirror'
|| prop.yaxisScaleMin ===
'middle'
|| prop.yaxisScaleMin ===
'center'
) {
var
mirrorScale =
true
;
prop.yaxisScaleMin = 0;
}
this
.scale = RG.SVG.getScale({
object:
this
,
numlabels: prop.yaxisLabelsCount,
unitsPre: prop.yaxisScaleUnitsPre,
unitsPost: prop.yaxisScaleUnitsPost,
max: max,
min: prop.yaxisScaleMin,
point: prop.yaxisScalePoint,
round: prop.yaxisScaleRound,
thousand: prop.yaxisScaleThousand,
decimals: prop.yaxisScaleDecimals,
strict:
typeof
prop.yaxisScaleMax ===
'number'
,
formatter: prop.yaxisScaleFormatter
});
if
(mirrorScale) {
this
.scale = RG.SVG.getScale({
object:
this
,
numlabels: prop.yaxisLabelsCount,
unitsPre: prop.yaxisScaleUnitsPre,
unitsPost: prop.yaxisScaleUnitsPost,
max:
this
.scale.max,
min:
this
.scale.max * -1,
point: prop.yaxisScalePoint,
round:
false
,
thousand: prop.yaxisScaleThousand,
decimals: prop.yaxisScaleDecimals,
strict:
typeof
prop.yaxisScaleMax === 'number
',
formatter: prop.yaxisScaleFormatter
});
}
// Now the scale has been generated adopt its max value
this.max = this.scale.max;
this.min = this.scale.min;
prop.yaxisScaleMax = this.scale.max;
prop.yaxisScaleMin = this.scale.min;
// Draw the background first
RG.SVG.drawBackground(this);
// Draw the axes under the points
RG.SVG.drawXAxis(this);
RG.SVG.drawYAxis(this);
// Create a group for all of the datasets
var dataset_group = RGraph.SVG.create({
svg: this.svg,
type: '
g
',
parent: this.svg.all,
attr: {
className: '
scatter_datasets_
' + this.uid
}
});
// Draw the points for all of the datasets
for (var i=0; i<this.data.length; ++i) {
var group = RG.SVG.create({
svg: this.svg,
type: '
g
',
parent: this.svg.all,
attr: {
id: '
scatter_line_
' + i + this.uid
}
});
this.line_groups[i] = group;
this.drawPoints({
index: i,
data: this.data[i],
group: dataset_group
});
// Draw a line for this dataset
if (prop.line === true || (typeof prop.line === '
object
' && prop.line[i] === true)) {
this.drawLine({
index: i,
coords: this.coords2[i],
});
}
}
// Draw the key
if (typeof prop.key !== null && RG.SVG.drawKey) {
RG.SVG.drawKey(this);
} else if (!RGraph.SVG.isNull(prop.key)) {
alert('
The drawKey()
function
does not exist - have you forgotten to include the key library?
');
}
// Add the event listener that clears the highlight rect if
// there is any. Must be MOUSEDOWN (ie before the click event)
//var obj = this;
//doc.body.addEventListener('
mousedown
', function (e)
//{
//RG.SVG.removeHighlight(obj);
//}, false);
// Fire the draw event
RG.SVG.fireCustomEvent(this, '
ondraw
');
return this;
};
//
// Draws the Points
//
// @param opt object Options to the function which can consist of:
// o index: The numerical index of the DATASET
// o dataset: The dataset.
//
this.drawPoints = function (opt)
{
var index = opt.index,
data = opt.data,
group = opt.group;
// Initialise the array for coordinates
if (!this.coords2[index]) {
this.coords2[index] = [];
}
//
// Create the <g> tag that the datapoints are added to
//
var group = RG.SVG.create({
svg: this.svg,
type: '
g
',
parent: group,
attr: {
className: '
scatter_dataset_
' + index + '
_
' + this.uid
}
});
// Loop through the data
for (var i=0; i<data.length; ++i) {
var point = data[i];
if (typeof point.x === '
number
'&& typeof point.y === '
number
') {
var ret = this.drawSinglePoint({
dataset: data,
datasetIdx: index,
point: point,
index: i,
group: group, // The SVG <g> tag the points are added to
sequential: this.sequential
});
// Add the coordinates to the coords arrays
this.coords.push({
x: ret.x,
y: ret.y,
z: ret.size,
type: ret.type,
element: ret.mark,
object: this
});
this.coords2[index][i] = {
x: ret.x,
y: ret.y,
z: ret.size,
type: ret.type,
element: ret.mark,
object: this
};
this.sequential++
}
//
// Add tooltip highlight to the point
//
if ( (typeof data[i].tooltip === '
string
' && data[i].tooltip) || (typeof data[i].tooltip === '
number
') ) {
// Convert the tooltip to a string
data[i].tooltip = String(data[i].tooltip);
// Make the tooltipsEvent default to click
if (prop.tooltipsEvent !== '
mousemove
') {
prop.tooltipsEvent = '
click
';
}
if (!group_tooltip_hotspots) {
var group_tooltip_hotspots = RG.SVG.create({
svg: this.svg,
parent: this.svg.all,
type: '
g
',
attr: {
className: '
rgraph-scatter-tooltip-hotspots
'
}
});
}
var rect = RG.SVG.create({
svg: this.svg,
parent: this.svg.all,
type: '
rect
',
parent: group_tooltip_hotspots,
attr: {
x: ret.x - (ret.size / 2),
y: ret.y - (ret.size / 2),
width: ret.size,
height: ret.size,
fill: '
transparent
',
stroke: '
transparent
',
'
stroke-width
': 0
},
style: {
cursor: '
pointer
'
}
});
// Add the hotspot to the original tickmark
ret.mark.hotspot = rect;
(function (dataset, index, seq, obj)
{
rect.addEventListener(prop.tooltipsEvent, function (e)
{
var tooltip = RG.SVG.REG.get('
tooltip
');
if (tooltip && tooltip.__dataset__ === dataset && tooltip.__index__ === index) {
return;
}
obj.removeHighlight();
// Show the tooltip
RG.SVG.tooltip({
object: obj,
dataset: dataset,
index: index,
sequentialIndex: seq,
text: obj.data[dataset][index].tooltip,
event: e
});
// Highlight the shape that has been clicked on
if (RG.SVG.REG.get('
tooltip
')) {
obj.highlight(this);
}
}, false);
// Install the event listener that changes the
// cursor if necessary
if (prop.tooltipsEvent === '
click
') {
rect.addEventListener('
mousemove
', function (e)
{
e.target.style.cursor = '
pointer
';
}, false);
}
}(index, i, this.sequential - 1, this));
}
}
};
//
// Draws a single point on the chart
//
this.drawSinglePoint = function (opt)
{
var dataset = opt.dataset,
datasetIdx = opt.datasetIdx,
seq = opt.sequential,
point = opt.point,
index = opt.index,
valueX = opt.point.x,
valueY = opt.point.y,
conf = opt.point || {},
group = opt.group,
coordX = opt.coordx = this.getXCoord(valueX),
coordY = opt.coordy = this.getYCoord(valueY);
// Get the above label
if (conf.labelsAbove) {
var above = true;
} else if (conf.labelAbove) {
var above = true;
} else if (conf.above) {
var above = true;
}
// Allow shape to be synonym for type
if (typeof conf.type === '
undefined
' && typeof conf.shape !== '
undefined
') {
conf.type = conf.shape;
}
// set the type to the default if it'
s not set
if
(
typeof
conf.type ===
'string'
) {
}
else
if
(
typeof
prop.tickmarksStyle ===
'string'
) {
conf.type = prop.tickmarksStyle;
}
else
if
(
typeof
prop.tickmarksStyle ===
'object'
&&
typeof
prop.tickmarksStyle[datasetIdx] ===
'string'
) {
conf.type = prop.tickmarksStyle[datasetIdx];
}
if
(
typeof
conf.size !== 'number
' && typeof prop.tickmarksSize === '
number
') {
conf.size = prop.tickmarksSize;
} else if (typeof conf.size !== '
number
' && typeof prop.tickmarksSize === '
object
' && typeof prop.tickmarksSize[datasetIdx] === '
number
') {
conf.size = prop.tickmarksSize[datasetIdx];
}
// WBB adding optional '
rx
' property
// set the ellipse x-radius to the default, if it'
s not set
if
(
typeof
conf.rx !==
'number'
&&
typeof
prop.tickmarksSize ===
'number'
) {
conf.rx = prop.tickmarksSize;
}
else
if
(
typeof
conf.rx !==
'number'
&&
typeof
prop.tickmarksSize ===
'object'
&&
typeof
prop.tickmarksSize[datasetIdx] ===
'number'
) {
conf.rx = prop.tickmarksSize[datasetIdx];
}
if
(
typeof
conf.ry !== 'number
' && typeof prop.tickmarksSize === '
number
') {
conf.ry = prop.tickmarksSize;
} else if (typeof conf.rx !== '
number
' && typeof prop.tickmarksSize === '
object
' && typeof prop.tickmarksSize[datasetIdx] === '
number
') {
conf.ry = prop.tickmarksSize[datasetIdx];
}
// WBB adding optional '
rotate
' property
// set the rotation to 0, if undefined
if (typeof conf.rotate === '
undefined
') {
conf.rotate = 0;
}
// Set the color to the default if it'
s not set and then black
if
that
's not set either
if (typeof conf.color === '
string
') {
// nada
} else if (typeof prop.colors[datasetIdx] === '
string
') {
conf.color = prop.colors[datasetIdx];
} else {
conf.color = '
black
';
}
// WBB adding optional '
stroke
' property
// Set the stroke to '
none
', if not a string
if (typeof conf.stroke !== '
string
') {
conf.stroke = '
none
';
}
// Set the opacity of this point
if (typeof conf.opacity === '
undefined
') {
conf.opacity = 1;
} else if (typeof conf.opacity === '
number
') {
// nada
}
// Draw the errorbar here
//
// First convert the errorbar information in the data into an array in the properties
//
prop.errorbars = [];
for (var ds=0,max=0; ds<this.data.length; ++ds) {
for (var idx=0; idx<this.data[ds].length; ++idx) {
prop.errorbars.push(this.data[ds][idx].errorbar);
}
}
this.drawErrorbar({
object: this,
dataset: datasetIdx,
index: index,
group: group,
sequential: seq,
x: coordX,
y: coordY,
valueX: valueX,
valueY: valueY,
parent: group
});
// Bubble charts are drawn by their own function
if (prop.bubble) {
//return this.drawBubble(opt, conf);
this.drawBubble(opt, conf);
}
// Handle the various shapes for tickmarks here
switch (conf.type) {
case '
image:
' + conf.type.substr(6):
var src = conf.type.substr(6);
var img = new Image();
img.src = src;
var mark = RG.SVG.create({
svg: this.svg,
type: '
image
',
parent: group,
attr: {
preserveAspectRatio: '
xMidYMid meet
',
'
xlink:href
': src
}
});
// Once the image has loaded the x/y/width/height can be set
// (both the image and it'
s hotspot)
img.onload =
function
()
{
var
x = coordX - (img.width / 2),
y = coordY - (img.height / 2),
w = img.width,
h = img.height;
mark.setAttribute(
'x'
, x);
mark.setAttribute(
'y'
, y);
mark.setAttribute(
'width'
, w);
mark.setAttribute(
'height'
, h);
if
(mark && mark.hotspot) {
mark.hotspot.setAttribute(
'x'
, x);
mark.hotspot.setAttribute(
'y'
, y);
mark.hotspot.setAttribute(
'width'
, w);
mark.hotspot.setAttribute(
'height'
, h);
}
};
break
;
case
'triangle'
:
var
mark = RG.SVG.create({
svg:
this
.svg,
type:
'path'
,
parent: group,
attr: {
d:
'M {1} {2} L {3} {4} L {5} {6}'
.format(
coordX - (conf.size / 2),
coordY + (conf.size / 2),
coordX,
coordY - (conf.size / 2),
coordX + (conf.size / 2),
coordY + (conf.size / 2)
),
fill: conf.color,
'fill-opacity'
: conf.opacity
}
});
break
;
case
'plus'
:
var
mark = RG.SVG.create({
svg:
this
.svg,
type:
'path'
,
parent: group,
attr: {
d:
'M {1} {2} L {3} {4} M {5} {6} L {7} {8}'
.format(
coordX - (conf.size / 2),
coordY,
coordX + (conf.size / 2),
coordY,
coordX,
coordY - (conf.size / 2),
coordX,
coordY + (conf.size / 2)
),
stroke: conf.color,
'stroke-opacity'
: conf.opacity
}
});
break
;
case
'square'
:
var
mark = RG.SVG.create({
svg:
this
.svg,
type:
'rect'
,
parent: group,
attr: {
x: coordX - (conf.size / 2),
y: coordY - (conf.size / 2),
width: conf.size,
height: conf.size,
fill: conf.color,
stroke: conf.stroke,
'fill-opacity'
: conf.opacity,
'stroke-opacity'
: conf.opacity
}
});
break
;
case
'rect'
:
var
mark = RG.SVG.create({
svg:
this
.svg,
type:
'rect'
,
parent: group,
attr: {
x: coordX - conf.rx,
y: coordY - conf.ry,
width: conf.rx * 2,
height: conf.ry * 2,
fill: conf.color,
stroke: conf.stroke,
'fill-opacity'
: conf.opacity,
'stroke-opacity'
: conf.opacity
}
});
break
;
case
'dot'
:
case
'circle'
:
var
mark = RG.SVG.create({
svg:
this
.svg,
type:
'circle'
,
parent: group,
attr: {
cx: coordX,
cy: coordY,
r: conf.size / 2,
fill: conf.color,
stroke: conf.stroke,
'fill-opacity'
: conf.opacity,
'stroke-opacity'
: conf.opacity
}
});
break
;
case
'ellipse'
:
var
mark = RG.SVG.create({
svg:
this
.svg,
type:
'ellipse'
,
parent: group,
attr: {
cx: coordX,
cy: coordY,
rx: conf.rx,
ry: conf.ry,
transform:
'rotate({1} {2} {3})'
.format(conf.rotate, coordX, coordY),
fill: conf.color,
stroke: conf.stroke,
'fill-opacity'
: conf.opacity,
'stroke-opacity'
: conf.opacity
}
});
break
;
case
'cross'
:
default
:
var
mark = RG.SVG.create({
svg:
this
.svg,
type:
'path'
,
parent: group,
attr: {
d:
'M {1} {2} L {3} {4} M {5} {6} L {7} {8}'
.format(
coordX - (conf.size / 2), coordY - (conf.size / 2),
coordX + (conf.size / 2), coordY + (conf.size / 2),
coordX - (conf.size / 2), coordY + (conf.size / 2),
coordX + (conf.size / 2), coordY - (conf.size / 2)
),
stroke: conf.color,
'stroke-opacity'
: conf.opacity
}
});
break
;
}
if
(
typeof
conf.above === 'string
' || (typeof conf.above !== '
string
' && conf.above) ) {
this.drawLabelsAbove({
point: conf,
coordX: coordX,
coordY: coordY
});
}
// Add some data attributes that save various values
mark.setAttribute('
data-index
', index);
mark.setAttribute('
data-dataset
', datasetIdx);
mark.setAttribute('
data-original-opacity
', conf.opacity);
mark.setAttribute('
data-original-color
', conf.color);
mark.setAttribute('
data-original-coordx
', coordX);
mark.setAttribute('
data-original-coordy
', coordY);
mark.setAttribute('
data-size
', conf.size);
mark.setAttribute('
data-sequential
', seq);
mark.setAttribute('
data-type
', conf.type);
return {
x: coordX,
y: coordY,
size: conf.type.substr(0,6) === '
image:
' ? img.width : conf.size,
mark: mark,
type: conf.type
};
};
// Draw a bubble on a bubble chart
this.drawBubble = function (opt, conf)
{
var size = (conf.z / prop.bubbleMaxValue) * prop.bubbleMaxRadius;
var color = RG.SVG.parseColorRadial({
object: this,
color: prop.bubbleColorsSolid ? conf.color : '
Gradient(white:
' + conf.color + '
)
',
cx: opt.coordx + (size / 4),
cy: opt.coordy - (size / 4),
fx: opt.coordx + (size / 4),
fy: opt.coordy - (size / 4),
r: size * 1.5
});
var circle = RG.SVG.create({
svg: this.svg,
type: '
circle
',
attr: {
cx: opt.coordx,
cy: opt.coordy,
r: size,
fill: color,
'
fill-opacity
': conf.opacity
}
});
// Add some data attributes that save various values
circle.setAttribute('
data-index
', opt.index);
circle.setAttribute('
data-dataset
', opt.datasetIdx);
circle.setAttribute('
data-original-opacity
', conf.opacity);
circle.setAttribute('
data-original-color
', conf.color);
circle.setAttribute('
data-original-coordx
', opt.coordx);
circle.setAttribute('
data-original-coordy
', opt.coordy);
circle.setAttribute('
data-size
', size);
circle.setAttribute('
data-sequential
', opt.sequential);
circle.setAttribute('
data-type
', '
bubble
');
return {
x: opt.coordx,
y: opt.coordy,
z: opt.coordz
};
};
//
// This functions draws a line if required
//
this.drawLine = function (opt)
{
var linewidth = 1,
color = '
black
';
// Calculate the linewidth
if (typeof prop.lineLinewidth === '
object
' && typeof prop.lineLinewidth[opt.index] === '
number
') {
linewidth = prop.lineLinewidth[opt.index];
} else if (typeof prop.lineLinewidth === '
number
') {
linewidth = prop.lineLinewidth;
} else {
linewidth = 1;
}
// Determine the color
if (typeof prop.lineColors === '
object
' && prop.lineColors[opt.index]) {
color = prop.lineColors[opt.index];
} else if (prop.colors[opt.index] === '
string
') {
color = prop.colors[opt.index];
} else {
color = '
black
';
}
for (var i=0,path='
'; i<this.coords2[opt.index].length; ++i) {
path += '
{1} {2} {3}
'.format(
i === 0 ? '
M
' : '
L
',
this.coords2[opt.index][i].x,
this.coords2[opt.index][i].y
);
}
RG.SVG.create({
svg: this.svg,
type: '
path
',
parent: this.line_groups[opt.index],
attr: {
d: path,
fill: '
transparent
',
stroke: color,
'
stroke-width
': linewidth,
'
stroke-linecap
': '
round
',
'
stroke-linejoin
': '
round
'
}
});
};
/**
* This function can be used to retrieve the relevant X coordinate for a
* particular value.
*
* @param int value The value to get the X coordinate for
*/
this.getXCoord = function (value)
{
var x;
if (value > prop.xaxisScaleMax) {
return null;
}
if (value < prop.xaxisScaleMin) {
return null;
}
x = ((value - prop.xaxisScaleMin) / (prop.xaxisScaleMax - prop.xaxisScaleMin));
x *= (this.width - prop.marginLeft - prop.marginRight);
x = prop.marginLeft + x;
return x;
};
/**
* This function can be used 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)
{
var prop = this.properties;
if (value > this.scale.max) {
return null;
}
var y, xaxispos = prop.xaxispos;
if (value < this.scale.min) {
return null;
}
y = ((value - this.scale.min) / (this.scale.max - this.scale.min));
y *= (this.height - prop.marginTop - prop.marginBottom);
y = this.height - prop.marginBottom - y;
return y;
};
/**
* This function can be used to highlight a bar on the chart
*
* @param object rect The rectangle to highlight
*/
this.highlight = function (rect)
{
rect.setAttribute('
fill
', prop.highlightFill);
// Store the highlight rect in the registry so
// it can be reset later
RG.SVG.REG.set('
highlight
', rect);
};
//
// Draws the labelsAbove
//
// @param opt An object that consists of various arguments to the function
//
this.drawLabelsAbove = function (opt)
{
var conf = opt.point,
coordX = opt.coordX,
coordY = opt.coordY;
// Facilitate labelsAboveSpecific
if (typeof conf.above === '
string
') {
var str = conf.above;
} else {
conf.x = RG.SVG.numberFormat({
object: this,
num: conf.x.toFixed(prop.labelsAboveXDecimals ),
prepend: typeof prop.labelsAboveXUnitsPre === '
string
' ? prop.labelsAboveXUnitsPre : null,
append: typeof prop.labelsAboveXUnitsPost === '
string
' ? prop.labelsAboveXUnitsPost : null,
point: typeof prop.labelsAboveXPoint === '
string
' ? prop.labelsAboveXPoint : null,
thousand: typeof prop.labelsAboveXThousand === '
string
' ? prop.labelsAboveXThousand : null,
formatter: typeof prop.labelsAboveXFormatter === '
function
' ? prop.labelsAboveXFormatter : null
});
conf.y = RG.SVG.numberFormat({
object: this,
num: conf.y.toFixed(prop.labelsAboveYDecimals ),
prepend: typeof prop.labelsAboveYUnitsPre === '
string
' ? prop.labelsAboveYUnitsPre : null,
append: typeof prop.labelsAboveYUnitsPost === '
string
' ? prop.labelsAboveYUnitsPost : null,
point: typeof prop.labelsAboveYPoint === '
string
' ? prop.labelsAboveYPoint : null,
thousand: typeof prop.labelsAboveYThousand === '
string
' ? prop.labelsAboveYThousand : null,
formatter: typeof prop.labelsAboveYFormatter === '
function
' ? prop.labelsAboveYFormatter : null
});
var str = '
{1}{2}{3}
'.format(
conf.x,
prop.labelsAboveSeparator,
conf.y
);
}
// Add the text to the scene
RG.SVG.text({
object: this,
parent: this.svg.all,
tag: '
labels.above
',
text: str,
x: parseFloat(coordX) + prop.labelsAboveOffsetx,
y: parseFloat(coordY) + prop.labelsAboveOffsety,
halign: prop.labelsAboveHalign,
valign: prop.labelsAboveValign,
font: prop.labelsAboveFont || prop.textFont,
size: typeof prop.labelsAboveSize === '
number
' ? prop.labelsAboveSize : prop.textSize,
bold: typeof prop.labelsAboveBold === '
boolean
' ? prop.labelsAboveBold : prop.textBold,
italic: typeof prop.labelsAboveItalic === '
boolean
' ? prop.labelsAboveItalic : prop.textItalic,
color: prop.labelsAboveColor || prop.textColor,
background: prop.labelsAboveBackground || null,
padding: prop.labelsAboveBackgroundPadding || 0
});
};
/**
* This allows for easy specification of gradients
*/
this.parseColors = function ()
{
// TODO Loop thru the data parsing the color for gradients too
// Save the original colors so that they can be restored when
// the canvas is cleared
if (!Object.keys(this.originalColors).length) {
this.originalColors = {
colors: RG.SVG.arrayClone(prop.colors),
backgroundGridColor: RG.SVG.arrayClone(prop.backgroundGridColor),
highlightFill: RG.SVG.arrayClone(prop.highlightFill),
backgroundColor: RG.SVG.arrayClone(prop.backgroundColor)
}
}
// colors
var colors = prop.colors;
// IMPORTANT: Bubble chart gradients are parse in the drawBubble()
// function below
if (colors && !prop.bubble) {
for (var i=0; i<colors.length; ++i) {
colors[i] = RG.SVG.parseColorLinear({
object: this,
color: colors[i]
});
}
}
prop.backgroundGridColor = RG.SVG.parseColorLinear({object: this, color: prop.backgroundGridColor});
prop.highlightFill = RG.SVG.parseColorLinear({object: this, color: prop.highlightFill});
prop.backgroundColor = RG.SVG.parseColorLinear({object: this, color: prop.backgroundColor});
};
/**
* Using a function to add events makes it easier to facilitate method
* chaining
*
* @param string type The type of even to add
* @param function func
*/
this.on = function (type, func)
{
if (type.substr(0,2) !== '
on
') {
type = '
on
' + type;
}
RG.SVG.addCustomEventListener(this, type, func);
return this;
};
//
// Used in chaining. Runs a function there and then - not waiting for
// the events to fire (eg the onbeforedraw event)
//
// @param function func The function to execute
//
this.exec = function (func)
{
func(this);
return this;
};
//
// Remove highlight from the chart (tooltips)
//
this.removeHighlight = function ()
{
var highlight = RG.SVG.REG.get('
highlight
');
if (highlight) {
highlight.setAttribute('
fill
', '
transparent
');
RG.SVG.REG.set('
highlight
', null);
}
};
//
// Draws a single errorbar
//
this.drawErrorbar = function (opt)
{
// Get the error bar value
var max = RG.SVG.getErrorbarsMaxValue({
object: this,
index: opt.sequential
});
// Get the error bar value
var min = RG.SVG.getErrorbarsMinValue({
object: this,
index: opt.sequential
});
if (!max && !min) {
return;
}
var linewidth = RG.SVG.getErrorbarsLinewidth({object: this, index: opt.sequential}),
color = RG.SVG.getErrorbarsColor({object: this, index: opt.sequential}),
capwidth = RG.SVG.getErrorbarsCapWidth({object: this, index: opt.sequential}),
halfCapWidth = capwidth / 2;
if (max !== 0 || min !== 0) {
var y1 = this.getYCoord(opt.valueY + max)
y2 = this.getYCoord(opt.valueY - min);
// Draw the UPPER vertical line
var errorbarLine = RG.SVG.create({
svg: this.svg,
type: '
line
',
parent: opt.parent,
attr: {
x1: opt.x,
y1: opt.y,
x2: opt.x,
y2: y1,
stroke: color,
'
stroke-width
': linewidth
}
});
// Draw the cap to the UPPER line
var errorbarCap = RG.SVG.create({
svg: this.svg,
type: '
line
',
parent: opt.parent,
attr: {
x1: opt.x - halfCapWidth,
y1: y1,
x2: opt.x + halfCapWidth,
y2: y1,
stroke: color,
'
stroke-width
': linewidth
}
});
}
// Draw the minimum errorbar if necessary
if (typeof min === '
number
') {
var errorbarLine = RG.SVG.create({
svg: this.svg,
type: '
line
',
parent: opt.parent,
attr: {
x1: opt.x,
y1: opt.y,
x2: opt.x,
y2: y2,
stroke: color,
'
stroke-width
': linewidth
}
});
// Draw the cap to the UPPER line
var errorbarCap = RG.SVG.create({
svg: this.svg,
type: '
line
',
parent: opt.parent,
attr: {
x1: opt.x - halfCapWidth,
y1: y2,
x2: opt.x + halfCapWidth,
y2: y2,
stroke: color,
'
stroke-width': linewidth
}
});
}
};
};
return
this
;
})(window, document);