Carpincho/src/gui_common/libs/RGraph/RGraph.odo.js
2026-09-27 23:33:38 -03:00

1848 lines
62 KiB
JavaScript

'version:2026-08-11 (7.21)';
//
// o---------------------------------------------------------------------------o
// | This file is part of the RGraph package - you can learn more at: |
// | |
// | https://www.rgraph.net |
// | |
// | RGraph is dual-licensed under the Open Source GPL license. This means |
// | that it's free to use for any purpose. The GPL license does have |
// | consequences on the license of the software that you include it in, |
// | however. If this is not desirable, then there's an inexpensive commercial |
// | license option available. See the RGraph website for more details. |
// o---------------------------------------------------------------------------o
RGraph = window.RGraph || {isrgraph:true,isRGraph:true,rgraph:true};
//
// The odometer constructor. Pass it the ID of the canvas tag, the start value of the odo,
// the end value, and the value that the pointer should point to.
//
RGraph.Odometer = function (conf)
{
var id = conf.id,
canvas = document.getElementById(id),
min = conf.min,
max = conf.max,
value = conf.value;
this.id = id;
this.canvas = canvas;
this.context = this.canvas.getContext ? this.canvas.getContext("2d", {alpha: (typeof id === 'object' && id.alpha === false) ? false : true}) : null;
this.canvas.__object__ = this;
this.type = 'odo';
this.isRGraph = true;
this.isrgraph = true;
this.rgraph = true;
this.min = RGraph.stringsToNumbers(min);
this.max = RGraph.stringsToNumbers(max);
this.value = RGraph.stringsToNumbers(value);
this.currentValue = null;
this.uid = RGraph.createUID();
this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.createUID();
this.colorsParsed = false;
this.coordsText = [];
this.original_colors = [];
this.firstDraw = true; // After the first draw this will be false
this.stopAnimationRequested = false;// Used to control the animations
this.properties =
{
backgroundBorder: 'black',
backgroundColor: 'white',
backgroundLinesColor: '#ddd',
centerx: null,
centery: null,
radius: null,
labels: null,
labelsMargin: 35,
labelsFont: null,
labelsSize: null,
labelsColor: null,
labelsBold: null,
labelsItalic: null,
labelsValue: false,
labelsValueDecimals: 0,
labelsValuePoint: '.',
labelsValueThousand: ',',
labelsValueUnitsPre: '',
labelsValueUnitsPost: '',
labelsValueFont: null,
labelsValueSize: null,
labelsValueColor: null,
labelsValueBold: null,
labelsValueItalic: null,
labelsValueOffsetx: 0,
labelsValueOffsety: 0,
needleExtra: [],
needleColor: 'black',
needleLength: null,
needleWidth: 2,
needleHead: true,
needleTail: false,
needleType: 'pointer',
needleTriangleBorder: '#aaa',
textSize: 12,
textColor: 'black',
textFont: 'Arial, Verdana, sans-serif',
textBold: false,
textItalic: false,
textAccessible: false,
textAccessibleOverflow: 'visible',
textAccessiblePointerevents: false,
text: null,
colorsGreenMax: max * 0.75,
colorsGreenColor: '#00cc0088',
colorsYellowColor: '#ffff0088',
colorsRedMin: max * 0.9,
colorsRedColor: '#ff000088',
marginLeft: 35,
marginRight: 35,
marginTop: 35,
marginBottom: 35,
title: '',
titleFont: null,
titleBold: true,
titleItalic: null,
titleSize: null,
titleColor: null,
titleX: null,
titleY: null,
titleHalign: null,
titleValign: null,
titleOffsetx: 0,
titleOffsety: 0,
titleSubtitle: '',
titleSubtitleSize: null,
titleSubtitleColor: '#aaa',
titleSubtitleFont: null,
titleSubtitleBold: null,
titleSubtitleItalic: null,
titleSubtitleOffsetx: 0,
titleSubtitleOffsety: 0,
contextmenu: null,
linewidth: 1,
shadowInner: false,
shadowInnerColor: 'black',
shadowInnerOffsetx: 3,
shadowInnerOffsety: 3,
shadowInnerBlur: 6,
shadowOuter: false,
shadowOuterColor: 'black',
shadowOuterOffsetx: 3,
shadowOuterOffsety: 3,
shadowOuterBlur: 6,
annotatable: false,
annotatableColor: 'black',
annotatableLinewidth: 1,
scaleDecimals: 0,
scalePoint: '.',
scaleThousand: ',',
scaleUnitsPre: '',
scaleUnitsPost: '',
scaleZerostart: false,
border: false,
borderColor1: '#BEBCB0',
borderColor2: '#F0EFEA',
borderColor3: '#BEBCB0',
tickmarks: false,
tickmarksHighlighted: true,
tickmarksLargeColor: '#999',
key: null,
keyBackground: 'white',
keyPosition: 'graph',
keyShadow: false,
keyShadowColor: '#666',
keyShadowBlur: 3,
keyShadowOffsetx: 2,
keyShadowOffsety: 2,
keyPositionMarginBoxed: false,
keyPositionMarginHSpace: 0,
keyPositionX: null,
keyPositionY: null,
keyHalign: 'right',
keyColorShape: 'square',
keyRounded: true,
keyColors: null,
keyLabelsSize: null,
keyLabelsFont: null,
keyLabelsColor: null,
keyLabelsBold: null,
keyLabelsItalic: null,
keyLabelsOffsetx: 0,
keyLabelsOffsety: 0,
keyFormattedDecimals: 0,
keyFormattedPoint: '.',
keyFormattedThousand: ',',
keyFormattedUnitsPre: '',
keyFormattedUnitsPost: '',
keyFormattedValueSpecific: null,
keyFormattedItemsCount: null,
adjustable: false,
clearto: 'transparent',
events: {},
clip: null,
responsive: null,
scale: true,
scaleFactor: 2,
antialiasTranslate: false,
style: []
};
this.properties_scale = [
'centerx',
'centery',
'radius',
'labelsMargin',
'labelsSize',
'labelsValueSize',
'labelsValueOffsetx',
'labelsValueOffsety',
'needleWidth',
'needleLength',
'textSize',
'marginLeft',
'marginRight',
'marginTop',
'marginBottom',
'titleSize',
'titleX',
'titleY',
'titleOffsetx',
'titleOffsety',
'titleSubtitleSize',
'titleSubtitleOffsetx',
'titleSubtitleOffsety',
'linewidth',
'shadowInnerOffsetx',
'shadowInnerOffsety',
'shadowInnerBlur',
'shadowOuterOffsetx',
'shadowOuterOffsety',
'shadowOuterBlur',
'keyShadowBlur',
'keyShadowOffsetx',
'keyShadowOffsety',
'keyPositionMarginHSpace',
'keyPositionX',
'keyPositionY',
'keyLabelsSize',
'keyLabelsOffsetx',
'keyLabelsOffsety',
'annotatableLinewidth'
];
//
// Add the reverse look-up table for property names
// so that property names can be specified in any case.
//
this.properties_lowercase_map = [];
for (var i in this.properties) {
if (typeof i === 'string') {
this.properties_lowercase_map[i.toLowerCase()] = i;
}
}
// Easy access to properties and the path function
var properties = this.properties;
this.path = RGraph.pathObjectFunction;
//
// "Decorate" the object with the generic effects if the effects library has been included
//
if (RGraph.Effects && typeof RGraph.Effects.decorate === 'function') {
RGraph.Effects.decorate(this);
}
// Add the responsive method. This method resides in the common file.
this.responsive = RGraph.responsive;
//
// A peudo setter
//
this.set = function (name)
{
var value = typeof arguments[1] === 'undefined' ? null : arguments[1];
// Go through all of the properties and make sure
// that they're using the correct capitalisation
if (typeof name === 'string') {
name = this.properties_lowercase_map[name.toLowerCase()] || name;
}
// Set the colorsParsed flag to false if the colors
// property is being set
if (
name === 'colorsRedColor'
|| name === 'colorsYellowColor'
|| name === 'colorsGreenColor'
) {
this.colorsParsed = false;
}
// the number of arguments is only one and it's an
// object - parse it for configuration data and return.
if (arguments.length === 1 && typeof arguments[0] === 'object') {
for (i in arguments[0]) {
if (typeof i === 'string') {
this.set(i, arguments[0][i]);
}
}
return this;
}
properties[name] = value;
return this;
};
//
// A getter
//
// @param name string The name of the property to get
//
this.get = function (name)
{
// Go through all of the properties and make sure
// that they're using the correct capitalisation
name = this.properties_lowercase_map[name.toLowerCase()] || name;
return properties[name];
};
//
// Draws the odometer
//
this.draw = function ()
{
// MUST be the first thing that's done - but only
// once!!
RGraph.runOnce(`scale-up-the-canvas-once-in-the-draw-function-${this.id}-${this.uid}`, () =>
{
// Note that we're in an arrow function so the
// 'this' variable is OK to be used and refers
// to the RGraph Line chart object.
RGraph.scale(this);
});
var scaleFactor = RGraph.getScaleFactor(this);
//
// Fire the onbeforedraw event
//
RGraph.fireCustomEvent(this, 'onbeforedraw');
//
// Add any CSS that has been specified to the document.
// This is general CSS and does not necessarily have to
// pertain to the canvas tag. It only gets added once
// to the document no matter how many times this draw
// function is called.
//
// Add the CSS to a <style> block in the <head>.
RGraph.addConfigurationBasedCSS(this);
// Translate half a pixel for antialiasing purposes - but
// only if it hasn't been done already
//
// The old style antialias fix
//
if ( !this.properties.scale
&& this.properties.antialiasTranslate
&& !this.canvas.__rgraph_aa_translated__) {
this.context.translate(0.5,0.5);
this.canvas.__rgraph_aa_translated__ = true;
}
//
// Set the current value
//
this.currentValue = this.value;
//
// No longer allow values outside the range of the Odo
//
if (this.value > this.max) {
this.value = this.max;
}
if (this.value < this.min) {
this.value = this.min;
}
//
// Make the margins easy ro access
//
this.marginLeft = properties.marginLeft;
this.marginRight = properties.marginRight;
this.marginTop = properties.marginTop;
this.marginBottom = properties.marginBottom;
// Work out a few things
this.radius = Math.min(
(this.canvas.width - this.marginLeft - this.marginRight) / 2,
(this.canvas.height - this.marginTop - this.marginBottom) / 2
) - (properties.border ? 25 : 0);
this.diameter = 2 * this.radius;
this.centerx = ((this.canvas.width - this.marginLeft- this.marginRight) / 2) + this.marginLeft;
this.centery = ((this.canvas.height - this.marginTop - this.marginBottom) / 2) + this.marginTop;
this.range = this.max - this.min;
this.coordsText = [];
//
// Move the centerx if the key is defined
//
if (properties.key && properties.key.length > 0 && this.canvas.width > this.canvas.height) this.centerx = 5 + this.radius;
if (typeof properties.centerx === 'number') this.centerx = properties.centerx;
if (typeof properties.centery === 'number') this.centery = properties.centery;
//
// Allow the centerx/centery/radius to be a plus/minus
//
var scaleFactor = RGraph.getScaleFactor(this);
if (typeof properties.radius === 'string' && properties.radius.match(/^\+|-\d+$/) ) this.radius += parseFloat(properties.radius) * scaleFactor;
if (typeof properties.centerx === 'string' && properties.centerx.match(/^\+|-\d+$/) ) this.centerx += parseFloat(properties.centerx) * scaleFactor;
if (typeof properties.centery === 'string' && properties.centery.match(/^\+|-\d+$/) ) this.centery += parseFloat(properties.centery) * scaleFactor;
//
// Allow custom setting of the radius
//
if (typeof properties.radius === 'number') {
this.radius = properties.radius;
if (properties.border) {
this.radius -= 25;
}
}
//
// Parse the colors for gradients. Its down here so that the center X/Y can be used
//
if (!this.colorsParsed) {
this.parseColors();
// Don't want to do this again
this.colorsParsed = true;
}
//
// Install clipping
//
if (!RGraph.isNullish(this.properties.clip)) {
RGraph.clipTo.start(this, this.properties.clip);
}
this.context.lineWidth = properties.linewidth;
// Draw the background
this.drawBackground();
// And lastly, draw the labels
this.drawLabels();
// Draw the needle
if (RGraph.isArray(this.value)) {
for (let i=0; i<this.value.length; ++i) {
this.drawNeedle({
value: this.value[i],
color: RGraph.isArray(properties.needleColor) ? properties.needleColor[i] : properties.needleColor,
length: RGraph.isArray(properties.needleLength) ? properties.needleLength[i] : properties.needleLength,
type: RGraph.isArray(properties.needleType) ? properties.needleType[i] : properties.needleType,
linewidth: RGraph.isArray(properties.needleWidth) ? properties.needleWidth[i] : properties.needleWidth,
head: RGraph.isArray(properties.needleHead) ? properties.needleHead[i] : properties.needleHead,
tail: RGraph.isArray(properties.needleTail) ? properties.needleTail[i] : properties.needleTail,
triangleBorder: RGraph.isArray(properties.needleTriangleBorder) ? properties.needleTriangleBorder[i] : properties.needleTriangleBorder
});
}
} else {
this.drawNeedle({
value: this.value,
color: RGraph.isArray(properties.needleColor) ? properties.needleColor[0] : properties.needleColor,
length: RGraph.isArray(properties.needleLength) ? properties.needleLength[0] : properties.needleLength,
type: RGraph.isArray(properties.needleColor) ? properties.needleType[0] : properties.needleType,
linewidth: RGraph.isArray(properties.needleWidth) ? properties.needleWidth[0] : properties.needleWidth,
head: RGraph.isArray(properties.needleHead) ? properties.needleHead[0] : properties.needleHead,
tail: RGraph.isArray(properties.needleTail) ? properties.needleTail[0] : properties.needleTail,
triangleBorder: RGraph.isArray(properties.needleTriangleBorder) ? properties.needleTriangleBorder[0] : properties.needleTriangleBorder
});
}
//
// Draw the key if requested
//
if (properties.key && properties.key.length > 0) {
// Build a colors array out of the needle colors
var colors = RGraph.isArray(properties.needleColor) ? properties.needleColor : [properties.needleColor];
RGraph.drawKey(this,
properties.key,
colors
);
}
//
// Setup the context menu if required
//
if (properties.contextmenu) {
RGraph.showContext(this);
}
//
// Add custom text thats specified
//
RGraph.addCustomText(this);
//
// This installs the event listeners
//
RGraph.installEventListeners(this);
//
// End clipping
//
if (!RGraph.isNullish(this.properties.clip)) {
RGraph.clipTo.end();
}
//
// Fire the onfirstdraw event
//
if (this.firstDraw) {
this.firstDraw = false;
RGraph.fireCustomEvent(this, 'onfirstdraw');
this.firstDrawFunc();
}
//
// Fire the RGraph draw event
//
RGraph.fireCustomEvent(this, 'ondraw');
//
// Install any inline responsive configuration. This
// should be last in the draw function - even after
// the draw events.
//
RGraph.installInlineResponsive(this);
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;
};
//
// Draws the background
//
this.drawBackground = function ()
{
var scaleFactor = RGraph.getScaleFactor(this);
this.context.beginPath();
//
// Turn on the shadow if need be
//
if (properties.shadowOuter) {
RGraph.setShadow(
this,
properties.shadowOuterColor,
properties.shadowOuterOffsetx,
properties.shadowOuterOffsety,
properties.shadowOuterBlur
);
}
var backgroundColor = properties.backgroundColor;
// Draw the grey border
this.context.fillStyle = backgroundColor;
this.context.arc(
this.centerx,
this.centery,
this.radius + (properties.border ? (20 * scaleFactor) : 0),
0.0001,
RGraph.TWOPI,
false
);
this.context.fill();
//
// Turn off the shadow
//
RGraph.noShadow(this);
// Draw a circle
this.context.strokeStyle = '#666';
this.context.arc(
this.centerx,
this.centery,
this.radius,
0,
RGraph.TWOPI,
false
);
// Now draw a big white circle to make the lines appear
// as tick marks. This is solely for Chrome.
this.context.fillStyle = backgroundColor;
this.context.arc(this.centerx, this.centery, this.radius, 0, RGraph.TWOPI, false);
this.context.fill();
//
// Draw more tickmarks
//
if (properties.tickmarks) {
this.context.beginPath();
this.context.strokeStyle = '#bbb';
for (var i=0; i<=360; i+=3) {
this.context.arc(this.centerx, this.centery, this.radius, 0, i / 57.3, false);
this.context.lineTo(this.centerx, this.centery);
}
this.context.stroke();
}
this.context.beginPath();
this.context.lineWidth = 1;
this.context.strokeStyle = 'black';
// Now draw a big white circle to make the lines appear as tick marks
this.context.fillStyle = backgroundColor;
this.context.strokeStyle = backgroundColor;
this.context.arc(this.centerx, this.centery, this.radius - 5, 0, RGraph.TWOPI, false);
this.context.fill();
this.context.stroke();
// Gray lines at 18 degree intervals
this.context.beginPath();
this.context.strokeStyle = properties.backgroundLinesColor;
for (var i=0; i<360; i+=18) {
this.context.arc(this.centerx, this.centery, this.radius, 0, RGraph.toRadians(i), false);
this.context.lineTo(this.centerx, this.centery);
}
this.context.stroke();
// Redraw the outer circle
this.context.beginPath();
this.context.strokeStyle = properties.backgroundBorder;
this.context.arc(this.centerx, this.centery, this.radius, 0, RGraph.TWOPI, false);
this.context.stroke();
//
// Now draw the center bits shadow if need be
//
if (properties.shadowInner) {
this.context.beginPath();
RGraph.setShadow(
this,
properties.shadowInnerColor,
properties.shadowInnerOffsetx,
properties.shadowInnerOffsety,
properties.shadowInnerBlur
);
this.context.arc(this.centerx, this.centery, this.radius - properties.labelsMargin, 0, RGraph.TWOPI, 0);
this.context.fill();
this.context.stroke();
//
// Turn off the shadow
//
RGraph.noShadow(this);
}
//
// Draw the green area
//
var greengrad = properties.colorsGreenColor;
// Draw the "tick highlight"
if (properties.tickmarksHighlighted) {
this.context.beginPath();
this.context.lineWidth = 5 * scaleFactor;
this.context.strokeStyle = greengrad;
this.context.arc(this.centerx, this.centery, this.radius - 2.5,
-1 * RGraph.HALFPI,
(((properties.colorsGreenMax - this.min)/ (this.max - this.min)) * RGraph.TWOPI) - RGraph.HALFPI,
0);
this.context.stroke();
this.context.lineWidth = 1;
}
this.context.beginPath();
this.context.fillStyle = greengrad;
this.context.arc(
this.centerx,
this.centery,
this.radius - properties.labelsMargin,
0 - RGraph.HALFPI,
(((properties.colorsGreenMax - this.min)/ (this.max - this.min)) * RGraph.TWOPI) - RGraph.HALFPI,
false
);
this.context.lineTo(this.centerx, this.centery);
this.context.closePath();
this.context.fill();
//
// Draw the yellow area
//
var yellowgrad = properties.colorsYellowColor;
// Draw the "tick highlight"
if (properties.tickmarksHighlighted) {
this.context.beginPath();
this.context.lineWidth = 5 * scaleFactor;
this.context.strokeStyle = yellowgrad;
this.context.arc(this.centerx, this.centery, this.radius - 2.5, (
((properties.colorsGreenMax - this.min) / (this.max - this.min)) * RGraph.TWOPI) - RGraph.HALFPI,
(((properties.colorsRedMin - this.min) / (this.max - this.min)) * RGraph.TWOPI) - RGraph.HALFPI,
0);
this.context.stroke();
this.context.lineWidth = 1;
}
this.context.beginPath();
this.context.fillStyle = yellowgrad;
this.context.arc(
this.centerx,
this.centery,
this.radius - properties.labelsMargin,
( ((properties.colorsGreenMax - this.min) / (this.max - this.min)) * RGraph.TWOPI) - RGraph.HALFPI,
( ((properties.colorsRedMin - this.min) / (this.max - this.min)) * RGraph.TWOPI) - RGraph.HALFPI,
false
);
this.context.lineTo(this.centerx, this.centery);
this.context.closePath();
this.context.fill();
//
// Draw the red area
//
var redgrad = properties.colorsRedColor;
// Draw the "tick highlight"
if (properties.tickmarksHighlighted) {
this.context.beginPath();
this.context.lineWidth = 5 * scaleFactor;
this.context.strokeStyle = redgrad;
this.context.arc(this.centerx, this.centery, this.radius - 2.5,(((properties.colorsRedMin - this.min) / (this.max - this.min)) * RGraph.TWOPI) - RGraph.HALFPI,RGraph.TWOPI - RGraph.HALFPI,0);
this.context.stroke();
this.context.lineWidth = 1;
}
this.context.beginPath();
this.context.fillStyle = redgrad;
this.context.strokeStyle = redgrad;
this.context.arc(
this.centerx,
this.centery,
this.radius - properties.labelsMargin,
(((properties.colorsRedMin - this.min) / (this.max - this.min)) * RGraph.TWOPI) - RGraph.HALFPI,
RGraph.TWOPI - RGraph.HALFPI,
false
);
this.context.lineTo(this.centerx, this.centery);
this.context.closePath();
this.context.fill();
//
// Draw the thick border
//
if (properties.border) {
var grad = this.context.createRadialGradient(
this.centerx,
this.centery,
this.radius,
this.centerx,
this.centery,
this.radius + (20 * scaleFactor)
);
grad.addColorStop(0, properties.borderColor1);
grad.addColorStop(0.5, properties.borderColor2);
grad.addColorStop(1, properties.borderColor3);
this.context.beginPath();
this.context.fillStyle = grad;
this.context.strokeStyle = 'rgba(0,0,0,0)'
this.context.lineWidth = 0.001;
this.context.arc(
this.centerx,
this.centery,
this.radius + (20 * scaleFactor),
0,
RGraph.TWOPI,
0
);
this.context.arc(
this.centerx,
this.centery,
this.radius - 2,
RGraph.TWOPI,
0,
1
);
this.context.fill();
}
// Put the linewidth back to what it was
this.context.lineWidth = properties.linewidth;
//
// Draw the title if specified
//
if (properties.title) {
RGraph.drawTitle(this);
}
// Draw the big tick marks
if (!properties.tickmarksHighlighted) {
for (var i=18; i<=360; i+=36) {
this.context.beginPath();
this.context.strokeStyle = properties.tickmarksLargeColor;
this.context.lineWidth = 2;
this.context.arc(this.centerx, this.centery, this.radius - 1, RGraph.toRadians(i), RGraph.toRadians(i+0.01), false);
this.context.arc(this.centerx, this.centery, this.radius - 7, RGraph.toRadians(i), RGraph.toRadians(i+0.01), false);
this.context.stroke();
}
}
};
//
// Draws the needle of the odometer
//
// @param object opt An object map of the following
// properties:
// o value
// o head
// o width
// o length
// o type
// o color
// o triangleborder
//
this.drawNeedle = function (opt)
{
var scaleFactor = RGraph.getScaleFactor(this);
// The optional length of the needle
opt.length = opt.length ? opt.length : this.radius - properties.labelsMargin - 10;
// First draw a grey background circle at the center of
// the Odo
this.context.fillStyle = '#999';
this.context.beginPath();
this.context.moveTo(this.centerx, this.centery);
this.context.arc(
this.centerx, this.centery,
10 * scaleFactor,
0, RGraph.TWOPI,
false
);
this.context.closePath();
this.context.fill();
this.context.fill();
this.context.fillStyle = opt.color
this.context.strokeStyle = '#666';
//
// Draw the black centrepin bit
//
this.context.beginPath();
this.context.moveTo(this.centerx, this.centery);
this.context.arc(
this.centerx,
this.centery,
8 * scaleFactor,
0,
RGraph.TWOPI,
false
);
this.context.fill();
this.context.closePath();
this.context.stroke();
this.context.fill();
if (opt.type === 'pointer') {
this.context.strokeStyle = opt.color;
this.context.lineWidth = opt.linewidth;
this.context.lineCap = 'round';
this.context.lineJoin = 'round';
// Draw the needle
this.context.beginPath();
// The trailing bit on the opposite side of
// the dial.
this.context.beginPath();
this.context.moveTo(
this.centerx,
this.centery
);
if (opt.tail) {
this.context.arc(
this.centerx,
this.centery,
20 * scaleFactor,
(opt.value / this.range) * RGraph.TWOPI + RGraph.HALFPI,
(opt.value / this.range) * RGraph.TWOPI + RGraph.HALFPI,
false
);
}
// Draw the long bit on the opposite
// side
this.context.arc(
this.centerx,
this.centery,
opt.length - ((0.1 * opt.length) * scaleFactor),
(((opt.value / this.range) * 360) - 90) / (180 / RGraph.PI),
(((opt.value / this.range) * 360) - 90 + 0.1 ) / (180 / RGraph.PI),
false
);
this.context.closePath();
//this.context.stroke();
//this.context.fill();
} else if (opt.type === 'triangle') {
this.context.lineWidth = 0.01;
this.context.lineEnd = 'square';
this.context.lineJoin = 'miter';
//
// This draws the version of the pointer that
// becomes the border.
//
this.context.beginPath();
this.context.fillStyle = opt.triangleBorder;
this.context.arc(this.centerx, this.centery, 11 * scaleFactor, (((opt.value / this.range) * 360)) / 57.3, ((((opt.value / this.range) * 360)) + 0.01) / 57.3, 0);
this.context.arc(this.centerx, this.centery, 11 * scaleFactor, (((opt.value / this.range) * 360) + 180) / 57.3, ((((opt.value / this.range) * 360) + 180) + 0.01)/ 57.3, 0);
this.context.arc(this.centerx, this.centery, opt.length - 5, (((opt.value / this.range) * 360) - 90) / 57.3, ((((opt.value / this.range) * 360) - 90) / 57.3) + 0.01, 0);
this.context.closePath();
this.context.fill();
this.context.beginPath();
this.context.arc(
this.centerx,
this.centery,
15,
0,
RGraph.TWOPI,
false
);
this.context.closePath();
this.context.fill();
// This draws the pointer
this.path(
'b a % % % % % false a % % % % % false',
this.centerx,
this.centery,
7 * scaleFactor,
(((opt.value / this.range) * 360)) / 57.3,
((((opt.value / this.range) * 360)) + 0.01) / 57.3,
this.centerx,
this.centery,
7 * scaleFactor,
(((opt.value / this.range) * 360) + 180) / 57.3,
((((opt.value / this.range) * 360) + 180) + 0.01)/ 57.3
);
this.context.arc(this.centerx, this.centery, opt.length - 13, (((opt.value / this.range) * 360) - 90) / 57.3, ((((opt.value / this.range) * 360) - 90) / 57.3) + 0.01, 0);
this.context.closePath();
this.context.strokeStyle = 'black';
this.context.fillStyle = opt.color;
this.context.stroke();
this.context.fill();
}
this.context.stroke();
this.context.fill();
// Draw the mini center circle
this.context.beginPath();
this.context.fillStyle = opt.color;
this.context.arc(
this.centerx,
this.centery,
(opt.type === 'pointer' ? 7 : 12) * scaleFactor,
0.01,
RGraph.TWOPI,
false
);
this.context.fill();
// This draws the arrow at the end of the line
if (opt.head && opt.type === 'pointer') {
this.context.lineWidth = 1;
this.context.fillStyle = opt.color;
// round, bevel, miter
this.context.lineJoin = 'miter';
this.context.lineCap = 'butt';
this.context.beginPath();
this.context.arc(
this.centerx,
this.centery,
opt.length - 5,
RGraph.toRadians(((opt.value / this.range) * 360) - 90),
RGraph.toRadians(((opt.value / this.range) * 360) - 90 + 0.1),
false
);
this.context.arc(
this.centerx,
this.centery,
opt.length - ((opt.length * 0.1) * scaleFactor),
RGraph.toRadians( ((opt.value / this.range) * 360) - (this.radius > 300 ? (this.radius > 450 ? 87 : 85) : 82) ),
RGraph.toRadians( ((opt.value / this.range) * 360) - (this.radius > 300 ? (this.radius > 450 ? 93 : 95) : 97) ),
true
);
this.context.closePath();
this.context.fill();
//this.context.stroke();
}
//
// Draw a white circle at the centre
//
this.context.beginPath();
this.context.fillStyle = 'gray';
this.context.moveTo(this.centerx, this.centery);
this.context.arc(
this.centerx,
this.centery,
2,
0,
6.2795,
false
);
this.context.closePath();
this.context.fill();
};
//
// Draws the labels for the Odo
//
this.drawLabels = function ()
{
var centerx = this.centerx,
centery = this.centery,
r = this.radius - (properties.labelsMargin / 2) - 5,
start = this.min,
end = this.max,
decimals = properties.scaleDecimals,
point = properties.scalePoint,
thousand = properties.scaleThousand,
labels = properties.labels,
units_pre = properties.scaleUnitsPre,
units_post = properties.scaleUnitsPost;
this.context.beginPath();
this.context.fillStyle = properties.textColor;
var textConf = RGraph.getTextConf({
object: this,
prefix: 'labels'
});
//
// If labels are specified, use those
//
if (labels) {
for (var i=0; i<labels.length; ++i) {
RGraph.text({
object: this,
font: textConf.font,
size: textConf.size,
color: textConf.color,
bold: textConf.bold,
italic: textConf.italic,
x: centerx + (Math.cos(((i / labels.length) * RGraph.TWOPI) - RGraph.HALFPI) * (this.radius - (properties.labelsMargin / 2) ) ), // Sin A = Opp / Hyp
y: centery + (Math.sin(((i / labels.length) * RGraph.TWOPI) - RGraph.HALFPI) * (this.radius - (properties.labelsMargin / 2) ) ), // Cos A = Adj / Hyp
text: String(labels[i]),
valign: 'center',
halign: 'center',
tag: 'labels'
});
}
//
// If not, use the maximum value
//
} else {
this.scale2 = RGraph.getScale({object: this, options: {
'scale.max': this.max,
'scale.strict': true,
'scale.min': this.min,
'scale.thousand': properties.scaleThousand,
'scale.point': properties.scalePoint,
'scale.decimals': properties.scaleDecimals,
'scale.labels.count': 10,
'scale.round': false,
'scale.units.pre': properties.scaleUnitsPre,
'scale.units.post': properties.scaleUnitsPost
}});
RGraph.text({object:this,font: textConf.font, size: textConf.size, color: textConf.color, bold: textConf.bold, italic: textConf.italic,x:centerx + (0.588 * r ),y:centery - (0.809 * r ),text:RGraph.numberFormat({object: this, number: (((end - start) * (1/10)) + start).toFixed(decimals), unitspre: units_pre, unitspost: units_post,point: point,thousand: thousand}),halign:'center',valign:'center',angle:36,tag: 'scale'});
RGraph.text({object:this,font: textConf.font, size: textConf.size, color: textConf.color, bold: textConf.bold, italic: textConf.italic,x:centerx + (0.951 * r ),y:centery - (0.309 * r),text:RGraph.numberFormat({object: this, number: (((end - start) * (2/10)) + start).toFixed(decimals), unitspre:units_pre, unitspost: units_post,point: point,thousand: thousand}),halign:'center',valign:'center',angle:72,tag: 'scale'});
RGraph.text({object:this,font: textConf.font, size: textConf.size, color: textConf.color, bold: textConf.bold, italic: textConf.italic,x:centerx + (0.949 * r),y:centery + (0.31 * r),text:RGraph.numberFormat({object: this, number: (((end - start) * (3/10)) + start).toFixed(decimals), unitspre: units_pre, unitspost: units_post,point: point,thousand: thousand}),halign:'center',valign:'center',angle:108,tag: 'scale'});
RGraph.text({object:this,font: textConf.font, size: textConf.size, color: textConf.color, bold: textConf.bold, italic: textConf.italic,x:centerx + (0.588 * r ),y:centery + (0.809 * r ),text:RGraph.numberFormat({object: this, number: (((end - start) * (4/10)) + start).toFixed(decimals), unitspre: units_pre, unitspost: units_post,point: point,thousand: thousand}),halign:'center',valign:'center',angle:144,tag: 'scale'});
RGraph.text({object:this,font: textConf.font, size: textConf.size, color: textConf.color, bold: textConf.bold, italic: textConf.italic,x:centerx,y:centery + r,text:RGraph.numberFormat({object: this, number: (((end - start) * (5/10)) + start).toFixed(decimals),unitspre: units_pre, unitspost: units_post,point: point,thousand: thousand}),halign:'center',valign:'center',angle:180,tag: 'scale'});
RGraph.text({object:this,font: textConf.font, size: textConf.size, color: textConf.color, bold: textConf.bold, italic: textConf.italic,x:centerx - (0.588 * r ),y:centery + (0.809 * r ),text:RGraph.numberFormat({object: this, number: (((end - start) * (6/10)) + start).toFixed(decimals), unitspre: units_pre, unitspost: units_post,point: point,thousand: thousand}),halign:'center',valign:'center',angle:216,tag: 'scale'});
RGraph.text({object:this,font: textConf.font, size: textConf.size, color: textConf.color, bold: textConf.bold, italic: textConf.italic,x:centerx - (0.949 * r),y:centery + (0.300 * r),text:RGraph.numberFormat({object: this, number: (((end - start) * (7/10)) + start).toFixed(decimals), unitspre: units_pre, unitspost: units_post,point: point,thousand: thousand}),halign:'center',valign:'center',angle:252,tag: 'scale'});
RGraph.text({object:this,font: textConf.font, size: textConf.size, color: textConf.color, bold: textConf.bold, italic: textConf.italic,x:centerx - (0.951 * r),y:centery - (0.309 * r),text:RGraph.numberFormat({object: this, number: (((end - start) * (8/10)) + start).toFixed(decimals), unitspre: units_pre, unitspost: units_post,point: point,thousand: thousand}),halign:'center',valign:'center',angle:288,tag: 'scale'});
RGraph.text({object:this,font: textConf.font, size: textConf.size, color: textConf.color, bold: textConf.bold, italic: textConf.italic,x:centerx - (0.588 * r ),y:centery - (0.809 * r ),text:RGraph.numberFormat({object: this, number: (((end - start) * (9/10)) + start).toFixed(decimals), unitspre: units_pre, unitspost: units_post,point: point,thousand: thousand}),halign:'center',valign:'center',angle:324,tag: 'scale'});
RGraph.text({object:this,font: textConf.font, size: textConf.size, color: textConf.color, bold: textConf.bold, italic: textConf.italic,x:centerx,y:centery - r,text: RGraph.numberFormat({object: this, number: (((end - start) * (10/10)) + start).toFixed(decimals), unitspre: units_pre, unitspost: units_post,point: point,thousand: thousand}),halign:'center',valign:'center',tag: 'scale'});
}
this.context.fill();
//
// Draw the text label below the center point
//
if (properties.labelsValue) {
this.context.strokeStyle = 'black';
var textConf = RGraph.getTextConf({
object: this,
prefix: 'labelsValue'
});
RGraph.text({
object: this,
font: textConf.font,
size: textConf.size,
color: textConf.color,
bold: textConf.bold,
italic: textConf.italic,
x: centerx + properties.labelsValueOffsetx,
y: centery + textConf.size + 15 + properties.labelsValueOffsety,
text: RGraph.numberFormat({
object: this,
number: this.value.toFixed(this.value === 0 ? 0 : properties.labelsValueDecimals),
unitspre: properties.labelsValueUnitsPre,
unitspost: properties.labelsValueUnitsPost,
point: properties.labelsValuePoint,
thousand: properties.labelsValueThousand
}),
halign: 'center',
valign: 'center',
bounding: true,
boundingFill: 'rgba(255,255,255,0.7)',
boundingStroke: 'rgba(0,0,0,0)',
tag: 'value.text'
});
}
};
//
// A placeholder function
//
// @param object The event object
//
this.getShape = function (e) {};
//
// This function returns the pertinent value at the point of click
//
// @param object The event object
//
this.getValue = function (e)
{
var mouseXY = RGraph.getMouseXY(e)
var angle = RGraph.getAngleByXY(this.centerx, this.centery, mouseXY[0], mouseXY[1]);
angle += RGraph.HALFPI;
if (mouseXY[0] >= this.centerx && mouseXY[1] <= this.centery) {
angle -= RGraph.TWOPI;
}
var value = ((angle / RGraph.TWOPI) * (this.max - this.min)) + this.min;
return value;
};
//
// The getObjectByXY() worker method. Don't call this call:
//
// RGraph.ObjectRegistry.getObjectByXY(e)
//
// @param object e The event object
//
this.getObjectByXY = function (e)
{
var mouseXY = RGraph.getMouseXY(e);
var radius = RGraph.getHypLength(this.centerx, this.centery, mouseXY[0], mouseXY[1]);
if (
mouseXY[0] > (this.centerx - this.radius)
&& mouseXY[0] < (this.centerx + this.radius)
&& mouseXY[1] > (this.centery - this.radius)
&& mouseXY[1] < (this.centery + this.radius)
&& radius <= this.radius
) {
return this;
}
};
//
// This method handles the adjusting calculation for when the mouse is moved
//
// @param object e The event object
//
this.adjusting_mousemove = function (e)
{
//
// Handle adjusting for the Bar
//
if (properties.adjustable && RGraph.Registry.get('adjusting') && RGraph.Registry.get('adjusting').uid == this.uid) {
this.value = this.getValue(e);
RGraph.clear(this.canvas);
RGraph.redrawCanvas(this.canvas);
RGraph.fireCustomEvent(this, 'onadjust');
}
};
//
// This method returns the appropriate angle for a value
//
// @param number value The value
//
this.getAngle = function (value)
{
// Higher than max or lower than min
if (value > this.max || value < this.min) {
return null;
}
var angle = (((value - this.min) / (this.max - this.min)) * RGraph.TWOPI);
angle -= RGraph.HALFPI;
return angle;
};
//
// This allows for easy specification of gradients
//
this.parseColors = function ()
{
// Save the original colors so that they can be restored when the canvas is reset
if (this.original_colors.length === 0) {
this.original_colors.colorsGreenColor = RGraph.arrayClone(properties.colorsGreenColor);
this.original_colors.colorsYellowColor = RGraph.arrayClone(properties.colorsYellowColor);
this.original_colors.colorsRedColor = RGraph.arrayClone(properties.colorsRedColor);
}
// Parse the basic colors
properties.colorsGreenColor = this.parseSingleColorForGradient(properties.colorsGreenColor);
properties.colorsYellowColor = this.parseSingleColorForGradient(properties.colorsYellowColor);
properties.colorsRedColor = this.parseSingleColorForGradient(properties.colorsRedColor);
};
//
// Use this function to reset the object to the post-constructor state. Eg reset colors if
// need be etc
//
this.reset = function ()
{
};
//
// This parses a single color value
//
this.parseSingleColorForGradient = function (color)
{
if (!color || typeof color != 'string') {
return color;
}
if (color.match(/^gradient\((.*)\)$/i)) {
// Allow for JSON gradients
if (color.match(/^gradient\(({.*})\)$/i)) {
return RGraph.parseJSONGradient({object: this, def: RegExp.$1});
}
var parts = RegExp.$1.split(':');
// Create the gradient
var grad = this.context.createRadialGradient(this.centerx, this.centery, 0, this.centerx, this.centery, this.radius);
var diff = 1 / (parts.length - 1);
grad.addColorStop(0, RGraph.trim(parts[0]));
for (var j=1; j<parts.length; ++j) {
grad.addColorStop(j * diff, RGraph.trim(parts[j]));
}
}
return grad ? grad : color;
};
//
// 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;
}
if (typeof this[type] !== 'function') {
this[type] = func;
} else {
RGraph.addCustomEventListener(this, type, func);
}
return this;
};
//
// This function runs once only
// (put at the end of the file (before any effects))
//
this.firstDrawFunc = function ()
{
};
//
// Odo Grow
//
// This effect gradually increases the represented value
//
// @param An object of effect properties - eg: {frames: 30}
// @param function An optional callback function
//
this.grow = function ()
{
// Cancel any stop request if one is pending
this.cancelStopAnimation();
var obj = this;
var opt = arguments[0] || {};
var frames = opt.frames || 60;
var frame = 0;
var current = this.currentValue || 0;
var origValue = Number(obj.currentValue);
var newValue = this.value;
var diff = newValue - origValue;
var step = (diff / frames);
var callback = arguments[1] || function () {};
function iterator ()
{
if (obj.stopAnimationRequested) {
// Reset the flag
obj.stopAnimationRequested = false;
return;
}
obj.value = origValue + (frame * step);
RGraph.clear(obj.canvas);
RGraph.redrawCanvas(obj.canvas);
if (frame++ < frames) {
RGraph.Effects.updateCanvas(iterator);
} else {
callback(obj);
}
}
iterator();
return this;
};
//
// Couple of functions that allow you to control the
// animation effect
//
this.stopAnimation = function ()
{
this.stopAnimationRequested = true;
};
this.cancelStopAnimation = function ()
{
this.stopAnimationRequested = false;
};
//
// This returns the relevant value for the formatted key
// macro %{value}. THIS VALUE SHOULD NOT BE FORMATTED.
//
// @param number index The index in the dataset to get
// the value for
//
this.getKeyValue = function (index)
{
if (RGraph.isArray(this.properties.keyFormattedValueSpecific) && RGraph.isNumber(this.properties.keyFormattedValueSpecific[index])) {
return this.properties.keyFormattedValueSpecific[index];
} else {
if (index === 0) {
return this.value;
} else if (RGraph.isArray(this.properties.needleExtra) && this.properties.needleExtra[index - 1]) {
return this.properties.needleExtra[index - 1][0];
}
}
};
//
// Returns how many data-points there should be when a string
// based key property has been specified. For example, this:
//
// key: '%{property:_labels[%{index}]} %{value_formatted}'
//
// ...depending on how many bits of data ther is might get
// turned into this:
//
// key: [
// '%{property:_labels[%{index}]} %{value_formatted}',
// '%{property:_labels[%{index}]} %{value_formatted}',
// '%{property:_labels[%{index}]} %{value_formatted}',
// '%{property:_labels[%{index}]} %{value_formatted}',
// '%{property:_labels[%{index}]} %{value_formatted}',
// ]
//
// ... ie in that case there would be 4 data-points so the
// template is repeated 4 times.
//
this.getKeyNumDatapoints = function ()
{
var len = 1;
// Add the needleExtra count
len += this.properties.needleExtra.length;
return len;
};
//
// This function handles clipping to scale values. Because
// each chart handles scales differently, a worker function
// is needed instead of it all being done centrally in the
// RGraph.clipTo.start() function.
//
// @param string clip The clip string as supplied by the
// user in the chart configuration
//
this.clipToScaleWorker = function (clip)
{
// The Regular expression is actually done by the
// calling RGraph.clipTo.start() function in the core
// library
if (RegExp.$1 === 'min') from = this.min; else from = Number(RegExp.$1);
if (RegExp.$2 === 'max') to = this.max; else to = Number(RegExp.$2);
var a1 = this.getAngle(from),
a2 = this.getAngle(to);
// Change the radius if the number is "min"
if (RegExp.$1 === 'min') {
a1 = this.getAngle(this.min);
}
// Change the radius if the number is "max"
if (RegExp.$2 === 'max') {
a2 = this.getAngle(this.max);
}
this.path(
'sa b m % % a % % % % % false c cl',
this.centerx,
this.centery,
this.centerx,
this.centery,
Math.max(this.canvas.width, this.canvas.height),
a1, a2
);
};
//
// Scale worker function that increases the size of
// properties as required. Called by the RGraph.scale()
// function.
//
// @param string name The name of the property
// @param mixed value The value of the property
//
this.scalePropertiesWorker = function (name, value)
{
var scaleFactor = RGraph.getScaleFactor(this);
if (name === 'titleY') {
value = String(parseFloat(value) * scaleFactor);
} else if (name === 'titleX') {
value = String(parseFloat(value) * scaleFactor);
} else if (name === 'needleLength' || name === 'needleWidth') {
for (var i=0; i<value.length; ++i) {
if (RGraph.isNumeric(value[i])) {
value[i] *= scaleFactor;
}
}
}
return value;
};
//
// Register the object
//
RGraph.register(this);
//
// This is the 'end' of the constructor so if the first argument
// contains configuration data - handle that.
//
RGraph.parseObjectStyleConfig(this, conf.options);
};