Introduction
Very often developers want to add text or graphics to the layout of an existing jQuery widget. To add annotations is not that difficult if you know the architecture of the component. This article refers to the Infragistics jQuery Chart (part of NetAdvantage for jQuery), but the approach can be used for different components. To understand the example you need a basic knowledge of HTML5, JavaScript and jQuery.
Background
In this example you will learn how to add annotation as Fibonacci retracement to the Infragistics jQuery Chart. Fibonacci retracement is created by taking two extreme points on a chart and dividing the vertical distance by the key Fibonacci ratios. 0.0% is considered to be the start of the retracement, while 100.0% is a complete reversal to the original part of the move. Fibonacci ratios are mathematical relationships, expressed as ratios, derived from the Fibonacci sequence. The key Fibonacci ratios are 0%, 23.6%, 38.2%, and 100%. More details about Fibonacci retracement you could find in Wikipedia.

Using the code
This is a pure HTML/JavaScript/jQuery example. You have no need to implement a server-side logic. The base application contains igDataChart with financial series inside it. The application uses test data from random values.
To start you need to have references to Infragistics NetAdvantage for jQuery scripts and styles. The easiest approach is to use Infragistics Content Delivery Network (CDN) for NetAdvantage for jQuery. The files on the CDN are arranged with the same folder structure as is installed locally on a development machine. The best approach is add only Infragistics Loader (igLoader) as reference and after that to use this component to load references in the proper order.
<span class="xml-punctuation"><</span><span class="xml-tagname">script </span><span class="xml-attname">src</span><span class="xml-punctuation">=</span><span class="xml-attribute">"http://cdn-na.infragistics.com/jquery/20121/2023/js/infragistics.loader.js"</span><span class="xml-punctuation">>
The Infragistics Loader resolves all the Infragistics resources (styles and scripts) for you. You just need to provide the path to required CSS and JavaScript files and declare which resources the loader will fetch for the page.
<span class="js-variable">$</span><span class="js-punctuation">.</span><span class="js-property">ig</span><span class="js-punctuation">.</span><span class="js-property">loader</span><span class="js-punctuation">(</span><span class="js-punctuation">{</span>
<span class="whitespace"> </span><span class="js-property">scriptPath</span><span class="js-punctuation">: </span><span class="js-string">"http://cdn-na.infragistics.com/jquery/20121/2023/js/"</span><span class="js-punctuation">,</span>
<span class="whitespace"> </span><span class="js-property">cssPath</span><span class="js-punctuation">: </span><span class="js-string">"http://cdn-na.infragistics.com/jquery/20121/2023/css/"</span><span class="js-punctuation">,</span>
<span class="whitespace"> </span><span class="js-property">resources</span><span class="js-punctuation">: </span><span class="js-string">"igDataChart.*"</span>
<span class="js-punctuation">}</span><span class="js-punctuation">)</span><span class="js-punctuation">;</span>

Helper object
It is a good practice to have a helper, that contains all required code to
add annotations. Helper is named “FibRetracer” (from Fibonacci retracement).
This helper contains a method “attachCanvas” that adds an additional HTML canvas
over the canvas elements, used inside the igDataChart.
FibRetracer.prototype.attachCanvas = function (chartElement) {
if (this.attached) {
return;
}
this.element = chartElement;
var canvases = chartElement.find("canvas");
this.renderCanvas = $("<canvas class='fibCanvas' style='position: absolute; top: 0; left: 0;'></canvas>");
if (canvases.length > 0) {
this.overlay = $(canvases[canvases.length - 1]);
this.overlay.before(this.renderCanvas);
this.renderCanvas.attr("width", this.overlay.attr("width"));
this.renderCanvas.attr("height", this.overlay.attr("height"));
this.attached = true;
}
};
The main part (to generate annotations) is implemented in the method “draw”. In this method new elements are added inside the attached canvas
FibRetracer.prototype.draw = function () {
if (this.context == null) {
this.context = this.renderCanvas[0].getContext("2d");
}
if (this.fibPoint == null) {
return;
}
this.context.clearRect(0, 0, this.renderCanvas.attr("width"), this.renderCanvas.attr("height"));
var viewport = this.element.igDataChart("option", "gridAreaRect");
this.context.save();
this.context.beginPath();
this.context.moveTo(viewport.left, viewport.top);
this.context.lineTo(viewport.left + viewport.width, viewport.top);
this.context.lineTo(viewport.left + viewport.width, viewport.top + viewport.height);
this.context.lineTo(viewport.left, viewport.top + viewport.height);
this.context.lineTo(viewport.left, viewport.top);
this.context.clip();
var scaledPoint = this.getScaled(this.fibPoint);
var scaledRightBottom = this.getScaled({
x: this.fibPoint.x + this.fibWidth,
y: this.fibPoint.y - this.fibHeight
});
var viewport = {
top: scaledPoint.y,
left: scaledPoint.x,
width: scaledRightBottom.x - scaledPoint.x,
height: scaledRightBottom.y - scaledPoint.y
};
var values = [
0.0,
23.6,
38.2,
100.0
];
var entries = [];
var entry;
for (var i = 0; i < values.length; i++) {
var val = values[i];
entries.push(
{
text: val.toString(),
value: val
});
}
for (var j = 0; j < entries.length; j++) {
entry = entries[j];
entry.desiredWidth = this.context.measureText(entry.text).width;
}
if (viewport.width == 0 ||
viewport.height == 0 ||
isNaN(viewport.width) ||
isNaN(viewport.height)) {
return;
}
this.context.strokeStyle = "red";
this.context.fillStyle = "red";
this.context.lineWidth = 1.0;
for (var j = 0; j < entries.length; j++) {
entry = entries[j];
var yPos = (viewport.top + viewport.height) -
(viewport.height * entry.value / 100.0);
var xLeft = viewport.left;
var xRight = viewport.left + viewport.width;
if (isNaN(yPos) ||
isNaN(xLeft) ||
isNaN(xRight)) {
continue;
}
this.context.beginPath();
this.context.moveTo(xLeft, yPos);
this.context.lineTo(xRight, yPos);
this.context.stroke();
this.context.textBaseline = "bottom";
this.context.fillText(entry.text, xRight - entry.desiredWidth, yPos);
}
this.context.restore();
};
Points of Interest
To add new elements to your jQuery control is not difficult – you just need to add to it’s DOM required HTML element. Data Visualization related components like charts, maps etc. use canvas to render graphics. You could add own canvas with additional elements.
History
You can use jQuery
Chart fiddle to look at the code and play with this sample and make updates.
More details you could find in my blog here.
Mihail Mateev is a Senior Technical Evangelist, Team Lead at Infragistics Inc. He worked as a Software developer and team lead on WPF and Silverlight Line of Business production lines of the company. Now he works like a Technical evangelist in the Infragistics.
Mihail worked in various areas related to technology Microsoft: Silverlight, WPF, Windows Phone 7, Visual Studio LightSwitch, WCF RIA Services, ASP.Net MVC, Windows Metro Applications, MS SQL Server and Windows Azure. He also write many jQuery related blogs.
Over the past ten years, Mihail has written articles for Bulgarian Computer World magazine, blogs about .Net technologies. He is a contributor and a technical editor of publications PACKT Publishing and Wiley. Mihail did presentations for .Net and Silverlight user groups in Bulgaria. He has an Experience with GIS system over .Net framework. He worked more than five years in ESRI Bulgaria like a Software developer and a trainer. Several years Mihail did a lectures about Geographic Information Systems in the Sofia University “St. Kliment Ohridski” , Faculty of Mathematics and Informatics. Mihail is also a lecturer about Computer Systems in the University of the Architecture, Civil Engineering and Geodesy in Sofia at Computer Aided Engineering Department. Mihail holds master's degrees in Structural Engineering and Applied Mathematics and Informatics.