Added feature #9121: Move htmlArea RTE cut/copy/paste handling to own extension
authorStanislas Rolland <typo3@sjbr.ca>
Tue, 5 Aug 2008 02:31:22 +0000 (02:31 +0000)
committerStanislas Rolland <typo3@sjbr.ca>
Tue, 5 Aug 2008 02:31:22 +0000 (02:31 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@3932 709f56b5-9817-0410-a4d7-c38de5d9e867

12 files changed:
ChangeLog
typo3/sysext/rtehtmlarea/ChangeLog
typo3/sysext/rtehtmlarea/class.tx_rtehtmlarea_base.php
typo3/sysext/rtehtmlarea/ext_localconf.php
typo3/sysext/rtehtmlarea/extensions/CopyPaste/class.tx_rtehtmlarea_copypaste.php [new file with mode: 0644]
typo3/sysext/rtehtmlarea/extensions/CopyPaste/skin/htmlarea.css [new file with mode: 0644]
typo3/sysext/rtehtmlarea/extensions/CopyPaste/skin/images/copy.gif [new file with mode: 0644]
typo3/sysext/rtehtmlarea/extensions/CopyPaste/skin/images/cut.gif [new file with mode: 0644]
typo3/sysext/rtehtmlarea/extensions/CopyPaste/skin/images/paste.gif [new file with mode: 0644]
typo3/sysext/rtehtmlarea/htmlarea/htmlarea.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/CopyPaste/copy-paste.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/htmlarea/plugins/CopyPaste/locallang.xml [new file with mode: 0644]

index d3eff37..6d260c5 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,6 @@
+2008-08-04  Stanislas Rolland  <typo3@sjbr.ca>
+
+       * Added feature #9121: Move htmlArea RTE cut/copy/paste handling to own extension
 
 2008-08-03  Martin Kutschker  <masi@typo3.org>
 
index a4f4332..ab7a9fd 100644 (file)
@@ -1,3 +1,7 @@
+2008-08-04  Stanislas Rolland  <typo3@sjbr.ca>
+
+       * Added feature #9121: Move htmlArea RTE cut/copy/paste handling to own extension
+
 2008-06-29  Stanislas Rolland  <typo3@sjbr.ca>
 
        * Added feature #8791 (revisited): Let htmlArea RTE context menu use the RTE API
index 583387c..4fd6047 100644 (file)
@@ -70,12 +70,6 @@ class tx_rtehtmlarea_base extends t3lib_rteapi {
                'showhelp',             // Has no content yet
                );
        
-               // Hide toolbar buttons not implemented in client browsers
-       var $hideButtonsFromClient = array (
-               'safari'        =>      array('paste'),
-               'opera'         =>      array('copy', 'cut', 'paste'),
-               );
-       
                // Always show these toolbar buttons (TYPO3 button name)
        var $conf_toolbar_show = array (
                'undo',
@@ -86,24 +80,16 @@ class tx_rtehtmlarea_base extends t3lib_rteapi {
        
                // The order of the toolbar: the name is the TYPO3-button name
        var $defaultToolbarOrder;
-
-               // The default hotkeys: the name is the TYPO3-button name
-       var $defaultHotKeyList = 'selectall, cleanword, undo, redo';
-
+       
                // Conversion array: TYPO3 button names to htmlArea button names
        var $convertToolbarForHtmlAreaArray = array (
                'line'                  => 'InsertHorizontalRule',
-               'cut'                   => 'Cut',
-               'copy'                  => 'Copy',
-               'paste'                 => 'Paste',
                'chMode'                => 'HtmlMode',
                'showhelp'              => 'ShowHelp',
                'textindicator'         => 'TextIndicator',
                'space'                 => 'space',
                'bar'                   => 'separator',
                'linebreak'             => 'linebreak',
-               'undo'                  => 'Undo',
-               'redo'                  => 'Redo',
                );
        
        var $pluginList;
@@ -623,17 +609,8 @@ class tx_rtehtmlarea_base extends t3lib_rteapi {
                        }
                }
                
-                       // Hiding buttons not implemented in some clients
-               foreach ($this->hideButtonsFromClient as $client => $buttonArray) {
-                       if ($this->client['BROWSER'] == $client) {
-                               foreach($buttonArray as $buttonId) {
-                                       $hideButtons[] = $buttonId;
-                               }
-                       }
-               }
-               
-                       // Hiding the buttons
-               $show = array_diff($show, $this->conf_toolbar_hide, $hideButtons, t3lib_div::trimExplode(',',$this->thisConfig['hideButtons'],1));
+                       // Hiding buttons
+               $show = array_diff($show, $this->conf_toolbar_hide, t3lib_div::trimExplode(',',$this->thisConfig['hideButtons'],1));
                
                        // Apply toolbar constraints from registered plugins
                foreach ($this->registeredPlugins as $pluginId => $plugin) {
index f36f05b..77319d1 100644 (file)
@@ -153,6 +153,10 @@ $TYPO3_CONF_VARS['EXTCONF']['rtehtmlarea']['plugins']['UndoRedo'] = array();
 $TYPO3_CONF_VARS['EXTCONF']['rtehtmlarea']['plugins']['UndoRedo']['objectReference'] = 'EXT:'.$_EXTKEY.'/extensions/UndoRedo/class.tx_rtehtmlarea_undoredo.php:&tx_rtehtmlarea_undoredo';
 $TYPO3_CONF_VARS['EXTCONF']['rtehtmlarea']['plugins']['UndoRedo']['addIconsToSkin'] = 0;
 $TYPO3_CONF_VARS['EXTCONF']['rtehtmlarea']['plugins']['UndoRedo']['disableInFE'] = 0;
+$TYPO3_CONF_VARS['EXTCONF']['rtehtmlarea']['plugins']['CopyPaste'] = array();
+$TYPO3_CONF_VARS['EXTCONF']['rtehtmlarea']['plugins']['CopyPaste']['objectReference'] = 'EXT:'.$_EXTKEY.'/extensions/CopyPaste/class.tx_rtehtmlarea_copypaste.php:&tx_rtehtmlarea_copypaste';
+$TYPO3_CONF_VARS['EXTCONF']['rtehtmlarea']['plugins']['CopyPaste']['addIconsToSkin'] = 0;
+$TYPO3_CONF_VARS['EXTCONF']['rtehtmlarea']['plugins']['CopyPaste']['disableInFE'] =  0;
 
 $_EXTCONF = unserialize($_EXTCONF);    // unserializing the configuration so we can use it here:
 
diff --git a/typo3/sysext/rtehtmlarea/extensions/CopyPaste/class.tx_rtehtmlarea_copypaste.php b/typo3/sysext/rtehtmlarea/extensions/CopyPaste/class.tx_rtehtmlarea_copypaste.php
new file mode 100644 (file)
index 0000000..a41e2f5
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2008 Stanislas Rolland <typo3(arobas)sjbr.ca>
+*  All rights reserved
+*
+*  This script is part of the Typo3 project. The Typo3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * Copy Paste plugin for htmlArea RTE
+ *
+ * @author Stanislas Rolland <typo3(arobas)sjbr.ca>
+ *
+ * TYPO3 SVN ID: $Id: class.tx_rtehtmlarea_copypaste.php  $
+ *
+ */
+
+require_once(t3lib_extMgm::extPath('rtehtmlarea').'class.tx_rtehtmlareaapi.php');
+
+class tx_rtehtmlarea_copypaste extends tx_rtehtmlareaapi {
+
+       protected $extensionKey = 'rtehtmlarea';                        // The key of the extension that is extending htmlArea RTE
+       protected $pluginName = 'CopyPaste';                            // The name of the plugin registered by the extension
+       protected $relativePathToLocallangFile = '';                    // Path to this main locallang file of the extension relative to the extension dir.
+       protected $relativePathToSkin = 'extensions/CopyPaste/skin/htmlarea.css';       // Path to the skin (css) file relative to the extension dir.
+       protected $htmlAreaRTE;                                         // Reference to the invoking object
+       protected $thisConfig;                                          // Reference to RTE PageTSConfig
+       protected $toolbar;                                             // Reference to RTE toolbar array
+       protected $LOCAL_LANG;                                          // Frontend language array
+       
+       protected $pluginButtons = 'copy, cut, paste';
+       protected $convertToolbarForHtmlAreaArray = array (
+               'copy'  => 'Copy',
+               'cut'   => 'Cut',
+               'paste' => 'Paste',
+               );
+       protected $hideButtonsFromClient = array (                      // Hide buttons not implemented in client browsers
+               'safari'        =>      array('paste'),
+               );
+       
+       public function main($parentObject) {
+               $enabled = parent::main($parentObject) && $this->htmlAreaRTE->client['BROWSER'] != 'opera';
+                       // Hiding some buttons
+               if ($enabled && is_array($this->hideButtonsFromClient[$this->htmlAreaRTE->client['BROWSER']])) {
+                       $this->pluginButtons = implode(',', array_diff(t3lib_div::trimExplode(',', $this->pluginButtons, 1), $this->hideButtonsFromClient[$this->htmlAreaRTE->client['BROWSER']]));
+               }
+               return $enabled;
+       }
+       
+       /**
+        * Return JS configuration of the htmlArea plugins registered by the extension
+        *
+        * @param       integer         Relative id of the RTE editing area in the form
+        *
+        * @return string               JS configuration for registered plugins
+        *
+        * The returned string will be a set of JS instructions defining the configuration that will be provided to the plugin(s)
+        * Each of the instructions should be of the form:
+        *      RTEarea['.$RTEcounter.'].buttons.button-id.property = "value";
+        */
+       public function buildJavascriptConfiguration($RTEcounter) {
+               global $TSFE, $LANG;
+               
+               $registerRTEinJavascriptString = '';
+               return $registerRTEinJavascriptString;
+       }
+
+} // end of class
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rtehtmlarea/extensions/CopyPaste/class.tx_rtehtmlarea_copypaste.php']) {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rtehtmlarea/extensions/CopyPaste/class.tx_rtehtmlarea_copypaste.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/rtehtmlarea/extensions/CopyPaste/skin/htmlarea.css b/typo3/sysext/rtehtmlarea/extensions/CopyPaste/skin/htmlarea.css
new file mode 100644 (file)
index 0000000..410d1d8
--- /dev/null
@@ -0,0 +1,5 @@
+/* Selectors for the CopyPaste extension of htmlArea RTE */
+/* TYPO3 SVN ID: $Id htmlarea.css $ */
+.htmlarea .toolbar .Copy, .htmlarea-context-menu .Copy  {background-image:url("images/copy.gif");}
+.htmlarea .toolbar .Cut, .htmlarea-context-menu .Cut  {background-image:url("images/cut.gif");}
+.htmlarea .toolbar .Paste, .htmlarea-context-menu .Paste {background-image:url("images/paste.gif");}
diff --git a/typo3/sysext/rtehtmlarea/extensions/CopyPaste/skin/images/copy.gif b/typo3/sysext/rtehtmlarea/extensions/CopyPaste/skin/images/copy.gif
new file mode 100644 (file)
index 0000000..4e2f5d2
Binary files /dev/null and b/typo3/sysext/rtehtmlarea/extensions/CopyPaste/skin/images/copy.gif differ
diff --git a/typo3/sysext/rtehtmlarea/extensions/CopyPaste/skin/images/cut.gif b/typo3/sysext/rtehtmlarea/extensions/CopyPaste/skin/images/cut.gif
new file mode 100644 (file)
index 0000000..c712aed
Binary files /dev/null and b/typo3/sysext/rtehtmlarea/extensions/CopyPaste/skin/images/cut.gif differ
diff --git a/typo3/sysext/rtehtmlarea/extensions/CopyPaste/skin/images/paste.gif b/typo3/sysext/rtehtmlarea/extensions/CopyPaste/skin/images/paste.gif
new file mode 100644 (file)
index 0000000..4b67579
Binary files /dev/null and b/typo3/sysext/rtehtmlarea/extensions/CopyPaste/skin/images/paste.gif differ
index 4b20df7..fa6f5b1 100644 (file)
@@ -289,15 +289,11 @@ HTMLArea.Config = function () {
        this.btnList = {
                InsertHorizontalRule:   ["Horizontal Rule", "ed_hr.gif",false, function(editor) {editor.execCommand("InsertHorizontalRule");}],
                HtmlMode:               ["Toggle HTML Source", "ed_html.gif", true, function(editor) {editor.execCommand("HtmlMode");}],
-               SelectAll:              ["SelectAll", "", true, function(editor) {editor.execCommand("SelectAll");}, null, true, false],
-               Cut:                    ["Cut selection", "ed_cut.gif", false, function(editor) {editor.execCommand("Cut");}],
-               Copy:                   ["Copy selection", "ed_copy.gif", false, function(editor) {editor.execCommand("Copy");}],
-               Paste:                  ["Paste from clipboard", "ed_paste.gif", false, function(editor) {editor.execCommand("Paste");}]
+               SelectAll:              ["SelectAll", "", true, function(editor) {editor.execCommand("SelectAll");}, null, true, false]
        };
                // Default hotkeys
        this.hotKeyList = {
-               a:      { cmd:  "SelectAll",    action: null},
-               v:      { cmd:  "Paste",        action: null}
+               a:      { cmd:  "SelectAll",    action: null}
        };
 
                // Initialize tooltips from the I18N module, generate correct image path
@@ -1118,9 +1114,6 @@ HTMLArea.prototype.stylesLoaded = function() {
                HTMLArea._addEvent((this._iframe.contentWindow ? this._iframe.contentWindow : this._iframe.contentDocument), "unload", HTMLArea.removeEditorEvents);
        }
 
-               // set enableWordClean and intercept paste, dragdrop and drop events for wordClean
-       //if (this.config.enableWordClean) HTMLArea._addEvents((HTMLArea.is_ie ? doc.body : doc), ["paste","dragdrop","drop"], HTMLArea.wordClean, true);
-
        window.setTimeout("HTMLArea.generatePlugins(" + this._editorNumber + ");", 100);
 };
 
@@ -1463,7 +1456,7 @@ HTMLArea.prototype.popupURL = function(file) {
                if (this.config.pathToPluginDirectory[pluginId]) {
                        url = this.config.pathToPluginDirectory[pluginId] + "popups/" + popup;
                } else {
-                       url = _editor_url + "plugins/" + pluginId + "/popups/" + popup;
+                       url = _typo3_host_url + _editor_url + "plugins/" + pluginId + "/popups/" + popup;
                }
        } else {
                url = _typo3_host_url + _editor_url + this.config.popupURL + file;
@@ -1647,15 +1640,6 @@ HTMLArea.prototype.updateToolbar = function(noStatus) {
                                case "HtmlMode":
                                        btn.state("active", text);
                                        break;
-                               case "Paste":
-                                       if (!text) {
-                                               try {
-                                                       btn.state("enabled", doc.queryCommandEnabled('Paste'));
-                                               } catch(e) {
-                                                       btn.state("enabled", false);
-                                               }
-                                       }
-                                       break;
                                default:
                                        break;
                        }
@@ -1802,21 +1786,6 @@ HTMLArea.prototype.execCommand = function(cmdID, UI, param) {
                case "HtmlMode" :
                        this.setMode();
                        break;
-               case "Cut"      :
-               case "Copy"     :
-               case "Paste"    :
-                       try {
-                               this._doc.execCommand(cmdID, false, null);
-                                       // In FF3, the paste operation will indeed trigger the paste event
-                               if (HTMLArea.is_gecko && cmdID == "Paste" && this._toolbarObjects.CleanWord && navigator.productSub < 2008020514) {
-                                       this._toolbarObjects.CleanWord.cmd(this, "CleanWord");
-                               }
-                       } catch (e) {
-                               if (HTMLArea.is_gecko && !HTMLArea.is_safari && !HTMLArea.is_opera) {
-                                       this._mozillaPasteException(cmdID, UI, param);
-                               }
-                       }
-                       break;
                default         :
                        try {
                                this._doc.execCommand(cmdID, UI, param);
@@ -1877,22 +1846,6 @@ HTMLArea._editorEvent = function(ev) {
                                                HTMLArea._stopEvent(ev);
                                                return false;
                                                break;
-                                       case "Paste"    :
-                                               if (HTMLArea.is_ie || HTMLArea.is_safari) {
-                                                       cmd = editor.config.hotKeyList[key].cmd;
-                                                       editor.execCommand(cmd, false, null);
-                                                       HTMLArea._stopEvent(ev);
-                                                       return false;
-                                                       // In FF3, the paste operation will indeed trigger the paste event
-                                               } else if (HTMLArea.is_opera || (HTMLArea.is_gecko && navigator.productSub < 2008020514)) {
-                                                       if (editor._toolbarObjects.CleanWord) {
-                                                               var cleanLaterFunctRef = editor.plugins.DefaultClean ? editor.plugins.DefaultClean.instance.cleanLaterFunctRef : (editor.plugins.TYPO3HtmlParser ? editor.plugins.TYPO3HtmlParser.instance.cleanLaterFunctRef : null);
-                                                               if (cleanLaterFunctRef) {
-                                                                       window.setTimeout(cleanLaterFunctRef, 50);
-                                                               }
-                                                       }
-                                               }
-                                               break;
                                        default:
                                                if (editor.config.hotKeyList[key] && editor.config.hotKeyList[key].action) {
                                                        if (!editor.config.hotKeyList[key].action(editor, key)) {
@@ -2572,7 +2525,7 @@ HTMLArea.initEditor = function(editorNumber) {
 
 HTMLArea.allElementsAreDisplayed = function(elements) {
        for (var i=0, length=elements.length; i < length; i++) {
-               if (document.getElementById(elements[i]).style.display == 'none') {
+               if (document.getElementById(elements[i]).style.display == "none") {
                        return false;
                }
        }
diff --git a/typo3/sysext/rtehtmlarea/htmlarea/plugins/CopyPaste/copy-paste.js b/typo3/sysext/rtehtmlarea/htmlarea/plugins/CopyPaste/copy-paste.js
new file mode 100644 (file)
index 0000000..5fa7af4
--- /dev/null
@@ -0,0 +1,340 @@
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2008 Stanislas Rolland <typo3(arobas)sjbr.ca>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/*
+ * Copy Paste for TYPO3 htmlArea RTE
+ *
+ * TYPO3 SVN ID: $Id: copy-paste.js $
+ */
+CopyPaste = HTMLArea.Plugin.extend({
+               
+       constructor : function(editor, pluginName) {
+               this.base(editor, pluginName);
+       },
+       
+       /*
+        * This function gets called by the class constructor
+        */
+       configurePlugin : function (editor) {
+               
+               /*
+                * Setting up some properties from PageTSConfig
+                */
+               this.buttonsConfiguration = this.editorConfiguration.buttons;
+               
+               /*
+                * Registering plugin "About" information
+                */
+               var pluginInformation = {
+                       version         : "1.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()),
+                                       action          : "onButtonPress",
+                                       context         : button[0],
+                                       hotKey          : (this.buttonsConfiguration[button[2]] ? this.buttonsConfiguration[button[2]].hotKey : (button[1] ? button[1] : null))
+                               };
+                               this.registerButton(buttonConfiguration);
+                       }
+               }
+               
+               return true;
+        },
+        
+       /*
+        * The list of buttons added by this plugin
+        */
+       buttonList : {
+               Copy    : [null, "c", "copy"],
+               Cut     : [null, "x", "cut"],
+               Paste   : [null, "v", "paste"]
+       },
+       
+       /*
+        * 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) {
+                       // Could be a button or its hotkey
+               var buttonId = this.translateHotKey(id);
+               buttonId = buttonId ? buttonId : id;
+               this.editor.focusEditor();
+               if (!this.applyToTable(buttonId, target)) {
+                       switch (buttonId) {
+                               case "Copy":
+                               case "Cut" :
+                                       this.applyBrowserCommand(buttonId);
+                                       break;
+                               case "Paste":
+                                       if (!HTMLArea.is_opera) {
+                                               this.applyBrowserCommand(buttonId, (buttonId != id));
+                                       }
+                                               // In FF3, the paste operation will indeed trigger the paste event
+                                       if (HTMLArea.is_opera || (HTMLArea.is_gecko && navigator.productSub < 2008020514)) {
+                                               if (this.editor._toolbarObjects.CleanWord) {
+                                                       var cleanLaterFunctRef = this.editor.plugins.DefaultClean ? this.editor.plugins.DefaultClean.instance.cleanLaterFunctRef : (this.editor.plugins.TYPO3HtmlParser ? this.editor.plugins.TYPO3HtmlParser.instance.cleanLaterFunctRef : null);
+                                                       if (cleanLaterFunctRef) {
+                                                               window.setTimeout(cleanLaterFunctRef, 50);
+                                                       }
+                                               }
+                                       }
+                                       break;
+                       }
+               }
+               return false;
+       },
+       
+       applyBrowserCommand : function (buttonId, buttonPress) {
+               try {
+                       this.editor._doc.execCommand(buttonId, false, null);
+               } catch (e) {
+                       if (buttonId == "Paste" && HTMLArea.is_gecko && !HTMLArea.is_safari && !HTMLArea.is_opera) {
+                               this.editor._mozillaPasteException(buttonId, buttonPress, null);
+                       }
+               }
+       },
+       
+       /*
+        * This function gets called by the main editor when a copy/cut/paste operation is to be performed
+        */
+       applyToTable : function (buttonId, target) {
+               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" :
+                               HTMLArea.copiedCells = null;
+                               var endBlocks = this.editor.getEndBlocks(selection);
+                               if ((/^(tr)$/i.test(parent.nodeName) && HTMLArea.is_gecko) || (/^(td|th)$/i.test(endBlocks.start.nodeName) && /^(td|th)$/i.test(endBlocks.end.nodeName) && (HTMLArea.is_ie || HTMLArea.is_safari || HTMLArea.is_opera) && endBlocks.start != endBlocks.end)) {
+                                       HTMLArea.copiedCells = this.collectCells(buttonId, selection, endBlocks);
+                                       if (buttonId === "Cut") return true;
+                               }
+                               break;
+                       case "Paste":
+                               if (/^(tr|td|th)$/i.test(parent.nodeName) && HTMLArea.copiedCells) {
+                                       return this.pasteCells(selection, endBlocks);
+                               }
+                               break;
+               }
+               return false;
+       },
+       
+       pasteCells : function (selection, endBlocks) {
+               var cell = null;
+               if (HTMLArea.is_gecko && !HTMLArea.is_safari && !HTMLArea.is_opera) {
+                       range = selection.getRangeAt(0);
+                       cell = range.startContainer.childNodes[range.startOffset];
+                       while (cell && !HTMLArea.isBlockElement(cell)) {
+                               cell = cell.parentNode;
+                       }
+               }
+               if (!cell && /^(td|th)$/i.test(endBlocks.start.nodeName)) {
+                       cell = endBlocks.start;
+               }
+               if (!cell) 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()]
+               var rows = HTMLArea.copiedCells[tablePartIndex];
+               if (rows && rows[0]) {
+                       for (var i = 0, rowIndex = cell.parentNode.sectionRowIndex-1; i < rows.length && ++rowIndex < tablePart.rows.length; ++i) {
+                               var cells = rows[i];
+                               if (!cells) break;
+                               var row = tablePart.rows[rowIndex];
+                               for (var j = 0, cellIndex = cell.cellIndex-1; j < cells.length && ++cellIndex < row.cells.length; ++j) {
+                                       row.cells[cellIndex].innerHTML = cells[j];
+                               }
+                       }
+               }
+               var table = tablePart.parentNode;
+               for (var k = tablePartIndex +1; k < 3; ++k) {
+                       tablePart = table.getElementsByTagName(tableParts[k])[0];
+                       if (tablePart) {
+                               var rows = HTMLArea.copiedCells[k];
+                               for (var i = 0; i < rows.length && i < tablePart.rows.length; ++i) {
+                                       var cells = rows[i];
+                                       if (!cells) break;
+                                       var row = tablePart.rows[i];
+                                       for (var j = 0, cellIndex = cell.cellIndex-1; j < cells.length && ++cellIndex < row.cells.length; ++j) {
+                                               row.cells[cellIndex].innerHTML = cells[j];
+                                       }
+                               }
+                       }
+               }
+               return true;
+       },
+       
+       /*
+        * This function collects the selected table cells for copy/cut operations
+        */
+       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;
+               var rows = new Array();
+               for (var k = tableParts.length; --k >= 0;) {
+                       rows[k] = [];
+               }
+               var row = null;
+               var cutRows = [];
+               if (HTMLArea.is_gecko && !HTMLArea.is_safari && !HTMLArea.is_opera) {  // Firefox
+                       if (selection.rangeCount == 1) { // Collect the cells in the selected row
+                               cells = [];
+                               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") {
+                                               cutRows.push(endBlocks.start);
+                                       }
+                               }
+                               rows[tablePartsIndex[endBlocks.start.parentNode.nodeName.toLowerCase()]].push(cells);
+                       } else {
+                               try { // Collect the cells in some region of the table
+                                       var firstCellOfRow = false;
+                                       var lastCellOfRow = false;
+                                       while (range = selection.getRangeAt(i++)) {
+                                               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);
+                                                       row = cell.parentNode;
+                                                       cells = [];
+                                                       firstCellOfRow = false;
+                                                       lastCellOfRow = false;
+                                               }
+                                               cells.push(cell.innerHTML);
+                                               if (operation === "Cut") {
+                                                       cell.innerHTML = "<br />";
+                                               }
+                                               if (!cell.previousSibling) firstCellOfRow = true;
+                                               if (!cell.nextSibling) lastCellOfRow = true;
+                                       }
+                               } catch(e) {
+                                       /* finished walking through selection */
+                               }
+                               try { rows[tablePartsIndex[row.parentNode.nodeName.toLowerCase()]].push(cells); } catch(e) { }
+                               if (row && operation === "Cut" && firstCellOfRow && lastCellOfRow) {
+                                       cutRows.push(row);
+                               }
+                       }
+               } else { // Internet Explorer, Safari and Opera
+                       var firstRow = endBlocks.start.parentNode;
+                       var lastRow = endBlocks.end.parentNode;
+                       cells = [];
+                       var firstCellOfRow = false;
+                       var lastCellOfRow = false;
+                       if (firstRow == lastRow) { // Collect the selected cells on the row
+                               cell = endBlocks.start;
+                               while (cell) {
+                                       cells.push(cell.innerHTML);
+                                       if (operation === "Cut") {
+                                               cell.innerHTML = "";
+                                       }
+                                       if (!cell.previousSibling) firstCellOfRow = true;
+                                       if (!cell.nextSibling) lastCellOfRow = true;
+                                       if (cell == endBlocks.end) break;
+                                       cell = cell.nextSibling;
+                               }
+                               rows[tablePartsIndex[firstRow.parentNode.nodeName.toLowerCase()]].push(cells);
+                               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 = "";
+                                               }
+                                       }
+                                       rows[tablePartsIndex[row.parentNode.nodeName.toLowerCase()]].push(cells);
+                                       if (operation === "Cut") cutRows.push(row);
+                                       if (row == lastRow) break;
+                                       row = row.nextSibling;
+                               }
+                       }
+               }
+               for (var i = 0, n = cutRows.length; i < n; ++i) {
+                       if (i == n-1) {
+                               var tablePart = cutRows[i].parentNode;
+                               var next = cutRows[i].nextSibling;
+                               cutRows[i].parentNode.removeChild(cutRows[i]);
+                               if (next) {
+                                       this.editor.selectNodeContents(next.cells[0], true);
+                               } else if (tablePart.parentNode.rows.length) {
+                                       this.editor.selectNodeContents(tablePart.parentNode.rows[0].cells[0], true);
+                               }
+                       } else {
+                               cutRows[i].parentNode.removeChild(cutRows[i]);
+                       }
+               }
+               return rows;
+       },
+       
+       /*
+        * This function gets called when the toolbar is updated
+        */
+       onUpdateToolbar : function () {
+               if (this.editor.getMode() === "wysiwyg" || this.editor.isEditable()) {
+                       var buttonId = "Paste";
+                       if (typeof(this.editor._toolbarObjects[buttonId]) !== "undefined") {
+                               try {
+                                       this.editor._toolbarObjects[buttonId].state("enabled", this.editor._doc.queryCommandEnabled(buttonId));
+                               } catch(e) {
+                                       this.editor._toolbarObjects[buttonId].state("enabled", false);
+                               }
+                       }
+               }
+       }
+});
+
diff --git a/typo3/sysext/rtehtmlarea/htmlarea/plugins/CopyPaste/locallang.xml b/typo3/sysext/rtehtmlarea/htmlarea/plugins/CopyPaste/locallang.xml
new file mode 100644 (file)
index 0000000..9ea2c8c
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<!-- TYPO3 SVN ID: $Id: locallang.xml 2975 2008-01-29 23:01:39Z stanrolland $ -->
+<T3locallang>
+       <meta type="array">
+               <description>Labels for Copy Paste plugin of htmlArea RTE</description>
+               <type>module</type>
+       </meta>
+       <data type="array">
+               <languageKey index="default" type="array">
+                       <label index="How many columns would you like to copy?">How many columns would you like to copy?</label>
+                       <label index="How many rows would you like to copy?">How many rows would you like to copy?</label>
+                       <label index="How many columns would you like to cut?">How many columns would you like to cut?</label>
+                       <label index="How many rows would you like to cut?">How many rows would you like to cut?</label>
+                       <label index="Technische Universitat Ilmenau">Technische Universit├Ąt Ilmenau</label>
+               </languageKey>
+       </data>
+       <orig_hash type="array">
+               <languageKey index="default" type="array">
+               </languageKey>
+       </orig_hash>
+       <orig_text type="array">
+               <languageKey index="default" type="array">
+               </languageKey>
+       </orig_text>
+</T3locallang>
\ No newline at end of file