Index: typo3/sysext/rtehtmlarea/htmlarea/htmlarea.js
===================================================================
--- typo3/sysext/rtehtmlarea/htmlarea/htmlarea.js	(révision 7296)
+++ typo3/sysext/rtehtmlarea/htmlarea/htmlarea.js	(copie de travail)
@@ -87,8 +87,6 @@
 	if (!Ext.isString(HTMLArea.editedContentCSS)) {
 		HTMLArea.editedContentCSS = HTMLArea.editorSkin + 'htmlarea-edited-content.css';
 	}
-		// Initialize pending request flag for Opera
-	HTMLArea.pendingSynchronousXMLHttpRequest = false;
 		// Localization of core script
 	HTMLArea.I18N = HTMLArea_langArray;
 	HTMLArea.isReady = true;
@@ -1171,8 +1169,8 @@
 	 * Handler for other key events
 	 */
 	onAnyKey: function(event) {
-			// In Opera, inhibit key events while synchronous XMLHttpRequest is being processed
-		if (Ext.isOpera && HTMLArea.pendingSynchronousXMLHttpRequest) {
+			// Inhibit key events while server-based cleaning is being processed
+		if (this.getEditor().inhibitKeyboardInput) {
 			event.stopEvent();
 			return false;
 		}
@@ -2288,6 +2286,8 @@
 				this.registerPlugin(plugin);
 			}
 		}, this);
+			// Initialize keyboard input inhibit flag
+		this.inhibitKeyboardInput = false;
 		this.addEvents(
 			/*
 			 * @event editorready
@@ -4160,6 +4160,7 @@
 				success = true;
 			},
 			failure: function (response) {
+				this.editor.inhibitKeyboardInput = false;
 				this.appendToLog('getJavascriptFile', 'Unable to get ' + url + ' . Server reported ' + response.status);
 			},
 			scope: this
Index: typo3/sysext/rtehtmlarea/htmlarea/plugins/CopyPaste/copy-paste.js
===================================================================
--- typo3/sysext/rtehtmlarea/htmlarea/plugins/CopyPaste/copy-paste.js	(révision 7296)
+++ typo3/sysext/rtehtmlarea/htmlarea/plugins/CopyPaste/copy-paste.js	(copie de travail)
@@ -30,59 +30,45 @@
  * TYPO3 SVN ID: $Id$
  */
 CopyPaste = HTMLArea.Plugin.extend({
-		
-	constructor : function(editor, pluginName) {
+	constructor: function(editor, pluginName) {
 		this.base(editor, pluginName);
 	},
-	
 	/*
 	 * This function gets called by the class constructor
 	 */
-	configurePlugin : function (editor) {
-		
+	configurePlugin: function (editor) {
 		/*
 		 * Setting up some properties from PageTSConfig
 		 */
 		this.buttonsConfiguration = this.editorConfiguration.buttons;
-		
 		/*
 		 * Registering plugin "About" information
 		 */
 		var pluginInformation = {
-			version		: "2.0",
-			developer	: "Stanislas Rolland",
-			developerUrl	: "http://www.sjbr.ca/",
-			copyrightOwner	: "Stanislas Rolland",
-			sponsor		: this.localize("Technische Universitat Ilmenau"),
-			sponsorUrl	: "http://www.tu-ilmenau.de/",
-			license		: "GPL"
+			version		: '2.0',
+			developer	: 'Stanislas Rolland',
+			developerUrl	: 'http://www.sjbr.ca/',
+			copyrightOwner	: 'Stanislas Rolland',
+			sponsor		: this.localize('Technische Universitat Ilmenau'),
+			sponsorUrl	: 'http://www.tu-ilmenau.de/',
+			license		: 'GPL'
 		};
 		this.registerPluginInformation(pluginInformation);
 		/*
 		 * Registering the buttons
 		 */
-		for (var buttonId in this.buttonList) {
-			if (this.buttonList.hasOwnProperty(buttonId)) {
-				var button = this.buttonList[buttonId];
-				var buttonConfiguration = {
-					id		: buttonId,
-					tooltip		: this.localize(buttonId.toLowerCase()),
-					iconCls		: 'htmlarea-action-' + button[2],
-					action		: 'onButtonPress',
-					context		: button[0],
-					selection	: button[3],
-					hotKey		: (this.buttonsConfiguration[button[2]] ? this.buttonsConfiguration[button[2]].hotKey : (button[1] ? button[1] : null))
-				};
-				this.registerButton(buttonConfiguration);
-				if (!this.isButtonInToolbar(buttonId)) {
-					var hotKeyConfiguration = {
-						id	: buttonConfiguration.hotKey,
-						cmd	: buttonConfiguration.id
-					};
-					this.registerHotKey(hotKeyConfiguration);
-				}
-			}
-		}
+		Ext.iterate(this.buttonList, function (buttonId, button) {
+			var buttonConfiguration = {
+				id		: buttonId,
+				tooltip		: this.localize(buttonId.toLowerCase()),
+				iconCls		: 'htmlarea-action-' + button[2],
+				action		: 'onButtonPress',
+				context		: button[0],
+				selection	: button[3],
+				hotKey		: button[1]
+			};
+			this.registerButton(buttonConfiguration);
+		}, this);
 		return true;
 	},
 	/*
@@ -94,75 +80,134 @@
 		Paste	: [null, 'v', 'paste', false]
 	},
 	/*
+	 * This function gets called when the editor is generated
+	 */
+	onGenerate: function () {
+		this.editor.iframe.mon(Ext.get(Ext.isIE ? this.editor.document.body : this.editor.document.documentElement), 'cut', this.cutHandler, this);
+			// Add hot key handling if the button is not enabled in the toolbar
+		Ext.iterate(this.buttonList, function (buttonId, button) {
+			if (!this.isButtonInToolbar(buttonId)) {
+				this.editor.iframe.hotKeyMap.addBinding({
+					key: button[1].toUpperCase(),
+					ctrl: true,
+					shift: false,
+					alt: false,
+					handler: this.onHotKey,
+					scope: this
+				});
+					// Ensure the hot key can be translated
+				this.editorConfiguration.hotKeyList[button[1]] = {
+					id	: button[1],
+					cmd	: buttonId
+				};
+			}
+		}, this);
+	},
+	/*
 	 * This function gets called when a button or a hotkey was pressed.
 	 *
 	 * @param	object		editor: the editor instance
 	 * @param	string		id: the button id or the key
-	 * @param	object		target: the target element of the contextmenu event, when invoked from the context menu
 	 *
 	 * @return	boolean		false if action is completed
 	 */
-	onButtonPress : function (editor, id, target) {
+	onButtonPress: function (editor, id) {
 			// Could be a button or its hotkey
 		var buttonId = this.translateHotKey(id);
 		buttonId = buttonId ? buttonId : id;
 		this.editor.focus();
-		if (!this.applyToTable(buttonId, target)) {
+		if (!this.applyToTable(buttonId)) {
 				// If we are not handling table cells
 			switch (buttonId) {
-				case "Copy":
-				case "Cut" :
+				case 'Copy':
 					if (buttonId == id) {
 							// If we are handling a button, not a hotkey
 						this.applyBrowserCommand(buttonId);
-					} else if (buttonId == "Cut") {
-							// If we are handling the cut hotkey
-						this.removeEmptyLinkLater.defer(50, this);
 					}
 					break;
-				case "Paste":
+				case 'Cut' :
 					if (buttonId == id) {
 							// If we are handling a button, not a hotkey
 						this.applyBrowserCommand(buttonId);
 					}
+						// Opera will not trigger the onCut event
+					if (Ext.isOpera) {
+						this.cutHandler();
+					}
+					break;
+				case 'Paste':
+					if (buttonId == id) {
+							// If we are handling a button, not a hotkey
+						this.applyBrowserCommand(buttonId);
+					}
 						// In FF3, the paste operation will indeed trigger the onPaste event not in FF2; nor in Opera
-					if (Ext.isOpera || Ext.isGecko2 || Ext.isWebKit) {
-						var cleaner = this.getPluginInstance('DefaultClean');
-						if (!cleaner) {
-							cleaner = this.getPluginInstance('TYPO3HtmlParser');
-						}
+					if (Ext.isOpera || Ext.isGecko2) {
+						var cleaner = this.getButton('CleanWord');
 						if (cleaner) {
-							cleaner.clean.defer(50, cleaner);
+							cleaner.fireEvent.defer(250, cleaner, ['click', cleaner]);
 						}
 					}
 					break;
 				default:
 					break;
 			}
+				// Stop the event if a button was handled
 			return (buttonId != id);
 		} else {
-				// We handled the table case
+				// The table case was handled, let the event be stopped.
+				// No cleaning required as the pasted cells are copied from the editor.
+				// However paste by Opera cannot be stopped.
+				// Revert Opera's operation as it produces invalid html anyways
+			if (Ext.isOpera) {
+				this.editor.inhibitKeyboardInput = true;
+				var bookmark = this.editor.getBookmark(this.editor._createRange(this.editor._getSelection()));
+				var html = this.editor.getInnerHTML();
+				this.revertPaste.defer(200, this, [html, bookmark]);
+			}
 			return false;
 		}
 	},
-	
-	applyBrowserCommand : function (buttonId) {
+	/*
+	 * This funcion reverts the paste operation (performed by Opera)
+	 */
+	revertPaste: function (html, bookmark) {
+		this.editor.setHTML(html);
+		this.editor.selectRange(this.editor.moveToBookmark(bookmark));
+		this.editor.inhibitKeyboardInput = false;
+	},
+	/*
+	 * This function applies the browser command when a button is pressed
+	 * In the case of hot key, the browser does it automatically
+	 */
+	applyBrowserCommand: function (buttonId) {
 		try {
-			this.editor._doc.execCommand(buttonId, false, null);
+			this.editor.document.execCommand(buttonId, false, null);
 		} catch (e) {
 			if (Ext.isGecko) {
 				this.mozillaClipboardAccessException();
 			}
 		}
-		if (buttonId == "Cut") {
-			this.removeEmptyLink();
+	},
+	/*
+	 * Handler for hotkeys configured through the hotKeyMap while button not enabled in toolbar (see onGenerate above)
+	 */
+	onHotKey: function (key, event) {
+		var hotKey = String.fromCharCode(key).toLowerCase();
+			// Stop the event if it was handled here
+		if (!this.onButtonPress(this, hotKey)) {
+			event.stopEvent();
 		}
 	},
-	
 	/*
+	 * This function removes any link left over by the cut operation
+	 */
+	cutHandler: function (event) {
+		this.removeEmptyLink.defer(50, this);
+	},
+	/*
 	 * This function unlinks any empty link left over by the cut operation
 	 */
-	removeEmptyLink : function() {
+	removeEmptyLink: function() {
 		var selection = this.editor._getSelection();
 		var range = this.editor._createRange(selection);
 		var parent = this.editor.getParentElement(selection, range);
@@ -177,7 +222,7 @@
 					this.editor.removeMarkup(parent);
 						// Opera does not render empty list items
 					if (Ext.isOpera && /^(li)$/i.test(container.nodeName) && !container.firstChild) {
-						container.innerHTML = "<br />";
+						container.innerHTML = '<br />';
 						this.editor.selectNodeContents(container, true);
 					}
 				} else {
@@ -187,49 +232,45 @@
 		}
 		if (Ext.isWebKit) {
 				// Remove Apple's span and font tags
-			this.editor.cleanAppleStyleSpans(this.editor._doc.body);
+			this.editor.cleanAppleStyleSpans(this.editor.document.body);
 				// Reset Safari selection in order to prevent insertion of span and/or font tags on next text input
 			var bookmark = this.editor.getBookmark(this.editor._createRange(this.editor._getSelection()));
 			this.editor.selectRange(this.editor.moveToBookmark(bookmark));
 		}
-	},
-	
-	/*
-	 * This function removes any link left over by the cut operation triggered by hotkey
-	 */
-	removeEmptyLinkLater : function() {
-		this.removeEmptyLink();
 		this.editor.updateToolbar();
 	},
-	
 	/*
-	 * This function gets called by the main editor when a copy/cut/paste operation is to be performed
+	 * This function gets called when a copy/cut/paste operation is to be performed
+	 * This feature allows to paste a region of table cells
 	 */
-	applyToTable : function (buttonId, target) {
+	applyToTable: function (buttonId) {
 		var selection = this.editor._getSelection();
 		var range = this.editor._createRange(selection);
 		var parent = this.editor.getParentElement(selection, range);
 		var endBlocks = this.editor.getEndBlocks(selection);
 		switch (buttonId) {
-			case "Copy":
-			case "Cut" :
+			case 'Copy':
+			case 'Cut' :
 				HTMLArea.copiedCells = null;
 				var endBlocks = this.editor.getEndBlocks(selection);
 				if ((/^(tr)$/i.test(parent.nodeName) && !Ext.isIE) || (/^(td|th)$/i.test(endBlocks.start.nodeName) && /^(td|th)$/i.test(endBlocks.end.nodeName) && !Ext.isGecko && endBlocks.start != endBlocks.end)) {
 					HTMLArea.copiedCells = this.collectCells(buttonId, selection, endBlocks);
-					if (buttonId === "Cut") return true;
 				}
 				break;
-			case "Paste":
+			case 'Paste':
 				if (/^(tr|td|th)$/i.test(parent.nodeName) && HTMLArea.copiedCells) {
 					return this.pasteCells(selection, endBlocks);
 				}
 				break;
+			default:
+				break;
 		}
 		return false;
 	},
-	
-	pasteCells : function (selection, endBlocks) {
+	/*
+	 * This function handles pasting of a collection of table cells
+	 */
+	pasteCells: function (selection, endBlocks) {
 		var cell = null;
 		if (Ext.isGecko) {
 			range = selection.getRangeAt(0);
@@ -241,8 +282,11 @@
 		if (!cell && /^(td|th)$/i.test(endBlocks.start.nodeName)) {
 			cell = endBlocks.start;
 		}
-		if (!cell) return false;
-		var tableParts = ["thead", "tbody", "tfoot"];
+		if (!cell) {
+				// Let the browser do it
+			return false;
+		}
+		var tableParts = ['thead', 'tbody', 'tfoot'];
 		var tablePartsIndex = { thead : 0, tbody : 1, tfoot : 2 };
 		var tablePart = cell.parentNode.parentNode;
 		var tablePartIndex = tablePartsIndex[tablePart.nodeName.toLowerCase()]
@@ -274,12 +318,11 @@
 		}
 		return true;
 	},
-	
 	/*
 	 * This function collects the selected table cells for copy/cut operations
 	 */
-	collectCells : function (operation, selection, endBlocks) {
-		var tableParts = ["thead", "tbody", "tfoot"];
+	collectCells: function (operation, selection, endBlocks) {
+		var tableParts = ['thead', 'tbody', 'tfoot'];
 		var tablePartsIndex = { thead : 0, tbody : 1, tfoot : 2 };
 		var selection = this.editor._getSelection();
 		var range, i = 0, cell, cells = null;
@@ -295,10 +338,10 @@
 				for (var i = 0, n = endBlocks.start.cells.length; i < n; ++i) {
 					cell = endBlocks.start.cells[i];
 					cells.push(cell.innerHTML);
-					if (operation === "Cut") {
-						cell.innerHTML = "<br />";
+					if (operation === 'Cut') {
+						cell.innerHTML = '<br />';
 					}
-					if (operation === "Cut") {
+					if (operation === 'Cut') {
 						cutRows.push(endBlocks.start);
 					}
 				}
@@ -311,15 +354,15 @@
 						cell = range.startContainer.childNodes[range.startOffset];
 						if (cell.parentNode != row) {
 							(cells) && rows[tablePartsIndex[row.parentNode.nodeName.toLowerCase()]].push(cells);
-							if (operation === "Cut" && firstCellOfRow && lastCellOfRow) cutRows.push(row);
+							if (operation === 'Cut' && firstCellOfRow && lastCellOfRow) cutRows.push(row);
 							row = cell.parentNode;
 							cells = [];
 							firstCellOfRow = false;
 							lastCellOfRow = false;
 						}
 						cells.push(cell.innerHTML);
-						if (operation === "Cut") {
-							cell.innerHTML = "<br />";
+						if (operation === 'Cut') {
+							cell.innerHTML = '<br />';
 						}
 						if (!cell.previousSibling) firstCellOfRow = true;
 						if (!cell.nextSibling) lastCellOfRow = true;
@@ -328,7 +371,7 @@
 					/* finished walking through selection */
 				}
 				try { rows[tablePartsIndex[row.parentNode.nodeName.toLowerCase()]].push(cells); } catch(e) { }
-				if (row && operation === "Cut" && firstCellOfRow && lastCellOfRow) {
+				if (row && operation === 'Cut' && firstCellOfRow && lastCellOfRow) {
 					cutRows.push(row);
 				}
 			}
@@ -342,8 +385,8 @@
 				cell = endBlocks.start;
 				while (cell) {
 					cells.push(cell.innerHTML);
-					if (operation === "Cut") {
-						cell.innerHTML = "";
+					if (operation === 'Cut') {
+						cell.innerHTML = '';
 					}
 					if (!cell.previousSibling) firstCellOfRow = true;
 					if (!cell.nextSibling) lastCellOfRow = true;
@@ -351,19 +394,19 @@
 					cell = cell.nextSibling;
 				}
 				rows[tablePartsIndex[firstRow.parentNode.nodeName.toLowerCase()]].push(cells);
-				if (operation === "Cut" && firstCellOfRow && lastCellOfRow) cutRows.push(firstRow);
+				if (operation === 'Cut' && firstCellOfRow && lastCellOfRow) cutRows.push(firstRow);
 			} else { // Collect all cells on selected rows
 				row = firstRow;
 				while (row) {
 					cells = [];
 					for (var i = 0, n = row.cells.length; i < n ; ++i) {
 						cells.push(row.cells[i].innerHTML);
-						if (operation === "Cut") {
-							row.cells[i].innerHTML = "";
+						if (operation === 'Cut') {
+							row.cells[i].innerHTML = '';
 						}
 					}
 					rows[tablePartsIndex[row.parentNode.nodeName.toLowerCase()]].push(cells);
-					if (operation === "Cut") cutRows.push(row);
+					if (operation === 'Cut') cutRows.push(row);
 					if (row == lastRow) break;
 					row = row.nextSibling;
 				}
@@ -385,52 +428,48 @@
 		}
 		return rows;
 	},
-	
 	/*
 	 * This function gets called when the toolbar is updated
 	 */
 	onUpdateToolbar: function (button, mode, selectionEmpty, ancestors) {
 		if (mode === 'wysiwyg' && this.editor.isEditable() && button.itemId === 'Paste') {
 			try {
-				button.setDisabled(!this.editor._doc.queryCommandEnabled(button.itemId));
+				button.setDisabled(!this.editor.document.queryCommandEnabled(button.itemId));
 			} catch(e) {
 				button.setDisabled(true);
 			}
 		}
 	},
-	
 	/*
 	 * Mozilla clipboard access exception handler
 	 */
-	mozillaClipboardAccessException : function () {
+	mozillaClipboardAccessException: function () {
 		if (this.buttonsConfiguration.paste && this.buttonsConfiguration.paste.mozillaAllowClipboardURL) {
-			if (confirm(this.localize("Allow-Clipboard-Helper-Extension"))) {
+			if (confirm(this.localize('Allow-Clipboard-Helper-Extension'))) {
 				if (InstallTrigger.enabled()) {
 					var mozillaXpi = new Object();
-					mozillaXpi["AllowClipboard Helper"] = this.buttonsConfiguration.paste.mozillaAllowClipboardURL;
+					mozillaXpi['AllowClipboard Helper'] = this.buttonsConfiguration.paste.mozillaAllowClipboardURL;
 					InstallTrigger.install(mozillaXpi, this.mozillaInstallCallback);
 				} else {
-					alert(this.localize("Mozilla-Org-Install-Not-Enabled"));
-					this.appendToLog("mozillaClipboardAccessException", "Mozilla install was not enabled.");
+					alert(this.localize('Mozilla-Org-Install-Not-Enabled'));
+					this.appendToLog('mozillaClipboardAccessException', 'Mozilla install was not enabled.');
 					return;
 				}
 			}
-		} else if (confirm(this.localize("Moz-Clipboard"))) {
-			window.open("http://mozilla.org/editor/midasdemo/securityprefs.html");
+		} else if (confirm(this.localize('Moz-Clipboard'))) {
+			window.open('http://mozilla.org/editor/midasdemo/securityprefs.html');
 		}
 	},
-	
 	/*
 	 * Mozilla Add-on installer call back
 	 */
-	mozillaInstallCallback : function (url, returnCode) {
+	mozillaInstallCallback: function (url, returnCode) {
 		if (returnCode == 0) {
-			alert(this.localize("Allow-Clipboard-Helper-Extension-Success"));
+			alert(this.localize('Allow-Clipboard-Helper-Extension-Success'));
 		} else {
-			alert(this.localize("Moz-Extension-Failure"));
-			this.appendToLog("mozillaInstallCallback", "Mozilla install return code was: " + returnCode + ".");
+			alert(this.localize('Moz-Extension-Failure'));
+			this.appendToLog('mozillaInstallCallback', 'Mozilla install return code was: ' + returnCode + '.');
 		}
 		return;
 	}
 });
-
Index: typo3/sysext/rtehtmlarea/htmlarea/plugins/TYPO3HtmlParser/typo3html-parser.js
===================================================================
--- typo3/sysext/rtehtmlarea/htmlarea/plugins/TYPO3HtmlParser/typo3html-parser.js	(révision 7296)
+++ typo3/sysext/rtehtmlarea/htmlarea/plugins/TYPO3HtmlParser/typo3html-parser.js	(copie de travail)
@@ -86,6 +86,7 @@
 		this.editor.iframe.mon(Ext.get(Ext.isIE ? this.editor.document.body : this.editor.document.documentElement), 'paste', this.wordCleanHandler, this);
 	},
 	clean: function() {
+		this.editor.inhibitKeyboardInput = true;
 		var editor = this.editor;
 		if (Ext.isWebKit) {
 			editor.cleanAppleStyleSpans(editor._doc.body);
@@ -107,6 +108,7 @@
 					} else {
 						this.appendToLog('clean', 'Post request to ' + url + ' failed. Server reported ' + response.status);
 					}
+					this.editor.inhibitKeyboardInput = false;
 				}
 		);
 	},
@@ -114,6 +116,6 @@
 	 * Handler for paste, dragdrop and drop events
 	 */
 	wordCleanHandler: function (event) {
-		this.clean.defer(250, this);
+		this.clean.defer(50, this);
 	}
 });
