├── README.txt ├── index.html ├── jquery.simplegraph.js ├── sample.js ├── samples ├── images │ ├── chart-arrow-e.gif │ ├── chart-arrow-ene.gif │ ├── chart-arrow-ese.gif │ ├── chart-arrow-n.gif │ ├── chart-arrow-ne.gif │ ├── chart-arrow-nne.gif │ ├── chart-arrow-nnw.gif │ ├── chart-arrow-nw.gif │ ├── chart-arrow-s.gif │ ├── chart-arrow-se.gif │ ├── chart-arrow-sse.gif │ ├── chart-arrow-ssw.gif │ ├── chart-arrow-sw.gif │ ├── chart-arrow-w.gif │ ├── chart-arrow-wnw.gif │ ├── chart-arrow-wsw.gif │ ├── chart-swell.gif │ └── chart-wind.gif ├── swellchart.html └── swellchart.js ├── template └── customgraph.js └── vendor ├── jquery.js ├── raphael.js └── raphael.path.methods.js /README.txt: -------------------------------------------------------------------------------- 1 | = SimpleGraph 2 | 3 | http://benaskins.github.com/simplegraph 4 | 5 | == Description 6 | 7 | Simple javascript graphs using Raphael and jQuery. 8 | 9 | Based on the Raphael analytics example (http://raphaeljs.com/analytics.html) by Dmitry Baranovskiy 10 | 11 | == Usage 12 | 13 | The SimpleGraph function takes four arguments: 14 | 15 | target:: where the rendered graph will land 16 | data:: an array of values to plot against the y-axis 17 | labels:: an array of values for labelling the x-axis 18 | options:: hash of options to customise the graph (see below) 19 | 20 | Simple example: 21 | 22 | $("#myGraph").simplegraph([1,2,3,3,2,1], ["a","b","c","d","e","f"]); 23 | 24 | Will render a graph that looks a little something like this (open index.html for actual rendered samples): 25 | 26 | | ._. 27 | | ./ \. 28 | |/ \. 29 | |__________ 30 | a b c d e f 31 | 32 | == Options 33 | 34 | SimpleGraph takes a hash of options as it's third argument. Here's what you can customise: 35 | 36 | eg. parameter:: [default] description 37 | 38 | width:: [600] width of rendered image 39 | height:: [250] height of rendered image 40 | leftGutter:: [30] space to reserve to the left of the graph. Allocates space for y-axis caption and labels 41 | bottomGutter:: [20] space to reserve above the graph. Allocates space for x-axis 42 | topGutter:: [20] space to reserve below the graph 43 | labelColor:: [#000] text color for all labels 44 | labelFont:: ["Arial"] font for all labels 45 | labelFontSize:: ["9px"] font size for all labels 46 | gridBorderColor:: ["#ccc"] background grid color, points will be plotted on the grid 47 | drawPoints:: [true] whether or not to draw points on the graph 48 | pointColor:: ["#000"] point color 49 | pointRadius:: [3] point radius 50 | activePointRadius:: [5] active point radius - used when hovering on points 51 | drawLine:: [true] whether or not to join points on the graph with a line 52 | lineColor:: ["#000"] self explanatory yah? 53 | lineWidth:: [3] self explanatory yah? 54 | lineJoin:: ["round"] round | miter | bevel - how to join the lines on the graph 55 | fillUnderLine:: [false] fill under lines 56 | fillColor:: ["#000"] 57 | fillOpacity:: [0.2] 58 | drawBars:: [false] want a bar graph? 59 | barColor:: ["#000"] need i explain? 60 | addHover:: [true] oh hover.. displays a little popup with y and x axis values for the selected point 61 | mysteryFactor:: [0] it's a mystery 62 | 63 | == Contributors 64 | 65 | Dmitry Baranovskiy provided all of the initial code for plotting points on a graph using Raphael 66 | Ben Askins took that example and created the SimpleGraph function 67 | Lachie Cox improved sample code 68 | Lachlan Hardy removed lint 69 | Martin Stannard added bar graphs 70 | Colin Campbell-McPherson eliminated repetition from setStyleDefaults 71 | 72 | Fork it on github: http://github.com/benaskins/simplegraph 73 | 74 | Send feedback & suggestions to ben.askins [at] gmail.com 75 | 76 | == License 77 | 78 | Copyright (c) 2008 Ben Askins 79 | 80 | Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. 81 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SimpleGraph - simple javascript graphs with Raphael and jQuery 6 | 7 | 8 | 9 | 10 | 11 | 36 | 37 | 38 | 39 | Fork me on GitHub 40 | 41 |
42 |

SimpleGraph

43 |

44 | Simple javascript line graphs using Raphael and 45 | jQuery. Find me, download me, or fork me at 46 | github. The following examples should speak for themselves... 47 |

48 |
49 | 50 | 51 | 52 | 53 | 56 | 59 | 62 | 65 | 68 | 71 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 |
Temperature in 7 days
54 | Wed 17 Dec 55 | 57 | Thu 18 Dec 58 | 60 | Fri 19 Dec 61 | 63 | Sat 20 Dec 64 | 66 | Sun 21 Dec 67 | 69 | Mon 22 Dec 70 | 72 | Tue 23 Dec 73 |
20232328282225
-5-256-1-35
97 |
98 | 99 |
100 | 101 | 102 | 103 | 104 | 107 | 110 | 113 | 116 | 119 | 122 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 |
Rain in 7 days
105 | Wed 17 Dec 106 | 108 | Thu 18 Dec 109 | 111 | Fri 19 Dec 112 | 114 | Sat 20 Dec 115 | 117 | Sun 21 Dec 118 | 120 | Mon 22 Dec 121 | 123 | Tue 23 Dec 124 |
5005000
139 |
140 | 141 |

Simplest Graph (no customisation)

142 |
$("#simplest_graph_holder").simplegraph(temp_data, temp_labels);
143 |
144 | 145 |

Temperature in 7 days (with color, fill, and min y-axis value)

146 |
$("#temp_graph_holder").simplegraph(temp_data, temp_labels,
147 |   {penColor: "#f00", fillUnderLine: true, units: "ºC", minYAxisValue: 30});
148 |
149 | 150 |

Rain in 7 days (with y-axis labels)

151 |
$("#rain_graph_holder").simplegraph(rain_data, rain_labels,
152 |   {penColor: "#00f", units: "mm", leftGutter: 60, minYAxisValue: 10, yAxisCaption: "Max Rainfall"});
153 |
154 | 155 |

Combined Temperature / Rain

156 |
$("#combined_graph_holder").simplegraph(temp_data, temp_labels,
157 |   {penColor: "#f00", leftGutter: 90, units: "ºC", minYAxisValue: 30, yAxisCaption: "Max Temp", lowerBound: -10})
158 |     .simplegraph.more(rain_data, 
159 |       {penColor: "#00f", units: "mm", minYAxisValue: 10, yAxisCaption: "Max Rainfall"});
160 |
161 | 162 |

Temperature in 7 days bar graph (with color, and min y-axis value)

163 |
$("#bar_graph_holder").simplegraph(temp_data, temp_labels,
164 |   {penColor: "#f00", units: "ºC", minYAxisValue: 30, drawBars: true, drawLine: false, drawPoints: false, drawGrid: false, barWidth: 20});
165 |
166 | 167 |
168 | 169 | 170 | -------------------------------------------------------------------------------- /jquery.simplegraph.js: -------------------------------------------------------------------------------- 1 | function SimpleGraph(data, labels, canvas, settings) { 2 | 3 | this.settings = settings; 4 | 5 | setStyleDefaults(settings); 6 | 7 | this.dataSet = new DataSet(data, labels, this.settings); 8 | this.grid = new Grid(this.dataSet, this.settings); 9 | this.canvas = canvas; 10 | 11 | this.draw = function() { 12 | if (this.settings.drawGrid) { 13 | this.grid.draw(this.canvas); 14 | } 15 | if (this.settings.yAxisCaption) { 16 | this.dataSet.labelYAxis(this.grid, this.canvas); 17 | } 18 | this.dataSet.labelXAxis(this.grid, this.canvas); 19 | this.dataSet.plot(this.grid, this.canvas); 20 | }; 21 | 22 | this.replaceDataSet = function(dataSet) { 23 | this.dataSet = new DataSet(dataSet, dataSet.labels, this.settings); 24 | this.grid = new Grid(this.dataSet, this.settings); 25 | }; 26 | 27 | this.plotCurrentDataSet = function() { 28 | this.dataSet.plot(this.grid, this.canvas); 29 | }; 30 | 31 | function setStyleDefaults(settings) { 32 | var targets = ["xAxisLabel", "yAxisLabel", "yAxisCaption", "hoverLabel", "hoverValue"]; 33 | var types = ["Color", "Font", "FontSize", "FontWeight"]; 34 | jQuery.each(targets, function(index, target) { 35 | jQuery.each(types, function(index, type) { 36 | if (!settings[target + type]) { 37 | settings[target + type] = settings["label" + type]; 38 | } 39 | }); 40 | }); 41 | 42 | settings.labelStyle = { 43 | font: settings.labelFontSize + '"' + settings.labelFont + '"', 44 | fill: settings.labelColor 45 | }; 46 | 47 | jQuery.each(targets, function(index, target) { 48 | settings[target + "Style"] = { 49 | font: settings[target + "FontSize"] + ' ' + settings[target + "Font"], 50 | fill: settings[target + "Color"], 51 | "font-weight": settings[target + "FontWeight"] 52 | }; 53 | }); 54 | } 55 | } 56 | 57 | // Holds the data and labels to be plotted, provides methods for labelling the x and y axes, 58 | // and for plotting it's own points. Each method requires a grid object for translating values to 59 | // x,y pixel coordinates and a canvas object on which to draw. 60 | function DataSet(data, labels, settings) { 61 | this.data = data; 62 | this.labels = labels; 63 | this.settings = settings; 64 | 65 | this.labelXAxis = function(grid, canvas) { 66 | (function(ds) { 67 | jQuery.each(ds.labels, function(i, label) { 68 | var x = grid.x(i); 69 | canvas.text(x + ds.settings.xAxisLabelOffset, ds.settings.height - 6, label).attr(ds.settings.xAxisLabelStyle); 70 | }); 71 | })(this); 72 | }; 73 | 74 | this.labelYAxis = function(grid, canvas) { 75 | // Legend 76 | canvas.rect( 77 | grid.leftEdge - (30 + this.settings.yAxisOffset), //TODO PARAM - Label Colum Width 78 | grid.topEdge, 79 | 30, //TODO PARAM - Label Column Width 80 | grid.height 81 | ).attr({stroke: this.settings.lineColor, fill: this.settings.lineColor, opacity: 0.3}); //TODO PARAMS - legend border and fill style 82 | 83 | for (var i = 1, ii = (grid.rows); i < (ii - this.settings.lowerBound/2); i = i + 2) { 84 | var value = (ii - i)*2, 85 | y = grid.y(value) + 4, // TODO: Value of 4 works for default dimensions, expect will need to scale 86 | x = grid.leftEdge - (6 + this.settings.yAxisOffset); 87 | canvas.text(x, y, value).attr(this.settings.yAxisLabelStyle); 88 | } 89 | var caption = canvas.text( 90 | grid.leftEdge - (20 + this.settings.yAxisOffset), 91 | (grid.height/2) + (this.settings.yAxisCaption.length / 2), 92 | this.settings.yAxisCaption + " (" + this.settings.units + ")").attr(this.settings.yAxisCaptionStyle).rotate(270); 93 | // Increase the offset for the next caption (if any) 94 | this.settings.yAxisOffset = this.settings.yAxisOffset + 30; 95 | }; 96 | 97 | this.plot = function(grid, canvas) { 98 | var line_path = canvas.path().attr({ 99 | stroke: this.settings.lineColor, 100 | "stroke-width": this.settings.lineWidth, 101 | "stroke-linejoin": this.settings.lineJoin 102 | }); 103 | 104 | var fill_path = canvas.path().attr({ 105 | stroke: "none", 106 | fill: this.settings.fillColor, 107 | opacity: this.settings.fillOpacity 108 | }).moveTo(this.settings.leftGutter, this.settings.height - this.settings.bottomGutter); 109 | 110 | var hoverFrame = canvas.rect(10, 10, 100, 40, 5).attr({ 111 | fill: "#fff", stroke: "#474747", "stroke-width": 2}).hide(); //TODO PARAM - fill colour, border colour, border width 112 | var hoverText = []; 113 | hoverText[0] = canvas.text(60, 25, "").attr(this.settings.hoverValueStyle).hide(); 114 | hoverText[1] = canvas.text(60, 40, "").attr(this.settings.hoverLabelStyle).hide(); 115 | 116 | // Plot the points 117 | (function(dataSet) { 118 | jQuery.each(dataSet.data, function(i, value) { 119 | var y = grid.y(value), 120 | x = grid.x(i), 121 | label = dataSet.labels ? dataSet.labels[i] : " "; 122 | 123 | if (dataSet.settings.drawPoints) { 124 | var dot = canvas.circle(x, y, dataSet.settings.pointRadius).attr({fill: dataSet.settings.pointColor, stroke: "#fff"}); 125 | } 126 | if (dataSet.settings.drawBars) { 127 | canvas.rect(x + dataSet.settings.barOffset, y, dataSet.settings.barWidth, (dataSet.settings.height - dataSet.settings.bottomGutter) - y).attr({fill: dataSet.settings.barColor, stroke: "none"}); 128 | } 129 | if (dataSet.settings.drawLine) { 130 | line_path[i == 0 ? "moveTo" : "cplineTo"](x, y, dataSet.settings.cpWidth).attr({opacity: 0.7}); 131 | } 132 | if (dataSet.settings.fillUnderLine) { 133 | fill_path[i == 0 ? "lineTo" : "cplineTo"](x, y, dataSet.settings.cpWidth); 134 | } 135 | if (dataSet.settings.addHover) { 136 | var rect = canvas.rect(x - 50, y - 50, 100, 100).attr({stroke: "none", fill: "#fff", opacity: 0}); //TODO PARAM - hover target width / height 137 | jQuery(rect[0]).hover( function() { 138 | jQuery.fn.simplegraph.hoverIn(canvas, value, label, x, y, hoverFrame, hoverText, dot, dataSet.settings); 139 | }, 140 | function() { 141 | jQuery.fn.simplegraph.hoverOut(canvas, hoverFrame, hoverText, dot, dataSet.settings); 142 | }); 143 | } 144 | }); 145 | })(this); 146 | 147 | if (this.settings.fillUnderLine) { 148 | fill_path.lineTo(grid.x(this.data.length - 1), this.settings.height - this.settings.bottomGutter).andClose(); 149 | } 150 | hoverFrame.toFront(); 151 | }; 152 | } 153 | 154 | // Holds the dimensions of the grid, and provides methods to convert values into x,y 155 | // pixel coordinates. Also, provides a method to draw a grid on a supplied canvas. 156 | function Grid(dataSet, settings) { 157 | this.dataSet = dataSet; 158 | this.settings = settings; 159 | 160 | this.calculateMaxYAxis = function() { 161 | var max = Math.max.apply(Math, this.dataSet.data), 162 | maxOveride = this.settings.minYAxisValue; 163 | if (maxOveride && maxOveride > max) { 164 | max = maxOveride; 165 | } 166 | return max; 167 | }; 168 | 169 | this.setYAxis = function() { 170 | this.height = this.settings.height - this.settings.topGutter - this.settings.bottomGutter; 171 | this.maxValueYAxis = this.calculateMaxYAxis(); 172 | this.Y = this.height / (this.maxValueYAxis - this.settings.lowerBound); 173 | }; 174 | 175 | this.setXAxis = function() { 176 | this.X = (this.settings.width - this.settings.leftGutter) / this.dataSet.data.length; 177 | }; 178 | 179 | this.setDimensions = function() { 180 | this.leftEdge = this.settings.leftGutter; 181 | this.topEdge = this.settings.topGutter; 182 | this.width = this.settings.width - this.settings.leftGutter - this.X; 183 | this.columns = this.dataSet.data.length - 1; 184 | this.rows = (this.maxValueYAxis - this.settings.lowerBound) / 2; //TODO PARAM - steps per row 185 | }; 186 | 187 | this.draw = function(canvas) { 188 | canvas.drawGrid( 189 | this.leftEdge, 190 | this.topEdge, 191 | this.width, 192 | this.height, 193 | this.columns, 194 | this.rows, 195 | this.settings.gridBorderColor 196 | ); 197 | }; 198 | 199 | this.x = function(value) { 200 | return this.settings.leftGutter + this.X * value; 201 | }; 202 | 203 | this.y = function(value) { 204 | return this.settings.height - this.settings.bottomGutter - this.Y * (value - this.settings.lowerBound); 205 | }; 206 | 207 | this.setYAxis(); 208 | this.setXAxis(); 209 | this.setDimensions(); 210 | 211 | }; 212 | 213 | (function($) { 214 | 215 | //- required to implement hover function 216 | var isLabelVisible; 217 | var leaveTimer; 218 | 219 | $.fn.simplegraph = function(data, labels, options) { 220 | var settings = $.extend({}, $.fn.simplegraph.defaults, options); 221 | setPenColor(settings); 222 | 223 | return this.each( function() { 224 | var canvas = Raphael(this, settings.width, settings.height); 225 | var simplegraph = new SimpleGraph(data, labels, canvas, settings); 226 | 227 | simplegraph.draw(); 228 | 229 | // Stash simplegraph object away for future reference 230 | $.data(this, "simplegraph", simplegraph); 231 | }); 232 | }; 233 | 234 | // Plot another set of values on an existing graph, use it like this: 235 | // $("#target").simplegraph(data, labels).simplegraph_more(moreData); 236 | $.fn.simplegraph_more = function(data, options) { 237 | return this.each( function() { 238 | var sg = $.data(this, "simplegraph"); 239 | sg.dataSet = new DataSet(data, sg.dataSet.labels, sg.settings); 240 | sg.settings.penColor = options.penColor; 241 | setPenColor(sg.settings); 242 | sg.settings = $.extend(sg.settings, options); 243 | sg.grid = new Grid(sg.dataSet, sg.settings); 244 | sg.dataSet.labelYAxis(sg.grid, sg.canvas); 245 | sg.dataSet.plot(sg.grid, sg.canvas); 246 | }); 247 | }; 248 | 249 | // Public 250 | 251 | $.fn.simplegraph.defaults = { 252 | drawGrid: false, 253 | units: "", 254 | // Dimensions 255 | width: 600, 256 | height: 250, 257 | leftGutter: 30, 258 | bottomGutter: 20, 259 | topGutter: 20, 260 | // Label Style 261 | labelColor: "#000", 262 | labelFont: "Helvetica", 263 | labelFontSize: "10px", 264 | labelFontWeight: "normal", 265 | // Grid Style 266 | gridBorderColor: "#ccc", 267 | // -- Y Axis Captions 268 | yAxisOffset: 0, 269 | // -- Y Axis Captions 270 | xAxisLabelOffset: 0, 271 | // Graph Style 272 | // -- Points 273 | drawPoints: false, 274 | pointColor: "#000", 275 | pointRadius: 3, 276 | activePointRadius: 5, 277 | // -- Line 278 | drawLine: true, 279 | lineColor: "#000", 280 | lineWidth: 3, 281 | lineJoin: "round", 282 | cpWidth: 5, 283 | // -- Bars 284 | drawBars: false, 285 | barColor: "#000", 286 | barWidth: 10, 287 | barOffset: 0, 288 | // -- Fill 289 | fillUnderLine: false, 290 | fillColor: "#000", 291 | fillOpacity: 0.2, 292 | // -- Hover 293 | addHover: true, 294 | // Calculations 295 | lowerBound: 0 296 | }; 297 | 298 | // Default hoverIn callback, this is public and as such can be overwritten. You can write your 299 | // own call back with the same signature if you want different behaviour. 300 | $.fn.simplegraph.hoverIn = function(canvas, value, label, x, y, frame, hoverLabel, dot, settings) { 301 | clearTimeout(leaveTimer); 302 | var newcoord = {x: x * 1 + 7.5, y: y - 19}; 303 | if (newcoord.x + 100 > settings.width) { 304 | newcoord.x -= 114; 305 | } 306 | hoverLabel[0].attr({text: value}).show().animate({x : newcoord.x + 50, y : newcoord.y + 15}, (isLabelVisible ? 100 : 0)); 307 | hoverLabel[1].attr({text: label}).show().animate({x : newcoord.x + 50, y : newcoord.y + 30}, (isLabelVisible ? 100 : 0)); 308 | frame.show().animate({x: newcoord.x, y: newcoord.y}, (isLabelVisible ? 100 : 0)); 309 | if (settings.drawPoints) { 310 | dot.attr("r", settings.activePointRadius); 311 | } 312 | isLabelVisible = true; 313 | canvas.safari(); 314 | }; 315 | 316 | // Default hoverOut callback, this is public and as such can be overwritten. You can write your 317 | // own call back with the same signature if you want different behaviour. 318 | $.fn.simplegraph.hoverOut = function(canvas, frame, label, dot, settings) { 319 | if (settings.drawPoints) { 320 | dot.attr("r", settings.pointRadius); 321 | } 322 | canvas.safari(); 323 | leaveTimer = setTimeout(function () { 324 | isLabelVisible = false; 325 | frame.hide(); 326 | label[0].hide(); 327 | label[1].hide(); 328 | canvas.safari(); 329 | }, 1); 330 | }; 331 | 332 | // Private 333 | 334 | function setPenColor(settings) { 335 | if (settings.penColor) { 336 | settings.lineColor = settings.penColor; 337 | settings.pointColor = settings.penColor; 338 | settings.fillColor = settings.penColor; 339 | settings.barColor = settings.penColor; 340 | } 341 | } 342 | 343 | })(jQuery); 344 | -------------------------------------------------------------------------------- /sample.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | // Get the data 4 | var temp_labels = [], temp_data = [], neg_temp_data = []; 5 | $("#temp-data tfoot th").each(function () { 6 | temp_labels.push($(this).html()); 7 | }); 8 | $("#temp-data tbody td.pos").each(function () { 9 | temp_data.push($(this).html()); 10 | }); 11 | $("#temp-data tbody td.neg").each(function () { 12 | neg_temp_data.push($(this).html()); 13 | }); 14 | var rain_labels = [], rain_data = []; 15 | $("#rain-data tfoot th").each(function () { 16 | rain_labels.push($(this).html()); 17 | }); 18 | $("#rain-data tbody td").each(function () { 19 | rain_data.push($(this).html()); 20 | }); 21 | 22 | // Hide the data 23 | $("#rain-data").hide(); 24 | $("#temp-data").hide(); 25 | 26 | // Plot the data 27 | // - Simplest Possible Graph - no customisations 28 | $("#simplest_graph_holder").simplegraph(temp_data, temp_labels); 29 | 30 | // - Temperature Graph - adds colour, fill, and a minimum value for the y axis 31 | $("#temp_graph_holder").simplegraph(temp_data, temp_labels, 32 | {penColor: "#f00", fillUnderLine: true, units: "ºC", minYAxisValue: 30, drawPoints: true}); 33 | 34 | // - Rain Graph - adds a caption to the y axis 35 | $("#rain_graph_holder").simplegraph(rain_data, rain_labels, 36 | {penColor: "#00f", units: "mm", leftGutter: 60, minYAxisValue: 10, yAxisCaption: "Max Rainfall", yAxisLabelFont: "Times New Roman"}); 37 | 38 | // - Combined Graph - plots two data sets with different scales on the one graph 39 | $("#combined_graph_holder").simplegraph(neg_temp_data, temp_labels, 40 | {penColor: "#f00", leftGutter: 90, units: "ºC", minYAxisValue: 10, yAxisCaption: "Max Temp", lowerBound: -10, drawPoints: true}) 41 | .simplegraph_more(rain_data, {penColor: "#00f", units: "mm", minYAxisValue: 10, yAxisCaption: "Max Rainfall", lowerBound: 0}); 42 | 43 | // - Bar Temperature Graph - adds colour, fill, and a minimum value for the y axis 44 | $("#bar_graph_holder").simplegraph(temp_data, temp_labels, 45 | {penColor: "#f00", units: "ºC", minYAxisValue: 30, drawBars: true, drawLine: false, drawPoints: false, drawGrid: false, barWidth: 20}); 46 | 47 | }); 48 | -------------------------------------------------------------------------------- /samples/images/chart-arrow-e.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benaskins/simplegraph/fe6120356822cb1e1e3b9d28f978059f4f38c746/samples/images/chart-arrow-e.gif -------------------------------------------------------------------------------- /samples/images/chart-arrow-ene.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benaskins/simplegraph/fe6120356822cb1e1e3b9d28f978059f4f38c746/samples/images/chart-arrow-ene.gif -------------------------------------------------------------------------------- /samples/images/chart-arrow-ese.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benaskins/simplegraph/fe6120356822cb1e1e3b9d28f978059f4f38c746/samples/images/chart-arrow-ese.gif -------------------------------------------------------------------------------- /samples/images/chart-arrow-n.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benaskins/simplegraph/fe6120356822cb1e1e3b9d28f978059f4f38c746/samples/images/chart-arrow-n.gif -------------------------------------------------------------------------------- /samples/images/chart-arrow-ne.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benaskins/simplegraph/fe6120356822cb1e1e3b9d28f978059f4f38c746/samples/images/chart-arrow-ne.gif -------------------------------------------------------------------------------- /samples/images/chart-arrow-nne.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benaskins/simplegraph/fe6120356822cb1e1e3b9d28f978059f4f38c746/samples/images/chart-arrow-nne.gif -------------------------------------------------------------------------------- /samples/images/chart-arrow-nnw.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benaskins/simplegraph/fe6120356822cb1e1e3b9d28f978059f4f38c746/samples/images/chart-arrow-nnw.gif -------------------------------------------------------------------------------- /samples/images/chart-arrow-nw.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benaskins/simplegraph/fe6120356822cb1e1e3b9d28f978059f4f38c746/samples/images/chart-arrow-nw.gif -------------------------------------------------------------------------------- /samples/images/chart-arrow-s.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benaskins/simplegraph/fe6120356822cb1e1e3b9d28f978059f4f38c746/samples/images/chart-arrow-s.gif -------------------------------------------------------------------------------- /samples/images/chart-arrow-se.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benaskins/simplegraph/fe6120356822cb1e1e3b9d28f978059f4f38c746/samples/images/chart-arrow-se.gif -------------------------------------------------------------------------------- /samples/images/chart-arrow-sse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benaskins/simplegraph/fe6120356822cb1e1e3b9d28f978059f4f38c746/samples/images/chart-arrow-sse.gif -------------------------------------------------------------------------------- /samples/images/chart-arrow-ssw.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benaskins/simplegraph/fe6120356822cb1e1e3b9d28f978059f4f38c746/samples/images/chart-arrow-ssw.gif -------------------------------------------------------------------------------- /samples/images/chart-arrow-sw.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benaskins/simplegraph/fe6120356822cb1e1e3b9d28f978059f4f38c746/samples/images/chart-arrow-sw.gif -------------------------------------------------------------------------------- /samples/images/chart-arrow-w.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benaskins/simplegraph/fe6120356822cb1e1e3b9d28f978059f4f38c746/samples/images/chart-arrow-w.gif -------------------------------------------------------------------------------- /samples/images/chart-arrow-wnw.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benaskins/simplegraph/fe6120356822cb1e1e3b9d28f978059f4f38c746/samples/images/chart-arrow-wnw.gif -------------------------------------------------------------------------------- /samples/images/chart-arrow-wsw.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benaskins/simplegraph/fe6120356822cb1e1e3b9d28f978059f4f38c746/samples/images/chart-arrow-wsw.gif -------------------------------------------------------------------------------- /samples/images/chart-swell.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benaskins/simplegraph/fe6120356822cb1e1e3b9d28f978059f4f38c746/samples/images/chart-swell.gif -------------------------------------------------------------------------------- /samples/images/chart-wind.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benaskins/simplegraph/fe6120356822cb1e1e3b9d28f978059f4f38c746/samples/images/chart-wind.gif -------------------------------------------------------------------------------- /samples/swellchart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SimpleGraph - simple javascript graphs with Raphael and jQuery 6 | 7 | 8 | 9 | 10 | 38 | 39 | 40 | 41 | Fork me on GitHub 42 | 43 |
44 |

SimpleGraph - Swell Chart Example

45 |

46 | Using SimpleGraph to create more sophisticated graphs. See below for the sample code. 47 |

48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |
 
92 |
93 |
 94 |         (function($) {
 95 | 
 96 |           var SwellChart = {
 97 |             drawBackground: function() {
 98 |               this.canvas.rect(0, 0, 300, 330).attr({stroke: "none", fill: "#eaeaea"});
 99 |             },
100 | 
101 |             plotAmWaveHeights: function() {
102 |               this.dataSet.plot(this.grid, this.canvas);
103 |               this.addAmPmIndicators("am");          
104 |             },
105 | 
106 |             plotPmWaveHeights: function(wave_height_pm) {
107 |               this.settings.barOffset = this.settings.barWidth;
108 |               this.settings.barColor = "#fff";
109 |               this.replaceDataSet(wave_height_pm);
110 |               this.plotCurrentDataSet();
111 |               this.addAmPmIndicators("pm");
112 |             },
113 | 
114 |             drawYAxisSightLines: function() {
115 |               var increment = 1;
116 |               if (this.grid.maxValueYAxis > 10) {
117 |                 increment = 2;
118 |               }
119 |               if (this.grid.maxValueYAxis > 20) {
120 |                 increment = 3;
121 |               }
122 |               for (var i = 0, ii = (this.grid.maxValueYAxis); i <= ii; i = i + increment) {
123 |                 var value = i,
124 |                     y     = this.grid.y(value),
125 |                     x     = this.grid.leftEdge;
126 |                 this.canvas.path({stroke: "#fff"}).moveTo(x,y).lineTo(285, y);
127 |               }
128 |             },
129 | 
130 |             addAmPmIndicators: function(am_or_pm) {
131 |               (function(sg) {
132 |                 $.each(sg.dataSet.data, function(i, value) {
133 |                   var x = sg.grid.x(i);  
134 |                   var color = (am_or_pm == "am") ? "#fff" : "#000";
135 |                   sg.canvas.text(x + sg.dataSet.settings.barOffset + 10, 192, am_or_pm.toUpperCase()).attr({"font-family": "Arial", "font-size": "8px", fill: color});
136 |                 });            
137 |               })(this);
138 |             },
139 | 
140 |             labelYAxis: function() {
141 |               var increment  = 1;
142 |               var start_from = 1;
143 |               if (this.grid.maxValueYAxis > 10) {
144 |                 start_from = 2;
145 |                 increment = 2;
146 |               }
147 |               if (this.grid.maxValueYAxis > 20) {
148 |                 start_from = 3;
149 |                 increment = 3;
150 |               }
151 |               for (var i = start_from, ii = (this.grid.maxValueYAxis); i <= ii; i = i + increment) {
152 |                 var value = i,
153 |                     y     = this.grid.y(value) + 4,
154 |                     x     = this.grid.leftEdge - 15;
155 |                 this.canvas.text(x, y, value + "ft").attr(this.settings.yAxisLabelStyle);        
156 |               }
157 |             },
158 | 
159 |             plotSwellDirections: function(swell_direction) {
160 |               // Add the swell direction rectangle
161 |               this.canvas.rect(this.grid.leftEdge, 225, 285 - this.grid.leftEdge, 40).attr({stroke: "none", fill: "#fff"});
162 |               this.canvas.image("images/chart-swell.gif", this.grid.leftEdge - 15, 230, 8, 27);
163 |               // Plot the directions
164 |               (function(sg) {
165 |                 $.each(swell_direction, function(i, sdir) {
166 |                   var x = sg.grid.x(i);
167 |                   sg.canvas.image("images/chart-arrow-" + sdir + ".gif", x + 12, 232, 16, 16);
168 |                   sg.canvas.text(x + 20, 259, sdir.toUpperCase()).attr(sg.settings.windDirLabelStyle);
169 |                 });                
170 |               })(this);
171 |             },
172 | 
173 |             plotWindDirections: function(wind_direction_am, wind_direction_pm) {
174 |               // Add the wind direction rectangle
175 |               this.canvas.rect(this.grid.leftEdge, 275, 285 - this.grid.leftEdge, 40).attr({stroke: "none", fill: "#fff"});
176 |               this.canvas.image("images/chart-wind.gif", this.grid.leftEdge - 15, 285, 8, 19);
177 |               // Plot the directions
178 |               this.plotWindDirection(wind_direction_am, 2);
179 |               this.plotWindDirection(wind_direction_pm, 22);
180 |             },
181 | 
182 |             plotWindDirection: function(wind_direction, offset) {
183 |               (function(sg) {
184 |                 $.each(wind_direction, function(i, sdir) {
185 |                   var x = sg.grid.x(i);
186 |                   sg.canvas.image("images/chart-arrow-" + sdir + ".gif", x + offset, 283 , 16, 15).toFront();
187 |                   sg.canvas.text(x + offset + 8, 310, sdir.toUpperCase()).attr(sg.settings.windDirLabelStyle).toFront();
188 |                 });            
189 |               })(this);
190 |             },
191 | 
192 |             draw: function() {
193 |               this.drawBackground();
194 |               this.drawYAxisSightLines();
195 |               this.dataSet.labelXAxis(this.grid, this.canvas);
196 |               this.plotAmWaveHeights();
197 |               this.plotPmWaveHeights(wave_height_pm);
198 |               this.labelYAxis();
199 |               this.plotSwellDirections(swell_direction);
200 |               this.plotWindDirections(wind_direction_am, wind_direction_pm);
201 |             },
202 | 
203 |             defaults: {
204 |               width: 300,
205 |               height: 215,
206 |               drawLine: false,
207 |               drawBars: true,
208 |               barWidth: 20,
209 |               barOffset: 0,
210 |               barColor: "#416A97",
211 |               xAxisLabelOffset: 20,
212 |               // Day labels
213 |               xAxisLabelFont: "Helvetica",
214 |               xAxisLabelColor: "#58595B",
215 |               xAxisLabelFontSize: "11px",
216 |               xAxisLabelFontWeight: "600",
217 |               // Wave height labels (1ft, 2ft...)
218 |               yAxisLabelFont: "Helvetica",
219 |               yAxisLabelColor: "#58595B",
220 |               yAxisLabelFontSize: "11px",
221 |               yAxisLabelFontWeight: "600",
222 |               // Wind direction labels
223 |               windDirLabelFont: "Arial",
224 |               windDirLabelFontSize: "8px",
225 |               windDirLabelStyle: {
226 |                 font: "8px Arial"
227 |               }
228 |             }
229 |           };
230 | 
231 |           var wave_height_am    = [], 
232 |               wave_height_pm    = [],
233 |               swell_direction   = [],
234 |               wind_direction_am = [],
235 |               wind_direction_pm = [],
236 |               forecast_day      = [];
237 | 
238 |           $(function() {
239 |             gatherData();
240 |             $(".swellgraph .chart").each( function() {
241 |               var canvas = Raphael(this, 300, 330);
242 |               var swellchart = new SimpleGraph(wave_height_am, forecast_day, canvas, $.extend({minYAxisValue: maxWaveHeight()}, $.fn.simplegraph.defaults, SwellChart.defaults));
243 |               $.extend(swellchart, SwellChart);
244 |               swellchart.draw();
245 |             });
246 |             $(".swellgraph table").hide();  
247 |           });
248 | 
249 |           function gatherData() {
250 |             $("table.swelldata td.wave_height_am").each(function () {
251 |                 wave_height_am.push($(this).html());
252 |             });
253 | 
254 |             $("table.swelldata td.wave_height_pm").each(function () {
255 |                 wave_height_pm.push($(this).html());
256 |             });
257 | 
258 |             $("table.swelldata td.swell_direction").each(function () {
259 |                 swell_direction.push($(this).html());
260 |             });
261 | 
262 |             $("table.swelldata td.wind_direction_am").each(function () {
263 |                 wind_direction_am.push($(this).html());
264 |             });
265 | 
266 |             $("table.swelldata td.wind_direction_pm").each(function () {
267 |                 wind_direction_pm.push($(this).html());
268 |             });
269 | 
270 |             $("table.swelldata td.forecast_day").each(function () {
271 |                 forecast_day.push($(this).html());
272 |             });
273 |           }
274 | 
275 |           function maxWaveHeight() {
276 |             return Math.ceil(Math.max.apply(Math, wave_height_am.concat(wave_height_pm)));
277 |           }
278 | 
279 |         })(jQuery);      
280 |
281 | 282 | 283 | -------------------------------------------------------------------------------- /samples/swellchart.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 3 | var SwellChart = { 4 | drawBackground: function() { 5 | this.canvas.rect(0, 0, 300, 330).attr({stroke: "none", fill: "#eaeaea"}); 6 | }, 7 | 8 | plotAmWaveHeights: function() { 9 | this.dataSet.plot(this.grid, this.canvas); 10 | this.addAmPmIndicators("am"); 11 | }, 12 | 13 | plotPmWaveHeights: function(wave_height_pm) { 14 | this.settings.barOffset = this.settings.barWidth; 15 | this.settings.barColor = "#fff"; 16 | this.replaceDataSet(wave_height_pm); 17 | this.plotCurrentDataSet(); 18 | this.addAmPmIndicators("pm"); 19 | }, 20 | 21 | drawYAxisSightLines: function() { 22 | var increment = 1; 23 | if (this.grid.maxValueYAxis > 10) { 24 | increment = 2; 25 | } 26 | if (this.grid.maxValueYAxis > 20) { 27 | increment = 3; 28 | } 29 | for (var i = 0, ii = (this.grid.maxValueYAxis); i <= ii; i = i + increment) { 30 | var value = i, 31 | y = this.grid.y(value), 32 | x = this.grid.leftEdge; 33 | this.canvas.path({stroke: "#fff"}).moveTo(x,y).lineTo(285, y); 34 | } 35 | }, 36 | 37 | addAmPmIndicators: function(am_or_pm) { 38 | (function(sg) { 39 | $.each(sg.dataSet.data, function(i, value) { 40 | var x = sg.grid.x(i); 41 | var color = (am_or_pm == "am") ? "#fff" : "#000"; 42 | sg.canvas.text(x + sg.dataSet.settings.barOffset + 10, 192, am_or_pm.toUpperCase()).attr({"font-family": "Arial", "font-size": "8px", fill: color}); 43 | }); 44 | })(this); 45 | }, 46 | 47 | labelYAxis: function() { 48 | var increment = 1; 49 | var start_from = 1; 50 | if (this.grid.maxValueYAxis > 10) { 51 | start_from = 2; 52 | increment = 2; 53 | } 54 | if (this.grid.maxValueYAxis > 20) { 55 | start_from = 3; 56 | increment = 3; 57 | } 58 | for (var i = start_from, ii = (this.grid.maxValueYAxis); i <= ii; i = i + increment) { 59 | var value = i, 60 | y = this.grid.y(value) + 4, 61 | x = this.grid.leftEdge - 15; 62 | this.canvas.text(x, y, value + "ft").attr(this.settings.yAxisLabelStyle); 63 | } 64 | }, 65 | 66 | plotSwellDirections: function(swell_direction) { 67 | // Add the swell direction rectangle 68 | this.canvas.rect(this.grid.leftEdge, 225, 285 - this.grid.leftEdge, 40).attr({stroke: "none", fill: "#fff"}); 69 | this.canvas.image("images/chart-swell.gif", this.grid.leftEdge - 15, 230, 8, 27); 70 | // Plot the directions 71 | (function(sg) { 72 | $.each(swell_direction, function(i, sdir) { 73 | var x = sg.grid.x(i); 74 | sg.canvas.image("images/chart-arrow-" + sdir + ".gif", x + 12, 232, 16, 16); 75 | sg.canvas.text(x + 20, 259, sdir.toUpperCase()).attr(sg.settings.windDirLabelStyle); 76 | }); 77 | })(this); 78 | }, 79 | 80 | plotWindDirections: function(wind_direction_am, wind_direction_pm) { 81 | // Add the wind direction rectangle 82 | this.canvas.rect(this.grid.leftEdge, 275, 285 - this.grid.leftEdge, 40).attr({stroke: "none", fill: "#fff"}); 83 | this.canvas.image("images/chart-wind.gif", this.grid.leftEdge - 15, 285, 8, 19); 84 | // Plot the directions 85 | this.plotWindDirection(wind_direction_am, 2); 86 | this.plotWindDirection(wind_direction_pm, 22); 87 | }, 88 | 89 | plotWindDirection: function(wind_direction, offset) { 90 | (function(sg) { 91 | $.each(wind_direction, function(i, sdir) { 92 | var x = sg.grid.x(i); 93 | sg.canvas.image("images/chart-arrow-" + sdir + ".gif", x + offset, 283 , 16, 15).toFront(); 94 | sg.canvas.text(x + offset + 8, 310, sdir.toUpperCase()).attr(sg.settings.windDirLabelStyle).toFront(); 95 | }); 96 | })(this); 97 | }, 98 | 99 | draw: function() { 100 | this.drawBackground(); 101 | this.drawYAxisSightLines(); 102 | this.dataSet.labelXAxis(this.grid, this.canvas); 103 | this.plotAmWaveHeights(); 104 | this.plotPmWaveHeights(wave_height_pm); 105 | this.labelYAxis(); 106 | this.plotSwellDirections(swell_direction); 107 | this.plotWindDirections(wind_direction_am, wind_direction_pm); 108 | }, 109 | 110 | defaults: { 111 | width: 300, 112 | height: 215, 113 | drawLine: false, 114 | drawBars: true, 115 | barWidth: 20, 116 | barOffset: 0, 117 | barColor: "#416A97", 118 | xAxisLabelOffset: 20, 119 | addHover: false, 120 | // Day labels 121 | xAxisLabelFont: "Helvetica", 122 | xAxisLabelColor: "#58595B", 123 | xAxisLabelFontSize: "11px", 124 | xAxisLabelFontWeight: "600", 125 | // Wave height labels (1ft, 2ft...) 126 | yAxisLabelFont: "Helvetica", 127 | yAxisLabelColor: "#58595B", 128 | yAxisLabelFontSize: "11px", 129 | yAxisLabelFontWeight: "600", 130 | // Wind direction labels 131 | windDirLabelFont: "Arial", 132 | windDirLabelFontSize: "8px", 133 | windDirLabelStyle: { 134 | font: "8px Arial" 135 | } 136 | } 137 | }; 138 | 139 | var wave_height_am = [], 140 | wave_height_pm = [], 141 | swell_direction = [], 142 | wind_direction_am = [], 143 | wind_direction_pm = [], 144 | forecast_day = []; 145 | 146 | $(function() { 147 | gatherData(); 148 | $(".swellgraph .chart").each( function() { 149 | var canvas = Raphael(this, 300, 330); 150 | var swellchart = new SimpleGraph(wave_height_am, forecast_day, canvas, $.extend({minYAxisValue: maxWaveHeight()}, $.fn.simplegraph.defaults, SwellChart.defaults)); 151 | $.extend(swellchart, SwellChart); 152 | swellchart.draw(); 153 | }); 154 | $(".swellgraph table").hide(); 155 | }); 156 | 157 | function gatherData() { 158 | $("table.swelldata td.wave_height_am").each(function () { 159 | wave_height_am.push($(this).html()); 160 | }); 161 | 162 | $("table.swelldata td.wave_height_pm").each(function () { 163 | wave_height_pm.push($(this).html()); 164 | }); 165 | 166 | $("table.swelldata td.swell_direction").each(function () { 167 | swell_direction.push($(this).html()); 168 | }); 169 | 170 | $("table.swelldata td.wind_direction_am").each(function () { 171 | wind_direction_am.push($(this).html()); 172 | }); 173 | 174 | $("table.swelldata td.wind_direction_pm").each(function () { 175 | wind_direction_pm.push($(this).html()); 176 | }); 177 | 178 | $("table.swelldata td.forecast_day").each(function () { 179 | forecast_day.push($(this).html()); 180 | }); 181 | } 182 | 183 | function maxWaveHeight() { 184 | return Math.ceil(Math.max.apply(Math, wave_height_am.concat(wave_height_pm))); 185 | } 186 | 187 | })(jQuery); -------------------------------------------------------------------------------- /template/customgraph.js: -------------------------------------------------------------------------------- 1 | // Use this as a starting point to implement your own custom graphs 2 | 3 | (function($) { 4 | 5 | var CustomGraph = { 6 | 7 | // Add function definitions here, they'll be used to extend your SimpleGraph instances 8 | // e.g. 9 | // 10 | // drawBackground: function() { 11 | // this.canvas.rect(0, 0, 300, 300).attr({stroke: "none", fill: "#eaeaea"}); 12 | // }, 13 | // 14 | // Implement a draw() function if you want to change the standard implementation 15 | // e.g. 16 | // 17 | // draw: function() { 18 | // this.drawBackground(); 19 | // this.dataSet.labelXAxis(this.grid, this.canvas); 20 | // this.dataSet.labelYAxis(this.grid, this.canvas); 21 | // this.dataSet.plot(this.grid, this.canvas); 22 | // }, 23 | 24 | 25 | // Default settings for your custom graph 26 | // e.g. 27 | // 28 | // defaults: { 29 | // width: 200, 30 | // height: 200, 31 | // drawLine: false, 32 | // drawBars: true, 33 | // barWidth: 20, 34 | // barOffset: 0, 35 | // barColor: "#416A97" 36 | // } 37 | }; 38 | 39 | var data = [], 40 | labels = []; 41 | 42 | $(function() { 43 | gatherData(); 44 | $("#graph").each( function() { 45 | var canvas = Raphael(this, 300, 300); 46 | var custom_graph = new SimpleGraph(data, labels, canvas, $.extend({}, $.fn.simplegraph.defaults, CustomGraph.defaults)); 47 | $.extend(custom_graph, CustomGraph); 48 | custom_graph.draw(); 49 | }); 50 | }); 51 | 52 | function gatherData() { 53 | $("table td.data").each(function () { 54 | data.push($(this).html()); 55 | }); 56 | 57 | $("table td.label").each(function () { 58 | labels.push($(this).html()); 59 | }); 60 | } 61 | 62 | })(jQuery); -------------------------------------------------------------------------------- /vendor/jquery.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | /* 3 | * jQuery 1.2.6 - New Wave Javascript 4 | * 5 | * Copyright (c) 2008 John Resig (jquery.com) 6 | * Dual licensed under the MIT (MIT-LICENSE.txt) 7 | * and GPL (GPL-LICENSE.txt) licenses. 8 | * 9 | * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $ 10 | * $Rev: 5685 $ 11 | */ 12 | 13 | // Map over jQuery in case of overwrite 14 | var _jQuery = window.jQuery, 15 | // Map over the $ in case of overwrite 16 | _$ = window.$; 17 | 18 | var jQuery = window.jQuery = window.$ = function( selector, context ) { 19 | // The jQuery object is actually just the init constructor 'enhanced' 20 | return new jQuery.fn.init( selector, context ); 21 | }; 22 | 23 | // A simple way to check for HTML strings or ID strings 24 | // (both of which we optimize for) 25 | var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/, 26 | 27 | // Is it a simple selector 28 | isSimple = /^.[^:#\[\.]*$/, 29 | 30 | // Will speed up references to undefined, and allows munging its name. 31 | undefined; 32 | 33 | jQuery.fn = jQuery.prototype = { 34 | init: function( selector, context ) { 35 | // Make sure that a selection was provided 36 | selector = selector || document; 37 | 38 | // Handle $(DOMElement) 39 | if ( selector.nodeType ) { 40 | this[0] = selector; 41 | this.length = 1; 42 | return this; 43 | } 44 | // Handle HTML strings 45 | if ( typeof selector == "string" ) { 46 | // Are we dealing with HTML string or an ID? 47 | var match = quickExpr.exec( selector ); 48 | 49 | // Verify a match, and that no context was specified for #id 50 | if ( match && (match[1] || !context) ) { 51 | 52 | // HANDLE: $(html) -> $(array) 53 | if ( match[1] ) 54 | selector = jQuery.clean( [ match[1] ], context ); 55 | 56 | // HANDLE: $("#id") 57 | else { 58 | var elem = document.getElementById( match[3] ); 59 | 60 | // Make sure an element was located 61 | if ( elem ){ 62 | // Handle the case where IE and Opera return items 63 | // by name instead of ID 64 | if ( elem.id != match[3] ) 65 | return jQuery().find( selector ); 66 | 67 | // Otherwise, we inject the element directly into the jQuery object 68 | return jQuery( elem ); 69 | } 70 | selector = []; 71 | } 72 | 73 | // HANDLE: $(expr, [context]) 74 | // (which is just equivalent to: $(content).find(expr) 75 | } else 76 | return jQuery( context ).find( selector ); 77 | 78 | // HANDLE: $(function) 79 | // Shortcut for document ready 80 | } else if ( jQuery.isFunction( selector ) ) 81 | return jQuery( document )[ jQuery.fn.ready ? "ready" : "load" ]( selector ); 82 | 83 | return this.setArray(jQuery.makeArray(selector)); 84 | }, 85 | 86 | // The current version of jQuery being used 87 | jquery: "1.2.6", 88 | 89 | // The number of elements contained in the matched element set 90 | size: function() { 91 | return this.length; 92 | }, 93 | 94 | // The number of elements contained in the matched element set 95 | length: 0, 96 | 97 | // Get the Nth element in the matched element set OR 98 | // Get the whole matched element set as a clean array 99 | get: function( num ) { 100 | return num == undefined ? 101 | 102 | // Return a 'clean' array 103 | jQuery.makeArray( this ) : 104 | 105 | // Return just the object 106 | this[ num ]; 107 | }, 108 | 109 | // Take an array of elements and push it onto the stack 110 | // (returning the new matched element set) 111 | pushStack: function( elems ) { 112 | // Build a new jQuery matched element set 113 | var ret = jQuery( elems ); 114 | 115 | // Add the old object onto the stack (as a reference) 116 | ret.prevObject = this; 117 | 118 | // Return the newly-formed element set 119 | return ret; 120 | }, 121 | 122 | // Force the current matched set of elements to become 123 | // the specified array of elements (destroying the stack in the process) 124 | // You should use pushStack() in order to do this, but maintain the stack 125 | setArray: function( elems ) { 126 | // Resetting the length to 0, then using the native Array push 127 | // is a super-fast way to populate an object with array-like properties 128 | this.length = 0; 129 | Array.prototype.push.apply( this, elems ); 130 | 131 | return this; 132 | }, 133 | 134 | // Execute a callback for every element in the matched set. 135 | // (You can seed the arguments with an array of args, but this is 136 | // only used internally.) 137 | each: function( callback, args ) { 138 | return jQuery.each( this, callback, args ); 139 | }, 140 | 141 | // Determine the position of an element within 142 | // the matched set of elements 143 | index: function( elem ) { 144 | var ret = -1; 145 | 146 | // Locate the position of the desired element 147 | return jQuery.inArray( 148 | // If it receives a jQuery object, the first element is used 149 | elem && elem.jquery ? elem[0] : elem 150 | , this ); 151 | }, 152 | 153 | attr: function( name, value, type ) { 154 | var options = name; 155 | 156 | // Look for the case where we're accessing a style value 157 | if ( name.constructor == String ) 158 | if ( value === undefined ) 159 | return this[0] && jQuery[ type || "attr" ]( this[0], name ); 160 | 161 | else { 162 | options = {}; 163 | options[ name ] = value; 164 | } 165 | 166 | // Check to see if we're setting style values 167 | return this.each(function(i){ 168 | // Set all the styles 169 | for ( name in options ) 170 | jQuery.attr( 171 | type ? 172 | this.style : 173 | this, 174 | name, jQuery.prop( this, options[ name ], type, i, name ) 175 | ); 176 | }); 177 | }, 178 | 179 | css: function( key, value ) { 180 | // ignore negative width and height values 181 | if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 ) 182 | value = undefined; 183 | return this.attr( key, value, "curCSS" ); 184 | }, 185 | 186 | text: function( text ) { 187 | if ( typeof text != "object" && text != null ) 188 | return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); 189 | 190 | var ret = ""; 191 | 192 | jQuery.each( text || this, function(){ 193 | jQuery.each( this.childNodes, function(){ 194 | if ( this.nodeType != 8 ) 195 | ret += this.nodeType != 1 ? 196 | this.nodeValue : 197 | jQuery.fn.text( [ this ] ); 198 | }); 199 | }); 200 | 201 | return ret; 202 | }, 203 | 204 | wrapAll: function( html ) { 205 | if ( this[0] ) 206 | // The elements to wrap the target around 207 | jQuery( html, this[0].ownerDocument ) 208 | .clone() 209 | .insertBefore( this[0] ) 210 | .map(function(){ 211 | var elem = this; 212 | 213 | while ( elem.firstChild ) 214 | elem = elem.firstChild; 215 | 216 | return elem; 217 | }) 218 | .append(this); 219 | 220 | return this; 221 | }, 222 | 223 | wrapInner: function( html ) { 224 | return this.each(function(){ 225 | jQuery( this ).contents().wrapAll( html ); 226 | }); 227 | }, 228 | 229 | wrap: function( html ) { 230 | return this.each(function(){ 231 | jQuery( this ).wrapAll( html ); 232 | }); 233 | }, 234 | 235 | append: function() { 236 | return this.domManip(arguments, true, false, function(elem){ 237 | if (this.nodeType == 1) 238 | this.appendChild( elem ); 239 | }); 240 | }, 241 | 242 | prepend: function() { 243 | return this.domManip(arguments, true, true, function(elem){ 244 | if (this.nodeType == 1) 245 | this.insertBefore( elem, this.firstChild ); 246 | }); 247 | }, 248 | 249 | before: function() { 250 | return this.domManip(arguments, false, false, function(elem){ 251 | this.parentNode.insertBefore( elem, this ); 252 | }); 253 | }, 254 | 255 | after: function() { 256 | return this.domManip(arguments, false, true, function(elem){ 257 | this.parentNode.insertBefore( elem, this.nextSibling ); 258 | }); 259 | }, 260 | 261 | end: function() { 262 | return this.prevObject || jQuery( [] ); 263 | }, 264 | 265 | find: function( selector ) { 266 | var elems = jQuery.map(this, function(elem){ 267 | return jQuery.find( selector, elem ); 268 | }); 269 | 270 | return this.pushStack( /[^+>] [^+>]/.test( selector ) || selector.indexOf("..") > -1 ? 271 | jQuery.unique( elems ) : 272 | elems ); 273 | }, 274 | 275 | clone: function( events ) { 276 | // Do the clone 277 | var ret = this.map(function(){ 278 | if ( jQuery.browser.msie && !jQuery.isXMLDoc(this) ) { 279 | // IE copies events bound via attachEvent when 280 | // using cloneNode. Calling detachEvent on the 281 | // clone will also remove the events from the orignal 282 | // In order to get around this, we use innerHTML. 283 | // Unfortunately, this means some modifications to 284 | // attributes in IE that are actually only stored 285 | // as properties will not be copied (such as the 286 | // the name attribute on an input). 287 | var clone = this.cloneNode(true), 288 | container = document.createElement("div"); 289 | container.appendChild(clone); 290 | return jQuery.clean([container.innerHTML])[0]; 291 | } else 292 | return this.cloneNode(true); 293 | }); 294 | 295 | // Need to set the expando to null on the cloned set if it exists 296 | // removeData doesn't work here, IE removes it from the original as well 297 | // this is primarily for IE but the data expando shouldn't be copied over in any browser 298 | var clone = ret.find("*").andSelf().each(function(){ 299 | if ( this[ expando ] != undefined ) 300 | this[ expando ] = null; 301 | }); 302 | 303 | // Copy the events from the original to the clone 304 | if ( events === true ) 305 | this.find("*").andSelf().each(function(i){ 306 | if (this.nodeType == 3) 307 | return; 308 | var events = jQuery.data( this, "events" ); 309 | 310 | for ( var type in events ) 311 | for ( var handler in events[ type ] ) 312 | jQuery.event.add( clone[ i ], type, events[ type ][ handler ], events[ type ][ handler ].data ); 313 | }); 314 | 315 | // Return the cloned set 316 | return ret; 317 | }, 318 | 319 | filter: function( selector ) { 320 | return this.pushStack( 321 | jQuery.isFunction( selector ) && 322 | jQuery.grep(this, function(elem, i){ 323 | return selector.call( elem, i ); 324 | }) || 325 | 326 | jQuery.multiFilter( selector, this ) ); 327 | }, 328 | 329 | not: function( selector ) { 330 | if ( selector.constructor == String ) 331 | // test special case where just one selector is passed in 332 | if ( isSimple.test( selector ) ) 333 | return this.pushStack( jQuery.multiFilter( selector, this, true ) ); 334 | else 335 | selector = jQuery.multiFilter( selector, this ); 336 | 337 | var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType; 338 | return this.filter(function() { 339 | return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector; 340 | }); 341 | }, 342 | 343 | add: function( selector ) { 344 | return this.pushStack( jQuery.unique( jQuery.merge( 345 | this.get(), 346 | typeof selector == 'string' ? 347 | jQuery( selector ) : 348 | jQuery.makeArray( selector ) 349 | ))); 350 | }, 351 | 352 | is: function( selector ) { 353 | return !!selector && jQuery.multiFilter( selector, this ).length > 0; 354 | }, 355 | 356 | hasClass: function( selector ) { 357 | return this.is( "." + selector ); 358 | }, 359 | 360 | val: function( value ) { 361 | if ( value == undefined ) { 362 | 363 | if ( this.length ) { 364 | var elem = this[0]; 365 | 366 | // We need to handle select boxes special 367 | if ( jQuery.nodeName( elem, "select" ) ) { 368 | var index = elem.selectedIndex, 369 | values = [], 370 | options = elem.options, 371 | one = elem.type == "select-one"; 372 | 373 | // Nothing was selected 374 | if ( index < 0 ) 375 | return null; 376 | 377 | // Loop through all the selected options 378 | for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { 379 | var option = options[ i ]; 380 | 381 | if ( option.selected ) { 382 | // Get the specifc value for the option 383 | value = jQuery.browser.msie && !option.attributes.value.specified ? option.text : option.value; 384 | 385 | // We don't need an array for one selects 386 | if ( one ) 387 | return value; 388 | 389 | // Multi-Selects return an array 390 | values.push( value ); 391 | } 392 | } 393 | 394 | return values; 395 | 396 | // Everything else, we just grab the value 397 | } else 398 | return (this[0].value || "").replace(/\r/g, ""); 399 | 400 | } 401 | 402 | return undefined; 403 | } 404 | 405 | if( value.constructor == Number ) 406 | value += ''; 407 | 408 | return this.each(function(){ 409 | if ( this.nodeType != 1 ) 410 | return; 411 | 412 | if ( value.constructor == Array && /radio|checkbox/.test( this.type ) ) 413 | this.checked = (jQuery.inArray(this.value, value) >= 0 || 414 | jQuery.inArray(this.name, value) >= 0); 415 | 416 | else if ( jQuery.nodeName( this, "select" ) ) { 417 | var values = jQuery.makeArray(value); 418 | 419 | jQuery( "option", this ).each(function(){ 420 | this.selected = (jQuery.inArray( this.value, values ) >= 0 || 421 | jQuery.inArray( this.text, values ) >= 0); 422 | }); 423 | 424 | if ( !values.length ) 425 | this.selectedIndex = -1; 426 | 427 | } else 428 | this.value = value; 429 | }); 430 | }, 431 | 432 | html: function( value ) { 433 | return value == undefined ? 434 | (this[0] ? 435 | this[0].innerHTML : 436 | null) : 437 | this.empty().append( value ); 438 | }, 439 | 440 | replaceWith: function( value ) { 441 | return this.after( value ).remove(); 442 | }, 443 | 444 | eq: function( i ) { 445 | return this.slice( i, i + 1 ); 446 | }, 447 | 448 | slice: function() { 449 | return this.pushStack( Array.prototype.slice.apply( this, arguments ) ); 450 | }, 451 | 452 | map: function( callback ) { 453 | return this.pushStack( jQuery.map(this, function(elem, i){ 454 | return callback.call( elem, i, elem ); 455 | })); 456 | }, 457 | 458 | andSelf: function() { 459 | return this.add( this.prevObject ); 460 | }, 461 | 462 | data: function( key, value ){ 463 | var parts = key.split("."); 464 | parts[1] = parts[1] ? "." + parts[1] : ""; 465 | 466 | if ( value === undefined ) { 467 | var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); 468 | 469 | if ( data === undefined && this.length ) 470 | data = jQuery.data( this[0], key ); 471 | 472 | return data === undefined && parts[1] ? 473 | this.data( parts[0] ) : 474 | data; 475 | } else 476 | return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){ 477 | jQuery.data( this, key, value ); 478 | }); 479 | }, 480 | 481 | removeData: function( key ){ 482 | return this.each(function(){ 483 | jQuery.removeData( this, key ); 484 | }); 485 | }, 486 | 487 | domManip: function( args, table, reverse, callback ) { 488 | var clone = this.length > 1, elems; 489 | 490 | return this.each(function(){ 491 | if ( !elems ) { 492 | elems = jQuery.clean( args, this.ownerDocument ); 493 | 494 | if ( reverse ) 495 | elems.reverse(); 496 | } 497 | 498 | var obj = this; 499 | 500 | if ( table && jQuery.nodeName( this, "table" ) && jQuery.nodeName( elems[0], "tr" ) ) 501 | obj = this.getElementsByTagName("tbody")[0] || this.appendChild( this.ownerDocument.createElement("tbody") ); 502 | 503 | var scripts = jQuery( [] ); 504 | 505 | jQuery.each(elems, function(){ 506 | var elem = clone ? 507 | jQuery( this ).clone( true )[0] : 508 | this; 509 | 510 | // execute all scripts after the elements have been injected 511 | if ( jQuery.nodeName( elem, "script" ) ) 512 | scripts = scripts.add( elem ); 513 | else { 514 | // Remove any inner scripts for later evaluation 515 | if ( elem.nodeType == 1 ) 516 | scripts = scripts.add( jQuery( "script", elem ).remove() ); 517 | 518 | // Inject the elements into the document 519 | callback.call( obj, elem ); 520 | } 521 | }); 522 | 523 | scripts.each( evalScript ); 524 | }); 525 | } 526 | }; 527 | 528 | // Give the init function the jQuery prototype for later instantiation 529 | jQuery.fn.init.prototype = jQuery.fn; 530 | 531 | function evalScript( i, elem ) { 532 | if ( elem.src ) 533 | jQuery.ajax({ 534 | url: elem.src, 535 | async: false, 536 | dataType: "script" 537 | }); 538 | 539 | else 540 | jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); 541 | 542 | if ( elem.parentNode ) 543 | elem.parentNode.removeChild( elem ); 544 | } 545 | 546 | function now(){ 547 | return +new Date; 548 | } 549 | 550 | jQuery.extend = jQuery.fn.extend = function() { 551 | // copy reference to target object 552 | var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; 553 | 554 | // Handle a deep copy situation 555 | if ( target.constructor == Boolean ) { 556 | deep = target; 557 | target = arguments[1] || {}; 558 | // skip the boolean and the target 559 | i = 2; 560 | } 561 | 562 | // Handle case when target is a string or something (possible in deep copy) 563 | if ( typeof target != "object" && typeof target != "function" ) 564 | target = {}; 565 | 566 | // extend jQuery itself if only one argument is passed 567 | if ( length == i ) { 568 | target = this; 569 | --i; 570 | } 571 | 572 | for ( ; i < length; i++ ) 573 | // Only deal with non-null/undefined values 574 | if ( (options = arguments[ i ]) != null ) 575 | // Extend the base object 576 | for ( var name in options ) { 577 | var src = target[ name ], copy = options[ name ]; 578 | 579 | // Prevent never-ending loop 580 | if ( target === copy ) 581 | continue; 582 | 583 | // Recurse if we're merging object values 584 | if ( deep && copy && typeof copy == "object" && !copy.nodeType ) 585 | target[ name ] = jQuery.extend( deep, 586 | // Never move original objects, clone them 587 | src || ( copy.length != null ? [ ] : { } ) 588 | , copy ); 589 | 590 | // Don't bring in undefined values 591 | else if ( copy !== undefined ) 592 | target[ name ] = copy; 593 | 594 | } 595 | 596 | // Return the modified object 597 | return target; 598 | }; 599 | 600 | var expando = "jQuery" + now(), uuid = 0, windowData = {}, 601 | // exclude the following css properties to add px 602 | exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i, 603 | // cache defaultView 604 | defaultView = document.defaultView || {}; 605 | 606 | jQuery.extend({ 607 | noConflict: function( deep ) { 608 | window.$ = _$; 609 | 610 | if ( deep ) 611 | window.jQuery = _jQuery; 612 | 613 | return jQuery; 614 | }, 615 | 616 | // See test/unit/core.js for details concerning this function. 617 | isFunction: function( fn ) { 618 | return !!fn && typeof fn != "string" && !fn.nodeName && 619 | fn.constructor != Array && /^[\s[]?function/.test( fn + "" ); 620 | }, 621 | 622 | // check if an element is in a (or is an) XML document 623 | isXMLDoc: function( elem ) { 624 | return elem.documentElement && !elem.body || 625 | elem.tagName && elem.ownerDocument && !elem.ownerDocument.body; 626 | }, 627 | 628 | // Evalulates a script in a global context 629 | globalEval: function( data ) { 630 | data = jQuery.trim( data ); 631 | 632 | if ( data ) { 633 | // Inspired by code by Andrea Giammarchi 634 | // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html 635 | var head = document.getElementsByTagName("head")[0] || document.documentElement, 636 | script = document.createElement("script"); 637 | 638 | script.type = "text/javascript"; 639 | if ( jQuery.browser.msie ) 640 | script.text = data; 641 | else 642 | script.appendChild( document.createTextNode( data ) ); 643 | 644 | // Use insertBefore instead of appendChild to circumvent an IE6 bug. 645 | // This arises when a base node is used (#2709). 646 | head.insertBefore( script, head.firstChild ); 647 | head.removeChild( script ); 648 | } 649 | }, 650 | 651 | nodeName: function( elem, name ) { 652 | return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); 653 | }, 654 | 655 | cache: {}, 656 | 657 | data: function( elem, name, data ) { 658 | elem = elem == window ? 659 | windowData : 660 | elem; 661 | 662 | var id = elem[ expando ]; 663 | 664 | // Compute a unique ID for the element 665 | if ( !id ) 666 | id = elem[ expando ] = ++uuid; 667 | 668 | // Only generate the data cache if we're 669 | // trying to access or manipulate it 670 | if ( name && !jQuery.cache[ id ] ) 671 | jQuery.cache[ id ] = {}; 672 | 673 | // Prevent overriding the named cache with undefined values 674 | if ( data !== undefined ) 675 | jQuery.cache[ id ][ name ] = data; 676 | 677 | // Return the named cache data, or the ID for the element 678 | return name ? 679 | jQuery.cache[ id ][ name ] : 680 | id; 681 | }, 682 | 683 | removeData: function( elem, name ) { 684 | elem = elem == window ? 685 | windowData : 686 | elem; 687 | 688 | var id = elem[ expando ]; 689 | 690 | // If we want to remove a specific section of the element's data 691 | if ( name ) { 692 | if ( jQuery.cache[ id ] ) { 693 | // Remove the section of cache data 694 | delete jQuery.cache[ id ][ name ]; 695 | 696 | // If we've removed all the data, remove the element's cache 697 | name = ""; 698 | 699 | for ( name in jQuery.cache[ id ] ) 700 | break; 701 | 702 | if ( !name ) 703 | jQuery.removeData( elem ); 704 | } 705 | 706 | // Otherwise, we want to remove all of the element's data 707 | } else { 708 | // Clean up the element expando 709 | try { 710 | delete elem[ expando ]; 711 | } catch(e){ 712 | // IE has trouble directly removing the expando 713 | // but it's ok with using removeAttribute 714 | if ( elem.removeAttribute ) 715 | elem.removeAttribute( expando ); 716 | } 717 | 718 | // Completely remove the data cache 719 | delete jQuery.cache[ id ]; 720 | } 721 | }, 722 | 723 | // args is for internal usage only 724 | each: function( object, callback, args ) { 725 | var name, i = 0, length = object.length; 726 | 727 | if ( args ) { 728 | if ( length == undefined ) { 729 | for ( name in object ) 730 | if ( callback.apply( object[ name ], args ) === false ) 731 | break; 732 | } else 733 | for ( ; i < length; ) 734 | if ( callback.apply( object[ i++ ], args ) === false ) 735 | break; 736 | 737 | // A special, fast, case for the most common use of each 738 | } else { 739 | if ( length == undefined ) { 740 | for ( name in object ) 741 | if ( callback.call( object[ name ], name, object[ name ] ) === false ) 742 | break; 743 | } else 744 | for ( var value = object[0]; 745 | i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} 746 | } 747 | 748 | return object; 749 | }, 750 | 751 | prop: function( elem, value, type, i, name ) { 752 | // Handle executable functions 753 | if ( jQuery.isFunction( value ) ) 754 | value = value.call( elem, i ); 755 | 756 | // Handle passing in a number to a CSS property 757 | return value && value.constructor == Number && type == "curCSS" && !exclude.test( name ) ? 758 | value + "px" : 759 | value; 760 | }, 761 | 762 | className: { 763 | // internal only, use addClass("class") 764 | add: function( elem, classNames ) { 765 | jQuery.each((classNames || "").split(/\s+/), function(i, className){ 766 | if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) ) 767 | elem.className += (elem.className ? " " : "") + className; 768 | }); 769 | }, 770 | 771 | // internal only, use removeClass("class") 772 | remove: function( elem, classNames ) { 773 | if (elem.nodeType == 1) 774 | elem.className = classNames != undefined ? 775 | jQuery.grep(elem.className.split(/\s+/), function(className){ 776 | return !jQuery.className.has( classNames, className ); 777 | }).join(" ") : 778 | ""; 779 | }, 780 | 781 | // internal only, use hasClass("class") 782 | has: function( elem, className ) { 783 | return jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1; 784 | } 785 | }, 786 | 787 | // A method for quickly swapping in/out CSS properties to get correct calculations 788 | swap: function( elem, options, callback ) { 789 | var old = {}; 790 | // Remember the old values, and insert the new ones 791 | for ( var name in options ) { 792 | old[ name ] = elem.style[ name ]; 793 | elem.style[ name ] = options[ name ]; 794 | } 795 | 796 | callback.call( elem ); 797 | 798 | // Revert the old values 799 | for ( var name in options ) 800 | elem.style[ name ] = old[ name ]; 801 | }, 802 | 803 | css: function( elem, name, force ) { 804 | if ( name == "width" || name == "height" ) { 805 | var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ]; 806 | 807 | function getWH() { 808 | val = name == "width" ? elem.offsetWidth : elem.offsetHeight; 809 | var padding = 0, border = 0; 810 | jQuery.each( which, function() { 811 | padding += parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; 812 | border += parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; 813 | }); 814 | val -= Math.round(padding + border); 815 | } 816 | 817 | if ( jQuery(elem).is(":visible") ) 818 | getWH(); 819 | else 820 | jQuery.swap( elem, props, getWH ); 821 | 822 | return Math.max(0, val); 823 | } 824 | 825 | return jQuery.curCSS( elem, name, force ); 826 | }, 827 | 828 | curCSS: function( elem, name, force ) { 829 | var ret, style = elem.style; 830 | 831 | // A helper method for determining if an element's values are broken 832 | function color( elem ) { 833 | if ( !jQuery.browser.safari ) 834 | return false; 835 | 836 | // defaultView is cached 837 | var ret = defaultView.getComputedStyle( elem, null ); 838 | return !ret || ret.getPropertyValue("color") == ""; 839 | } 840 | 841 | // We need to handle opacity special in IE 842 | if ( name == "opacity" && jQuery.browser.msie ) { 843 | ret = jQuery.attr( style, "opacity" ); 844 | 845 | return ret == "" ? 846 | "1" : 847 | ret; 848 | } 849 | // Opera sometimes will give the wrong display answer, this fixes it, see #2037 850 | if ( jQuery.browser.opera && name == "display" ) { 851 | var save = style.outline; 852 | style.outline = "0 solid black"; 853 | style.outline = save; 854 | } 855 | 856 | // Make sure we're using the right name for getting the float value 857 | if ( name.match( /float/i ) ) 858 | name = styleFloat; 859 | 860 | if ( !force && style && style[ name ] ) 861 | ret = style[ name ]; 862 | 863 | else if ( defaultView.getComputedStyle ) { 864 | 865 | // Only "float" is needed here 866 | if ( name.match( /float/i ) ) 867 | name = "float"; 868 | 869 | name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase(); 870 | 871 | var computedStyle = defaultView.getComputedStyle( elem, null ); 872 | 873 | if ( computedStyle && !color( elem ) ) 874 | ret = computedStyle.getPropertyValue( name ); 875 | 876 | // If the element isn't reporting its values properly in Safari 877 | // then some display: none elements are involved 878 | else { 879 | var swap = [], stack = [], a = elem, i = 0; 880 | 881 | // Locate all of the parent display: none elements 882 | for ( ; a && color(a); a = a.parentNode ) 883 | stack.unshift(a); 884 | 885 | // Go through and make them visible, but in reverse 886 | // (It would be better if we knew the exact display type that they had) 887 | for ( ; i < stack.length; i++ ) 888 | if ( color( stack[ i ] ) ) { 889 | swap[ i ] = stack[ i ].style.display; 890 | stack[ i ].style.display = "block"; 891 | } 892 | 893 | // Since we flip the display style, we have to handle that 894 | // one special, otherwise get the value 895 | ret = name == "display" && swap[ stack.length - 1 ] != null ? 896 | "none" : 897 | ( computedStyle && computedStyle.getPropertyValue( name ) ) || ""; 898 | 899 | // Finally, revert the display styles back 900 | for ( i = 0; i < swap.length; i++ ) 901 | if ( swap[ i ] != null ) 902 | stack[ i ].style.display = swap[ i ]; 903 | } 904 | 905 | // We should always get a number back from opacity 906 | if ( name == "opacity" && ret == "" ) 907 | ret = "1"; 908 | 909 | } else if ( elem.currentStyle ) { 910 | var camelCase = name.replace(/\-(\w)/g, function(all, letter){ 911 | return letter.toUpperCase(); 912 | }); 913 | 914 | ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ]; 915 | 916 | // From the awesome hack by Dean Edwards 917 | // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 918 | 919 | // If we're not dealing with a regular pixel number 920 | // but a number that has a weird ending, we need to convert it to pixels 921 | if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) { 922 | // Remember the original values 923 | var left = style.left, rsLeft = elem.runtimeStyle.left; 924 | 925 | // Put in the new values to get a computed value out 926 | elem.runtimeStyle.left = elem.currentStyle.left; 927 | style.left = ret || 0; 928 | ret = style.pixelLeft + "px"; 929 | 930 | // Revert the changed values 931 | style.left = left; 932 | elem.runtimeStyle.left = rsLeft; 933 | } 934 | } 935 | 936 | return ret; 937 | }, 938 | 939 | clean: function( elems, context ) { 940 | var ret = []; 941 | context = context || document; 942 | // !context.createElement fails in IE with an error but returns typeof 'object' 943 | if (typeof context.createElement == 'undefined') 944 | context = context.ownerDocument || context[0] && context[0].ownerDocument || document; 945 | 946 | jQuery.each(elems, function(i, elem){ 947 | if ( !elem ) 948 | return; 949 | 950 | if ( elem.constructor == Number ) 951 | elem += ''; 952 | 953 | // Convert html string into DOM nodes 954 | if ( typeof elem == "string" ) { 955 | // Fix "XHTML"-style tags in all browsers 956 | elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ 957 | return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? 958 | all : 959 | front + ">"; 960 | }); 961 | 962 | // Trim whitespace, otherwise indexOf won't work as expected 963 | var tags = jQuery.trim( elem ).toLowerCase(), div = context.createElement("div"); 964 | 965 | var wrap = 966 | // option or optgroup 967 | !tags.indexOf("", "" ] || 969 | 970 | !tags.indexOf("", "" ] || 972 | 973 | tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && 974 | [ 1, "", "
" ] || 975 | 976 | !tags.indexOf("", "" ] || 978 | 979 | // matched above 980 | (!tags.indexOf("", "" ] || 982 | 983 | !tags.indexOf("", "" ] || 985 | 986 | // IE can't serialize and