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 = "
";
+ container.innerHTML = '
';
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 = "
";
+ if (operation === 'Cut') {
+ cell.innerHTML = '
';
}
- 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 = "
";
+ if (operation === 'Cut') {
+ cell.innerHTML = '
';
}
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);
}
});