From d9886d8dca3be3bbf0f57b0b32438eccf684269f Mon Sep 17 00:00:00 2001
From: Sebastian Grewe
Date: Thu, 13 Jun 2013 22:00:28 +0200
Subject: [PATCH] re-adding tooltips for graphs
---
.../mmcFE/js/jquery.tooltip.visualize.js | 106 ++
.../site_assets/mmcFE/js/jquery.visualize.js | 1601 ++++++++---------
2 files changed, 901 insertions(+), 806 deletions(-)
create mode 100644 public/site_assets/mmcFE/js/jquery.tooltip.visualize.js
diff --git a/public/site_assets/mmcFE/js/jquery.tooltip.visualize.js b/public/site_assets/mmcFE/js/jquery.tooltip.visualize.js
new file mode 100644
index 00000000..f6f71fd9
--- /dev/null
+++ b/public/site_assets/mmcFE/js/jquery.tooltip.visualize.js
@@ -0,0 +1,106 @@
+/**
+ * --------------------------------------------------------------------
+ * Tooltip plugin for the jQuery-Plugin "Visualize"
+ * Tolltip by Iraê Carvalho, irae@irae.pro.br, http://irae.pro.br/en/
+ * Copyright (c) 2010 Iraê Carvalho
+ * Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses.
+ *
+ * Visualize plugin by Scott Jehl, scott@filamentgroup.com
+ * Copyright (c) 2009 Filament Group, http://www.filamentgroup.com
+ *
+ * --------------------------------------------------------------------
+ */
+
+(function($){
+ $.visualizePlugins.push(function visualizeTooltip(options,tableData) {
+ //configuration
+ var o = $.extend({
+ tooltip: false,
+ tooltipalign: 'auto', // also available 'left' and 'right'
+ tooltipvalign: 'top',
+ tooltipclass: 'visualize-tooltip',
+ tooltiphtml: function(data){
+ if(options.multiHover) {
+ var html='';
+ for(var i=0;i'+data.point[i].value+' - '+data.point[i].yLabels[0]+'
';
+ }
+ return html;
+ } else {
+ return ''+data.point.value+' - '+data.point.yLabels[0]+'
';
+ }
+ },
+ delay:false
+ },options);
+
+ // don't go any further if we are not to show anything
+ if(!o.tooltip) {return;}
+
+ var self = $(this),
+ canvasContain = self.next(),
+ scroller = canvasContain.find('.visualize-scroller'),
+ scrollerW = scroller.width(),
+ tracker = canvasContain.find('.visualize-interaction-tracker');
+
+ // IE needs background color and opacity white or the tracker stays behind the tooltip
+ tracker.css({
+ backgroundColor:'white',
+ opacity:0,
+ zIndex:100
+ });
+
+ var tooltip = $('').css({
+ position:'absolute',
+ display:'none',
+ zIndex:90
+ })
+ .insertAfter(scroller.find('canvas'));
+
+ var usescroll = true;
+
+ if( typeof(G_vmlCanvasManager) != 'undefined' ){
+ scroller.css({'position':'absolute'});
+ tracker.css({marginTop:'-'+(o.height)+'px'});
+ }
+
+
+ self.bind('vizualizeOver',function visualizeTooltipOver(e,data){
+ if(data.canvasContain.get(0) != canvasContain.get(0)) {return;} // for multiple graphs originated from same table
+ if(o.multiHover) {
+ var p = data.point[0].canvasCords;
+ } else {
+ var p = data.point.canvasCords;
+ }
+ var left,right,top,clasRem,clasAd,bottom,x=Math.round(p[0]+data.tableData.zeroLocX),y=Math.round(p[1]+data.tableData.zeroLocY);
+ if(o.tooltipalign == 'left' || ( o.tooltipalign=='auto' && x-scroller.scrollLeft()<=scrollerW/2 ) ) {
+ if($.browser.msie && ($.browser.version == 7 || $.browser.version == 6) ) {usescroll=false;} else {usescroll=true;}
+ left = (x-(usescroll?scroller.scrollLeft():0))+'px';
+ right = '';
+ clasAdd="tooltipleft";
+ clasRem="tooltipright";
+ } else {
+ if($.browser.msie && $.browser.version == 7) {usescroll=false;} else {usescroll=true;}
+ left = '';
+ right = (Math.abs(x-o.width)- (o.width-(usescroll?scroller.scrollLeft():0)-scrollerW) )+'px';
+ clasAdd="tooltipright";
+ clasRem="tooltipleft";
+ }
+
+ tooltip
+ .addClass(clasAdd)
+ .removeClass(clasRem)
+ .html(o.tooltiphtml(data))
+ .css({
+ display:'block',
+ top: y+'px',
+ left: left,
+ right: right
+ });
+ });
+
+ self.bind('vizualizeOut',function visualizeTooltipOut(e,data){
+ tooltip.css({display:'none'});
+ });
+
+ });
+})(jQuery);
\ No newline at end of file
diff --git a/public/site_assets/mmcFE/js/jquery.visualize.js b/public/site_assets/mmcFE/js/jquery.visualize.js
index 3daf800a..1043aaca 100644
--- a/public/site_assets/mmcFE/js/jquery.visualize.js
+++ b/public/site_assets/mmcFE/js/jquery.visualize.js
@@ -1,806 +1,795 @@
-/**
- * --------------------------------------------------------------------
- * jQuery-Plugin "visualize"
- * by Scott Jehl, scott@filamentgroup.com
- * http://www.filamentgroup.com
- * Copyright (c) 2009 Filament Group
- * Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses.
- *
- * --------------------------------------------------------------------
- */
-(function ($) {
- $.fn.visualize = function (options, container) {
- return $(this).each(function () {
- //configuration
- var o = $.extend({
- type: 'bar',
- //also available: area, pie, line
- width: $(this).width(),
- //height of canvas - defaults to table height
- height: $(this).height(),
- //height of canvas - defaults to table height
- appendTitle: true,
- //table caption text is added to chart
- title: null,
- //grabs from table caption if null
- appendKey: true,
- //color key is added to chart
- colors: ['#be1e2d', '#666699', '#92d5ea', '#ee8310', '#8d10ee', '#5a3b16', '#26a4ed', '#f45a90', '#e9e744'],
- textColors: [],
- //corresponds with colors array. null/undefined items will fall back to CSS
- parseDirection: 'x',
- //which direction to parse the table data
- pieMargin: 10,
- //pie charts only - spacing around pie
- pieLabelsAsPercent: true,
- pieLabelPos: 'inside',
- lineWeight: 4,
- //for line and area - stroke weight
- lineDots: false,
- //also available: 'single', 'double'
- dotInnerColor: "#ffffff",
- // only used for lineDots:'double'
- lineMargin: (options.lineDots ? 15 : 0),
- //for line and area - spacing around lines
- barGroupMargin: 10,
- chartId: '',
- xLabelParser: null,
- // function to parse labels as values
- valueParser: null,
- // function to parse values. must return a Number
- chartId: '',
- chartClass: '',
- barMargin: 1,
- //space around bars in bar chart (added to both sides of bar)
- yLabelInterval: 30,
- //distance between y labels
- interaction: false // only used for lineDots != false -- triggers mouseover and mouseout on original table
- }, options);
-
- //reset width, height to numbers
- o.width = parseFloat(o.width);
- o.height = parseFloat(o.height);
-
- // reset padding if graph is not lines
- if (o.type != 'line' && o.type != 'area') {
- o.lineMargin = 0;
- }
-
- var self = $(this);
-
- // scrape data from html table
- var tableData = {};
- var colors = o.colors;
- var textColors = o.textColors;
-
-
- var parseLabels = function (direction) {
- var labels = [];
- if (direction == 'x') {
- self.find('thead tr').each(function (i) {
- $(this).find('th').each(function (j) {
- if (!labels[j]) {
- labels[j] = [];
- }
- labels[j][i] = $(this).text()
- })
- });
- } else {
- self.find('tbody tr').each(function (i) {
- $(this).find('th').each(function (j) {
- if (!labels[i]) {
- labels[i] = [];
- }
- labels[i][j] = $(this).text()
- });
- });
- }
- return labels;
- };
-
- var fnParse = o.valueParser || parseFloat;
- var dataGroups = tableData.dataGroups = [];
- if (o.parseDirection == 'x') {
- self.find('tbody tr').each(function (i, tr) {
- dataGroups[i] = {};
- dataGroups[i].points = [];
- dataGroups[i].color = colors[i];
- if (textColors[i]) {
- dataGroups[i].textColor = textColors[i];
- }
- $(tr).find('td').each(function (j, td) {
- dataGroups[i].points.push({
- value: fnParse($(td).text()),
- elem: td,
- tableCords: [i, j]
- });
- });
- });
- } else {
- var cols = self.find('tbody tr:eq(0) td').size();
- for (var i = 0; i < cols; i++) {
- dataGroups[i] = {};
- dataGroups[i].points = [];
- dataGroups[i].color = colors[i];
- if (textColors[i]) {
- dataGroups[i].textColor = textColors[i];
- }
- self.find('tbody tr').each(function (j) {
- dataGroups[i].points.push({
- value: $(this).find('td').eq(i).text() * 1,
- elem: this,
- tableCords: [i, j]
- });
- });
- };
- }
-
-
- var allItems = tableData.allItems = [];
- $(dataGroups).each(function (i, row) {
- var count = 0;
- $.each(row.points, function (j, point) {
- allItems.push(point);
- count += point.value;
- });
- row.groupTotal = count;
- });
-
- tableData.dataSum = 0;
- tableData.topValue = 0;
- tableData.bottomValue = Infinity;
- $.each(allItems, function (i, item) {
- tableData.dataSum += fnParse(item.value);
- if (fnParse(item.value, 10) > tableData.topValue) {
- tableData.topValue = fnParse(item.value, 10);
- }
- if (item.value < tableData.bottomValue) {
- tableData.bottomValue = fnParse(item.value);
- }
- });
- var dataSum = tableData.dataSum;
- var topValue = tableData.topValue;
- var bottomValue = tableData.bottomValue;
-
- var xAllLabels = tableData.xAllLabels = parseLabels(o.parseDirection);
- var yAllLabels = tableData.yAllLabels = parseLabels(o.parseDirection === 'x' ? 'y' : 'x');
-
- var xLabels = tableData.xLabels = [];
- $.each(tableData.xAllLabels, function (i, labels) {
- tableData.xLabels.push(labels[0]);
- });
-
- var totalYRange = tableData.totalYRange = tableData.topValue - tableData.bottomValue;
-
- var zeroLocX = tableData.zeroLocX = 0;
-
- if ($.isFunction(o.xLabelParser)) {
-
- var xTopValue = null;
- var xBottomValue = null;
-
- $.each(xLabels, function (i, label) {
- label = xLabels[i] = o.xLabelParser(label);
- if (i === 0) {
- xTopValue = label;
- xBottomValue = label;
- }
- if (label > xTopValue) {
- xTopValue = label;
- }
- if (label < xBottomValue) {
- xBottomValue = label;
- }
- });
-
- var totalXRange = tableData.totalXRange = xTopValue - xBottomValue;
-
-
- var xScale = tableData.xScale = (o.width - 2 * o.lineMargin) / totalXRange;
- var marginDiffX = 0;
- if (o.lineMargin) {
- var marginDiffX = -2 * xScale - o.lineMargin;
- }
- zeroLocX = tableData.zeroLocX = xBottomValue + o.lineMargin;
-
- tableData.xBottomValue = xBottomValue;
- tableData.xTopValue = xTopValue;
- tableData.totalXRange = totalXRange;
- }
-
- var yScale = tableData.yScale = (o.height - 2 * o.lineMargin) / totalYRange;
- var zeroLocY = tableData.zeroLocY = (o.height - 2 * o.lineMargin) * (tableData.topValue / tableData.totalYRange) + o.lineMargin;
-
- var yLabels = tableData.yLabels = [];
-
- var numLabels = Math.floor((o.height - 2 * o.lineMargin) / 30);
-
- var loopInterval = tableData.totalYRange / numLabels; //fix provided from lab
- loopInterval = Math.round(parseFloat(loopInterval) / 5) * 5;
- loopInterval = Math.max(loopInterval, 1);
-
- // var start =
- for (var j = Math.round(parseInt(tableData.bottomValue) / 5) * 5; j <= tableData.topValue - loopInterval; j += loopInterval) {
- yLabels.push(j);
- }
- if (yLabels[yLabels.length - 1] > tableData.topValue + loopInterval) {
- yLabels.pop();
- } else if (yLabels[yLabels.length - 1] <= tableData.topValue - 10) {
- yLabels.push(tableData.topValue);
- }
-
- // populate some data
- $.each(dataGroups, function (i, row) {
- row.yLabels = tableData.yAllLabels[i];
- $.each(row.points, function (j, point) {
- point.zeroLocY = tableData.zeroLocY;
- point.zeroLocX = tableData.zeroLocX;
- point.xLabels = tableData.xAllLabels[j];
- point.yLabels = tableData.yAllLabels[i];
- point.color = row.color;
- });
- });
-
- try {
- console.log(tableData);
- } catch (e) {}
-
- var charts = {};
-
- charts.pie = {
- interactionPoints: dataGroups,
-
- setup: function () {
- charts.pie.draw(true);
- },
- draw: function (drawHtml) {
-
- var centerx = Math.round(canvas.width() / 2);
- var centery = Math.round(canvas.height() / 2);
- var radius = centery - o.pieMargin;
- var counter = 0.0;
-
- if (drawHtml) {
- canvasContain.addClass('visualize-pie');
-
- if (o.pieLabelPos == 'outside') {
- canvasContain.addClass('visualize-pie-outside');
- }
-
- var toRad = function (integer) {
- return (Math.PI / 180) * integer;
- };
- var labels = $('').insertAfter(canvas);
- }
-
-
- //draw the pie pieces
- $.each(dataGroups, function (i, row) {
- var fraction = row.groupTotal / dataSum;
- if (fraction <= 0 || isNaN(fraction)) return;
- ctx.beginPath();
- ctx.moveTo(centerx, centery);
- ctx.arc(centerx, centery, radius, counter * Math.PI * 2 - Math.PI * 0.5, (counter + fraction) * Math.PI * 2 - Math.PI * 0.5, false);
- ctx.lineTo(centerx, centery);
- ctx.closePath();
- ctx.fillStyle = dataGroups[i].color;
- ctx.fill();
- // draw labels
- if (drawHtml) {
- var sliceMiddle = (counter + fraction / 2);
- var distance = o.pieLabelPos == 'inside' ? radius / 1.5 : radius + radius / 5;
- var labelx = Math.round(centerx + Math.sin(sliceMiddle * Math.PI * 2) * (distance));
- var labely = Math.round(centery - Math.cos(sliceMiddle * Math.PI * 2) * (distance));
- var leftRight = (labelx > centerx) ? 'right' : 'left';
- var topBottom = (labely > centery) ? 'bottom' : 'top';
- var percentage = parseFloat((fraction * 100).toFixed(2));
-
- // interaction variables
- row.canvasCords = [labelx, labely];
- row.zeroLocY = tableData.zeroLocY = 0; // related to zeroLocY and plugin API
- row.zeroLocX = tableData.zeroLocX = 0; // related to zeroLocX and plugin API
- row.value = row.groupTotal;
-
-
- if (percentage) {
- var labelval = (o.pieLabelsAsPercent) ? percentage + '%' : row.groupTotal;
- var labeltext = $('' + labelval + '').css(leftRight, 0).css(topBottom, 0);
- if (labeltext) var label = $('').appendTo(labels).css({
- left: labelx,
- top: labely
- }).append(labeltext);
- labeltext.css('font-size', radius / 8).css('margin-' + leftRight, -labeltext.width() / 2).css('margin-' + topBottom, -labeltext.outerHeight() / 2);
-
- if (dataGroups[i].textColor) {
- labeltext.css('color', dataGroups[i].textColor);
- }
-
- }
- }
- counter += fraction;
- });
- }
- };
-
- (function () {
-
- var xInterval;
-
- var drawPoint = function (ctx, x, y, color, size) {
- ctx.moveTo(x, y);
- ctx.beginPath();
- ctx.arc(x, y, size / 2, 0, 2 * Math.PI, false);
- ctx.closePath();
- ctx.fillStyle = color;
- ctx.fill();
- };
-
- charts.line = {
-
- interactionPoints: allItems,
-
- setup: function (area) {
-
- if (area) {
- canvasContain.addClass('visualize-area');
- } else {
- canvasContain.addClass('visualize-line');
- }
-
- //write X labels
- var xlabelsUL = $('').width(canvas.width()).height(canvas.height()).insertBefore(canvas);
-
- if (!o.customXLabels) {
- xInterval = (canvas.width() - 2 * o.lineMargin) / (xLabels.length - 1);
- $.each(xLabels, function (i) {
- var thisLi = $('' + this + '').prepend('').css('left', o.lineMargin + xInterval * i).appendTo(xlabelsUL);
- var label = thisLi.find('span:not(.line)');
- var leftOffset = label.width() / -2;
- if (i == 0) {
- leftOffset = -20;
- } else if (i == xLabels.length - 1) {
- leftOffset = -label.width() + 20;
- }
- label.css('margin-left', leftOffset).addClass('label');
- });
- } else {
- o.customXLabels(tableData, xlabelsUL);
- }
-
- //write Y labels
- var liBottom = (canvas.height() - 2 * o.lineMargin) / (yLabels.length - 1);
- var ylabelsUL = $('').width(canvas.width()).height(canvas.height())
- // .css('margin-top',-o.lineMargin)
- .insertBefore(scroller);
-
- $.each(yLabels, function (i) {
- var value = Math.floor(this);
- var posB = (value - bottomValue) * yScale + o.lineMargin;
- if (posB >= o.height - 1 || posB < 0) {
- return;
- }
- var thisLi = $('' + value + '').css('bottom', posB);
- if (Math.abs(posB) < o.height - 1) {
- thisLi.prepend('');
- }
- thisLi.prependTo(ylabelsUL);
-
- var label = thisLi.find('span:not(.line)');
- var topOffset = label.height() / -2;
- if (!o.lineMargin) {
- if (i == 0) {
- topOffset = -label.height();
- } else if (i == yLabels.length - 1) {
- topOffset = 0;
- }
- }
- label.css('margin-top', topOffset).addClass('label');
- });
-
- //start from the bottom left
- ctx.translate(zeroLocX, zeroLocY);
-
- charts.line.draw(area);
-
- },
-
- draw: function (area) {
- // prevent drawing on top of previous draw
- ctx.clearRect(-zeroLocX, -zeroLocY, o.width, o.height);
- // Calculate each point properties before hand
- var integer;
- $.each(dataGroups, function (i, row) {
- integer = o.lineMargin; // the current offset
- $.each(row.points, function (j, point) {
- if (o.xLabelParser) {
- point.canvasCords = [(xLabels[j] - zeroLocX) * xScale - xBottomValue, -(point.value * yScale)];
- } else {
- point.canvasCords = [integer, -(point.value * yScale)];
- }
-
- if (o.lineDots) {
- point.dotSize = o.dotSize || o.lineWeight * Math.PI;
- point.dotInnerSize = o.dotInnerSize || o.lineWeight * Math.PI / 2;
- if (o.lineDots == 'double') {
- point.innerColor = o.dotInnerColor;
- }
- }
- integer += xInterval;
- });
- });
- // fire custom event so we can enable rich interaction
- self.trigger('vizualizeBeforeDraw', {
- options: o,
- table: self,
- canvasContain: canvasContain,
- tableData: tableData
- });
- // draw lines and areas
- $.each(dataGroups, function (h) {
- // Draw lines
- ctx.beginPath();
- ctx.lineWidth = o.lineWeight;
- ctx.lineJoin = 'round';
- $.each(this.points, function (g) {
- var loc = this.canvasCords;
- if (g == 0) {
- ctx.moveTo(loc[0], loc[1]);
- }
- ctx.lineTo(loc[0], loc[1]);
- });
- ctx.strokeStyle = this.color;
- ctx.stroke();
- // Draw fills
- if (area) {
- var integer = this.points[this.points.length - 1].canvasCords[0];
- if (isFinite(integer)) ctx.lineTo(integer, 0);
- ctx.lineTo(o.lineMargin, 0);
- ctx.closePath();
- ctx.fillStyle = this.color;
- ctx.globalAlpha = .3;
- ctx.fill();
- ctx.globalAlpha = 1.0;
- } else {
- ctx.closePath();
- }
- });
- // draw points
- if (o.lineDots) {
- $.each(dataGroups, function (h) {
- $.each(this.points, function (g) {
- drawPoint(ctx, this.canvasCords[0], this.canvasCords[1], this.color, this.dotSize);
- if (o.lineDots === 'double') {
- drawPoint(ctx, this.canvasCords[0], this.canvasCords[1], this.innerColor, this.dotInnerSize);
- }
- });
- });
- }
-
- }
- };
-
- })();
-
- charts.area = {
- setup: function () {
- charts.line.setup(true);
- },
- draw: charts.line.draw
- };
-
- (function () {
-
- var horizontal, bottomLabels;
-
- charts.bar = {
- setup: function () {
- /**
- * We can draw horizontal or vertical bars depending on the
- * value of the 'barDirection' option (which may be 'vertical' or
- * 'horizontal').
- */
-
- horizontal = (o.barDirection == 'horizontal');
-
- canvasContain.addClass('visualize-bar');
-
- /**
- * Write labels along the bottom of the chart. If we're drawing
- * horizontal bars, these will be the yLabels, otherwise they
- * will be the xLabels. The positioning also varies slightly:
- * yLabels are values, hence they will span the whole width of
- * the canvas, whereas xLabels are supposed to line up with the
- * bars.
- */
- bottomLabels = horizontal ? yLabels : xLabels;
-
- var xInterval = canvas.width() / (bottomLabels.length - (horizontal ? 1 : 0));
-
- var xlabelsUL = $('').width(canvas.width()).height(canvas.height()).insertBefore(canvas);
-
- $.each(bottomLabels, function (i) {
- var thisLi = $('' + this + '').prepend('').css('left', xInterval * i).width(xInterval).appendTo(xlabelsUL);
-
- if (horizontal) {
- var label = thisLi.find('span.label');
- label.css("margin-left", -label.width() / 2);
- }
- });
-
- /**
- * Write labels along the left of the chart. Follows the same idea
- * as the bottom labels.
- */
- var leftLabels = horizontal ? xLabels : yLabels;
- var liBottom = canvas.height() / (leftLabels.length - (horizontal ? 0 : 1));
-
- var ylabelsUL = $('').width(canvas.width()).height(canvas.height()).insertBefore(canvas);
-
- $.each(leftLabels, function (i) {
- var thisLi = $('' + this + '').prependTo(ylabelsUL);
-
- var label = thisLi.find('span:not(.line)').addClass('label');
-
- if (horizontal) {
- /**
- * For left labels, we want to vertically align the text
- * to the middle of its container, but we don't know how
- * many lines of text we will have, since the labels could
- * be very long.
- *
- * So we set a min-height of liBottom, and a max-height
- * of liBottom + 1, so we can then check the label's actual
- * height to determine if it spans one line or more lines.
- */
- label.css({
- 'min-height': liBottom,
- 'max-height': liBottom + 1,
- 'vertical-align': 'middle'
- });
- thisLi.css({
- 'top': liBottom * i,
- 'min-height': liBottom
- });
-
- var r = label[0].getClientRects()[0];
- if (r.bottom - r.top == liBottom) {
-/* This means we have only one line of text; hence
- * we can centre the text vertically by setting the line-height,
- * as described at:
- * http://www.ampsoft.net/webdesign-l/vertical-aligned-nav-list.html
- *
- * (Although firefox has .height on the rectangle, IE doesn't,
- * so we use r.bottom - r.top rather than r.height.)
- */
- label.css('line-height', parseInt(liBottom) + 'px');
- } else {
-/*
- * If there is more than one line of text, then we shouldn't
- * touch the line height, but we should make sure the text
- * doesn't overflow the container.
- */
- label.css("overflow", "hidden");
- }
- } else {
- thisLi.css('bottom', liBottom * i).prepend('');
- label.css('margin-top', -label.height() / 2)
- }
- });
-
- charts.bar.draw();
-
- },
-
- draw: function () {
- // Draw bars
- if (horizontal) {
- // for horizontal, keep the same code, but rotate everything 90 degrees
- // clockwise.
- ctx.rotate(Math.PI / 2);
- } else {
- // for vertical, translate to the top left corner.
- ctx.translate(0, zeroLocY);
- }
-
- // Don't attempt to draw anything if all the values are zero,
- // otherwise we will get weird exceptions from the canvas methods.
- if (totalYRange <= 0) return;
-
- var yScale = (horizontal ? canvas.width() : canvas.height()) / totalYRange;
- var barWidth = horizontal ? (canvas.height() / xLabels.length) : (canvas.width() / (bottomLabels.length));
- var linewidth = (barWidth - o.barGroupMargin * 2) / dataGroups.length;
-
- for (var h = 0; h < dataGroups.length; h++) {
- ctx.beginPath();
-
- var strokeWidth = linewidth - (o.barMargin * 2);
- ctx.lineWidth = strokeWidth;
- var points = dataGroups[h].points;
- var integer = 0;
- for (var i = 0; i < points.length; i++) {
- // If the last value is zero, IE will go nuts and not draw anything,
- // so don't try to draw zero values at all.
- if (points[i].value != 0) {
- var xVal = (integer - o.barGroupMargin) + (h * linewidth) + linewidth / 2;
- xVal += o.barGroupMargin * 2;
- ctx.moveTo(xVal, 0);
- ctx.lineTo(xVal, Math.round(-points[i].value * yScale));
- }
- integer += barWidth;
- }
- ctx.strokeStyle = dataGroups[h].color;
- ctx.stroke();
- ctx.closePath();
- }
-
- }
- };
-
- })();
-
- //create new canvas, set w&h attrs (not inline styles)
- var canvasNode = document.createElement("canvas");
- var canvas = $(canvasNode).attr({
- 'height': o.height,
- 'width': o.width
- });
-
- //get title for chart
- var title = o.title || self.find('caption').text();
-
- //create canvas wrapper div, set inline w&h, append
- var canvasContain = (container || $('')).height(o.height).width(o.width);
-
- var scroller = $('').appendTo(canvasContain).append(canvas);
-
- //title/key container
- if (o.appendTitle || o.appendKey) {
- var infoContain = $('').appendTo(canvasContain);
- }
-
- //append title
- if (o.appendTitle) {
- $('' + title + '
').appendTo(infoContain);
- }
-
-
- //append key
- if (o.appendKey) {
- var newKey = $('');
- $.each(yAllLabels, function (i, label) {
- $('' + label + '').appendTo(newKey);
- });
- newKey.appendTo(infoContain);
- };
-
- // init interaction
- if (o.interaction) {
- // sets the canvas to track interaction
- // IE needs one div on top of the canvas since the VML shapes prevent mousemove from triggering correctly.
- // Pie charts needs tracker because labels goes on top of the canvas and also messes up with mousemove
- var tracker = $('').css({
- 'height': o.height + 'px',
- 'width': o.width + 'px',
- 'position': 'relative',
- 'z-index': 200
- }).insertAfter(canvas);
-
- var triggerInteraction = function (overOut, data) {
- var data = $.extend({
- canvasContain: canvasContain,
- tableData: tableData
- }, data);
- self.trigger('vizualize' + overOut, data);
- };
-
- var over = false,
- last = false,
- started = false;
- tracker.mousemove(function (e) {
- var x, y, x1, y1, data, dist, i, current, selector, zLabel, elem, color, minDist, found, ev = e.originalEvent;
-
- // get mouse position relative to the tracker/canvas
- x = ev.layerX || ev.offsetX || 0;
- y = ev.layerY || ev.offsetY || 0;
-
- found = false;
- minDist = started ? 30000 : (o.type == 'pie' ? (Math.round(canvas.height() / 2) - o.pieMargin) / 3 : o.lineWeight * 4);
- // iterate datagroups to find points with matching
- $.each(charts[o.type].interactionPoints, function (i, current) {
- x1 = current.canvasCords[0] + zeroLocX;
- y1 = current.canvasCords[1] + (o.type == "pie" ? 0 : zeroLocY);
- dist = Math.sqrt((x1 - x) * (x1 - x) + (y1 - y) * (y1 - y));
- if (dist < minDist) {
- found = current;
- minDist = dist;
- }
- });
-
- if (o.multiHover && found) {
- x = found.canvasCords[0] + zeroLocX;
- y = found.canvasCords[1] + (o.type == "pie" ? 0 : zeroLocY);
- found = [found];
- $.each(charts[o.type].interactionPoints, function (i, current) {
- if (current == found[0]) {
- return;
- }
- x1 = current.canvasCords[0] + zeroLocX;
- y1 = current.canvasCords[1] + zeroLocY;
- dist = Math.sqrt((x1 - x) * (x1 - x) + (y1 - y) * (y1 - y));
- if (dist <= o.multiHover) {
- found.push(current);
- }
- });
- }
- // trigger over and out only when state changes, instead of on every mousemove
- over = found;
- if (over != last) {
- if (over) {
- if (last) {
- triggerInteraction('Out', {
- point: last
- });
- }
- triggerInteraction('Over', {
- point: over
- });
- last = over;
- }
- if (last && !over) {
- triggerInteraction('Out', {
- point: last
- });
- last = false;
- }
- started = true;
- }
- });
- tracker.mouseleave(function () {
- triggerInteraction('Out', {
- point: last,
- mouseOutGraph: true
- });
- over = (last = false);
- });
- }
-
- //append new canvas to page
- if (!container) {
- canvasContain.insertAfter(this);
- }
- if (typeof (G_vmlCanvasManager) != 'undefined') {
- G_vmlCanvasManager.init();
- G_vmlCanvasManager.initElement(canvas[0]);
- }
-
- //set up the drawing board
- var ctx = canvas[0].getContext('2d');
-
- // Scroll graphs
- scroller.scrollLeft(o.width - scroller.width());
-
- // init plugins
- $.each($.visualizePlugins, function (i, plugin) {
- plugin.call(self, o, tableData);
- });
-
- //create chart
- charts[o.type].setup();
-
- if (!container) {
- //add event for updating
- self.bind('visualizeRefresh', function () {
- self.visualize(o, $(this).empty());
- });
- //add event for redraw
- self.bind('visualizeRedraw', function () {
- charts[o.type].draw();
- });
- }
- }).next(); //returns canvas(es)
- };
- // create array for plugins. if you wish to make a plugin,
- // just push your init funcion into this array
- $.visualizePlugins = [];
-
-})(jQuery);
+/**
+ * --------------------------------------------------------------------
+ * jQuery-Plugin "visualize"
+ * by Scott Jehl, scott@filamentgroup.com
+ * http://www.filamentgroup.com
+ * Copyright (c) 2009 Filament Group
+ * Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses.
+ *
+ * --------------------------------------------------------------------
+ */
+(function($) {
+$.fn.visualize = function(options, container){
+ return $(this).each(function(){
+ //configuration
+ var o = $.extend({
+ type: 'bar', //also available: area, pie, line
+ width: $(this).width(), //height of canvas - defaults to table height
+ height: $(this).height(), //height of canvas - defaults to table height
+ appendTitle: true, //table caption text is added to chart
+ title: null, //grabs from table caption if null
+ appendKey: true, //color key is added to chart
+ colors: ['#be1e2d','#666699','#92d5ea','#ee8310','#8d10ee','#5a3b16','#26a4ed','#f45a90','#e9e744'],
+ textColors: [], //corresponds with colors array. null/undefined items will fall back to CSS
+ parseDirection: 'x', //which direction to parse the table data
+ pieMargin: 10, //pie charts only - spacing around pie
+ pieLabelsAsPercent: true,
+ pieLabelPos: 'inside',
+ lineWeight: 4, //for line and area - stroke weight
+ lineDots: false, //also available: 'single', 'double'
+ dotInnerColor: "#ffffff", // only used for lineDots:'double'
+ lineMargin: (options.lineDots?15:0), //for line and area - spacing around lines
+ barGroupMargin: 10,
+ chartId: '',
+ xLabelParser: null, // function to parse labels as values
+ valueParser: null, // function to parse values. must return a Number
+ chartId: '',
+ chartClass: '',
+ barMargin: 1, //space around bars in bar chart (added to both sides of bar)
+ yLabelInterval: 30, //distance between y labels
+ interaction: false // only used for lineDots != false -- triggers mouseover and mouseout on original table
+ },options);
+
+ //reset width, height to numbers
+ o.width = parseFloat(o.width);
+ o.height = parseFloat(o.height);
+
+ // reset padding if graph is not lines
+ if(o.type != 'line' && o.type != 'area' ) {
+ o.lineMargin = 0;
+ }
+
+ var self = $(this);
+
+ // scrape data from html table
+ var tableData = {};
+ var colors = o.colors;
+ var textColors = o.textColors;
+
+
+ var parseLabels = function(direction){
+ var labels = [];
+ if(direction == 'x'){
+ self.find('thead tr').each(function(i){
+ $(this).find('th').each(function(j){
+ if(!labels[j]) {
+ labels[j] = [];
+ }
+ labels[j][i] = $(this).text()
+ })
+ });
+ }
+ else {
+ self.find('tbody tr').each(function(i){
+ $(this).find('th').each(function(j) {
+ if(!labels[i]) {
+ labels[i] = [];
+ }
+ labels[i][j] = $(this).text()
+ });
+ });
+ }
+ return labels;
+ };
+
+ var fnParse = o.valueParser || parseFloat;
+ var dataGroups = tableData.dataGroups = [];
+ if(o.parseDirection == 'x'){
+ self.find('tbody tr').each(function(i,tr){
+ dataGroups[i] = {};
+ dataGroups[i].points = [];
+ dataGroups[i].color = colors[i];
+ if(textColors[i]){ dataGroups[i].textColor = textColors[i]; }
+ $(tr).find('td').each(function(j,td){
+ dataGroups[i].points.push( {
+ value: fnParse($(td).text()),
+ elem: td,
+ tableCords: [i,j]
+ } );
+ });
+ });
+ } else {
+ var cols = self.find('tbody tr:eq(0) td').size();
+ for(var i=0; itableData.topValue) {
+ tableData.topValue = fnParse(item.value,10);
+ }
+ if(item.valuexTopValue) {
+ xTopValue = label;
+ }
+ if(label tableData.topValue+loopInterval) {
+ yLabels.pop();
+ } else if (yLabels[yLabels.length-1] <= tableData.topValue-10) {
+ yLabels.push(tableData.topValue);
+ }
+
+ // populate some data
+ $.each(dataGroups,function(i,row){
+ row.yLabels = tableData.yAllLabels[i];
+ $.each(row.points, function(j,point){
+ point.zeroLocY = tableData.zeroLocY;
+ point.zeroLocX = tableData.zeroLocX;
+ point.xLabels = tableData.xAllLabels[j];
+ point.yLabels = tableData.yAllLabels[i];
+ point.color = row.color;
+ });
+ });
+
+ try{console.log(tableData);}catch(e){}
+
+ var charts = {};
+
+ charts.pie = {
+ interactionPoints: dataGroups,
+
+ setup: function() {
+ charts.pie.draw(true);
+ },
+ draw: function(drawHtml){
+
+ var centerx = Math.round(canvas.width()/2);
+ var centery = Math.round(canvas.height()/2);
+ var radius = centery - o.pieMargin;
+ var counter = 0.0;
+
+ if(drawHtml) {
+ canvasContain.addClass('visualize-pie');
+
+ if(o.pieLabelPos == 'outside'){ canvasContain.addClass('visualize-pie-outside'); }
+
+ var toRad = function(integer){ return (Math.PI/180)*integer; };
+ var labels = $('')
+ .insertAfter(canvas);
+ }
+
+
+ //draw the pie pieces
+ $.each(dataGroups, function(i,row){
+ var fraction = row.groupTotal / dataSum;
+ if (fraction <= 0 || isNaN(fraction))
+ return;
+ ctx.beginPath();
+ ctx.moveTo(centerx, centery);
+ ctx.arc(centerx, centery, radius,
+ counter * Math.PI * 2 - Math.PI * 0.5,
+ (counter + fraction) * Math.PI * 2 - Math.PI * 0.5,
+ false);
+ ctx.lineTo(centerx, centery);
+ ctx.closePath();
+ ctx.fillStyle = dataGroups[i].color;
+ ctx.fill();
+ // draw labels
+ if(drawHtml) {
+ var sliceMiddle = (counter + fraction/2);
+ var distance = o.pieLabelPos == 'inside' ? radius/1.5 : radius + radius / 5;
+ var labelx = Math.round(centerx + Math.sin(sliceMiddle * Math.PI * 2) * (distance));
+ var labely = Math.round(centery - Math.cos(sliceMiddle * Math.PI * 2) * (distance));
+ var leftRight = (labelx > centerx) ? 'right' : 'left';
+ var topBottom = (labely > centery) ? 'bottom' : 'top';
+ var percentage = parseFloat((fraction*100).toFixed(2));
+
+ // interaction variables
+ row.canvasCords = [labelx,labely];
+ row.zeroLocY = tableData.zeroLocY = 0; // related to zeroLocY and plugin API
+ row.zeroLocX = tableData.zeroLocX = 0; // related to zeroLocX and plugin API
+ row.value = row.groupTotal;
+
+
+ if(percentage){
+ var labelval = (o.pieLabelsAsPercent) ? percentage + '%' : row.groupTotal;
+ var labeltext = $('' + labelval +'')
+ .css(leftRight, 0)
+ .css(topBottom, 0);
+ if(labeltext)
+ var label = $('')
+ .appendTo(labels)
+ .css({left: labelx, top: labely})
+ .append(labeltext);
+ labeltext
+ .css('font-size', radius / 8)
+ .css('margin-'+leftRight, -labeltext.width()/2)
+ .css('margin-'+topBottom, -labeltext.outerHeight()/2);
+
+ if(dataGroups[i].textColor){ labeltext.css('color', dataGroups[i].textColor); }
+
+ }
+ }
+ counter+=fraction;
+ });
+ }
+ };
+
+ (function(){
+
+ var xInterval;
+
+ var drawPoint = function (ctx,x,y,color,size) {
+ ctx.moveTo(x,y);
+ ctx.beginPath();
+ ctx.arc(x,y,size/2,0,2*Math.PI,false);
+ ctx.closePath();
+ ctx.fillStyle = color;
+ ctx.fill();
+ };
+
+ charts.line = {
+
+ interactionPoints: allItems,
+
+ setup: function(area){
+
+ if(area){ canvasContain.addClass('visualize-area'); }
+ else{ canvasContain.addClass('visualize-line'); }
+
+ //write X labels
+ var xlabelsUL = $('')
+ .width(canvas.width())
+ .height(canvas.height())
+ .insertBefore(canvas);
+
+ if(!o.customXLabels) {
+ xInterval = (canvas.width() - 2*o.lineMargin) / (xLabels.length -1);
+ $.each(xLabels, function(i){
+ var thisLi = $(''+this+'')
+ .prepend('')
+ .css('left', o.lineMargin + xInterval * i)
+ .appendTo(xlabelsUL);
+ var label = thisLi.find('span:not(.line)');
+ var leftOffset = label.width()/-2;
+ if(i == 0){ leftOffset = 0; }
+ else if(i== xLabels.length-1){ leftOffset = -label.width(); }
+ label
+ .css('margin-left', leftOffset)
+ .addClass('label');
+ });
+ } else {
+ o.customXLabels(tableData,xlabelsUL);
+ }
+
+ //write Y labels
+ var liBottom = (canvas.height() - 2*o.lineMargin) / (yLabels.length-1);
+ var ylabelsUL = $('')
+ .width(canvas.width())
+ .height(canvas.height())
+ // .css('margin-top',-o.lineMargin)
+ .insertBefore(scroller);
+
+ $.each(yLabels, function(i){
+ var value = Math.floor(this);
+ var posB = (value-bottomValue)*yScale + o.lineMargin;
+ if(posB >= o.height-1 || posB < 0) {
+ return;
+ }
+ var thisLi = $(''+value+'')
+ .css('bottom', posB);
+ if(Math.abs(posB) < o.height-1) {
+ thisLi.prepend('');
+ }
+ thisLi.prependTo(ylabelsUL);
+
+ var label = thisLi.find('span:not(.line)');
+ var topOffset = label.height()/-2;
+ if(!o.lineMargin) {
+ if(i == 0){ topOffset = -label.height(); }
+ else if(i== yLabels.length-1){ topOffset = 0; }
+ }
+ label
+ .css('margin-top', topOffset)
+ .addClass('label');
+ });
+
+ //start from the bottom left
+ ctx.translate(zeroLocX,zeroLocY);
+
+ charts.line.draw(area);
+
+ },
+
+ draw: function(area) {
+ // prevent drawing on top of previous draw
+ ctx.clearRect(-zeroLocX,-zeroLocY,o.width,o.height);
+ // Calculate each point properties before hand
+ var integer;
+ $.each(dataGroups,function(i,row){
+ integer = o.lineMargin; // the current offset
+ $.each(row.points, function(j,point){
+ if(o.xLabelParser) {
+ point.canvasCords = [(xLabels[j]-zeroLocX)*xScale - xBottomValue,-(point.value*yScale)];
+ } else {
+ point.canvasCords = [integer,-(point.value*yScale)];
+ }
+
+ if(o.lineDots) {
+ point.dotSize = o.dotSize||o.lineWeight*Math.PI;
+ point.dotInnerSize = o.dotInnerSize||o.lineWeight*Math.PI/2;
+ if(o.lineDots == 'double') {
+ point.innerColor = o.dotInnerColor;
+ }
+ }
+ integer+=xInterval;
+ });
+ });
+ // fire custom event so we can enable rich interaction
+ self.trigger('vizualizeBeforeDraw',{options:o,table:self,canvasContain:canvasContain,tableData:tableData});
+ // draw lines and areas
+ $.each(dataGroups,function(h){
+ // Draw lines
+ ctx.beginPath();
+ ctx.lineWidth = o.lineWeight;
+ ctx.lineJoin = 'round';
+ $.each(this.points, function(g){
+ var loc = this.canvasCords;
+ if(g == 0) {
+ ctx.moveTo(loc[0],loc[1]);
+ }
+ ctx.lineTo(loc[0],loc[1]);
+ });
+ ctx.strokeStyle = this.color;
+ ctx.stroke();
+ // Draw fills
+ if(area){
+ var integer = this.points[this.points.length-1].canvasCords[0];
+ if (isFinite(integer))
+ ctx.lineTo(integer,0);
+ ctx.lineTo(o.lineMargin,0);
+ ctx.closePath();
+ ctx.fillStyle = this.color;
+ ctx.globalAlpha = .3;
+ ctx.fill();
+ ctx.globalAlpha = 1.0;
+ }
+ else {ctx.closePath();}
+ });
+ // draw points
+ if(o.lineDots) {
+ $.each(dataGroups,function(h){
+ $.each(this.points, function(g){
+ drawPoint(ctx,this.canvasCords[0],this.canvasCords[1],this.color,this.dotSize);
+ if(o.lineDots === 'double') {
+ drawPoint(ctx,this.canvasCords[0],this.canvasCords[1],this.innerColor,this.dotInnerSize);
+ }
+ });
+ });
+ }
+
+ }
+ };
+
+ })();
+
+ charts.area = {
+ setup: function() {
+ charts.line.setup(true);
+ },
+ draw: charts.line.draw
+ };
+
+ (function(){
+
+ var horizontal,bottomLabels;
+
+ charts.bar = {
+ setup:function(){
+ /**
+ * We can draw horizontal or vertical bars depending on the
+ * value of the 'barDirection' option (which may be 'vertical' or
+ * 'horizontal').
+ */
+
+ horizontal = (o.barDirection == 'horizontal');
+
+ canvasContain.addClass('visualize-bar');
+
+ /**
+ * Write labels along the bottom of the chart. If we're drawing
+ * horizontal bars, these will be the yLabels, otherwise they
+ * will be the xLabels. The positioning also varies slightly:
+ * yLabels are values, hence they will span the whole width of
+ * the canvas, whereas xLabels are supposed to line up with the
+ * bars.
+ */
+ bottomLabels = horizontal ? yLabels : xLabels;
+
+ var xInterval = canvas.width() / (bottomLabels.length - (horizontal ? 1 : 0));
+
+ var xlabelsUL = $('')
+ .width(canvas.width())
+ .height(canvas.height())
+ .insertBefore(canvas);
+
+ $.each(bottomLabels, function(i){
+ var thisLi = $(''+this+'')
+ .prepend('')
+ .css('left', xInterval * i)
+ .width(xInterval)
+ .appendTo(xlabelsUL);
+
+ if (horizontal) {
+ var label = thisLi.find('span.label');
+ label.css("margin-left", -label.width() / 2);
+ }
+ });
+
+ /**
+ * Write labels along the left of the chart. Follows the same idea
+ * as the bottom labels.
+ */
+ var leftLabels = horizontal ? xLabels : yLabels;
+ var liBottom = canvas.height() / (leftLabels.length - (horizontal ? 0 : 1));
+
+ var ylabelsUL = $('')
+ .width(canvas.width())
+ .height(canvas.height())
+ .insertBefore(canvas);
+
+ $.each(leftLabels, function(i){
+ var thisLi = $(''+this+'').prependTo(ylabelsUL);
+
+ var label = thisLi.find('span:not(.line)').addClass('label');
+
+ if (horizontal) {
+ /**
+ * For left labels, we want to vertically align the text
+ * to the middle of its container, but we don't know how
+ * many lines of text we will have, since the labels could
+ * be very long.
+ *
+ * So we set a min-height of liBottom, and a max-height
+ * of liBottom + 1, so we can then check the label's actual
+ * height to determine if it spans one line or more lines.
+ */
+ label.css({
+ 'min-height': liBottom,
+ 'max-height': liBottom + 1,
+ 'vertical-align': 'middle'
+ });
+ thisLi.css({'top': liBottom * i, 'min-height': liBottom});
+
+ var r = label[0].getClientRects()[0];
+ if (r.bottom - r.top == liBottom) {
+ /* This means we have only one line of text; hence
+ * we can centre the text vertically by setting the line-height,
+ * as described at:
+ * http://www.ampsoft.net/webdesign-l/vertical-aligned-nav-list.html
+ *
+ * (Although firefox has .height on the rectangle, IE doesn't,
+ * so we use r.bottom - r.top rather than r.height.)
+ */
+ label.css('line-height', parseInt(liBottom) + 'px');
+ }
+ else {
+ /*
+ * If there is more than one line of text, then we shouldn't
+ * touch the line height, but we should make sure the text
+ * doesn't overflow the container.
+ */
+ label.css("overflow", "hidden");
+ }
+ }
+ else {
+ thisLi.css('bottom', liBottom * i).prepend('');
+ label.css('margin-top', -label.height() / 2)
+ }
+ });
+
+ charts.bar.draw();
+
+ },
+
+ draw: function() {
+ // Draw bars
+
+ if (horizontal) {
+ // for horizontal, keep the same code, but rotate everything 90 degrees
+ // clockwise.
+ ctx.rotate(Math.PI / 2);
+ }
+ else {
+ // for vertical, translate to the top left corner.
+ ctx.translate(0, zeroLocY);
+ }
+
+ // Don't attempt to draw anything if all the values are zero,
+ // otherwise we will get weird exceptions from the canvas methods.
+ if (totalYRange <= 0)
+ return;
+
+ var yScale = (horizontal ? canvas.width() : canvas.height()) / totalYRange;
+ var barWidth = horizontal ? (canvas.height() / xLabels.length) : (canvas.width() / (bottomLabels.length));
+ var linewidth = (barWidth - o.barGroupMargin*2) / dataGroups.length;
+
+ for(var h=0; h'))
+ .height(o.height)
+ .width(o.width);
+
+ var scroller = $('')
+ .appendTo(canvasContain)
+ .append(canvas);
+
+ //title/key container
+ if(o.appendTitle || o.appendKey){
+ var infoContain = $('')
+ .appendTo(canvasContain);
+ }
+
+ //append title
+ if(o.appendTitle){
+ $(''+ title +'
').appendTo(infoContain);
+ }
+
+
+ //append key
+ if(o.appendKey){
+ var newKey = $('');
+ $.each(yAllLabels, function(i,label){
+ $(''+ label +'')
+ .appendTo(newKey);
+ });
+ newKey.appendTo(infoContain);
+ };
+
+ // init interaction
+ if(o.interaction) {
+ // sets the canvas to track interaction
+ // IE needs one div on top of the canvas since the VML shapes prevent mousemove from triggering correctly.
+ // Pie charts needs tracker because labels goes on top of the canvas and also messes up with mousemove
+ var tracker = $('')
+ .css({
+ 'height': o.height + 'px',
+ 'width': o.width + 'px',
+ 'position':'relative',
+ 'z-index': 200
+ })
+ .insertAfter(canvas);
+
+ var triggerInteraction = function(overOut,data) {
+ var data = $.extend({
+ canvasContain:canvasContain,
+ tableData:tableData
+ },data);
+ self.trigger('vizualize'+overOut,data);
+ };
+
+ var over=false, last=false, started=false;
+ tracker.mousemove(function(e){
+ var x,y,x1,y1,data,dist,i,current,selector,zLabel,elem,color,minDist,found,ev=e.originalEvent;
+
+ // get mouse position relative to the tracker/canvas
+ x = ev.layerX || ev.offsetX || 0;
+ y = ev.layerY || ev.offsetY || 0;
+
+ found = false;
+ minDist = started?30000:(o.type=='pie'?(Math.round(canvas.height()/2)-o.pieMargin)/3:o.lineWeight*4);
+ // iterate datagroups to find points with matching
+ $.each(charts[o.type].interactionPoints,function(i,current){
+ x1 = current.canvasCords[0] + zeroLocX;
+ y1 = current.canvasCords[1] + (o.type=="pie"?0:zeroLocY);
+ dist = Math.sqrt( (x1 - x)*(x1 - x) + (y1 - y)*(y1 - y) );
+ if(dist < minDist) {
+ found = current;
+ minDist = dist;
+ }
+ });
+
+ if(o.multiHover && found) {
+ x = found.canvasCords[0] + zeroLocX;
+ y = found.canvasCords[1] + (o.type=="pie"?0:zeroLocY);
+ found = [found];
+ $.each(charts[o.type].interactionPoints,function(i,current){
+ if(current == found[0]) {return;}
+ x1 = current.canvasCords[0] + zeroLocX;
+ y1 = current.canvasCords[1] + zeroLocY;
+ dist = Math.sqrt( (x1 - x)*(x1 - x) + (y1 - y)*(y1 - y) );
+ if(dist <= o.multiHover) {
+ found.push(current);
+ }
+ });
+ }
+ // trigger over and out only when state changes, instead of on every mousemove
+ over = found;
+ if(over != last) {
+ if(over) {
+ if(last) {
+ triggerInteraction('Out',{point:last});
+ }
+ triggerInteraction('Over',{point:over});
+ last = over;
+ }
+ if(last && !over) {
+ triggerInteraction('Out',{point:last});
+ last=false;
+ }
+ started=true;
+ }
+ });
+ tracker.mouseleave(function(){
+ triggerInteraction('Out',{
+ point:last,
+ mouseOutGraph:true
+ });
+ over = (last = false);
+ });
+ }
+
+ //append new canvas to page
+ if(!container){canvasContain.insertAfter(this); }
+ if( typeof(G_vmlCanvasManager) != 'undefined' ){ G_vmlCanvasManager.init(); G_vmlCanvasManager.initElement(canvas[0]); }
+
+ //set up the drawing board
+ var ctx = canvas[0].getContext('2d');
+
+ // Scroll graphs
+ scroller.scrollLeft(o.width-scroller.width());
+
+ // init plugins
+ $.each($.visualizePlugins,function(i,plugin){
+ plugin.call(self,o,tableData);
+ });
+
+ //create chart
+ charts[o.type].setup();
+
+ if(!container){
+ //add event for updating
+ self.bind('visualizeRefresh', function(){
+ self.visualize(o, $(this).empty());
+ });
+ //add event for redraw
+ self.bind('visualizeRedraw', function(){
+ charts[o.type].draw();
+ });
+ }
+ }).next(); //returns canvas(es)
+};
+// create array for plugins. if you wish to make a plugin,
+// just push your init funcion into this array
+$.visualizePlugins = [];
+
+})(jQuery);
+
+