Skip to content

Commit

Permalink
Restructure to eliminate need for globals. Hat tip to @lachie.
Browse files Browse the repository at this point in the history
  • Loading branch information
benaskins committed Feb 3, 2009
1 parent 92b75de commit 47444a4
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 138 deletions.
252 changes: 119 additions & 133 deletions jquery.simplegraph.js
Original file line number Diff line number Diff line change
@@ -1,42 +1,43 @@
(function($) {

// Global, yet private to the world beyond the closure.
//
// closures are natural
// closures are good
// not every language has them
// but every language should
//
//- convenience
var canvas;
var settings;
var graph;
var grid;
//- required to implement hover function
var isLabelVisible;
var leaveTimer;

$.fn.simplegraph = function(data, labels, options) {
settings = $.extend({}, $.fn.simplegraph.defaults, options);

setStyleDefaults();
setPenColor();
$.fn.simplegraph = function(data, labels, options) {
var settings = $.extend({}, $.fn.simplegraph.defaults, options);
setStyleDefaults(settings);
setPenColor(settings);

graph = new Graph(data, labels);
grid = new Grid(graph, settings);

return this.each( function() {
canvas = Raphael(this, settings.width, settings.height);
if (settings.autoDraw) {
$.fn.simplegraph.draw();
}
var canvas = Raphael(this, settings.width, settings.height);
var simplegraph = new SimpleGraph(data, labels, canvas, settings);

simplegraph.draw();

// Stash simplegraph object away for future reference
$.data(this, "simplegraph", simplegraph);
})
};

// Plot another set of values on an existing graph, use it like this:
// $("#target").simplegraph(data, labels).simplegraph_more(moreData);
$.fn.simplegraph_more = function(data, options) {
return this.each( function() {
var sg = $.data(this, "simplegraph");
sg.dataSet = new DataSet(data, sg.dataSet.labels, sg.settings);
sg.settings.penColor = options.penColor;
setPenColor(sg.settings);
sg.settings = $.extend(sg.settings, options);
sg.grid = new Grid(sg.dataSet, sg.settings);
sg.dataSet.labelYAxis(sg.grid, sg.canvas);
sg.dataSet.plot(sg.grid, sg.canvas);
})
}

// Public

$.fn.simplegraph.defaults = {
autoDraw: true,
drawGrid: true,
units: "",
// Dimensions
Expand Down Expand Up @@ -75,51 +76,10 @@
// -- Hover
addHover: true
};

$.fn.simplegraph.draw = function() {
if (settings.drawGrid) {
$.fn.simplegraph.drawGrid();
}
if (settings.yAxisCaption) {
$.fn.simplegraph.labelYAxis();
}
$.fn.simplegraph.labelXAxis();
$.fn.simplegraph.plot();
}

$.fn.simplegraph.drawGrid = function() {
grid.draw(canvas);
}

$.fn.simplegraph.labelYAxis = function() {
graph.labelYAxis(grid, canvas, settings);
}

$.fn.simplegraph.labelXAxis = function() {
graph.labelXAxis(grid, canvas, settings.xAxisLabelStyle);
}

$.fn.simplegraph.plot = function() {
graph.plot(grid, canvas, settings);
}

// Plot another set of values on an existing graph, use it like this:
// $("#target").simplegraph(data, labels).simplegraph.more(moreData);
$.fn.simplegraph.more = function(data, options) {
var new_graph = new Graph(data, graph.labels);
settings.penColor = options.penColor;
setPenColor();
settings = $.extend(settings, options);
var new_grid = new Grid(new_graph, settings);
if (settings.autoDraw) {
new_graph.labelYAxis(new_grid, canvas, settings);
new_graph.plot(new_grid, canvas, settings);
}
}

// Default hoverIn callback, this is public and as such can be overwritten. You can write your
// own call back with the same signature if you want different behaviour.
$.fn.simplegraph.hoverIn = function(value, label, x, y, frame, hoverLabel, dot) {
$.fn.simplegraph.hoverIn = function(canvas, value, label, x, y, frame, hoverLabel, dot, settings) {
clearTimeout(leaveTimer);
var newcoord = {x: x * 1 + 7.5, y: y - 19};
if (newcoord.x + 100 > settings.width) {
Expand All @@ -137,7 +97,7 @@

// Default hoverOut callback, this is public and as such can be overwritten. You can write your
// own call back with the same signature if you want different behaviour.
$.fn.simplegraph.hoverOut = function(frame, label, dot) {
$.fn.simplegraph.hoverOut = function(canvas, frame, label, dot, settings) {
if (settings.drawPoints) {
dot.attr("r", settings.pointRadius);
}
Expand All @@ -153,55 +113,77 @@

// Private

function SimpleGraph(data, labels, canvas, settings) {

this.settings = settings;
this.dataSet = new DataSet(data, labels, this.settings);
this.grid = new Grid(this.dataSet, this.settings);
this.canvas = canvas;

this.draw = function() {
if (this.settings.drawGrid) {
this.grid.draw(this.canvas);
}
if (this.settings.yAxisCaption) {
this.dataSet.labelYAxis(this.grid, this.canvas);
}
this.dataSet.labelXAxis(this.grid, this.canvas);
this.dataSet.plot(this.grid, this.canvas);
}
}

// Holds the data and labels to be plotted, provides methods for labelling the x and y axes,
// and for plotting it's own points. Each method requires a grid object for translating values to
// x,y pixel coordinates and canvas object on which to draw.
function Graph(data, labels) {
this.data = data;
this.labels = labels;

this.labelXAxis = function(grid, canvas, style) {
$.each(this.labels, function(i, label) {
var x = grid.x(i);
canvas.text(x, canvas.height - 6, label).attr(style).toBack();
})
// x,y pixel coordinates and a canvas object on which to draw.
function DataSet(data, labels, settings) {
this.data = data;
this.labels = labels;
this.settings = settings;

this.labelXAxis = function(grid, canvas) {
(function(ds) {
$.each(ds.labels, function(i, label) {
var x = grid.x(i);
canvas.text(x, canvas.height - 6, label).attr(ds.settings.xAxisLabelStyle).toBack();
});
})(this);
};

this.labelYAxis = function(grid, canvas, settings) {
this.labelYAxis = function(grid, canvas) {
// Legend
canvas.rect(
grid.leftEdge - (30 + settings.yAxisOffset), //TODO PARAM - Label Colum Width
grid.leftEdge - (30 + this.settings.yAxisOffset), //TODO PARAM - Label Colum Width
grid.topEdge,
30, //TODO PARAM - Label Column Width
grid.height
).attr({stroke: settings.lineColor, fill: settings.lineColor, opacity: 0.3}); //TODO PARAMS - legend border and fill style
).attr({stroke: this.settings.lineColor, fill: this.settings.lineColor, opacity: 0.3}); //TODO PARAMS - legend border and fill style

for (var i = 1, ii = (grid.rows) + 1; i < ii; i = i + 2) {
var value = (ii - i)*2,
y = grid.y(value) + 4, // TODO: Value of 4 works for default dimensions, expect will need to scale
x = grid.leftEdge - (6 + settings.yAxisOffset);
canvas.text(x, y, value).attr(settings.yAxisLabelStyle);
x = grid.leftEdge - (6 + this.settings.yAxisOffset);
canvas.text(x, y, value).attr(this.settings.yAxisLabelStyle);
}
var caption = canvas.text(
grid.leftEdge - (20 + settings.yAxisOffset),
(grid.height/2) + (settings.yAxisCaption.length / 2),
settings.yAxisCaption + " (" + settings.units + ")").attr(settings.yAxisCaptionStyle).rotate(270);
grid.leftEdge - (20 + this.settings.yAxisOffset),
(grid.height/2) + (this.settings.yAxisCaption.length / 2),
this.settings.yAxisCaption + " (" + this.settings.units + ")").attr(this.settings.yAxisCaptionStyle).rotate(270);
// Increase the offset for the next caption (if any)
settings.yAxisOffset = settings.yAxisOffset + 30;
this.settings.yAxisOffset = this.settings.yAxisOffset + 30;
}

this.plot = function(grid, canvas, settings) {
this.plot = function(grid, canvas) {
var line_path = canvas.path({
stroke: settings.lineColor,
"stroke-width": settings.lineWidth,
"stroke-linejoin": settings.lineJoin
stroke: this.settings.lineColor,
"stroke-width": this.settings.lineWidth,
"stroke-linejoin": this.settings.lineJoin
});

var fill_path = canvas.path({
stroke: "none",
fill: settings.fillColor,
opacity: settings.fillOpacity
}).moveTo(settings.leftGutter, settings.height - settings.bottomGutter);
fill: this.settings.fillColor,
opacity: this.settings.fillOpacity
}).moveTo(this.settings.leftGutter, this.settings.height - this.settings.bottomGutter);

var bars = canvas.group(),
dots = canvas.group(),
Expand All @@ -210,65 +192,77 @@
var hoverFrame = dots.rect(10, 10, 100, 40, 5).attr({
fill: "#fff", stroke: "#474747", "stroke-width": 2}).hide(); //TODO PARAM - fill colour, border colour, border width
var hoverText = [];
hoverText[0] = canvas.text(60, 25, "").attr(settings.hoverValueStyle).hide();
hoverText[1] = canvas.text(60, 40, "").attr(settings.hoverLabelStyle).hide();
hoverText[0] = canvas.text(60, 25, "").attr(this.settings.hoverValueStyle).hide();
hoverText[1] = canvas.text(60, 40, "").attr(this.settings.hoverLabelStyle).hide();

// Plot the points
(function(graph) {
$.each(graph.data, function(i, value) {
(function(dataSet) {
$.each(dataSet.data, function(i, value) {
var y = grid.y(value),
x = grid.x(i),
label = graph.labels ? graph.labels[i] : " ";
label = dataSet.labels ? dataSet.labels[i] : " ";

if (settings.drawPoints) {
var dot = dots.circle(x, y, settings.pointRadius).attr({fill: settings.pointColor, stroke: settings.pointColor});
if (dataSet.settings.drawPoints) {
var dot = dots.circle(x, y, dataSet.settings.pointRadius).attr({fill: dataSet.settings.pointColor, stroke: dataSet.settings.pointColor});
}
if (settings.drawBars) {
bars.rect(x, y, settings.barWidth, (settings.height - settings.bottomGutter) - y).attr({fill: settings.barColor, stroke: "none"});
if (dataSet.settings.drawBars) {
bars.rect(x, y, dataSet.settings.barWidth, (dataSet.settings.height - dataSet.settings.bottomGutter) - y).attr({fill: dataSet.settings.barColor, stroke: "none"});
}
if (settings.drawLine) {
if (dataSet.settings.drawLine) {
line_path[i == 0 ? "moveTo" : "cplineTo"](x, y, 10);
}
if (settings.fillUnderLine) {
if (dataSet.settings.fillUnderLine) {
fill_path[i == 0 ? "lineTo" : "cplineTo"](x, y, 10);
}
if (settings.addHover) {
if (dataSet.settings.addHover) {
var rect = canvas.rect(x - 50, y - 50, 100, 100).attr({stroke: "none", fill: "#fff", opacity: 0}); //TODO PARAM - hover target width / height
$(rect[0]).hover( function() {
$.fn.simplegraph.hoverIn(value, label, x, y, hoverFrame, hoverText, dot);
$.fn.simplegraph.hoverIn(canvas, value, label, x, y, hoverFrame, hoverText, dot, dataSet.settings);
},
function() {
$.fn.simplegraph.hoverOut(hoverFrame, hoverText, dot);
$.fn.simplegraph.hoverOut(canvas, hoverFrame, hoverText, dot, dataSet.settings);
});
}
});
})(this);

if (settings.fillUnderLine) {
fill_path.lineTo(grid.x(this.data.length - 1), settings.height - settings.bottomGutter).andClose();
if (this.settings.fillUnderLine) {
fill_path.lineTo(grid.x(this.data.length - 1), this.settings.height - this.settings.bottomGutter).andClose();
}
hoverFrame.toFront();
}
}

// Holds the dimensions of the grid, and provides methods to convert values into x,y
// pixel coordinates. Also, provides a method to draw a grid on a supplied canvas.
function Grid(graph, settings) {
function Grid(dataSet, settings) {
this.dataSet = dataSet;
this.settings = settings;

this.calculateMaxYAxis = function() {
var max = Math.max.apply(Math, this.dataSet.data),
maxOveride = this.settings.minYAxisValue;
if (maxOveride && maxOveride > max) {
max = maxOveride;
}
return max;
};

this.setYAxis = function() {
this.height = settings.height - settings.topGutter - settings.bottomGutter;
this.maxValueYAxis = calculateMaxYAxis();
this.Y = this.height / this.maxValueYAxis;
this.height = this.settings.height - this.settings.topGutter - this.settings.bottomGutter;
this.maxValueYAxis = this.calculateMaxYAxis();
this.Y = this.height / this.maxValueYAxis;
}

this.setXAxis = function() {
this.X = (settings.width - settings.leftGutter) / graph.data.length;
this.X = (this.settings.width - this.settings.leftGutter) / this.dataSet.data.length;
}

this.setDimensions = function() {
this.leftEdge = settings.leftGutter;
this.topEdge = settings.topGutter;
this.width = settings.width - settings.leftGutter - this.X;
this.columns = graph.data.length - 1;
this.leftEdge = this.settings.leftGutter;
this.topEdge = this.settings.topGutter;
this.width = this.settings.width - this.settings.leftGutter - this.X;
this.columns = this.dataSet.data.length - 1;
this.rows = this.maxValueYAxis / 2; //TODO PARAM - steps per row
}

Expand All @@ -280,34 +274,26 @@
this.height,
this.columns,
this.rows,
settings.gridBorderColor
this.settings.gridBorderColor
);
}

this.x = function(value) {
return settings.leftGutter + this.X * value;
return this.settings.leftGutter + this.X * value;
}

this.y = function(value) {
return settings.height - settings.bottomGutter - this.Y * value;
return this.settings.height - this.settings.bottomGutter - this.Y * value;
}

this.setYAxis();
this.setXAxis()
this.setXAxis();
this.setDimensions();

// Private
function calculateMaxYAxis() {
var max = Math.max.apply(Math, graph.data),
maxOveride = settings.minYAxisValue;
if (maxOveride && maxOveride > max) {
max = maxOveride;
}
return max;
};
}

function setStyleDefaults() {
function setStyleDefaults(settings) {
var targets = ["xAxisLabel", "yAxisLabel", "yAxisCaption", "hoverLabel", "hoverValue"];
var types = ["Color", "Font", "FontSize"];
$.each(targets, function(index, target) {
Expand All @@ -331,7 +317,7 @@
});
}

function setPenColor() {
function setPenColor(settings) {
if (settings.penColor) {
settings.lineColor = settings.penColor;
settings.pointColor = settings.penColor;
Expand Down
Loading

0 comments on commit 47444a4

Please sign in to comment.