Skip to content

Commit a703b35

Browse files
committed
Merge pull request #162 from Indigo744/Indigo744-patch-newarch-destroy
Implements destroy() method
2 parents 30e3859 + 3a685e9 commit a703b35

File tree

2 files changed

+113
-36
lines changed

2 files changed

+113
-36
lines changed

js/jquery.mapael.js

+81-24
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@
4949

5050
// the global options
5151
self.options = self.extendDefaultOptions(options);
52+
53+
// Save initial HTML content (used by destroy method)
54+
self.initialHTMLContent = self.$container.html();
5255

5356
// zoom TimeOut handler (used to set and clear)
5457
self.zoomTO = 0;
@@ -216,40 +219,92 @@
216219
self.onUpdateEvent(e, opt);
217220
});
218221

219-
// Handle resizing of the map
222+
// Handle map size
220223
if (self.options.map.width) {
224+
// NOT responsive: map has a fixed width
221225
self.paper.setSize(self.options.map.width, self.mapConf.height * (self.options.map.width / self.mapConf.width));
222226

223227
// Create the legends for plots taking into account the scale of the map
224228
self.createLegends("plot", self.plots, (self.options.map.width / self.mapConf.width));
225229
} else {
226-
$(window).on("resize." + pluginName, function () {
227-
clearTimeout(self.resizeTO);
228-
self.resizeTO = setTimeout(function () {
229-
self.$map.trigger("resizeEnd." + pluginName);
230-
}, 150);
231-
});
232-
233-
// Create the legends for plots taking into account the scale of the map
234-
var createPlotLegend = function () {
235-
self.createLegends("plot", self.plots, (self.$map.width() / self.mapConf.width));
236-
237-
self.$map.off("resizeEnd." + pluginName, createPlotLegend);
238-
};
239-
240-
self.$map.on("resizeEnd." + pluginName, function () {
241-
var containerWidth = self.$map.width();
242-
if (self.paper.width != containerWidth) {
243-
self.paper.setSize(containerWidth, self.mapConf.height * (containerWidth / self.mapConf.width));
244-
}
245-
}).on("resizeEnd." + pluginName, createPlotLegend).trigger("resizeEnd." + pluginName);
230+
// Responsive: handle resizing of the map
231+
self.handleMapResizing();
246232
}
247233

248234
// Hook that allows to add custom processing on the map
249235
if (self.options.map.afterInit) self.options.map.afterInit(self.$container, self.paper, self.areas, self.plots, self.options);
250236

251237
$(self.paper.desc).append(" and Mapael " + self.version + " (http://www.vincentbroute.fr/mapael/)");
252238
},
239+
240+
/*
241+
* Destroy mapael
242+
* This function effectively detach mapael from the container
243+
* - Set the container back to the way it was before mapael instanciation
244+
* - Remove all data associated to it (memory can then be free'ed by browser)
245+
*
246+
* This method can be call directly by user:
247+
* $(".mapcontainer").data("mapael").destroy();
248+
*
249+
* This method is also automatically called if the user try to call mapael
250+
* on a container already containing a mapael instance
251+
*/
252+
destroy: function() {
253+
var self = this;
254+
// Empty the container (this will also detach all event listeners)
255+
self.$container.empty();
256+
// Detach the global resize event handler
257+
if (self.onResizeEvent) $(window).off("resize." + pluginName, self.onResizeEvent);
258+
// Replace initial HTML content
259+
self.$container.html(self.initialHTMLContent);
260+
// Remove mapael class
261+
self.$container.removeClass(pluginName);
262+
// Remove the data
263+
self.$container.removeData(pluginName);
264+
// Remove all internal reference
265+
self.container = undefined;
266+
self.$container = undefined;
267+
self.options = undefined;
268+
self.paper = undefined;
269+
self.$map = undefined;
270+
self.$tooltip = undefined;
271+
self.mapConf = undefined;
272+
self.areas = undefined;
273+
self.plots = undefined;
274+
self.links = undefined;
275+
},
276+
277+
handleMapResizing: function() {
278+
var self = this;
279+
// Create the legends for plots taking into account the scale of the map
280+
var createPlotLegend = function () {
281+
self.createLegends("plot", self.plots, (self.$map.width() / self.mapConf.width));
282+
283+
self.$map.off("resizeEnd." + pluginName, createPlotLegend);
284+
};
285+
286+
// onResizeEvent: call when the window element trigger the resize event
287+
// We create it inside this function (and not in the prototype) in order to have a closure
288+
// Otherwise, in the prototype, 'this' when triggered is *not* the mapael object but the global window
289+
self.onResizeEvent = function () {
290+
// Clear any previous setTimeout (avoid too much triggering)
291+
clearTimeout(self.resizeTO);
292+
// setTimeout to wait for the user to finish its resizing
293+
self.resizeTO = setTimeout(function () {
294+
self.$map.trigger("resizeEnd." + pluginName);
295+
}, 150);
296+
};
297+
298+
// Attach resize handler
299+
$(window).on("resize." + pluginName, self.onResizeEvent);
300+
301+
self.$map.on("resizeEnd." + pluginName, function () {
302+
var containerWidth = self.$map.width();
303+
if (self.paper.width != containerWidth) {
304+
self.paper.setSize(containerWidth, self.mapConf.height * (containerWidth / self.mapConf.width));
305+
}
306+
}).on("resizeEnd." + pluginName, createPlotLegend).trigger("resizeEnd." + pluginName);
307+
},
253308

254309
/*
255310
* Extend the user option with the default one
@@ -1856,10 +1911,12 @@
18561911
$.fn[pluginName] = function (options) {
18571912
// Call Mapael on each element
18581913
return this.each(function () {
1859-
// Avoid multiple instanciation
1860-
if ($.data(this, pluginName)) throw new Error("Mapael already exists on this element.");
1914+
// Avoid leaking problem on multiple instanciation by removing an old mapael object on a container
1915+
if ($.data(this, pluginName)) {
1916+
$.data(this, pluginName).destroy();
1917+
}
18611918
// Create Mapael and save it as jQuery data
1862-
// This allow external access to Mapael using $(".mapcontainer").data.mapael
1919+
// This allow external access to Mapael using $(".mapcontainer").data("mapael")
18631920
$.data(this, pluginName, new Mapael(this, options));
18641921
});
18651922
};

test/test-basic.js

+32-12
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Module: Basic
44
*
55
* Here are tested:
6-
* - Basic map creation
6+
* - Basic map creation/destruction
77
* - Basic map interaction
88
* - options.map.name
99
*/
@@ -32,28 +32,48 @@ $(function() {
3232

3333
});
3434

35-
QUnit.test("Creation fail: wrong map", function(assert) {
36-
37-
/* Error if wrong map name */
38-
assert.throws(function(){
39-
$(".mapcontainer").mapael($.extend(true, {}, CST_MAPCONF_NOANIMDURATION, {
40-
map: { name: "not_existing_map" }
41-
}));
42-
}, "Throw error" );
35+
QUnit.test("Instance destruction", function(assert) {
4336

44-
assert.notOk($(".mapcontainer svg")[0], "Map not existing" );
37+
$(".mapcontainer").mapael($.extend(true, {}, CST_MAPCONF_NOANIMDURATION, {
38+
map: { name: "france_departments" }
39+
}));
4540

41+
assert.ok($(".mapcontainer svg")[0], "Map existing" );
42+
43+
$(".mapcontainer").data("mapael").destroy();
44+
45+
assert.notOk($(".mapcontainer .map svg")[0], "SVG map not created" );
46+
assert.notOk($(".mapcontainer").hasClass("mapael"), "Has not mapael class" );
47+
assert.notOk(typeof $(".mapcontainer").data("mapael") === "object", "Has not mapael data" );
48+
assert.notOk($(".mapcontainer .map .mapTooltip")[0], "Has not tooltip div" );
4649
});
4750

48-
QUnit.test("Creation fail: existing map", function(assert) {
51+
QUnit.test("Instance creation: existing map", function(assert) {
52+
4953
$(".mapcontainer").mapael($.extend(true, {}, CST_MAPCONF_NOANIMDURATION, {
5054
map: { name: "france_departments" }
5155
}));
56+
57+
assert.ok($(".mapcontainer svg")[0], "First map existing" );
58+
59+
$(".mapcontainer").mapael($.extend(true, {}, CST_MAPCONF_NOANIMDURATION, {
60+
map: { name: "france_departments" }
61+
}));
62+
63+
assert.ok($(".mapcontainer svg")[0], "Second map existing" );
64+
});
65+
66+
QUnit.test("Creation fail: wrong map", function(assert) {
67+
68+
/* Error if wrong map name */
5269
assert.throws(function(){
5370
$(".mapcontainer").mapael($.extend(true, {}, CST_MAPCONF_NOANIMDURATION, {
54-
map: { name: "france_departments" }
71+
map: { name: "not_existing_map" }
5572
}));
5673
}, "Throw error" );
74+
75+
assert.notOk($(".mapcontainer svg")[0], "Map not existing" );
76+
5777
});
5878

5979
QUnit.test("Mouseover", function(assert) {

0 commit comments

Comments
 (0)