PageSourceSearch

https://algospot.com/static/js/jquery/jquery.visualize.js

js algospot.com collected 2026-10-04 19:36:21 UTC 25,781 bytes, 793 lines download raw bytes

1/**
2 * --------------------------------------------------------------------
3 * jQuery-Plugin "visualize"
4 * by Scott Jehl, [email protected]
5 * http://www.filamentgroup.com
6 * Copyright (c) 2009 Filament Group 
7 * Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses.
8 * 	
9 * --------------------------------------------------------------------
10 */
11(function($) { 
12$.fn.visualize = function(options, container){
13	return $(this).each(function(){
14		//configuration
15		var o = $.extend({
16			type: 'bar', //also available: area, pie, line
17			width: $(this).width(), //height of canvas - defaults to table height
18			height: $(this).height(), //height of canvas - defaults to table height
19			appendTitle: true, //table caption text is added to chart
20			title: null, //grabs from table caption if null
21			appendKey: true, //color key is added to chart
22			colors: ['#ae432e','#77ab13','#058dc7','#ef561a','#8d10ee','#5a3b16','#26a4ed','#f45a90','#e9e744'],
23			textColors: [], //corresponds with colors array. null/undefined items will fall back to CSS
24			parseDirection: 'x', //which direction to parse the table data
25			pieMargin: 10, //pie charts only - spacing around pie
26			pieLabelsAsPercent: true,
27			pieLabelPos: 'inside',
28			lineWeight: 4, //for line and area - stroke weight
29			lineDots: false, //also available: 'single', 'double'
30			dotInnerColor: "#ffffff", // only used for lineDots:'double'
31			lineMargin: (options.lineDots?15:0), //for line and area - spacing around lines
32			barGroupMargin: 10,
33			chartId: '',
34			xLabelParser: null, // function to parse labels as values
35			valueParser: null, // function to parse values. must return a Number
36			chartId: '',
37			chartClass: '',
38			barMargin: 1, //space around bars in bar chart (added to both sides of bar)
39			yLabelInterval: 30, //distance between y labels
40			interaction: false // only used for lineDots != false -- triggers mouseover and mouseout on original table
41		},options);
42		
43		//reset width, height to numbers
44		o.width = parseFloat(o.width);
45		o.height = parseFloat(o.height);
46		
47		// reset padding if graph is not lines
48		if(o.type != 'line' && o.type != 'area' ) {
49			o.lineMargin = 0;
50		}
51		
52		var self = $(this);
53		
54		// scrape data from html table
55		var tableData = {};
56		var colors = o.colors;
57		var textColors = o.textColors;
58		
59		
60		var parseLabels = function(direction){
61			var labels = [];
62			if(direction == 'x'){
63				self.find('thead tr').each(function(i){
64					$(this).find('th').each(function(j){
65						if(!labels[j]) {
66							labels[j] = [];
67						}
68						labels[j][i] = $(this).text()
69					})
70				});
71			}
72			else {
73				self.find('tbody tr').each(function(i){
74					$(this).find('th').each(function(j) {
75						if(!labels[i]) {
76							labels[i] = [];
77						}
78						labels[i][j] = $(this).text()
79					});
80				});
81			}
82			return labels;
83		};
84		
85		var fnParse = o.valueParser || parseFloat;
86		var dataGroups = tableData.dataGroups = [];
87		if(o.parseDirection == 'x'){
88			self.find('tbody tr').each(function(i,tr){
89				dataGroups[i] = {};
90				dataGroups[i].points = [];
91				dataGroups[i].color = colors[i];
92				if(textColors[i]){ dataGroups[i].textColor = textColors[i]; }
93				$(tr).find('td').each(function(j,td){
94					dataGroups[i].points.push( {
95						value: fnParse($(td).text()),
96						elem: td,
97						tableCords: [i,j]
98					} );
99				});
100			});
101		} else {
102			var cols = self.find('tbody tr:eq(0) td').size();
103			for(var i=0; i<cols; i++){
104				dataGroups[i] = {};
105				dataGroups[i].points = [];
106				dataGroups[i].color = colors[i];
107				if(textColors[i]){ dataGroups[i].textColor = textColors[i]; }
108				self.find('tbody tr').each(function(j){
109					dataGroups[i].points.push( {
110						value:  $(this).find('td').eq(i).text()*1,
111						elem: this,
112						tableCords: [i,j]
113					} );
114				});
115			};
116		}
117		
118	
119		var allItems = tableData.allItems = [];
120		$(dataGroups).each(function(i,row){
121			var count = 0;
122			$.each(row.points,function(j,point){
123				allItems.push(point);
124				count += point.value;
125			});
126			row.groupTotal = count;
127		});
128		
129		tableData.dataSum = 0;
130		tableData.topValue = 0;
131		tableData.bottomValue = Infinity;
132		$.each(allItems,function(i,item){
133			tableData.dataSum += fnParse(item.value);
134			if(fnParse(item.value,10)>tableData.topValue) {
135				tableData.topValue = fnParse(item.value,10);
136			}
137			if(item.value<tableData.bottomValue) {
138				tableData.bottomValue = fnParse(item.value);	
139			}
140		});
141		var dataSum = tableData.dataSum;
142		var topValue = tableData.topValue;
143		var bottomValue = tableData.bottomValue;
144		
145		var xAllLabels = tableData.xAllLabels = parseLabels(o.parseDirection);
146		var yAllLabels = tableData.yAllLabels = parseLabels(o.parseDirection==='x'?'y':'x');
147	
148		var xLabels = tableData.xLabels = [];
149		$.each(tableData.xAllLabels,function(i,labels) {
150			tableData.xLabels.push(labels[0]);
151		});
152		
153		var totalYRange = tableData.totalYRange = tableData.topValue - tableData.bottomValue;
154		
155		var zeroLocX = tableData.zeroLocX = 0;
156		
157		if($.isFunction(o.xLabelParser)) {
158			
159			var xTopValue = null;
160			var xBottomValue = null;
161
162			$.each(xLabels,function(i,label) {
163				label = xLabels[i] = o.xLabelParser(label);
164				if(i === 0) {
165					xTopValue = label;
166					xBottomValue = label;
167				}
168				if(label>xTopValue) {
169					xTopValue = label;
170				}
171				if(label<xBottomValue) {
172					xBottomValue = label;
173				}
174			});
175
176			var totalXRange = tableData.totalXRange = xTopValue - xBottomValue;
177
178			
179			var	xScale = tableData.xScale = (o.width -2*o.lineMargin) / totalXRange;
180			var marginDiffX = 0;
181			if(o.lineMargin) {
182				var marginDiffX = -2*xScale-o.lineMargin;
183			}
184			zeroLocX = tableData.zeroLocX = xBottomValue + o.lineMargin;
185			
186			tableData.xBottomValue = xBottomValue;
187			tableData.xTopValue = xTopValue;
188			tableData.totalXRange = totalXRange;
189		}
190		
191		var	yScale = tableData.yScale = (o.height - 2*o.lineMargin) / totalYRange;
192		var zeroLocY = tableData.zeroLocY = (o.height-2*o.lineMargin) * (tableData.topValue/tableData.totalYRange) + o.lineMargin;
193		
194		var yLabels = tableData.yLabels = [];
195
196		var numLabels = Math.floor((o.height - 2*o.lineMargin) / 30);
197		var loopInterval = tableData.totalYRange / numLabels; //fix provided from lab
198		loopInterval = Math.round(parseFloat(loopInterval)/5)*5;
199		loopInterval = Math.max(loopInterval, 1);
200		// var start = 
201		for(var j=Math.round(parseInt(tableData.bottomValue)/5)*5; j<=tableData.topValue+loopInterval; j+=loopInterval){
202			yLabels.push(j); 
203		}
204		if(yLabels[yLabels.length-1] > tableData.topValue+loopInterval) {
205			yLabels.pop();
206		} else if (yLabels[yLabels.length-1] <= tableData.topValue-10) {
207			yLabels.push(tableData.topValue);
208		}
209				
210		// populate some data
211		$.each(dataGroups,function(i,row){
212			row.yLabels = tableData.yAllLabels[i];
213			$.each(row.points, function(j,point){
214				point.zeroLocY = tableData.zeroLocY;
215				point.zeroLocX = tableData.zeroLocX;
216				point.xLabels = tableData.xAllLabels[j];
217				point.yLabels = tableData.yAllLabels[i];
218				point.color = row.color;
219			});
220		});
221		
222		try{console.log(tableData);}catch(e){}
223		
224		var charts = {};
225		
226		charts.pie = {
227			interactionPoints: dataGroups,
228			
229			setup: function() {
230				charts.pie.draw(true);
231			},
232			draw: function(drawHtml){	
233
234				var centerx = Math.round(canvas.width()/2);
235				var centery = Math.round(canvas.height()/2);
236				var radius = centery - o.pieMargin;				
237				var counter = 0.0;
238
239				if(drawHtml) {
240					canvasContain.addClass('visualize-pie');
241
242					if(o.pieLabelPos == 'outside'){ canvasContain.addClass('visualize-pie-outside'); }	
243
244					var toRad = function(integer){ return (Math.PI/180)*integer; };
245					var labels = $('<ul class="visualize-labels"></ul>')
246						.insertAfter(canvas);
247				}
248
249
250				//draw the pie pieces
251				$.each(dataGroups, function(i,row){
252					var fraction = row.groupTotal / dataSum;
253                    if (fraction <= 0 || isNaN(fraction))
254                        return;
255					ctx.beginPath();
256					ctx.moveTo(centerx, centery);
257					ctx.arc(centerx, centery, radius, 
258						counter * Math.PI * 2 - Math.PI * 0.5,
259						(counter + fraction) * Math.PI * 2 - Math.PI * 0.5,
260		                false);
261			        ctx.lineTo(centerx, centery);
262			        ctx.closePath();
263			        ctx.fillStyle = dataGroups[i].color;
264			        ctx.fill();
265			        // draw labels
266					if(drawHtml) {
267				       	var sliceMiddle = (counter + fraction/2);
268				       	var distance = o.pieLabelPos == 'inside' ? radius/1.5 : radius +  radius / 5;
269				        var labelx = Math.round(centerx + Math.sin(sliceMiddle * Math.PI * 2) * (distance));
270				        var labely = Math.round(centery - Math.cos(sliceMiddle * Math.PI * 2) * (distance));
271				        var leftRight = (labelx > centerx) ? 'right' : 'left';
272				        var topBottom = (labely > centery) ? 'bottom' : 'top';
273				        var percentage = parseFloat((fraction*100).toFixed(2));
274
275						// interaction variables
276						row.canvasCords = [labelx,labely];
277						row.zeroLocY = tableData.zeroLocY = 0; // related to zeroLocY and plugin API
278						row.zeroLocX = tableData.zeroLocX = 0; // related to zeroLocX and plugin API
279						row.value = row.groupTotal;
280
281
282				        if(percentage){
283				        	var labelval = (o.pieLabelsAsPercent) ? percentage + '%' : row.groupTotal;
284					        var labeltext = $('<span class="visualize-label">' + labelval +'</span>')
285					        	.css(leftRight, 0)
286					        	.css(topBottom, 0);
287					        	if(labeltext)
288				        var label = $('<li class="visualize-label-pos"></li>')
289				       			.appendTo(labels)
290				        		.css({left: labelx, top: labely})
291				        		.append(labeltext);	
292				        labeltext
293				        	.css('font-size', radius / 8)
294				        	.css('margin-'+leftRight, -labeltext.width()/2)
295				        	.css('margin-'+topBottom, -labeltext.outerHeight()/2);
296
297				        if(dataGroups[i].textColor){ labeltext.css('color', dataGroups[i].textColor); }
298
299				        }
300					}
301			      	counter+=fraction;
302				});
303			}
304		};
305		
306		(function(){
307			
308			var xInterval;
309
310			var drawPoint = function (ctx,x,y,color,size) {
311				ctx.moveTo(x,y);
312				ctx.beginPath();
313				ctx.arc(x,y,size/2,0,2*Math.PI,false);
314				ctx.closePath();
315				ctx.fillStyle = color;
316				ctx.fill();
317			};
318
319			charts.line = {
320				
321				interactionPoints: allItems,
322
323				setup: function(area){
324
325					if(area){ canvasContain.addClass('visualize-area'); }
326					else{ canvasContain.addClass('visualize-line'); }
327
328					//write X labels
329					var xlabelsUL = $('<ul class="visualize-labels-x"></ul>')
330						.width(canvas.width())
331						.height(canvas.height())
332						.insertBefore(canvas);
333
334					if(!o.customXLabels) {
335						xInterval = (canvas.width() - 2*o.lineMargin) / (xLabels.length -1);
336						$.each(xLabels, function(i){ 
337							var thisLi = $('<li><span>'+this+'</span></li>')
338								.prepend('<span class="line" />')
339								.css('left', o.lineMargin + xInterval * i)
340								.appendTo(xlabelsUL);						
341							var label = thisLi.find('span:not(.line)');
342							var leftOffset = label.width()/-2;
343							if(i == 0){ leftOffset = 0; }
344							else if(i== xLabels.length-1){ leftOffset = -label.width(); }
345							label
346								.css('margin-left', leftOffset)
347								.addClass('label');
348						});
349					} else {
350						o.customXLabels(tableData,xlabelsUL);
351					}
352
353					//write Y labels
354					var liBottom = (canvas.height() - 2*o.lineMargin) / (yLabels.length-1);
355					var ylabelsUL = $('<ul class="visualize-labels-y"></ul>')
356						.width(canvas.width())
357						.height(canvas.height())
358						// .css('margin-top',-o.lineMargin)
359						.insertBefore(scroller);
360
361					$.each(yLabels, function(i){
362						var value = Math.floor(this);
363						var posB = (value-bottomValue)*yScale + o.lineMargin;
364						if(posB >= o.height-1 || posB < 0) {
365							return;
366						}
367						var thisLi = $('<li><span>'+value+'</span></li>')
368							.css('bottom', posB);
369						if(Math.abs(posB) < o.height-1) {
370							thisLi.prepend('<span class="line"  />');
371						}
372						thisLi.prependTo(ylabelsUL);
373						
374						var label = thisLi.find('span:not(.line)');
375						var topOffset = label.height()/-2;
376						if(!o.lineMargin) {
377							if(i == 0){ topOffset = -label.height(); }
378							else if(i== yLabels.length-1){ topOffset = 0; }
379						}
380						label
381							.css('margin-top', topOffset)
382							.addClass('label');
383					});
384					
385					//start from the bottom left
386					ctx.translate(zeroLocX,zeroLocY);
387					
388					charts.line.draw(area);
389
390				},
391				
392				draw: function(area) {
393					// prevent drawing on top of previous draw
394					ctx.clearRect(-zeroLocX,-zeroLocY,o.width,o.height);
395					// Calculate each point properties before hand
396					var integer;
397					$.each(dataGroups,function(i,row){
398						integer = o.lineMargin; // the current offset
399						$.each(row.points, function(j,point){
400							if(o.xLabelParser) {
401								point.canvasCords = [(xLabels[j]-zeroLocX)*xScale - xBottomValue,-(point.value*yScale)];
402							} else {
403								point.canvasCords = [integer,-(point.value*yScale)];
404							}
405							
406							if(o.lineDots) {
407								point.dotSize = o.dotSize||o.lineWeight*Math.PI;
408								point.dotInnerSize = o.dotInnerSize||o.lineWeight*Math.PI/2;
409								if(o.lineDots == 'double') {
410									point.innerColor = o.dotInnerColor;
411								}
412							}
413							integer+=xInterval;
414						});
415					});
416					// fire custom event so we can enable rich interaction
417					self.trigger('vizualizeBeforeDraw',{options:o,table:self,canvasContain:canvasContain,tableData:tableData});
418					// draw lines and areas
419					$.each(dataGroups,function(h){
420						// Draw lines
421						ctx.beginPath();
422						ctx.lineWidth = o.lineWeight;
423						ctx.lineJoin = 'round';
424						$.each(this.points, function(g){
425							var loc = this.canvasCords;
426							if(g == 0) {
427								ctx.moveTo(loc[0],loc[1]);
428							}
429							ctx.lineTo(loc[0],loc[1]);
430						});
431						ctx.strokeStyle = this.color;
432						ctx.stroke();
433						// Draw fills
434						if(area){
435							var integer = this.points[this.points.length-1].canvasCords[0];
436							if (isFinite(integer))
437								ctx.lineTo(integer,0);
438							ctx.lineTo(o.lineMargin,0);
439							ctx.closePath();
440							ctx.fillStyle = this.color;
441							ctx.globalAlpha = .3;
442							ctx.fill();
443							ctx.globalAlpha = 1.0;
444						}
445						else {ctx.closePath();}
446					});
447					// draw points
448					if(o.lineDots) {
449						$.each(dataGroups,function(h){
450							$.each(this.points, function(g){
451								drawPoint(ctx,this.canvasCords[0],this.canvasCords[1],this.color,this.dotSize);
452								if(o.lineDots === 'double') {
453									drawPoint(ctx,this.canvasCords[0],this.canvasCords[1],this.innerColor,this.dotInnerSize);
454								}
455							});
456						});
457					}
458					
459				}
460			};
461		
462		})();
463		
464		charts.area = {
465			setup: function() {
466				charts.line.setup(true);
467			},
468			draw: charts.line.draw
469		};
470		
471		(function(){
472
473			var horizontal,bottomLabels;
474
475			charts.bar = {
476				setup:function(){
477					/**
478					 * We can draw horizontal or vertical bars depending on the
479					 * value of the 'barDirection' option (which may be 'vertical' or
480					 * 'horizontal').
481					 */
482
483					horizontal = (o.barDirection == 'horizontal');
484
485					canvasContain.addClass('visualize-bar');
486
487					/**
488					 * Write labels along the bottom of the chart.	If we're drawing
489					 * horizontal bars, these will be the yLabels, otherwise they
490					 * will be the xLabels.	The positioning also varies slightly:
491					 * yLabels are values, hence they will span the whole width of
492					 * the canvas, whereas xLabels are supposed to line up with the
493					 * bars.
494					 */
495					bottomLabels = horizontal ? yLabels : xLabels;
496
497					var xInterval = canvas.width() / (bottomLabels.length - (horizontal ? 1 : 0));
498
499					var xlabelsUL = $('<ul class="visualize-labels-x"></ul>')
500						.width(canvas.width())
501						.height(canvas.height())
502						.insertBefore(canvas);
503
504					$.each(bottomLabels, function(i){
505						var thisLi = $('<li><span class="label">'+this+'</span></li>')
506							.prepend('<span class="line" />')
507							.css('left', xInterval * i)
508							.width(xInterval)
509							.appendTo(xlabelsUL);
510
511						if (horizontal)	{
512							var label = thisLi.find('span.label');
513							label.css("margin-left", -label.width() / 2);
514						}
515					});
516
517					/**
518					 * Write labels along the left of the chart.	Follows the same idea
519					 * as the bottom labels.
520					 */
521					var leftLabels = horizontal ? xLabels : yLabels;
522					var liBottom = canvas.height() / (leftLabels.length - (horizontal ? 0 : 1));
523
524					var ylabelsUL = $('<ul class="visualize-labels-y"></ul>')
525						.width(canvas.width())
526						.height(canvas.height())
527						.insertBefore(canvas);
528
529					$.each(leftLabels, function(i){
530						var thisLi = $('<li><span>'+this+'</span></li>').prependTo(ylabelsUL);
531
532						var label = thisLi.find('span:not(.line)').addClass('label');
533
534						if (horizontal) {
535							/**
536							 * For left labels, we want to vertically align the text
537							 * to the middle of its container, but we don't know how
538							 * many lines of text we will have, since the labels could
539							 * be very long.
540							 *
541							 * So we set a min-height of liBottom, and a max-height
542							 * of liBottom + 1, so we can then check the label's actual
543							 * height to determine if it spans one line or more lines.
544							 */
545							label.css({
546								'min-height': liBottom,
547								'max-height': liBottom + 1,
548								'vertical-align': 'middle'
549							});
550							thisLi.css({'top': liBottom * i, 'min-height': liBottom});
551
552							var r = label[0].getClientRects()[0];
553							if (r.bottom - r.top == liBottom) {
554								/* This means we have only one line of text; hence
555								 * we can centre the text vertically by setting the line-height,
556								 * as described at:
557								 *   http://www.ampsoft.net/webdesign-l/vertical-aligned-nav-list.html
558								 *
559								 * (Although firefox has .height on the rectangle, IE doesn't,
560								 * so we use r.bottom - r.top rather than r.height.)
561								 */
562								label.css('line-height', parseInt(liBottom) + 'px');
563							}
564							else {
565								/*
566								 * If there is more than one line of text, then we shouldn't
567								 * touch the line height, but we should make sure the text
568								 * doesn't overflow the container.
569								 */
570								label.css("overflow", "hidden");
571							}
572						}
573						else {
574							thisLi.css('bottom', liBottom * i).prepend('<span class="line" />');
575							label.css('margin-top', -label.height() / 2)
576						}
577					});
578
579					charts.bar.draw();
580
581				},
582
583				draw: function() {
584					// Draw bars
585
586					if (horizontal) {
587						// for horizontal, keep the same code, but rotate everything 90 degrees
588						// clockwise.
589						ctx.rotate(Math.PI / 2);
590					}
591					else {
592						// for vertical, translate to the top left corner.
593						ctx.translate(0, zeroLocY);
594					}
595
596					// Don't attempt to draw anything if all the values are zero,
597					// otherwise we will get weird exceptions from the canvas methods.
598					if (totalYRange <= 0)
599						return;
600
601					var yScale = (horizontal ? canvas.width() : canvas.height()) / totalYRange;
602					var barWidth = horizontal ? (canvas.height() / xLabels.length) : (canvas.width() / (bottomLabels.length));
603					var linewidth = (barWidth - o.barGroupMargin*2) / dataGroups.length;
604
605					for(var h=0; h<dataGroups.length; h++){
606						ctx.beginPath();
607
608						var strokeWidth = linewidth - (o.barMargin*2);
609						ctx.lineWidth = strokeWidth;
610						var points = dataGroups[h].points;
611						var integer = 0;
612						for(var i=0; i<points.length; i++){
613							// If the last value is zero, IE will go nuts and not draw anything,
614							// so don't try to draw zero values at all.
615							if (points[i].value != 0) {
616								var xVal = (integer-o.barGroupMargin)+(h*linewidth)+linewidth/2;
617								xVal += o.barGroupMargin*2;
618
619								ctx.moveTo(xVal, 0);
620								ctx.lineTo(xVal, Math.round(-points[i].value*yScale));
621	                        }
622							integer+=barWidth;
623						}
624						ctx.strokeStyle = dataGroups[h].color;
625						ctx.stroke();
626						ctx.closePath();
627					}
628
629				}
630			};
631			
632		})();
633	
634		//create new canvas, set w&h attrs (not inline styles)
635		var canvasNode = document.createElement("canvas"); 
636		var canvas = $(canvasNode)
637			.attr({
638				'height': o.height,
639				'width': o.width
640			});
641		
642		//get title for chart
643		var title = o.title || self.find('caption').text();
644		
645		//create canvas wrapper div, set inline w&h, append
646		var canvasContain = (container || $('<div '+(o.chartId?'id="'+o.chartId+'" ':'')+'class="visualize '+o.chartClass+'" role="img" aria-label="Chart representing data from the table: '+ title +'" />'))
647			.height(o.height)
648			.width(o.width);
649		
650		var scroller = $('<div class="visualize-scroller"></div>')
651			.appendTo(canvasContain)
652			.append(canvas);
653
654		//title/key container
655		if(o.appendTitle || o.appendKey){
656			var infoContain = $('<div class="visualize-info"></div>')
657				.appendTo(canvasContain);
658		}
659		
660		//append title
661		if(o.appendTitle){
662			$('<div class="visualize-title">'+ title +'</div>').appendTo(infoContain);
663		}
664		
665		
666		//append key
667		if(o.appendKey){
668			var newKey = $('<ul class="visualize-key"></ul>');
669			$.each(yAllLabels, function(i,label){
670				$('<li><span class="visualize-key-color" style="background: '+dataGroups[i].color+'"></span><span class="visualize-key-label">'+ label +'</span></li>')
671					.appendTo(newKey);
672			});
673			newKey.appendTo(infoContain);
674		};		
675		
676		// init interaction
677		if(o.interaction) {
678			// sets the canvas to track interaction
679			// IE needs one div on top of the canvas since the VML shapes prevent mousemove from triggering correctly.
680			// Pie charts needs tracker because labels goes on top of the canvas and also messes up with mousemove
681			var tracker = $('<div class="visualize-interaction-tracker"/>')
682				.css({
683					'height': o.height + 'px',
684					'width': o.width + 'px',
685					'position':'relative',
686					'z-index': 200
687				})
688				.insertAfter(canvas);
689
690			var triggerInteraction = function(overOut,data) {
691				var data = $.extend({
692					canvasContain:canvasContain,
693					tableData:tableData
694				},data);
695				self.trigger('vizualize'+overOut,data);
696			};
697
698			var over=false, last=false, started=false;
699			tracker.mousemove(function(e){
700				var x,y,x1,y1,data,dist,i,current,selector,zLabel,elem,color,minDist,found,ev=e.originalEvent;
701
702				// get mouse position relative to the tracker/canvas
703				x = ev.layerX || ev.offsetX || 0;
704				y = ev.layerY || ev.offsetY || 0;
705
706				found = false;
707				minDist = started?30000:(o.type=='pie'?(Math.round(canvas.height()/2)-o.pieMargin)/3:o.lineWeight*4);
708				// iterate datagroups to find points with matching
709				$.each(charts[o.type].interactionPoints,function(i,current){
710					x1 = current.canvasCords[0] + zeroLocX;
711					y1 = current.canvasCords[1] + (o.type=="pie"?0:zeroLocY);
712					dist = Math.sqrt( (x1 - x)*(x1 - x) + (y1 - y)*(y1 - y) );
713					if(dist < minDist) {
714						found = current;
715						minDist = dist;
716					}
717				});
718				
719				if(o.multiHover && found) {
720					x = found.canvasCords[0] + zeroLocX;
721					y = found.canvasCords[1] + (o.type=="pie"?0:zeroLocY);
722					found = [found];
723					$.each(charts[o.type].interactionPoints,function(i,current){
724						if(current == found[0]) {return;}
725						x1 = current.canvasCords[0] + zeroLocX;
726						y1 = current.canvasCords[1] + zeroLocY;
727						dist = Math.sqrt( (x1 - x)*(x1 - x) + (y1 - y)*(y1 - y) );
728						if(dist <= o.multiHover) {
729							found.push(current);
730						}
731					});
732				}
733				// trigger over and out only when state changes, instead of on every mousemove
734				over = found;
735				if(over != last) {
736					if(over) {
737						if(last) {
738							triggerInteraction('Out',{point:last});
739						}
740						triggerInteraction('Over',{point:over});
741						last = over;
742					}
743					if(last && !over) {
744						triggerInteraction('Out',{point:last});
745						last=false;
746					}
747					started=true;
748				}
749			});
750			tracker.mouseleave(function(){
751				triggerInteraction('Out',{
752					point:last,
753					mouseOutGraph:true
754				});
755				over = (last = false);
756			});
757		}
758		
759		//append new canvas to page
760		if(!container){canvasContain.insertAfter(this); }
761		if( typeof(G_vmlCanvasManager) != 'undefined' ){ G_vmlCanvasManager.init(); G_vmlCanvasManager.initElement(canvas[0]); }	
762		
763		//set up the drawing board
764		var ctx = canvas[0].getContext('2d');
765		
766		// Scroll graphs
767		scroller.scrollLeft(o.width-scroller.width());
768
769		// init plugins
770		$.each($.visualizePlugins,function(i,plugin){
771			plugin.call(self,o,tableData);
772		});
773
774		//create chart
775		charts[o.type].setup();
776		
777		if(!container){
778			//add event for updating
779			self.bind('visualizeRefresh', function(){
780				self.visualize(o, $(this).empty()); 
781			});
782			//add event for redraw
783			self.bind('visualizeRedraw', function(){
784				charts[o.type].draw();
785			});
786		}
787	}).next(); //returns canvas(es)
788};
789// create array for plugins. if you wish to make a plugin,
790// just push your init funcion into this array
791$.visualizePlugins = [];
792
793})(jQuery);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.