Skip to content

Commit 4e02781

Browse files
Refactor replot behaviour so Flot tries to reuse the existing canvas,
adding shutdown() methods to the plot (based on patch by Ryley Breiddal, issue 269). This prevents a memory leak in Chrome and hopefully makes replotting faster for those who are using $.plot instead of .setData()/.draw(). Also update jQuery to 1.5.1 to prevent IE leaks fixed some time ago in jQuery. git-svn-id: https://flot.googlecode.com/svn/trunk@317 1e0a6537-2640-0410-bfb7-f154510ff394
1 parent e47b431 commit 4e02781

File tree

8 files changed

+7542
-3424
lines changed

8 files changed

+7542
-3424
lines changed

API.txt

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -901,7 +901,19 @@ can call:
901901

902902
o = pointOffset({ x: xpos, y: ypos, xaxis: 2, yaxis: 3 })
903903
// o.left and o.top now contains the offset within the div
904-
904+
905+
- resize()
906+
907+
Tells Flot to resize the drawing canvas to the size of the
908+
placeholder. You need to run setupGrid() and draw() afterwards as
909+
canvas resizing is a destructive operation. This is used
910+
internally by the resize plugin.
911+
912+
- shutdown()
913+
914+
Cleans up any event handlers Flot has currently registered. This
915+
is used internally.
916+
905917

906918
There are also some members that let you peek inside the internal
907919
workings of Flot which is useful in some cases. Note that if you change
@@ -998,6 +1010,8 @@ Here's an overview of the phases Flot goes through:
9981010

9991011
7. Responding to events, if any
10001012

1013+
8. Shutdown: this mostly happens in case a plot is overwritten
1014+
10011015
Each hook is simply a function which is put in the appropriate array.
10021016
You can add them through the "hooks" option, and they are also available
10031017
after the plot is constructed as the "hooks" attribute on the returned
@@ -1146,6 +1160,16 @@ hooks in the plugins bundled with Flot.
11461160
crosshair plugin for an example.
11471161

11481162

1163+
- shutdown [phase 8]
1164+
1165+
function (plot, eventHolder)
1166+
1167+
Run when plot.shutdown() is called, which usually only happens in
1168+
case a plot is overwritten by a new plot. If you're writing a
1169+
plugin that adds extra DOM elements or event handlers, you should
1170+
add a callback to clean up after you. Take a look at the section in
1171+
PLUGINS.txt for more info.
1172+
11491173

11501174
Plugins
11511175
-------
@@ -1163,7 +1187,7 @@ Here's a brief explanation of how the plugin plumbings work:
11631187
Each plugin registers itself in the global array $.plot.plugins. When
11641188
you make a new plot object with $.plot, Flot goes through this array
11651189
calling the "init" function of each plugin and merging default options
1166-
from the plugin's "option" attribute. The init function gets a
1190+
from the "option" attribute of the plugin. The init function gets a
11671191
reference to the plot object created and uses this to register hooks
11681192
and add new public methods if needed.
11691193

NEWS.txt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,14 @@ Changes:
9494
- The version comment is now included in the minified jquery.flot.min.js.
9595
- New options.grid.minBorderMargin for adjusting the minimum margin
9696
provided around the border (based on patch by corani, issue 188).
97-
98-
- New hooks: drawSeries
97+
- Refactor replot behaviour so Flot tries to reuse the existing
98+
canvas, adding shutdown() methods to the plot (based on patch by
99+
Ryley Breiddal, issue 269). This prevents a memory leak in Chrome
100+
and hopefully makes replotting faster for those who are using $.plot
101+
instead of .setData()/.draw(). Also update jQuery to 1.5.1 to
102+
prevent IE leaks fixed in jQuery.
103+
104+
- New hooks: drawSeries, shutdown
99105

100106
Bug fixes:
101107

PLUGINS.txt

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
Writing plugins
22
---------------
33

4-
To make a new plugin, create an init function and a set of options (if
5-
needed), stuff it into an object and put it in the $.plot.plugins
6-
array. For example:
4+
All you need to do to make a new plugin is creating an init function
5+
and a set of options (if needed), stuffing it into an object and
6+
putting it in the $.plot.plugins array. For example:
77

8-
function myCoolPluginInit(plot) { plot.coolstring = "Hello!" };
9-
var myCoolOptions = { coolstuff: { show: true } }
10-
$.plot.plugins.push({ init: myCoolPluginInit, options: myCoolOptions });
8+
function myCoolPluginInit(plot) {
9+
plot.coolstring = "Hello!";
10+
};
1111

12-
// now when $.plot is called, the returned object will have the
12+
$.plot.plugins.push({ init: myCoolPluginInit, options: { ... } });
13+
14+
// if $.plot is called, it will return a plot object with the
1315
// attribute "coolstring"
1416

1517
Now, given that the plugin might run in many different places, it's
16-
a good idea to avoid leaking names. We can avoid this by wrapping the
17-
above lines in an anonymous function which we call immediately, like
18+
a good idea to avoid leaking names. The usual trick here is wrap the
19+
above lines in an anonymous function which is called immediately, like
1820
this: (function () { inner code ... })(). To make it even more robust
1921
in case $ is not bound to jQuery but some other Javascript library, we
2022
can write it as
@@ -24,6 +26,13 @@ can write it as
2426
// ...
2527
})(jQuery);
2628

29+
There's a complete example below, but you should also check out the
30+
plugins bundled with Flot.
31+
32+
33+
Complete example
34+
----------------
35+
2736
Here is a simple debug plugin which alerts each of the series in the
2837
plot. It has a single option that control whether it is enabled and
2938
how much info to output:
@@ -75,16 +84,41 @@ This simple plugin illustrates a couple of points:
7584
- Variables in the init function can be used to store plot-specific
7685
state between the hooks.
7786

87+
The two last points are important because there may be multiple plots
88+
on the same page, and you'd want to make sure they are not mixed up.
89+
90+
91+
Shutting down a plugin
92+
----------------------
93+
94+
Each plot object has a shutdown hook which is run when plot.shutdown()
95+
is called. This usually mostly happens in case another plot is made on
96+
top of an existing one.
97+
98+
The purpose of the hook is to give you a chance to unbind any event
99+
handlers you've registered and remove any extra DOM things you've
100+
inserted.
101+
102+
The problem with event handlers is that you can have registered a
103+
handler which is run in some point in the future, e.g. with
104+
setTimeout(). Meanwhile, the plot may have been shutdown and removed,
105+
but because your event handler is still referencing it, it can't be
106+
garbage collected yet, and worse, if your handler eventually runs, it
107+
may overwrite stuff on a completely different plot.
108+
78109

79-
Options guidelines
80-
==================
110+
Some hints on the options
111+
-------------------------
81112

82113
Plugins should always support appropriate options to enable/disable
83114
them because the plugin user may have several plots on the same page
84-
where only one should use the plugin.
115+
where only one should use the plugin. In most cases it's probably a
116+
good idea if the plugin is turned off rather than on per default, just
117+
like most of the powerful features in Flot.
85118

86-
If the plugin needs series-specific options, you can put them in
87-
"series" in the options object, e.g.
119+
If the plugin needs options that are specific to each series, like the
120+
points or lines options in core Flot, you can put them in "series" in
121+
the options object, e.g.
88122

89123
var options = {
90124
series: {
@@ -95,10 +129,8 @@ If the plugin needs series-specific options, you can put them in
95129
}
96130
}
97131

98-
Then they will be copied by Flot into each series, providing the
99-
defaults in case the plugin user doesn't specify any. Again, in most
100-
cases it's probably a good idea if the plugin is turned off rather
101-
than on per default, just like most of the powerful features in Flot.
132+
Then they will be copied by Flot into each series, providing default
133+
values in case none are specified.
102134

103135
Think hard and long about naming the options. These names are going to
104136
be public API, and code is going to depend on them if the plugin is

jquery.flot.crosshair.js

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -90,34 +90,37 @@ The plugin also adds four public methods:
9090
crosshair.locked = false;
9191
}
9292

93-
plot.hooks.bindEvents.push(function (plot, eventHolder) {
94-
if (!plot.getOptions().crosshair.mode)
93+
function onMouseOut(e) {
94+
if (crosshair.locked)
9595
return;
9696

97-
eventHolder.mouseout(function () {
98-
if (crosshair.locked)
99-
return;
97+
if (crosshair.x != -1) {
98+
crosshair.x = -1;
99+
plot.triggerRedrawOverlay();
100+
}
101+
}
100102

101-
if (crosshair.x != -1) {
102-
crosshair.x = -1;
103-
plot.triggerRedrawOverlay();
104-
}
105-
});
106-
107-
eventHolder.mousemove(function (e) {
108-
if (crosshair.locked)
109-
return;
103+
function onMouseMove(e) {
104+
if (crosshair.locked)
105+
return;
110106

111-
if (plot.getSelection && plot.getSelection()) {
112-
crosshair.x = -1; // hide the crosshair while selecting
113-
return;
114-
}
107+
if (plot.getSelection && plot.getSelection()) {
108+
crosshair.x = -1; // hide the crosshair while selecting
109+
return;
110+
}
115111

116-
var offset = plot.offset();
117-
crosshair.x = Math.max(0, Math.min(e.pageX - offset.left, plot.width()));
118-
crosshair.y = Math.max(0, Math.min(e.pageY - offset.top, plot.height()));
119-
plot.triggerRedrawOverlay();
120-
});
112+
var offset = plot.offset();
113+
crosshair.x = Math.max(0, Math.min(e.pageX - offset.left, plot.width()));
114+
crosshair.y = Math.max(0, Math.min(e.pageY - offset.top, plot.height()));
115+
plot.triggerRedrawOverlay();
116+
}
117+
118+
plot.hooks.bindEvents.push(function (plot, eventHolder) {
119+
if (!plot.getOptions().crosshair.mode)
120+
return;
121+
122+
eventHolder.mouseout(onMouseOut);
123+
eventHolder.mousemove(onMouseMove);
121124
});
122125

123126
plot.hooks.drawOverlay.push(function (plot, ctx) {
@@ -148,6 +151,11 @@ The plugin also adds four public methods:
148151
}
149152
ctx.restore();
150153
});
154+
155+
plot.hooks.shutdown.push(function (plot, eventHolder) {
156+
eventHolder.unbind("mouseout", onMouseOut);
157+
eventHolder.unbind("mousemove", onMouseMove);
158+
});
151159
}
152160

153161
$.plot.plugins.push({

0 commit comments

Comments
 (0)