Index: typo3/sysext/rtehtmlarea/htmlarea/htmlarea.js
===================================================================
--- typo3/sysext/rtehtmlarea/htmlarea/htmlarea.js	(révision 7637)
+++ typo3/sysext/rtehtmlarea/htmlarea/htmlarea.js	(copie de travail)
@@ -466,6 +466,10 @@
 			},
 			hotkey: {
 				fn: this.onHotKey
+			},
+			beforedestroy: {
+				fn: this.onBeforeDestroy,
+				single: true
 			}
 		});
 			// Monitor toolbar updates in order to refresh the state of the combo
@@ -609,6 +613,15 @@
 				// Restore the selection if combo was triggered
 			this.mon(iframe.getEl(), 'focus', this.restoreSelection, this);
 		}
+	},
+	/*
+	 * Cleanup
+	 */
+	onBeforeDestroy: function () {
+		this.controlRange = null;
+		this.bookmark = null;
+		this.getStore().removeAll();
+		this.getStore().destroy();
 	}
 });
 Ext.reg('htmlareacombo', Ext.ux.form.HTMLAreaCombo);
@@ -646,6 +659,12 @@
 	 * Initialize listeners
 	 */
 	initEventListeners: function () {
+		this.addListener({
+			beforedestroy: {
+				fn: this.onBeforeDestroy,
+				single: true
+			}
+		});
 			// Monitor editor becoming ready
 		this.mon(this.getEditor(), 'editorready', this.update, this, {single: true});
 	},
@@ -736,6 +755,13 @@
 			endPointsInSameBlock = editor.endPointsInSameBlock();
 		}
 		this.fireEvent('update', mode, selectionEmpty, ancestors, endPointsInSameBlock);
+	},
+	/*
+	 * Cleanup
+	 */
+	onBeforeDestroy: function () {
+		this.removeAll(true);
+		return true;
 	}
 });
 Ext.reg('htmlareatoolbar', HTMLArea.Toolbar);
@@ -760,7 +786,7 @@
 				fn: this.initEventListeners,
 				single: true
 			},
-			beforeDestroy: {
+			beforedestroy: {
 				fn: this.onBeforeDestroy,
 				single: true
 			}
@@ -1087,9 +1113,8 @@
 	 * Start listening to things happening in the iframe
 	 */
 	startListening: function () {
-		var documentElement = Ext.get(this.document.documentElement);
 			// Create keyMap so that plugins may bind key handlers
-		this.keyMap = new Ext.KeyMap(documentElement, [], (Ext.isIE || Ext.isWebKit) ? 'keydown' : 'keypress');
+		this.keyMap = new Ext.KeyMap(Ext.get(this.document.documentElement), [], (Ext.isIE || Ext.isWebKit) ? 'keydown' : 'keypress');
 			// Special keys map
 		this.keyMap.addBinding([
 			{
@@ -1141,7 +1166,7 @@
 				scope: this
 			});
 		}
-			// Hot key map (on keydown for all brwosers)
+			// Hot key map (on keydown for all browsers)
 		var hotKeys = '';
 		Ext.iterate(this.config.hotKeyList, function (key) {
 			if (key.length == 1) {
@@ -1149,7 +1174,7 @@
 			}
 		});
 			// Make hot key map available, even if empty, so that plugins may add bindings
-		this.hotKeyMap = new Ext.KeyMap(documentElement);
+		this.hotKeyMap = new Ext.KeyMap(Ext.get(this.document.documentElement));
 		if (!Ext.isEmpty(hotKeys)) {
 			this.hotKeyMap.addBinding({
 				key: hotKeys,
@@ -1160,10 +1185,10 @@
 				scope: this
 			});
 		}
-		this.mon(documentElement, (Ext.isIE || Ext.isWebKit) ? 'keydown' : 'keypress', this.onAnyKey, this);
-		this.mon(documentElement, 'mouseup', this.onMouse, this);
-		this.mon(documentElement, 'click', this.onMouse, this);
-		this.mon(documentElement, Ext.isWebKit ? 'dragend' : 'drop', this.onDrop, this);
+		this.mon(Ext.get(this.document.documentElement), (Ext.isIE || Ext.isWebKit) ? 'keydown' : 'keypress', this.onAnyKey, this);
+		this.mon(Ext.get(this.document.documentElement), 'mouseup', this.onMouse, this);
+		this.mon(Ext.get(this.document.documentElement), 'click', this.onMouse, this);
+		this.mon(Ext.get(this.document.documentElement), Ext.isWebKit ? 'dragend' : 'drop', this.onDrop, this);
 	},
 	/*
 	 * Handler for other key events
@@ -1340,8 +1365,33 @@
 	 * Cleanup
 	 */
 	onBeforeDestroy: function () {
+			// ExtJS KeyMap object makes IE leak memory
+			// Nullify EXTJS private handlers
+		Ext.each(this.keyMap.bindings, function (binding, index) {
+			this.keyMap.bindings[index] = null;
+		});
+		this.keyMap.handleKeyDown = null;
+		Ext.each(this.hotKeyMap.bindings, function (binding, index) {
+			this.hotKeyMap.bindings[index] = null;
+		});
+		this.hotKeyMap.handleKeyDown = null;
 		this.keyMap.disable();
 		this.hotKeyMap.disable();
+			// Cleaning references to DOM in order to avoid IE memory leaks
+		Ext.get(this.document.body).purgeAllListeners();
+		Ext.get(this.document.body).dom = null;
+		Ext.get(this.document.documentElement).purgeAllListeners();
+		Ext.get(this.document.documentElement).dom = null;
+		this.document = null;
+		this.getEditor().document = null;
+		this.getEditor()._doc = null;
+		this.getEditor()._iframe = null;
+		Ext.each(this.nestedParentElements.sorted, function (nested) {
+			Ext.get(nested).purgeAllListeners();
+			Ext.get(nested).dom = null;
+		});
+		Ext.destroy(this.autoEl, this.el, this.resizeEl, this.positionEl);
+		return true;
 	}
 });
 Ext.reg('htmlareaiframe', HTMLArea.Iframe);
@@ -1789,7 +1839,7 @@
 	initEventListeners: function () {
 		this.addListener({
 			beforedestroy: {
-				fn: this.clear,
+				fn: this.onBeforeDestroy,
 				single: true
 			}
 		});
@@ -1833,6 +1883,8 @@
 		this.statusBarTree.removeAllListeners();
 		Ext.each(this.statusBarTree.query('a'), function (node) {
 			Ext.QuickTips.unregister(node);
+			Ext.get(node).dom.ancestor = null;
+			Ext.destroy(node);
 		});
 		this.statusBarTree.update('');
 		this.setSelection(null);
@@ -1995,6 +2047,15 @@
 	onContextMenu: function (event, target) {
 		this.selectElement(target);
 		return this.getEditor().getPlugin('ContextMenu') ? this.getEditor().getPlugin('ContextMenu').show(event, target.ancestor) : false;
+	},
+	/*
+	 * Cleanup
+	 */
+	onBeforeDestroy: function() {
+		this.clear();
+		this.removeAll(true);
+		Ext.destroy(this.statusBarTree, this.statusBarTextMode);
+		return true;
 	}
 });
 Ext.reg('htmlareastatusbar', HTMLArea.StatusBar);
@@ -2254,10 +2315,21 @@
 	 */
 	onBeforeDestroy: function () {
 		Ext.EventManager.removeResizeListener(this.onWindowResize, this);
+			// Cleaning references to DOM in order to avoid IE memory leaks
 		var form = this.textArea.dom.form;
 		if (form) {
 			form.htmlAreaPreviousOnReset = null;
+			Ext.get(form).dom = null;
 		}
+		Ext.getBody().dom = null;
+			// ExtJS is not releasing any resources when the iframe is unloaded
+		this.toolbar.destroy();
+		this.statusBar.destroy();
+		this.removeAll(true);
+		if (this.resizable) {
+			this.resizer.destroy();
+		}
+		return true;
 	}
 });
 Ext.reg('htmlareaframework', HTMLArea.Framework);
@@ -2322,7 +2394,12 @@
 			 * @event modeChange
 			 * Fires when the editor changes mode
 			 */
-			'modeChange'
+			'modeChange',
+			/*
+			 * @event beforedestroy
+			 * Fires before the editor is to be destroyed
+			 */
+			'beforedestroy'
 		);
 	},
 	/*
@@ -2381,12 +2458,23 @@
 					itemId: 'textAreaContainer',
 					anchor: '100%',
 					width: (this.textAreaInitialSize.width.indexOf('%') === -1) ? parseInt(this.textAreaInitialSize.width) : 300,
-						// Let the framework swallow the textarea
+						// Let the framework swallow the textarea and throw it back
 					listeners: {
-						afterRender: {
-							fn: function (textAreaContainer) { textAreaContainer.getEl().appendChild(this.textArea); },
+						afterrender: {
+							fn: function (textAreaContainer) {
+								this.originalParent = this.textArea.parent().dom;
+								textAreaContainer.getEl().appendChild(this.textArea);
+							},
 							single: true,
 							scope: this
+						},
+						beforedestroy: {
+							fn: function (textAreaContainer) {
+								this.originalParent.appendChild(this.textArea.dom);
+								return true;
+							},
+							single: true,
+							scope: this
 						}
 					}
 				}
@@ -2593,6 +2681,16 @@
 		return (this.plugins[pluginName] ? this.plugins[pluginName].instance : null);
 	},
 	/*
+	 * Unregister the instance of the specified plugin
+	 *
+	 * @param	string		pluginName: the name of the plugin
+	 * @return	void
+	 */
+	unRegisterPlugin: function(pluginName) {
+		delete this.plugins[pluginName].instance;
+		delete this.plugins[pluginName];
+	},
+	/*
 	 * Focus on the editor
 	 */
 	focus: function () {
@@ -2626,7 +2724,7 @@
 	/*
 	 * Iframe unload handler: Update the textarea for submission and cleanup
 	 */
-	onUnload: function (event) {;
+	onUnload: function (event) {
 			// Save the HTML content into the original textarea for submit, back/forward, etc.
 		if (this.ready) {
 			this.textArea.set({
@@ -2634,8 +2732,19 @@
 			}, false);
 		}
 			// Cleanup
+		this.fireEvent('beforedestroy');
 		Ext.TaskMgr.stopAll();
+			// ExtJS is not releasing any resources when the iframe is unloaded
 		this.htmlArea.destroy();
+		Ext.iterate(this.plugins, function (pluginId) {
+			this.unRegisterPlugin(pluginId);
+		}, this);
+		this.purgeListeners()
+			// Cleaning references to DOM in order to avoid IE memory leaks
+		this.wizards.dom = null;
+		this.textArea.parent().parent().dom = null;
+		this.textArea.parent().dom = null;
+		this.textArea.dom = null;;
 		RTEarea[this.editorId].editor = null;
 	}
 });
@@ -2736,6 +2845,7 @@
 			if (docHeader) {
 				size.height -= docHeader.getHeight();
 			}
+			docHeader.dom = null;
 			return size;
 		}
 	}
Index: typo3/sysext/rtehtmlarea/htmlarea/plugins/ContextMenu/context-menu.js
===================================================================
--- typo3/sysext/rtehtmlarea/htmlarea/plugins/ContextMenu/context-menu.js	(révision 7637)
+++ typo3/sysext/rtehtmlarea/htmlarea/plugins/ContextMenu/context-menu.js	(copie de travail)
@@ -54,13 +54,13 @@
 		 * Registering plugin "About" information
 		 */
 		var pluginInformation = {
-			version		: "3.0",
-			developer	: "Mihai Bazon & Stanislas Rolland",
-			developerUrl	: "http://www.sjbr.ca/",
-			copyrightOwner	: "dynarch.com & Stanislas Rolland",
-			sponsor		: "American Bible Society & SJBR",
-			sponsorUrl	: "http://www.sjbr.ca/",
-			license		: "GPL"
+			version		: '3.0',
+			developer	: 'Mihai Bazon & Stanislas Rolland',
+			developerUrl	: 'http://www.sjbr.ca/',
+			copyrightOwner	: 'dynarch.com & Stanislas Rolland',
+			sponsor		: 'American Bible Society & SJBR',
+			sponsorUrl	: 'http://www.sjbr.ca/',
+			license		: 'GPL'
 		};
 		this.registerPluginInformation(pluginInformation);
 		return true;
@@ -91,6 +91,8 @@
 		}, this.pageTSConfiguration));
 			// Monitor contextmenu clicks on the iframe
 		this.menu.mon(Ext.get(this.editor.document.documentElement), 'contextmenu', this.show, this, {single: true});
+			// Monitor editor being destroyed
+		this.menu.mon(this.editor, 'beforedestroy', this.onBeforeDestroy, this, {single: true});
 	},
 	/*
 	 * Create the menu items config
@@ -258,5 +260,15 @@
 			this.deleteTarget.remove();
 			this.editor.updateToolbar();
 		}
+	},
+	/*
+	 * Handler invoked when the editor is about to be destroyed
+	 */
+	onBeforeDestroy: function () {
+		this.menu.items.each(function (menuItem) {
+			Ext.QuickTips.unregister(menuItem);
+		});
+	 	this.menu.removeAll(true);
+	 	this.menu.destroy();
 	}
 });
