[TASK] RTE: Migrate to RequireJS 31/35131/3
authorStanislas Rolland <typo3@sjbr.ca>
Sun, 7 Dec 2014 03:50:04 +0000 (22:50 -0500)
committerStanislas Rolland <typo3@sjbr.ca>
Sun, 7 Dec 2014 19:11:07 +0000 (20:11 +0100)
Releases: master
Resolves: #63636

Change-Id: Id44a8313fede29e9efe0eb2efb9142798852e4f0
Reviewed-on: http://review.typo3.org/35131
Reviewed-by: Stanislas Rolland <typo3@sjbr.ca>
Tested-by: Stanislas Rolland <typo3@sjbr.ca>
97 files changed:
typo3/sysext/rtehtmlarea/Classes/Controller/FrontendRteController.php
typo3/sysext/rtehtmlarea/Classes/RteHtmlAreaBase.php
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Ajax/Ajax.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/CSS/Parser.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Configuration/Config.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/DOM/BookMark.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/DOM/DOM.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/DOM/Node.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/DOM/Selection.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/DOM/Walker.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Editor/Editor.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Editor/Framework.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Editor/Iframe.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Editor/StatusBar.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Editor/Toolbar.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Event/Event.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Event/KeyMap.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ColorPalette.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/Ext.ColorPalette.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Button.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/ColorMenu.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/ColorPaletteField.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Combo.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Ext.ux.HTMLAreaButton.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Ext.ux.Toolbar.HTMLAreaToolbarText.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Ext.ux.form.ColorPaletteField.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Ext.ux.form.HTMLAreaCombo.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Ext.ux.menu.HTMLAreaColorMenu.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/ToolbarText.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/HTMLArea.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Plugin/Plugin.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/UserAgent/UserAgent.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Util/Color.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Util/TYPO3.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Util/Tips.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Util/Util.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/AboutEditor.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/BlockElements.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/BlockStyle.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/CharacterMap.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/ContextMenu.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/CopyPaste.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/DefaultClean.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/DefaultImage.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/DefaultInline.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/DefaultLink.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/DefinitionList.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/EditElement.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/EditorMode.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/FindReplace.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/InlineElements.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/InsertSmiley.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/MicrodataSchema.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/PlainText.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/QuickTag.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/RemoveFormat.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/SelectFont.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/SpellChecker.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/TYPO3HtmlParser.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/TableOperations.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/TextIndicator.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/TextStyle.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/UndoRedo.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/UserElements.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/abbreviation.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/about-editor.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/block-elements.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/block-style.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/character-map.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/context-menu.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/copy-paste.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/default-clean.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/default-image.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/default-inline.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/default-link.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/definition-list.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/edit-element.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/editor-mode.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/find-replace.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/inline-elements.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/insert-smiley.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/language.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/microdata-schema.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/plain-text.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/quick-tag.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/remove-format.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/select-font.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/spell-checker.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/table-operations.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/text-indicator.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/text-style.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/typo3color.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/typo3html-parser.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/typo3image.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/typo3link.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/undo-redo.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/user-elements.js [deleted file]

index 27d8a5b..7abfe44 100644 (file)
@@ -212,6 +212,7 @@ class FrontendRteController extends \TYPO3\CMS\Rtehtmlarea\RteHtmlAreaBase {
                $this->TCEform->additionalJS_post[] = $this->wrapCDATA($this->registerRTEinJS($this->TCEform->RTEcounter, '', '', '', $textAreaId));
                // Set the save option for the RTE:
                $this->TCEform->additionalJS_submit[] = $this->setSaveRTE($this->TCEform->RTEcounter, $this->TCEform->formName, $textAreaId);
+               $this->pageRenderer->loadRequireJs();
                // Loading ExtJs JavaScript files and inline code, if not configured in TS setup
                if (!is_array($GLOBALS['TSFE']->pSetup['javascriptLibs.']['ExtJs.'])) {
                        $this->pageRenderer->loadExtJs();
index ee41ab9..0320bfe 100644 (file)
@@ -476,6 +476,7 @@ class RteHtmlAreaBase extends \TYPO3\CMS\Backend\Rte\AbstractRte {
                        // Add RTE JavaScript
                        $this->addRteJsFiles($this->TCEform->RTEcounter);
                        $this->pageRenderer->addJsFile($this->buildJSMainLangFile($this->TCEform->RTEcounter));
+                       //$this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Rtehtmlarea/HTMLArea/HTMLArea');
                        $this->pageRenderer->addJsInlineCode('HTMLArea-init', $this->getRteInitJsCode(), TRUE);
                        /* =======================================
                         * DRAW THE EDITOR
@@ -819,33 +820,35 @@ class RteHtmlAreaBase extends \TYPO3\CMS\Backend\Rte\AbstractRte {
                        'DOM/Selection',
                        'DOM/Walker',
                        'Configuration/Config',
-                       'Extjs/ux/Ext.ux.HTMLAreaButton',
-                       'Extjs/ux/Ext.ux.Toolbar.HTMLAreaToolbarText',
-                       'Extjs/ux/Ext.ux.form.HTMLAreaCombo',
+                       'Extjs/ux/Button',
+                       'Extjs/ux/ToolbarText',
+                       'Extjs/ux/Combo',
+                       'Extjs/ColorPalette',
+                       'Extjs/ux/ColorMenu',
+                       'Extjs/ux/ColorPaletteField',
+                       'LoremIpsum',
+                       'Plugin/Plugin'
+               );
+               $components2 = array(
                        'Editor/Toolbar',
                        'Editor/Iframe',
                        'Editor/StatusBar',
                        'Editor/Framework',
                        'Editor/Editor',
-                       'HTMLArea',
-                       'Extjs/Ext.ColorPalette',
-                       'Extjs/ux/Ext.ux.menu.HTMLAreaColorMenu',
-                       'Extjs/ux/Ext.ux.form.ColorPaletteField',
-                       'LoremIpsum',
-                       'Plugin/Plugin'
+                       'HTMLArea'
                );
                foreach ($components as $component) {
                        $this->pageRenderer->addJsFile($this->getFullFileName('EXT:' . $this->ID . '/Resources/Public/JavaScript/HTMLArea/' . $component . '.js'));
                }
                foreach ($this->pluginEnabledCumulativeArray[$RTEcounter] as $pluginId) {
                        $extensionKey = is_object($this->registeredPlugins[$pluginId]) ? $this->registeredPlugins[$pluginId]->getExtensionKey() : $this->ID;
-                       $pluginName = strtolower(preg_replace('/([a-z])([A-Z])([a-z])/', '$1-$2$3', $pluginId));
-                       $fileName = 'EXT:' . $extensionKey . '/Resources/Public/JavaScript/Plugins/' . $pluginName . '.js';
+                       $fileName = 'EXT:' . $extensionKey . '/Resources/Public/JavaScript/Plugins/' . $pluginId . '.js';
                        $absolutePath = GeneralUtility::getFileAbsFileName($fileName);
                        if (file_exists($absolutePath)) {
                                $this->pageRenderer->addJsFile($this->getFullFileName($fileName));
                        } else {
                                // Backward compatibility
+                               $pluginName = strtolower(preg_replace('/([a-z])([A-Z])([a-z])/', '$1-$2$3', $pluginId));
                                $fileName = 'EXT:' . $extensionKey . '/htmlarea/plugins/' . $pluginId . '/' . $pluginName . '.js';
                                $absolutePath = GeneralUtility::getFileAbsFileName($fileName);
                                if (file_exists($absolutePath)) {
@@ -853,6 +856,9 @@ class RteHtmlAreaBase extends \TYPO3\CMS\Backend\Rte\AbstractRte {
                                }
                        }
                }
+               foreach ($components2 as $component) {
+                       $this->pageRenderer->addJsFile($this->getFullFileName('EXT:' . $this->ID . '/Resources/Public/JavaScript/HTMLArea/' . $component . '.js'));
+               }
                $this->pageRenderer->addJsFile($this->getFullFileName('EXT:' . $this->ID . '/Resources/Public/JavaScript/HTMLArea/Util/Wrap.close.js'));
        }
 
@@ -862,7 +868,7 @@ class RteHtmlAreaBase extends \TYPO3\CMS\Backend\Rte\AbstractRte {
         * @return string RTE initialization inline JavaScript code
         */
        protected function getRteInitJsCode() {
-               return '
+               return 'require(["TYPO3/CMS/Rtehtmlarea/HTMLArea/HTMLArea"], function (HTMLArea) {
                        if (typeof RTEarea === "undefined") {
                                RTEarea = new Object();
                                RTEarea[0] = new Object();
@@ -892,7 +898,8 @@ class RteHtmlAreaBase extends \TYPO3\CMS\Backend\Rte\AbstractRte {
                                        }
                                };
                        }
-                       RTEarea.init();';
+                       RTEarea.init();
+               });';
        }
 
        /**
index 561ac07..0dfe69c 100644 (file)
 /**
  * Ajax object
  */
-HTMLArea.Ajax = function($, Util) {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Ajax/Ajax',
+       ['jquery',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util'],
+       function($, Util) {
 
        /**
         * Constructor method
@@ -101,4 +104,4 @@ HTMLArea.Ajax = function($, Util) {
 
        return Ajax;
 
-}(HTMLArea.jQuery, HTMLArea.util);
+});
index cf1aa8f..6bcecb0 100644 (file)
@@ -1,7 +1,23 @@
+/**
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
 /***************************************************
  *  HTMLArea.CSS.Parser: CSS Parser
  ***************************************************/
-HTMLArea.CSS.Parser = function (UserAgent, Util, Event) {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/CSS/Parser',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event'],
+       function (UserAgent, Util, Event) {
 
        var Parser = Ext.extend(Ext.util.Observable, {
 
@@ -369,4 +385,4 @@ HTMLArea.CSS.Parser = function (UserAgent, Util, Event) {
 
        return Parser;
 
-}(HTMLArea.UserAgent, HTMLArea.util, HTMLArea.Event);
+});
index aa56687..b2aee5d 100644 (file)
 /**
  * Configuration of af an Editor of TYPO3 htmlArea RTE
  */
-HTMLArea.Config = function(Util) {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Configuration/Config',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util'],
+       function (UserAgent, Util) {
 
        /**
         *  Constructor: Sets editor configuration defaults
@@ -81,16 +84,16 @@ HTMLArea.Config = function(Util) {
                        htmlareabutton: {
                                cls: 'button',
                                overCls: 'buttonHover',
-                                       // Erratic behaviour of click event in WebKit and IE browsers
-                               clickEvent: (HTMLArea.UserAgent.isWebKit || HTMLArea.UserAgent.isIE) ? 'mousedown' : 'click'
+                               // Erratic behaviour of click event in WebKit and IE browsers
+                               clickEvent: (UserAgent.isWebKit || UserAgent.isIE) ? 'mousedown' : 'click'
                        },
                        htmlareacombo: {
                                cls: 'select',
                                typeAhead: true,
                                lastQuery: '',
                                triggerAction: 'all',
-                               editable: !HTMLArea.UserAgent.isIE,
-                               selectOnFocus: !HTMLArea.UserAgent.isIE,
+                               editable: !UserAgent.isIE,
+                               selectOnFocus: !UserAgent.isIE,
                                validationEvent: false,
                                validateOnBlur: false,
                                submitValue: false,
@@ -152,7 +155,7 @@ HTMLArea.Config = function(Util) {
                                                url: config.storeUrl
                                        });
                                }
-                               config.hideLabel = typeof config.fieldLabel !== 'string' || !config.fieldLabel.length || HTMLArea.UserAgent.isIE6;
+                               config.hideLabel = typeof config.fieldLabel !== 'string' || !config.fieldLabel.length || UserAgent.isIE6;
                                config.helpTitle = config.tooltip;
                                break;
                        default:
@@ -192,4 +195,4 @@ HTMLArea.Config = function(Util) {
 
        return Config;
 
-}(HTMLArea.util);
+});
index dbf7ec4..ed478e4 100644 (file)
 /***************************************************
  *  HTMLArea.DOM.BookMark: BookMark object
  ***************************************************/
-HTMLArea.DOM.BookMark = function(UserAgent, Util, Dom) {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/BookMark',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/DOM'],
+       function (UserAgent, Util, Dom) {
 
        /**
         * Constructor method
@@ -329,4 +333,4 @@ HTMLArea.DOM.BookMark = function(UserAgent, Util, Dom) {
 
        return BookMark;
 
-}(HTMLArea.UserAgent, HTMLArea.util, HTMLArea.DOM);
+});
index fcf0a4b..1d6f48f 100644 (file)
@@ -1,7 +1,21 @@
+/**
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
 /*****************************************************************
  * HTMLArea.DOM: Utility functions for dealing with the DOM tree *
  *****************************************************************/
-HTMLArea.DOM = function (UserAgent) {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/DOM',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent'],
+       function (UserAgent) {
 
        var Dom = {
 
@@ -484,4 +498,4 @@ HTMLArea.DOM = function (UserAgent) {
 
        return Dom;
 
-}(HTMLArea.UserAgent);
+});
index e537d6b..95d82c8 100644 (file)
 /***************************************************
  *  HTMLArea.DOM.Node: Node object
  ***************************************************/
-HTMLArea.DOM.Node = function(UserAgent, Util, Dom) {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/Node',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/DOM'],
+       function (UserAgent, Util, Dom) {
 
        /**
         * Constructor method
@@ -256,4 +260,4 @@ HTMLArea.DOM.Node = function(UserAgent, Util, Dom) {
 
        return Node;
 
-}(HTMLArea.UserAgent, HTMLArea.util, HTMLArea.DOM);
+});
index 41991ac..0176e70 100644 (file)
 /***************************************************
  *  HTMLArea.DOM.Selection: Selection object
  ***************************************************/
-HTMLArea.DOM.Selection = function(UserAgent, Util, Dom, Event) {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/Selection',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/DOM',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event'],
+       function (UserAgent, Util, Dom, Event) {
 
        /**
         * Constructor method
@@ -1081,4 +1086,4 @@ HTMLArea.DOM.Selection = function(UserAgent, Util, Dom, Event) {
 
        return Selection;
 
-}(HTMLArea.UserAgent, HTMLArea.util, HTMLArea.DOM, HTMLArea.Event);
+});
index d7e7bcc..b9688ec 100644 (file)
 /***************************************************
  *  HTMLArea.DOM.Walker: DOM tree walk
  ***************************************************/
-HTMLArea.DOM.Walker = function(UserAgent, Util, Dom) {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/Walker',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/DOM'],
+       function (UserAgent, Util, Dom) {
 
        /**
         * Constructor method
@@ -253,4 +257,4 @@ HTMLArea.DOM.Walker = function(UserAgent, Util, Dom) {
 
        return Walker;
 
-}(HTMLArea.UserAgent, HTMLArea.util, HTMLArea.DOM);
+});
index 3d38828..519c1a8 100644 (file)
 /**
  * Editor extends Ext.util.Observable
  */
-HTMLArea.Editor = function(UserAgent, Util, Ajax, Dom, Event, Selection, BookMark, Node, Typo3, Framework) {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Editor/Editor',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Ajax/Ajax',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/DOM',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/Selection',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/BookMark',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/Node',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/TYPO3',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Editor/Framework'],
+       function (UserAgent, Util, Ajax, Dom, Event, Selection, BookMark, Node, Typo3, Framework) {
 
        var Editor = Ext.extend(Ext.util.Observable, {
 
@@ -65,7 +76,7 @@ HTMLArea.Editor = function(UserAgent, Util, Ajax, Dom, Event, Selection, BookMar
                                        this.registerPlugin(plugin);
                                }
                        }
-                               // Create Ajax object
+                       // Create Ajax object
                        this.ajax = new Ajax({
                                editor: this
                        });
@@ -126,9 +137,23 @@ HTMLArea.Editor = function(UserAgent, Util, Ajax, Dom, Event, Selection, BookMar
                },
 
                /**
-                * Create the htmlArea framework
+                * Generate the editor framework
                 */
                generate: function () {
+                       if (this.allPluginsRegistered()) {
+                               this.createFramework();
+                       } else {
+                               var self = this;
+                               window.setTimeout(function () {
+                                       self.generate();
+                               }, 50);
+                       }
+               },
+
+               /**
+                * Create the htmlArea framework
+                */
+               createFramework: function () {
                        // Create the editor framework
                        this.htmlArea = new Framework({
                                id: this.editorId + '-htmlArea',
@@ -210,6 +235,7 @@ HTMLArea.Editor = function(UserAgent, Util, Ajax, Dom, Event, Selection, BookMar
                        var self = this;
                        Event.one(this.htmlArea, 'HTMLAreaEventFrameworkReady', function (event) { Event.stopEvent(event); self.onFrameworkReady(); return false; });
                },
+
                /**
                 * Initialize the editor
                 */
@@ -373,7 +399,8 @@ HTMLArea.Editor = function(UserAgent, Util, Ajax, Dom, Event, Selection, BookMar
                                        return '';
                        }
                },
-               /*
+
+               /**
                 * Replace the html content
                 *
                 * @param       string          html: the textual html
@@ -390,32 +417,44 @@ HTMLArea.Editor = function(UserAgent, Util, Ajax, Dom, Event, Selection, BookMar
                                        break;
                        }
                },
-       
+
                /**
-                * Instantiate the specified plugin and register it with the editor
+                * Require and instantiate the specified plugin and register it with the editor
                 *
-                * @param       string          plugin: the name of the plugin
-                *
-                * @return      boolean         true if the plugin was successfully registered
+                * @param string plugin: the name of the plugin
+                * @return void
                 */
                registerPlugin: function (pluginName) {
-                       var plugin = HTMLArea[pluginName],
-                               isRegistered = false;
-                       if (typeof plugin === 'function') {
-                               var pluginInstance = new plugin(this, pluginName);
+                       var self = this;
+                       require(['TYPO3/CMS/Rtehtmlarea/Plugins/' + pluginName], function (Plugin) {
+                               var pluginInstance = new Plugin(self, pluginName);
                                if (pluginInstance) {
                                        var pluginInformation = pluginInstance.getPluginInformation();
                                        pluginInformation.instance = pluginInstance;
-                                       this.plugins[pluginName] = pluginInformation;
-                                       isRegistered = true;
+                                       self.plugins[pluginName] = pluginInformation;
+                               } else {
+                                       self.appendToLog('HTMLArea.Editor', 'registerPlugin', 'Could not register plugin ' + pluginName + '.', 'warn');
+                               }
+                       });
+               },
+
+               /**
+                * Determine if all configured plugins are registered
+                *
+                * @return true if all configured plugins are registered
+                */
+               allPluginsRegistered: function () {
+                       for (var plugin in this.config.plugin) {
+                               if (this.config.plugin[plugin]) {
+                                       if (!this.plugins[plugin]) {
+                                               return false;
+                                       }
                                }
                        }
-                       if (!isRegistered) {
-                               this.appendToLog('HTMLArea.Editor', 'registerPlugin', 'Could not register plugin ' + pluginName + '.', 'warn');
-                       }
-                       return isRegistered;
+                       return true;
                },
-               /*
+
+               /**
                 * Generate registered plugins
                 */
                generatePlugins: function () {
@@ -424,7 +463,8 @@ HTMLArea.Editor = function(UserAgent, Util, Ajax, Dom, Event, Selection, BookMar
                                plugin.onGenerate();
                        }
                },
-               /*
+
+               /**
                 * Get the instance of the specified plugin, if it exists
                 *
                 * @param       string          pluginName: the name of the plugin
@@ -433,7 +473,8 @@ HTMLArea.Editor = function(UserAgent, Util, Ajax, Dom, Event, Selection, BookMar
                getPlugin: function(pluginName) {
                        return (this.plugins[pluginName] ? this.plugins[pluginName].instance : null);
                },
-               /*
+
+               /**
                 * Unregister the instance of the specified plugin
                 *
                 * @param       string          pluginName: the name of the plugin
@@ -443,7 +484,8 @@ HTMLArea.Editor = function(UserAgent, Util, Ajax, Dom, Event, Selection, BookMar
                        delete this.plugins[pluginName].instance;
                        delete this.plugins[pluginName];
                },
-               /*
+
+               /**
                 * Update the edito toolbar
                 */
                updateToolbar: function (noStatus) {
@@ -537,4 +579,4 @@ HTMLArea.Editor = function(UserAgent, Util, Ajax, Dom, Event, Selection, BookMar
 
        return Editor;
 
-}(HTMLArea.UserAgent, HTMLArea.util, HTMLArea.Ajax, HTMLArea.DOM, HTMLArea.Event, HTMLArea.DOM.Selection, HTMLArea.DOM.BookMark, HTMLArea.DOM.Node, HTMLArea.util.TYPO3, HTMLArea.Framework);
+});
index b56d68f..b067c5b 100644 (file)
 /**
  * Framework extends Ext.Panel and is the visual component of the Editor and contains the tool bar, the iframe, the textarea and the status bar
  */
-HTMLArea.Framework = function(Typo3, Event) {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Editor/Framework',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/TYPO3',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Editor/Toolbar',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Editor/Iframe',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Editor/StatusBar'],
+       function (Typo3, Event, Toolbar, Iframe, Statusbar) {
 
        var Framework = Ext.extend(Ext.Panel, {
 
@@ -21,7 +27,8 @@ HTMLArea.Framework = function(Typo3, Event) {
                 * Constructor
                 */
                initComponent: function () {
-                       Framework.superclass.initComponent.call(this);
+                       this.superClass = Framework.superclass;
+                       this.superClass.initComponent.call(this);
                        // Set some references
                        this.toolbar = this.getTopToolbar();
                        this.statusBar = this.getBottomToolbar();
@@ -145,7 +152,7 @@ HTMLArea.Framework = function(Typo3, Event) {
                                // Clone the array of nested tabs and inline levels instead of using a reference as HTMLArea.util.TYPO3.accessParentElements will modify the array
                                var parentElements = [].concat(this.nestedParentElements.sorted);
                                // Walk through all nested tabs and inline levels to get correct sizes
-                               Typo3.accessParentElements(parentElements, 'HTMLArea.Framework.superclass.doLayout.call(args[0])', [this]);
+                               Typo3.accessParentElements(parentElements, 'args[0].superClass.doLayout.call(args[0])', [this]);
                        }
                },
 
@@ -159,7 +166,7 @@ HTMLArea.Framework = function(Typo3, Event) {
                                // Clone the array of nested tabs and inline levels instead of using a reference as HTMLArea.util.TYPO3.accessParentElements will modify the array
                                var parentElements = [].concat(this.nestedParentElements.sorted);
                                // Walk through all nested tabs and inline levels to get correct sizes
-                               Typo3.accessParentElements(parentElements, 'HTMLArea.Framework.superclass.onLayout.call(args[0])', [this]);
+                               Typo3.accessParentElements(parentElements, 'args[0].superClass.onLayout.call(args[0])', [this]);
                        }
                },
 
@@ -335,5 +342,4 @@ HTMLArea.Framework = function(Typo3, Event) {
 
        return Framework;
 
-}(HTMLArea.util.TYPO3, HTMLArea.Event);
-Ext.reg('htmlareaframework', HTMLArea.Framework);
+});
index 48691cf..7119d6c 100644 (file)
 /**
  * HTMLArea.Iframe extends Ext.BoxComponent
  */
-HTMLArea.Iframe = function (UserAgent, Walker, Typo3, Dom, Event, KeyMap) {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Editor/Iframe',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/Walker',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/TYPO3',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/DOM',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/KeyMap'],
+       function (UserAgent, Walker, Typo3, Dom, Event, KeyMap) {
 
        var Iframe = Ext.extend(Ext.BoxComponent, {
 
@@ -149,16 +156,23 @@ HTMLArea.Iframe = function (UserAgent, Walker, Typo3, Dom, Event, KeyMap) {
                 * Proceed to build the iframe document head and ensure style sheets are available after the iframe document becomes available
                 */
                initializeIframe: function () {
+                       var self = this;
                        var iframe = this.getEl().dom;
                        // All browsers
                        if (!iframe || (!iframe.contentWindow && !iframe.contentDocument)) {
-                               this.initializeIframe.defer(50, this);
+                               window.setTimeout(function () {
+                                       self.initializeIframe();
+                               }, 50);
                        // All except WebKit
                        } else if (iframe.contentWindow && !UserAgent.isWebKit && (!iframe.contentWindow.document || !iframe.contentWindow.document.documentElement)) {
-                               this.initializeIframe.defer(50, this);
+                               window.setTimeout(function () {
+                                       self.initializeIframe();
+                               }, 50);
                        // WebKit
                        } else if (UserAgent.isWebKit && (!iframe.contentDocument.documentElement || !iframe.contentDocument.body)) {
-                               this.initializeIframe.defer(50, this);
+                               window.setTimeout(function () {
+                                       self.initializeIframe();
+                               }, 50);
                        } else {
                                this.document = iframe.contentWindow ? iframe.contentWindow.document : iframe.contentDocument;
                                this.getEditor().document = this.document;
@@ -716,14 +730,8 @@ HTMLArea.Iframe = function (UserAgent, Walker, Typo3, Dom, Event, KeyMap) {
                        if (this.isNested && UserAgent.isGecko) {
                                for (var i = this.nestedParentElements.sorted.length; --i >= 0;) {
                                        var nestedElement = document.getElementById(this.nestedParentElements.sorted[i]);
-                                       Event.off(
-                                               nestedElement,
-                                               'DOMAttrModified'
-                                       );
-                                       Event.off(
-                                               nestedElement.parentNode,
-                                               'DOMAttrModified'
-                                       );
+                                       Event.off(nestedElement);
+                                       Event.off(nestedElement.parentNode);
                                }
                        }
                        Event.off(this);
@@ -738,7 +746,7 @@ HTMLArea.Iframe = function (UserAgent, Walker, Typo3, Dom, Event, KeyMap) {
                }
        });
 
+       Ext.reg('htmlareaiframe', Iframe);
        return Iframe;
 
-}(HTMLArea.UserAgent, HTMLArea.DOM.Walker, HTMLArea.util.TYPO3, HTMLArea.DOM, HTMLArea.Event, HTMLArea.Event.KeyMap);
-Ext.reg('htmlareaiframe', HTMLArea.Iframe);
+});
index 9491212..dfd5f9f 100644 (file)
 /**
  * HTMLArea.StatusBar extends Ext.Container
  */
-HTMLArea.StatusBar = function (UserAgent, Dom, Event) {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Editor/StatusBar',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/DOM',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event'],
+       function (UserAgent, Dom, Event) {
 
        var StatusBar = Ext.extend(Ext.Container, {
 
@@ -323,7 +327,7 @@ HTMLArea.StatusBar = function (UserAgent, Dom, Event) {
                }
        });
 
+       Ext.reg('htmlareastatusbar', StatusBar);
        return StatusBar;
 
-}(HTMLArea.UserAgent, HTMLArea.DOM, HTMLArea.Event);
-Ext.reg('htmlareastatusbar', HTMLArea.StatusBar);
+});
index e561468..bdccff4 100644 (file)
 /**
  * HTMLArea.Toolbar extends Ext.Container
  */
-HTMLArea.Toolbar = function (Event) {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Editor/Toolbar',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Extjs/ux/Combo',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Extjs/ux/Button',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Extjs/ux/ToolbarText'],
+       function (Event, Combo, Button, ToolbarText) {
 
-       Toolbar = Ext.extend(Ext.Container, {
+       var Toolbar = Ext.extend(Ext.Container, {
 
                /**
                 * Constructor
@@ -173,7 +178,7 @@ HTMLArea.Toolbar = function (Event) {
                }
        });
 
+       Ext.reg('htmlareatoolbar', Toolbar);
        return Toolbar;
 
-}(HTMLArea.Event);
-Ext.reg('htmlareatoolbar', HTMLArea.Toolbar);
+});
index 611a3c6..729c5e7 100644 (file)
 /*****************************************************************
  * HTMLArea.Event: Utility functions for dealing with events     *
  *****************************************************************/
-HTMLArea.Event = function ($, UserAgent, Util) {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event',
+       ['jquery',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util'],
+       function ($, UserAgent, Util) {
 
        var Event = {
 
@@ -196,4 +200,4 @@ HTMLArea.Event = function ($, UserAgent, Util) {
 
        return Event;
 
-}(HTMLArea.jQuery, HTMLArea.UserAgent, HTMLArea.util);
+});
index 1bb8a63..9870f9f 100644 (file)
@@ -13,7 +13,9 @@
 /********************************************************************
  * HTMLArea.KeyMap: Utility functions for dealing with key events   *
  ********************************************************************/
-HTMLArea.Event.KeyMap = function ($, Event) {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/KeyMap',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event'],
+       function (Event) {
 
        /**
         * Constructor method
@@ -90,4 +92,4 @@ HTMLArea.Event.KeyMap = function ($, Event) {
 
        return KeyMap;
 
-}(HTMLArea.jQuery, HTMLArea.Event);
+});
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ColorPalette.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ColorPalette.js
new file mode 100644 (file)
index 0000000..79807c3
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+/**
+ * Intercept Ext.ColorPalette.prototype.select
+ */
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Extjs/ColorPalette',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Color'],
+       function (Color) {
+
+       Ext.ColorPalette.prototype.select = Ext.ColorPalette.prototype.select.createInterceptor(Color.checkIfColorInPalette);
+       /**
+        * Add deSelect method to Ext.ColorPalette
+        */
+       Ext.override(Ext.ColorPalette, {
+               deSelect: function () {
+                       if (this.el && this.value){
+                               this.el.child('a.color-' + this.value).removeClass('x-color-palette-sel');
+                               this.value = null;
+                       }
+               }
+       });
+});
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/Ext.ColorPalette.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/Ext.ColorPalette.js
deleted file mode 100644 (file)
index 95716bf..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * Intercept Ext.ColorPalette.prototype.select
- */
-Ext.ColorPalette.prototype.select = Ext.ColorPalette.prototype.select.createInterceptor(HTMLArea.util.Color.checkIfColorInPalette);
-/**
- * Add deSelect method to Ext.ColorPalette
- */
-Ext.override(Ext.ColorPalette, {
-       deSelect: function () {
-               if (this.el && this.value){
-                       this.el.child('a.color-' + this.value).removeClass('x-color-palette-sel');
-                       this.value = null;
-               }
-       }
-});
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Button.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Button.js
new file mode 100644 (file)
index 0000000..4ba0232
--- /dev/null
@@ -0,0 +1,154 @@
+/**
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+/**
+ * Ext.ux.HTMLAreaButton extends Ext.Button
+ */
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Extjs/ux/Button',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event'],
+       function (UserAgent, Event) {
+
+       var Button = Ext.extend(Ext.Button, {
+
+               /**
+                * Component initialization
+                */
+               initComponent: function () {
+                       Button.superclass.initComponent.call(this);
+                       this.addListener({
+                               afterrender: {
+                                       fn: this.initEventListeners,
+                                       single: true
+                               }
+                       });
+               },
+
+               /**
+                * Initialize listeners
+                */
+               initEventListeners: function () {
+                       var self = this;
+                       Event.on(this, 'HTMLAreaEventHotkey', function (event, key, keyEvent) { return self.onHotKey(key, keyEvent); });
+                       Event.on(this, 'HTMLAreaEventContextMenu', function (event, button) { return self.onButtonClick(button, event); });
+                       this.setHandler(this.onButtonClick, this);
+                       // Monitor toolbar updates in order to refresh the state of the button
+                       Event.on(this.getToolbar(), 'HTMLAreaEventToolbarUpdate', function (event, mode, selectionEmpty, ancestors, endPointsInSameBlock) { Event.stopEvent(event); self.onUpdateToolbar(mode, selectionEmpty, ancestors, endPointsInSameBlock); return false; });
+               },
+
+               /**
+                * Get a reference to the editor
+                */
+               getEditor: function() {
+                       return RTEarea[this.ownerCt.editorId].editor;
+               },
+
+               /**
+                * Get a reference to the toolbar
+                */
+               getToolbar: function() {
+                       return this.ownerCt;
+               },
+
+               /**
+                * Add properties and function to set button active or not depending on current selection
+                */
+               inactive: true,
+               activeClass: 'buttonActive',
+               setInactive: function (inactive) {
+                       this.inactive = inactive;
+                       return inactive ? this.removeClass(this.activeClass) : this.addClass(this.activeClass);
+               },
+
+               /**
+                * Determine if the button should be enabled based on the current selection and context configuration property
+                */
+               isInContext: function (mode, selectionEmpty, ancestors) {
+                       var editor = this.getEditor();
+                       var inContext = true;
+                       if (mode === 'wysiwyg' && this.context) {
+                               var attributes = [],
+                                       contexts = [];
+                               if (/(.*)\[(.*?)\]/.test(this.context)) {
+                                       contexts = RegExp.$1.split(',');
+                                       attributes = RegExp.$2.split(',');
+                               } else {
+                                       contexts = this.context.split(',');
+                               }
+                               contexts = new RegExp( '^(' + contexts.join('|') + ')$', 'i');
+                               var matchAny = contexts.test('*');
+                               var i, j, n;
+                               for (i = 0, n = ancestors.length; i < n; i++) {
+                                       var ancestor = ancestors[i];
+                                       inContext = matchAny || contexts.test(ancestor.nodeName);
+                                       if (inContext) {
+                                               for (j = attributes.length; --j >= 0;) {
+                                                       inContext = eval("ancestor." + attributes[j]);
+                                                       if (!inContext) {
+                                                               break;
+                                                       }
+                                               }
+                                       }
+                                       if (inContext) {
+                                               break;
+                                       }
+                               }
+                       }
+                       return inContext && (!this.selection || !selectionEmpty);
+               },
+
+               /**
+                * Handler invoked when the button is clicked
+                */
+               onButtonClick: function (button, event, key) {
+                       if (!this.disabled) {
+                               if (!this.plugins[this.action](this.getEditor(), key || this.itemId) && event) {
+                                       Event.stopEvent(event);
+                               }
+                               if (UserAgent.isOpera) {
+                                       this.getEditor().focus();
+                               }
+                               if (this.dialog) {
+                                       this.setDisabled(true);
+                               } else {
+                                       this.getToolbar().update();
+                               }
+                       }
+                       return false;
+               },
+
+               /**
+                * Handler invoked when the hotkey configured for this button is pressed
+                */
+               onHotKey: function (key, event) {
+                       return this.onButtonClick(this, event, key);
+               },
+
+               /**
+                * Handler invoked when the toolbar is updated
+                */
+               onUpdateToolbar: function (mode, selectionEmpty, ancestors, endPointsInSameBlock) {
+                       this.setDisabled(mode === 'textmode' && !this.textMode);
+                       if (!this.disabled) {
+                               if (!this.noAutoUpdate) {
+                                       this.setDisabled(!this.isInContext(mode, selectionEmpty, ancestors));
+                               }
+                               this.plugins['onUpdateToolbar'](this, mode, selectionEmpty, ancestors, endPointsInSameBlock);
+                       }
+               }
+       });
+
+       Ext.reg('htmlareabutton', Button);
+       Ext.ux.HTMLAreaButton = Button;
+       return Button;
+
+});
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/ColorMenu.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/ColorMenu.js
new file mode 100644 (file)
index 0000000..6cf3147
--- /dev/null
@@ -0,0 +1,94 @@
+/**
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+/**
+ * Color menu
+ */
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Extjs/ux/ColorMenu',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/Extjs/ColorPalette'],
+       function (ColorPalette) {
+               Ext.ux.menu.HTMLAreaColorMenu = Ext.extend(Ext.menu.Menu, {
+                       enableScrolling: false,
+                       hideOnClick: true,
+                       cls: 'x-color-menu',
+                       colorPaletteValue: '',
+                       customColorsValue: '',
+                       plain: true,
+                       showSeparator: false,
+                       initComponent: function () {
+                               var paletteItems = [];
+                               var width = 'auto';
+                               if (this.colorsConfiguration) {
+                                       paletteItems.push({
+                                               xtype: 'container',
+                                               layout: 'anchor',
+                                               width: 160,
+                                               style: { float: 'right' },
+                                               items: {
+                                                       xtype: 'colorpalette',
+                                                       itemId: 'custom-colors',
+                                                       cls: 'htmlarea-custom-colors',
+                                                       colors: this.colorsConfiguration,
+                                                       value: this.value,
+                                                       allowReselect: true,
+                                                       tpl: new Ext.XTemplate(
+                                                               '<tpl for="."><a href="#" class="color-{1}" hidefocus="on"><em><span style="background:#{1}" unselectable="on">&#160;</span></em><span unselectable="on">{0}</span></a></tpl>'
+                                                       )
+                                               }
+                                       });
+                               }
+                               if (this.colors.length) {
+                                       paletteItems.push({
+                                               xtype: 'container',
+                                               layout: 'anchor',
+                                               items: {
+                                                       xtype: 'colorpalette',
+                                                       itemId: 'color-palette',
+                                                       cls: 'color-palette',
+                                                       colors: this.colors,
+                                                       value: this.value,
+                                                       allowReselect: true
+                                               }
+                                       });
+                               }
+                               if (this.colorsConfiguration && this.colors.length) {
+                                       width = 350;
+                               }
+                               Ext.apply(this, {
+                                       layout: 'menu',
+                                       width: width,
+                                       items: paletteItems
+                               });
+                               Ext.ux.menu.HTMLAreaColorMenu.superclass.initComponent.call(this);
+                               this.standardPalette = this.find('itemId', 'color-palette')[0];
+                               this.customPalette = this.find('itemId', 'custom-colors')[0];
+                               if (this.standardPalette) {
+                                       this.standardPalette.purgeListeners();
+                                       this.relayEvents(this.standardPalette, ['select']);
+                               }
+                               if (this.customPalette) {
+                                       this.customPalette.purgeListeners();
+                                       this.relayEvents(this.customPalette, ['select']);
+                               }
+                               this.on('select', this.menuHide, this);
+                               if (this.handler){
+                                       this.on('select', this.handler, this.scope || this);
+                               }
+                       },
+                       menuHide: function() {
+                               if (this.hideOnClick){
+                                       this.hide(true);
+                               }
+                       }
+               });
+               Ext.reg('htmlareacolormenu', Ext.ux.menu.HTMLAreaColorMenu);
+});
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/ColorPaletteField.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/ColorPaletteField.js
new file mode 100644 (file)
index 0000000..97ddfa8
--- /dev/null
@@ -0,0 +1,131 @@
+/**
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+/**
+ * Color palette trigger field
+ * Based on http://www.extjs.com/forum/showthread.php?t=89312
+ */
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Extjs/ux/ColorPaletteField',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/Extjs/ux/ColorMenu'],
+       function (ColorMenu) {
+               Ext.ux.form.ColorPaletteField = Ext.extend(Ext.form.TriggerField, {
+                       triggerClass: 'x-form-color-trigger',
+                       defaultColors: [
+                               '000000', '222222', '444444', '666666', '999999', 'BBBBBB', 'DDDDDD', 'FFFFFF',
+                               '660000', '663300', '996633', '003300', '003399', '000066', '330066', '660066',
+                               '990000', '993300', 'CC9900', '006600', '0033FF', '000099', '660099', '990066',
+                               'CC0000', 'CC3300', 'FFCC00', '009900', '0066FF', '0000CC', '663399', 'CC0099',
+                               'FF0000', 'FF3300', 'FFFF00', '00CC00', '0099FF', '0000FF', '9900CC', 'FF0099',
+                               'CC3333', 'FF6600', 'FFFF33', '00FF00', '00CCFF', '3366FF', '9933FF', 'FF00FF',
+                               'FF6666', 'FF6633', 'FFFF66', '66FF66', '00FFFF', '3399FF', '9966FF', 'FF66FF',
+                               'FF9999', 'FF9966', 'FFFF99', '99FF99', '99FFFF', '66CCFF', '9999FF', 'FF99FF',
+                               'FFCCCC', 'FFCC99', 'FFFFCC', 'CCFFCC', 'CCFFFF', '99CCFF', 'CCCCFF', 'FFCCFF'
+                       ],
+                               // Whether or not the field background, text, or triggerbackgroud are set to the selected color
+                       colorizeFieldBackgroud: true,
+                       colorizeFieldText: true,
+                       colorizeTrigger: false,
+                       editable: true,
+                       initComponent: function () {
+                               Ext.ux.form.ColorPaletteField.superclass.initComponent.call(this);
+                               if (!this.colors) {
+                                       this.colors = this.defaultColors;
+                               }
+                               this.addEvents(
+                                       'select'
+                               );
+                       },
+                               // private
+                       validateBlur: function () {
+                               return !this.menu || !this.menu.isVisible();
+                       },
+                       setValue: function (color) {
+                               if (color) {
+                                       if (this.colorizeFieldBackgroud) {
+                                               this.el.applyStyles('background: #' + color  + ';');
+                                       }
+                                       if (this.colorizeFieldText) {
+                                               this.el.applyStyles('color: #' + this.rgbToHex(this.invert(this.hexToRgb(color)))  + ';');
+                                       }
+                                       if (this.colorizeTrigger) {
+                                               this.trigger.applyStyles('background-color: #' + color  + ';');
+                                       }
+                               }
+                               return Ext.ux.form.ColorPaletteField.superclass.setValue.call(this, color);
+                       },
+                               // private
+                       onDestroy: function () {
+                               Ext.destroy(this.menu);
+                               Ext.ux.form.ColorPaletteField.superclass.onDestroy.call(this);
+                       },
+                               // private
+                       onTriggerClick: function () {
+                               if (this.disabled) {
+                                       return;
+                               }
+                               if (this.menu == null) {
+                                       this.menu = new Ext.ux.menu.HTMLAreaColorMenu({
+                                               cls: 'htmlarea-color-menu',
+                                               hideOnClick: false,
+                                               colors: this.colors,
+                                               colorsConfiguration: this.colorsConfiguration,
+                                               value: this.getValue()
+                                       });
+                               }
+                               this.onFocus();
+                               this.menu.show(this.el, "tl-bl?");
+                               this.menuEvents('on');
+                       },
+                               //private
+                       menuEvents: function (method) {
+                               this.menu[method]('select', this.onSelect, this);
+                               this.menu[method]('hide', this.onMenuHide, this);
+                               this.menu[method]('show', this.onFocus, this);
+                       },
+                       onSelect: function (m, d) {
+                               this.setValue(d);
+                               this.fireEvent('select', this, d);
+                               this.menu.hide();
+                       },
+                       onMenuHide: function () {
+                               this.focus(false, 60);
+                               this.menuEvents('un');
+                       },
+                       invert: function ( r, g, b ) {
+                               if( r instanceof Array ) { return this.invert.call( this, r[0], r[1], r[2] ); }
+                               return [255-r,255-g,255-b];
+                       },
+                       hexToRgb: function ( hex ) {
+                               return [ this.hexToDec( hex.substr(0, 2) ), this.hexToDec( hex.substr(2, 2) ), this.hexToDec( hex.substr(4, 2) ) ];
+                       },
+                       hexToDec: function( hex ) {
+                               var s = hex.split('');
+                               return ( ( this.getHCharPos( s[0] ) * 16 ) + this.getHCharPos( s[1] ) );
+                       },
+                       getHCharPos: function( c ) {
+                               var HCHARS = '0123456789ABCDEF';
+                               return HCHARS.indexOf( c.toUpperCase() );
+                       },
+                       rgbToHex: function( r, g, b ) {
+                               if( r instanceof Array ) { return this.rgbToHex.call( this, r[0], r[1], r[2] ); }
+                               return this.decToHex( r ) + this.decToHex( g ) + this.decToHex( b );
+                       },
+                       decToHex: function( n ) {
+                               var HCHARS = '0123456789ABCDEF';
+                               n = parseInt(n, 10);
+                               n = ( !isNaN( n )) ? n : 0;
+                               n = (n > 255 || n < 0) ? 0 : n;
+                               return HCHARS.charAt( ( n - n % 16 ) / 16 ) + HCHARS.charAt( n % 16 );
+                       }
+               });
+               Ext.reg('colorpalettefield', Ext.ux.form.ColorPaletteField);
+});
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Combo.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Combo.js
new file mode 100644 (file)
index 0000000..2a72cc4
--- /dev/null
@@ -0,0 +1,209 @@
+/**
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+/**
+ * Ext.ux.form.HTMLAreaCombo extends Ext.form.ComboBox
+ */
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Extjs/ux/Combo',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event'],
+       function (UserAgent, Event) {
+
+       var Combo = Ext.extend(Ext.form.ComboBox, {
+
+               /**
+                * Constructor
+                */
+               initComponent: function () {
+                       Ext.ux.form.HTMLAreaCombo.superclass.initComponent.call(this);
+                       this.addListener({
+                               afterrender: {
+                                       fn: this.initEventListeners,
+                                       single: true
+                               }
+                       });
+               },
+
+               /**
+                * Initialize listeners
+                */
+               initEventListeners: function () {
+                       var self = this;
+                       Event.on(this, 'HTMLAreaEventHotkey', function (event, key, event) { return self.onHotKey(key); });
+                       this.addListener({
+                               select: {
+                                       fn: this.onComboSelect
+                               },
+                               specialkey: {
+                                       fn: this.onSpecialKey
+                               },
+                               beforedestroy: {
+                                       fn: this.onBeforeDestroy,
+                                       single: true
+                               }
+                       });
+                       // Monitor toolbar updates in order to refresh the state of the combo
+                       Event.on(this.getToolbar(), 'HTMLAreaEventToolbarUpdate', function (event, mode, selectionEmpty, ancestors, endPointsInSameBlock) { Event.stopEvent(event); self.onUpdateToolbar(mode, selectionEmpty, ancestors, endPointsInSameBlock); return false; });
+                       // Monitor framework becoming ready
+                       Event.one(this.getToolbar().ownerCt, 'HTMLAreaEventFrameworkReady', function (event) { Event.stopEvent(event); self.onFrameworkReady(); return false; });
+               },
+
+               /**
+                * Get a reference to the editor
+                */
+               getEditor: function() {
+                       return RTEarea[this.ownerCt.editorId].editor;
+               },
+
+               /**
+                * Get a reference to the toolbar
+                */
+               getToolbar: function() {
+                       return this.ownerCt;
+               },
+
+               /**
+                * Handler invoked when an item is selected in the dropdown list
+                */
+               onComboSelect: function (combo, record, index) {
+                       if (!combo.disabled) {
+                               var editor = this.getEditor();
+                                       // In IE, reclaim lost focus on the editor iframe and restore the bookmarked selection
+                               if (UserAgent.isIE) {
+                                       if (typeof this.savedRange === 'object' && this.savedRange !== null) {
+                                               editor.getSelection().selectRange(this.savedRange);
+                                               this.savedRange = null;
+                                       }
+                               }
+                                       // Invoke the plugin onChange handler
+                               this.plugins[this.action](editor, combo, record, index);
+                                       // In IE, bookmark the updated selection as the editor will be loosing focus
+                               if (UserAgent.isIE) {
+                                       this.savedRange = editor.getSelection().createRange();
+                                       this.triggered = true;
+                               }
+                               if (UserAgent.isOpera) {
+                                       editor.focus();
+                               }
+                               this.getToolbar().update();
+                       }
+                       return false;
+               },
+
+               /**
+                * Handler invoked when the trigger element is clicked
+                * In IE, need to reclaim lost focus for the editor in order to restore the selection
+                */
+               onTriggerClick: function () {
+                       Ext.ux.form.HTMLAreaCombo.superclass.onTriggerClick.call(this);
+                       // In IE, avoid focus being stolen and selection being lost
+                       if (UserAgent.isIE) {
+                               this.triggered = true;
+                               this.getEditor().focus();
+                       }
+               },
+
+               /**
+                * Handler invoked when the list of options is clicked in
+                */
+               onViewClick: function (doFocus) {
+                       // Avoid stealing focus from the editor
+                       Ext.ux.form.HTMLAreaCombo.superclass.onViewClick.call(this, false);
+               },
+
+               /**
+                * Handler invoked in IE when the mouse moves out of the editor iframe
+                */
+               saveSelection: function (event) {
+                       var editor = this.getEditor();
+                       if (editor.document.hasFocus()) {
+                               this.savedRange = editor.getSelection().createRange();
+                       }
+               },
+
+               /**
+                * Handler invoked in IE when the editor gets the focus back
+                */
+               restoreSelection: function (event) {
+                       if (typeof this.savedRange === 'object' && this.savedRange !== null && this.triggered) {
+                               this.getEditor().getSelection().selectRange(this.savedRange);
+                               this.triggered = false;
+                       }
+               },
+
+               /**
+                * Handler invoked when the enter key is pressed while the combo has focus
+                */
+               onSpecialKey: function (combo, event) {
+                       if (event.getKey() == event.ENTER) {
+                               event.stopEvent();
+                       }
+                       return false;
+               },
+
+               /**
+                * Handler invoked when a hot key configured for this dropdown list is pressed
+                */
+               onHotKey: function (key) {
+                       if (!this.disabled) {
+                               this.plugins.onHotKey(this.getEditor(), key);
+                               if (UserAgent.isOpera) {
+                                       this.getEditor().focus();
+                               }
+                               this.getToolbar().update();
+                       }
+                       return false;
+               },
+
+               /**
+                * Handler invoked when the toolbar is updated
+                */
+               onUpdateToolbar: function (mode, selectionEmpty, ancestors, endPointsInSameBlock) {
+                       this.setDisabled(mode === 'textmode' && !this.textMode);
+                       if (!this.disabled) {
+                               this.plugins['onUpdateToolbar'](this, mode, selectionEmpty, ancestors, endPointsInSameBlock);
+                       }
+               },
+
+               /**
+                * The iframe must have been rendered
+                */
+               onFrameworkReady: function () {
+                       var iframe = this.getEditor().iframe;
+                       // Close the combo on a click in the iframe
+                       // Note: ExtJS is monitoring events only on the parent window
+                       var self = this;
+                       Event.on(iframe.document.documentElement, 'click', function (event) { self.collapse(); return true; });
+                       // Special handling for combo stealing focus in IE
+                       if (UserAgent.isIE) {
+                               // Take a bookmark in case the editor looses focus by activation of this combo
+                               Event.on(iframe.getEl().dom, 'mouseleave', function (event) { self.saveSelection(event); return true; });
+                               // Restore the selection if combo was triggered
+                               Event.on(iframe.getEl().dom, 'focus', function (event) { self.restoreSelection(event); return true; });
+                       }
+               },
+
+               /**
+                * Cleanup
+                */
+               onBeforeDestroy: function () {
+                       this.savedRange = null;
+                       this.getStore().removeAll();
+                       this.getStore().destroy();
+               }
+       });
+
+       Ext.reg('htmlareacombo', Combo);
+       Ext.ux.form.HTMLAreaCombo = Combo;
+       return Combo;
+
+});
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Ext.ux.HTMLAreaButton.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Ext.ux.HTMLAreaButton.js
deleted file mode 100644 (file)
index a941d9a..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-/**
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-/**
- * Ext.ux.HTMLAreaButton extends Ext.Button
- */
-Ext.ux.HTMLAreaButton = function (UserAgent, Event) {
-
-       var Button = Ext.extend(Ext.Button, {
-
-               /**
-                * Component initialization
-                */
-               initComponent: function () {
-                       Button.superclass.initComponent.call(this);
-                       this.addListener({
-                               afterrender: {
-                                       fn: this.initEventListeners,
-                                       single: true
-                               }
-                       });
-               },
-
-               /**
-                * Initialize listeners
-                */
-               initEventListeners: function () {
-                       var self = this;
-                       Event.on(this, 'HTMLAreaEventHotkey', function (event, key, keyEvent) { return self.onHotKey(key, keyEvent); });
-                       Event.on(this, 'HTMLAreaEventContextMenu', function (event, button) { return self.onButtonClick(button, event); });
-                       this.setHandler(this.onButtonClick, this);
-                       // Monitor toolbar updates in order to refresh the state of the button
-                       Event.on(this.getToolbar(), 'HTMLAreaEventToolbarUpdate', function (event, mode, selectionEmpty, ancestors, endPointsInSameBlock) { Event.stopEvent(event); self.onUpdateToolbar(mode, selectionEmpty, ancestors, endPointsInSameBlock); return false; });
-               },
-
-               /**
-                * Get a reference to the editor
-                */
-               getEditor: function() {
-                       return RTEarea[this.ownerCt.editorId].editor;
-               },
-
-               /**
-                * Get a reference to the toolbar
-                */
-               getToolbar: function() {
-                       return this.ownerCt;
-               },
-
-               /**
-                * Add properties and function to set button active or not depending on current selection
-                */
-               inactive: true,
-               activeClass: 'buttonActive',
-               setInactive: function (inactive) {
-                       this.inactive = inactive;
-                       return inactive ? this.removeClass(this.activeClass) : this.addClass(this.activeClass);
-               },
-
-               /**
-                * Determine if the button should be enabled based on the current selection and context configuration property
-                */
-               isInContext: function (mode, selectionEmpty, ancestors) {
-                       var editor = this.getEditor();
-                       var inContext = true;
-                       if (mode === 'wysiwyg' && this.context) {
-                               var attributes = [],
-                                       contexts = [];
-                               if (/(.*)\[(.*?)\]/.test(this.context)) {
-                                       contexts = RegExp.$1.split(',');
-                                       attributes = RegExp.$2.split(',');
-                               } else {
-                                       contexts = this.context.split(',');
-                               }
-                               contexts = new RegExp( '^(' + contexts.join('|') + ')$', 'i');
-                               var matchAny = contexts.test('*');
-                               var i, j, n;
-                               for (i = 0, n = ancestors.length; i < n; i++) {
-                                       var ancestor = ancestors[i];
-                                       inContext = matchAny || contexts.test(ancestor.nodeName);
-                                       if (inContext) {
-                                               for (j = attributes.length; --j >= 0;) {
-                                                       inContext = eval("ancestor." + attributes[j]);
-                                                       if (!inContext) {
-                                                               break;
-                                                       }
-                                               }
-                                       }
-                                       if (inContext) {
-                                               break;
-                                       }
-                               }
-                       }
-                       return inContext && (!this.selection || !selectionEmpty);
-               },
-
-               /**
-                * Handler invoked when the button is clicked
-                */
-               onButtonClick: function (button, event, key) {
-                       if (!this.disabled) {
-                               if (!this.plugins[this.action](this.getEditor(), key || this.itemId) && event) {
-                                       Event.stopEvent(event);
-                               }
-                               if (UserAgent.isOpera) {
-                                       this.getEditor().focus();
-                               }
-                               if (this.dialog) {
-                                       this.setDisabled(true);
-                               } else {
-                                       this.getToolbar().update();
-                               }
-                       }
-                       return false;
-               },
-
-               /**
-                * Handler invoked when the hotkey configured for this button is pressed
-                */
-               onHotKey: function (key, event) {
-                       return this.onButtonClick(this, event, key);
-               },
-
-               /**
-                * Handler invoked when the toolbar is updated
-                */
-               onUpdateToolbar: function (mode, selectionEmpty, ancestors, endPointsInSameBlock) {
-                       this.setDisabled(mode === 'textmode' && !this.textMode);
-                       if (!this.disabled) {
-                               if (!this.noAutoUpdate) {
-                                       this.setDisabled(!this.isInContext(mode, selectionEmpty, ancestors));
-                               }
-                               this.plugins['onUpdateToolbar'](this, mode, selectionEmpty, ancestors, endPointsInSameBlock);
-                       }
-               }
-       });
-
-       return Button;
-
-}(HTMLArea.UserAgent, HTMLArea.Event);
-Ext.reg('htmlareabutton', Ext.ux.HTMLAreaButton);
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Ext.ux.Toolbar.HTMLAreaToolbarText.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Ext.ux.Toolbar.HTMLAreaToolbarText.js
deleted file mode 100644 (file)
index 72c92aa..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-/**
- * Ext.ux.Toolbar.HTMLAreaToolbarText extends Ext.Toolbar.TextItem
- */
-Ext.ux.Toolbar.HTMLAreaToolbarText = function (Event) {
-
-       var ToolbarText = Ext.extend(Ext.Toolbar.TextItem, {
-
-               /**
-                * Constructor
-                */
-               initComponent: function () {
-                       Ext.ux.Toolbar.HTMLAreaToolbarText.superclass.initComponent.call(this);
-                       this.addListener({
-                               afterrender: {
-                                       fn: this.initEventListeners,
-                                       single: true
-                               }
-                       });
-               },
-
-               /**
-                * Initialize listeners
-                */
-               initEventListeners: function () {
-                       // Monitor toolbar updates in order to refresh the state of the button
-                       var self = this;
-                       Event.on(this.getToolbar(), 'HTMLAreaEventToolbarUpdate', function (event, mode, selectionEmpty, ancestors, endPointsInSameBlock) { Event.stopEvent(event); self.onUpdateToolbar(mode, selectionEmpty, ancestors, endPointsInSameBlock); return false; });
-               },
-
-               /**
-                * Get a reference to the editor
-                */
-               getEditor: function() {
-                       return RTEarea[this.ownerCt.editorId].editor;
-               },
-
-               /**
-                * Get a reference to the toolbar
-                */
-               getToolbar: function() {
-                       return this.ownerCt;
-               },
-
-               /**
-                * Handler invoked when the toolbar is updated
-                */
-               onUpdateToolbar: function (mode, selectionEmpty, ancestors, endPointsInSameBlock) {
-                       this.setDisabled(mode === 'textmode' && !this.textMode);
-                       if (!this.disabled) {
-                               this.plugins['onUpdateToolbar'](this, mode, selectionEmpty, ancestors, endPointsInSameBlock);
-                       }
-               }
-       });
-
-       return ToolbarText;
-
-}(HTMLArea.Event);
-Ext.reg('htmlareatoolbartext', Ext.ux.Toolbar.HTMLAreaToolbarText);
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Ext.ux.form.ColorPaletteField.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Ext.ux.form.ColorPaletteField.js
deleted file mode 100644 (file)
index c5e21c2..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/**
- * Color palette trigger field
- * Based on http://www.extjs.com/forum/showthread.php?t=89312
- */
-Ext.ux.form.ColorPaletteField = Ext.extend(Ext.form.TriggerField, {
-       triggerClass: 'x-form-color-trigger',
-       defaultColors: [
-               '000000', '222222', '444444', '666666', '999999', 'BBBBBB', 'DDDDDD', 'FFFFFF',
-               '660000', '663300', '996633', '003300', '003399', '000066', '330066', '660066',
-               '990000', '993300', 'CC9900', '006600', '0033FF', '000099', '660099', '990066',
-               'CC0000', 'CC3300', 'FFCC00', '009900', '0066FF', '0000CC', '663399', 'CC0099',
-               'FF0000', 'FF3300', 'FFFF00', '00CC00', '0099FF', '0000FF', '9900CC', 'FF0099',
-               'CC3333', 'FF6600', 'FFFF33', '00FF00', '00CCFF', '3366FF', '9933FF', 'FF00FF',
-               'FF6666', 'FF6633', 'FFFF66', '66FF66', '00FFFF', '3399FF', '9966FF', 'FF66FF',
-               'FF9999', 'FF9966', 'FFFF99', '99FF99', '99FFFF', '66CCFF', '9999FF', 'FF99FF',
-               'FFCCCC', 'FFCC99', 'FFFFCC', 'CCFFCC', 'CCFFFF', '99CCFF', 'CCCCFF', 'FFCCFF'
-       ],
-               // Whether or not the field background, text, or triggerbackgroud are set to the selected color
-       colorizeFieldBackgroud: true,
-       colorizeFieldText: true,
-       colorizeTrigger: false,
-       editable: true,
-       initComponent: function () {
-               Ext.ux.form.ColorPaletteField.superclass.initComponent.call(this);
-               if (!this.colors) {
-                       this.colors = this.defaultColors;
-               }
-               this.addEvents(
-                       'select'
-               );
-       },
-               // private
-       validateBlur: function () {
-               return !this.menu || !this.menu.isVisible();
-       },
-       setValue: function (color) {
-               if (color) {
-                       if (this.colorizeFieldBackgroud) {
-                               this.el.applyStyles('background: #' + color  + ';');
-                       }
-                       if (this.colorizeFieldText) {
-                               this.el.applyStyles('color: #' + this.rgbToHex(this.invert(this.hexToRgb(color)))  + ';');
-                       }
-                       if (this.colorizeTrigger) {
-                               this.trigger.applyStyles('background-color: #' + color  + ';');
-                       }
-               }
-               return Ext.ux.form.ColorPaletteField.superclass.setValue.call(this, color);
-       },
-               // private
-       onDestroy: function () {
-               Ext.destroy(this.menu);
-               Ext.ux.form.ColorPaletteField.superclass.onDestroy.call(this);
-       },
-               // private
-       onTriggerClick: function () {
-               if (this.disabled) {
-                       return;
-               }
-               if (this.menu == null) {
-                       this.menu = new Ext.ux.menu.HTMLAreaColorMenu({
-                               cls: 'htmlarea-color-menu',
-                               hideOnClick: false,
-                               colors: this.colors,
-                               colorsConfiguration: this.colorsConfiguration,
-                               value: this.getValue()
-                       });
-               }
-               this.onFocus();
-               this.menu.show(this.el, "tl-bl?");
-               this.menuEvents('on');
-       },
-               //private
-       menuEvents: function (method) {
-               this.menu[method]('select', this.onSelect, this);
-               this.menu[method]('hide', this.onMenuHide, this);
-               this.menu[method]('show', this.onFocus, this);
-       },
-       onSelect: function (m, d) {
-               this.setValue(d);
-               this.fireEvent('select', this, d);
-               this.menu.hide();
-       },
-       onMenuHide: function () {
-               this.focus(false, 60);
-               this.menuEvents('un');
-       },
-       invert: function ( r, g, b ) {
-               if( r instanceof Array ) { return this.invert.call( this, r[0], r[1], r[2] ); }
-               return [255-r,255-g,255-b];
-       },
-       hexToRgb: function ( hex ) {
-               return [ this.hexToDec( hex.substr(0, 2) ), this.hexToDec( hex.substr(2, 2) ), this.hexToDec( hex.substr(4, 2) ) ];
-       },
-       hexToDec: function( hex ) {
-               var s = hex.split('');
-               return ( ( this.getHCharPos( s[0] ) * 16 ) + this.getHCharPos( s[1] ) );
-       },
-       getHCharPos: function( c ) {
-               var HCHARS = '0123456789ABCDEF';
-               return HCHARS.indexOf( c.toUpperCase() );
-       },
-       rgbToHex: function( r, g, b ) {
-               if( r instanceof Array ) { return this.rgbToHex.call( this, r[0], r[1], r[2] ); }
-               return this.decToHex( r ) + this.decToHex( g ) + this.decToHex( b );
-       },
-       decToHex: function( n ) {
-               var HCHARS = '0123456789ABCDEF';
-               n = parseInt(n, 10);
-               n = ( !isNaN( n )) ? n : 0;
-               n = (n > 255 || n < 0) ? 0 : n;
-               return HCHARS.charAt( ( n - n % 16 ) / 16 ) + HCHARS.charAt( n % 16 );
-       }
-});
-Ext.reg('colorpalettefield', Ext.ux.form.ColorPaletteField);
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Ext.ux.form.HTMLAreaCombo.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Ext.ux.form.HTMLAreaCombo.js
deleted file mode 100644 (file)
index e4e8be5..0000000
+++ /dev/null
@@ -1,205 +0,0 @@
-/**
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-/**
- * Ext.ux.form.HTMLAreaCombo extends Ext.form.ComboBox
- */
-Ext.ux.form.HTMLAreaCombo = function (UserAgent, Event) {
-
-       var Combo = Ext.extend(Ext.form.ComboBox, {
-
-               /**
-                * Constructor
-                */
-               initComponent: function () {
-                       Ext.ux.form.HTMLAreaCombo.superclass.initComponent.call(this);
-                       this.addListener({
-                               afterrender: {
-                                       fn: this.initEventListeners,
-                                       single: true
-                               }
-                       });
-               },
-
-               /**
-                * Initialize listeners
-                */
-               initEventListeners: function () {
-                       var self = this;
-                       Event.on(this, 'HTMLAreaEventHotkey', function (event, key, event) { return self.onHotKey(key); });
-                       this.addListener({
-                               select: {
-                                       fn: this.onComboSelect
-                               },
-                               specialkey: {
-                                       fn: this.onSpecialKey
-                               },
-                               beforedestroy: {
-                                       fn: this.onBeforeDestroy,
-                                       single: true
-                               }
-                       });
-                       // Monitor toolbar updates in order to refresh the state of the combo
-                       Event.on(this.getToolbar(), 'HTMLAreaEventToolbarUpdate', function (event, mode, selectionEmpty, ancestors, endPointsInSameBlock) { Event.stopEvent(event); self.onUpdateToolbar(mode, selectionEmpty, ancestors, endPointsInSameBlock); return false; });
-                       // Monitor framework becoming ready
-                       Event.one(this.getToolbar().ownerCt, 'HTMLAreaEventFrameworkReady', function (event) { Event.stopEvent(event); self.onFrameworkReady(); return false; });
-               },
-
-               /**
-                * Get a reference to the editor
-                */
-               getEditor: function() {
-                       return RTEarea[this.ownerCt.editorId].editor;
-               },
-
-               /**
-                * Get a reference to the toolbar
-                */
-               getToolbar: function() {
-                       return this.ownerCt;
-               },
-
-               /**
-                * Handler invoked when an item is selected in the dropdown list
-                */
-               onComboSelect: function (combo, record, index) {
-                       if (!combo.disabled) {
-                               var editor = this.getEditor();
-                                       // In IE, reclaim lost focus on the editor iframe and restore the bookmarked selection
-                               if (UserAgent.isIE) {
-                                       if (typeof this.savedRange === 'object' && this.savedRange !== null) {
-                                               editor.getSelection().selectRange(this.savedRange);
-                                               this.savedRange = null;
-                                       }
-                               }
-                                       // Invoke the plugin onChange handler
-                               this.plugins[this.action](editor, combo, record, index);
-                                       // In IE, bookmark the updated selection as the editor will be loosing focus
-                               if (UserAgent.isIE) {
-                                       this.savedRange = editor.getSelection().createRange();
-                                       this.triggered = true;
-                               }
-                               if (UserAgent.isOpera) {
-                                       editor.focus();
-                               }
-                               this.getToolbar().update();
-                       }
-                       return false;
-               },
-
-               /**
-                * Handler invoked when the trigger element is clicked
-                * In IE, need to reclaim lost focus for the editor in order to restore the selection
-                */
-               onTriggerClick: function () {
-                       Ext.ux.form.HTMLAreaCombo.superclass.onTriggerClick.call(this);
-                       // In IE, avoid focus being stolen and selection being lost
-                       if (UserAgent.isIE) {
-                               this.triggered = true;
-                               this.getEditor().focus();
-                       }
-               },
-
-               /**
-                * Handler invoked when the list of options is clicked in
-                */
-               onViewClick: function (doFocus) {
-                       // Avoid stealing focus from the editor
-                       Ext.ux.form.HTMLAreaCombo.superclass.onViewClick.call(this, false);
-               },
-
-               /**
-                * Handler invoked in IE when the mouse moves out of the editor iframe
-                */
-               saveSelection: function (event) {
-                       var editor = this.getEditor();
-                       if (editor.document.hasFocus()) {
-                               this.savedRange = editor.getSelection().createRange();
-                       }
-               },
-
-               /**
-                * Handler invoked in IE when the editor gets the focus back
-                */
-               restoreSelection: function (event) {
-                       if (typeof this.savedRange === 'object' && this.savedRange !== null && this.triggered) {
-                               this.getEditor().getSelection().selectRange(this.savedRange);
-                               this.triggered = false;
-                       }
-               },
-
-               /**
-                * Handler invoked when the enter key is pressed while the combo has focus
-                */
-               onSpecialKey: function (combo, event) {
-                       if (event.getKey() == event.ENTER) {
-                               event.stopEvent();
-                       }
-                       return false;
-               },
-
-               /**
-                * Handler invoked when a hot key configured for this dropdown list is pressed
-                */
-               onHotKey: function (key) {
-                       if (!this.disabled) {
-                               this.plugins.onHotKey(this.getEditor(), key);
-                               if (UserAgent.isOpera) {
-                                       this.getEditor().focus();
-                               }
-                               this.getToolbar().update();
-                       }
-                       return false;
-               },
-
-               /**
-                * Handler invoked when the toolbar is updated
-                */
-               onUpdateToolbar: function (mode, selectionEmpty, ancestors, endPointsInSameBlock) {
-                       this.setDisabled(mode === 'textmode' && !this.textMode);
-                       if (!this.disabled) {
-                               this.plugins['onUpdateToolbar'](this, mode, selectionEmpty, ancestors, endPointsInSameBlock);
-                       }
-               },
-
-               /**
-                * The iframe must have been rendered
-                */
-               onFrameworkReady: function () {
-                       var iframe = this.getEditor().iframe;
-                       // Close the combo on a click in the iframe
-                       // Note: ExtJS is monitoring events only on the parent window
-                       var self = this;
-                       Event.on(iframe.document.documentElement, 'click', function (event) { self.collapse(); return true; });
-                       // Special handling for combo stealing focus in IE
-                       if (UserAgent.isIE) {
-                               // Take a bookmark in case the editor looses focus by activation of this combo
-                               Event.on(iframe.getEl().dom, 'mouseleave', function (event) { self.saveSelection(event); return true; });
-                               // Restore the selection if combo was triggered
-                               Event.on(iframe.getEl().dom, 'focus', function (event) { self.restoreSelection(event); return true; });
-                       }
-               },
-
-               /**
-                * Cleanup
-                */
-               onBeforeDestroy: function () {
-                       this.savedRange = null;
-                       this.getStore().removeAll();
-                       this.getStore().destroy();
-               }
-       });
-
-       return Combo;
-
-}(HTMLArea.UserAgent, HTMLArea.Event);
-Ext.reg('htmlareacombo', Ext.ux.form.HTMLAreaCombo);
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Ext.ux.menu.HTMLAreaColorMenu.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Ext.ux.menu.HTMLAreaColorMenu.js
deleted file mode 100644 (file)
index 9edc4e8..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/**
- * Color menu
- */
-Ext.ux.menu.HTMLAreaColorMenu = Ext.extend(Ext.menu.Menu, {
-       enableScrolling: false,
-       hideOnClick: true,
-       cls: 'x-color-menu',
-       colorPaletteValue: '',
-       customColorsValue: '',
-       plain: true,
-       showSeparator: false,
-       initComponent: function () {
-               var paletteItems = [];
-               var width = 'auto';
-               if (this.colorsConfiguration) {
-                       paletteItems.push({
-                               xtype: 'container',
-                               layout: 'anchor',
-                               width: 160,
-                               style: { float: 'right' },
-                               items: {
-                                       xtype: 'colorpalette',
-                                       itemId: 'custom-colors',
-                                       cls: 'htmlarea-custom-colors',
-                                       colors: this.colorsConfiguration,
-                                       value: this.value,
-                                       allowReselect: true,
-                                       tpl: new Ext.XTemplate(
-                                               '<tpl for="."><a href="#" class="color-{1}" hidefocus="on"><em><span style="background:#{1}" unselectable="on">&#160;</span></em><span unselectable="on">{0}</span></a></tpl>'
-                                       )
-                               }
-                       });
-               }
-               if (this.colors.length) {
-                       paletteItems.push({
-                               xtype: 'container',
-                               layout: 'anchor',
-                               items: {
-                                       xtype: 'colorpalette',
-                                       itemId: 'color-palette',
-                                       cls: 'color-palette',
-                                       colors: this.colors,
-                                       value: this.value,
-                                       allowReselect: true
-                               }
-                       });
-               }
-               if (this.colorsConfiguration && this.colors.length) {
-                       width = 350;
-               }
-               Ext.apply(this, {
-                       layout: 'menu',
-                       width: width,
-                       items: paletteItems
-               });
-               Ext.ux.menu.HTMLAreaColorMenu.superclass.initComponent.call(this);
-               this.standardPalette = this.find('itemId', 'color-palette')[0];
-               this.customPalette = this.find('itemId', 'custom-colors')[0];
-               if (this.standardPalette) {
-                       this.standardPalette.purgeListeners();
-                       this.relayEvents(this.standardPalette, ['select']);
-               }
-               if (this.customPalette) {
-                       this.customPalette.purgeListeners();
-                       this.relayEvents(this.customPalette, ['select']);
-               }
-               this.on('select', this.menuHide, this);
-               if (this.handler){
-                       this.on('select', this.handler, this.scope || this);
-               }
-       },
-       menuHide: function() {
-               if (this.hideOnClick){
-                       this.hide(true);
-               }
-       }
-});
-Ext.reg('htmlareacolormenu', Ext.ux.menu.HTMLAreaColorMenu);
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/ToolbarText.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/ToolbarText.js
new file mode 100644 (file)
index 0000000..7c18a42
--- /dev/null
@@ -0,0 +1,73 @@
+/**
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+/**
+ * Ext.ux.Toolbar.HTMLAreaToolbarText extends Ext.Toolbar.TextItem
+ */
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Extjs/ux/ToolbarText',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event'],
+       function (Event) {
+
+       var ToolbarText = Ext.extend(Ext.Toolbar.TextItem, {
+
+               /**
+                * Constructor
+                */
+               initComponent: function () {
+                       Ext.ux.Toolbar.HTMLAreaToolbarText.superclass.initComponent.call(this);
+                       this.addListener({
+                               afterrender: {
+                                       fn: this.initEventListeners,
+                                       single: true
+                               }
+                       });
+               },
+
+               /**
+                * Initialize listeners
+                */
+               initEventListeners: function () {
+                       // Monitor toolbar updates in order to refresh the state of the button
+                       var self = this;
+                       Event.on(this.getToolbar(), 'HTMLAreaEventToolbarUpdate', function (event, mode, selectionEmpty, ancestors, endPointsInSameBlock) { Event.stopEvent(event); self.onUpdateToolbar(mode, selectionEmpty, ancestors, endPointsInSameBlock); return false; });
+               },
+
+               /**
+                * Get a reference to the editor
+                */
+               getEditor: function() {
+                       return RTEarea[this.ownerCt.editorId].editor;
+               },
+
+               /**
+                * Get a reference to the toolbar
+                */
+               getToolbar: function() {
+                       return this.ownerCt;
+               },
+
+               /**
+                * Handler invoked when the toolbar is updated
+                */
+               onUpdateToolbar: function (mode, selectionEmpty, ancestors, endPointsInSameBlock) {
+                       this.setDisabled(mode === 'textmode' && !this.textMode);
+                       if (!this.disabled) {
+                               this.plugins['onUpdateToolbar'](this, mode, selectionEmpty, ancestors, endPointsInSameBlock);
+                       }
+               }
+       });
+
+       Ext.ux.Toolbar.HTMLAreaToolbarText = ToolbarText;
+       Ext.reg('htmlareatoolbartext', ToolbarText);
+       return ToolbarText;
+
+});
index 6514be0..a638ad1 100644 (file)
 /**
  * Initialization script of TYPO3 htmlArea RTE
  */
-HTMLArea = function(UserAgent, Util, Config, Editor) {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/HTMLArea',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Configuration/Config',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Editor/Editor'],
+       function (UserAgent, Util, Config, Editor) {
 
        var HtmlArea = {
 
@@ -35,20 +40,22 @@ HTMLArea = function(UserAgent, Util, Config, Editor) {
                init: function () {
                        if (!HTMLArea.isReady) {
                                // Apply global configuration settings
-                               Util.apply(HTMLArea, RTEarea[0]);
-                               if (!HTMLArea.editorSkin) {
-                                       HTMLArea.editorSkin = HTMLArea.editorUrl + 'Resources/Public/Css/Skin/';
+                               Util.apply(HtmlArea, RTEarea[0]);
+                               if (!HtmlArea.editorSkin) {
+                                       HtmlArea.editorSkin = HtmlArea.editorUrl + 'Resources/Public/Css/Skin/';
                                }
-                               if (!HTMLArea.editorCSS) {
-                                       HTMLArea.editorCSS = HTMLArea.editorUrl + 'Resources/Public/Css/Skin/htmlarea.css';
+                               if (!HtmlArea.editorCSS) {
+                                       HtmlArea.editorCSS = HtmlArea.editorUrl + 'Resources/Public/Css/Skin/htmlarea.css';
                                }
-                               if (typeof HTMLArea.editedContentCSS !== 'string' || HTMLArea.editedContentCSS === '') {
-                                       HTMLArea.editedContentCSS = HTMLArea.editorSkin + 'htmlarea-edited-content.css';
+                               if (typeof HtmlArea.editedContentCSS !== 'string' || HtmlArea.editedContentCSS === '') {
+                                       HtmlArea.editedContentCSS = HtmlArea.editorSkin + 'htmlarea-edited-content.css';
                                }
                                HTMLArea.isReady = true;
-                               HtmlArea.appendToLog('', 'HTMLArea', 'init', 'Editor url set to: ' + HTMLArea.editorUrl, 'info');
-                               HtmlArea.appendToLog('', 'HTMLArea', 'init', 'Editor skin CSS set to: ' + HTMLArea.editorCSS, 'info');
-                               HtmlArea.appendToLog('', 'HTMLArea', 'init', 'Editor content skin CSS set to: ' + HTMLArea.editedContentCSS, 'info');
+                               HtmlArea.appendToLog('', 'HTMLArea', 'init', 'Editor url set to: ' + HtmlArea.editorUrl, 'info');
+                               HtmlArea.appendToLog('', 'HTMLArea', 'init', 'Editor skin CSS set to: ' + HtmlArea.editorCSS, 'info');
+                               HtmlArea.appendToLog('', 'HTMLArea', 'init', 'Editor content skin CSS set to: ' + HtmlArea.editedContentCSS, 'info');
+
+                               Util.apply(HTMLArea, HtmlArea);
                        }
                },
 
@@ -64,8 +71,9 @@ HTMLArea = function(UserAgent, Util, Config, Editor) {
                                        document.getElementById('pleasewait' + editorId).style.display = 'block';
                                        document.getElementById('editorWrap' + editorId).style.visibility = 'hidden';
                                        if (!HTMLArea.isReady) {
+                                               var self = this;
                                                window.setTimeout(function () {
-                                                       return HtmlArea.initEditor(editorId);
+                                                       return self.initEditor(editorId);
                                                }, 150);
                                        } else {
                                                // Create an editor for the textarea
@@ -127,4 +135,4 @@ HTMLArea = function(UserAgent, Util, Config, Editor) {
 
        return Util.apply(HTMLArea, HtmlArea);
 
-}(HTMLArea.UserAgent, HTMLArea.util, HTMLArea.Config, HTMLArea.Editor);
+});
index ab886e0..5c24cf1 100644 (file)
  * Every plugin should be a subclass of this class
  *
  */
-HTMLArea.Plugin = function(UserAgent, Util) {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util'],
+       function (UserAgent, Util) {
 
        /**
         * Constructor method
@@ -653,4 +656,4 @@ HTMLArea.Plugin = function(UserAgent, Util) {
 
        return Plugin;
 
-}(HTMLArea.UserAgent, HTMLArea.util);
+});
index 971006b..26c8d56 100644 (file)
@@ -13,7 +13,8 @@
 /**
  * Identify the current user agent
  */
-HTMLArea.UserAgent = function () {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent', [], function () {
+
        var userAgent = navigator.userAgent.toLowerCase();
        var documentMode = document.documentMode,
                isOpera = /opera/i.test(userAgent),
@@ -56,4 +57,4 @@ HTMLArea.UserAgent = function () {
                        return isGecko || isWebKit || isOpera || isIE;
                }
        };
-}();
+});
index f3357ab..08366e4 100644 (file)
@@ -13,7 +13,8 @@
 /***************************************************
  *  Color utilities
  ***************************************************/
-HTMLArea.util.Color = function () {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Color', [], function () {
+
        return {
 
                /**
@@ -73,4 +74,4 @@ HTMLArea.util.Color = function () {
                        }
                }
        }
-}();
+});
index c88d3a1..6f4a330 100644 (file)
@@ -13,7 +13,9 @@
 /***************************************************
  * HTMLArea.util.TYPO3: Utility functions for dealing with tabs and inline elements in TYPO3 forms
  ***************************************************/
-HTMLArea.util.TYPO3 = function (UserAgent) {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/TYPO3',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent'],
+       function (UserAgent) {
 
        return {
 
@@ -132,4 +134,4 @@ HTMLArea.util.TYPO3 = function (UserAgent) {
                        return size;
                }
        }
-}(HTMLArea.UserAgent);
+});
index 9173919..2d52b78 100644 (file)
@@ -5,8 +5,11 @@
  * Intercept Ext.form.Field.afterRender in order to provide tips on form fields and menu items
  * Adapted from: http://www.extjs.com/forum/showthread.php?t=36642
  */
-HTMLArea.util.Tips = function (UserAgent) {
-       return {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Tips',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent'],
+       function (UserAgent) {
+
+       Tips = {
                tipsOnFormFields: function () {
                        if (this.helpText || this.helpTitle) {
                                if (!this.helpDisplay) {
@@ -46,7 +49,10 @@ HTMLArea.util.Tips = function (UserAgent) {
                                });
                        }
                }
-       }
-}(HTMLArea.UserAgent);
-Ext.form.Field.prototype.afterRender = Ext.form.Field.prototype.afterRender.createInterceptor(HTMLArea.util.Tips.tipsOnFormFields);
-Ext.menu.BaseItem.prototype.afterRender = Ext.menu.BaseItem.prototype.afterRender.createInterceptor(HTMLArea.util.Tips.tipsOnMenuItems);
+       };
+
+       Ext.form.Field.prototype.afterRender = Ext.form.Field.prototype.afterRender.createInterceptor(Tips.tipsOnFormFields);
+       Ext.menu.BaseItem.prototype.afterRender = Ext.menu.BaseItem.prototype.afterRender.createInterceptor(Tips.tipsOnMenuItems);
+
+       return Tips;
+});
index 0c05b67..a11e66a 100644 (file)
@@ -13,7 +13,7 @@
 /***************************************************
  *  UTILITY FUNCTIONS
  ***************************************************/
-HTMLArea.util = function() {
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util', [], function() {
 
        var Util = {
 
@@ -89,4 +89,4 @@ HTMLArea.util = function() {
 
        return Util;
 
-}();
+});
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/AboutEditor.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/AboutEditor.js
new file mode 100644 (file)
index 0000000..3190307
--- /dev/null
@@ -0,0 +1,212 @@
+/**
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+/**
+ * About Plugin for TYPO3 htmlArea RTE
+ */
+define('TYPO3/CMS/Rtehtmlarea/Plugins/AboutEditor',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin'],
+       function (Plugin) {
+
+       var AboutEditor = Ext.extend(Plugin, {
+
+               /**
+                * This function gets called by the class constructor
+                */
+               configurePlugin: function(editor) {
+                       /*
+                        * Registering plugin "About" information
+                        */
+                       var pluginInformation = {
+                               version         : '2.1',
+                               developer       : 'Stanislas Rolland',
+                               developerUrl    : 'http://www.sjbr.ca/',
+                               copyrightOwner  : 'Stanislas Rolland',
+                               sponsor         : 'SJBR',
+                               sponsorUrl      : 'http://www.sjbr.ca/',
+                               license         : 'GPL'
+                       };
+                       this.registerPluginInformation(pluginInformation);
+                       /*
+                        * Registering the button
+                        */
+                       var buttonId = 'About';
+                       var buttonConfiguration = {
+                               id              : buttonId,
+                               tooltip         : this.localize(buttonId.toLowerCase()),
+                               action          : 'onButtonPress',
+                               textMode        : true,
+                               dialog          : true,
+                               iconCls         : 'htmlarea-action-editor-show-about'
+                       };
+                       this.registerButton(buttonConfiguration);
+                       return true;
+                },
+               /*
+                * Supported browsers
+                */
+               browsers: [
+                        'Firefox 1.5+',
+                        'Google Chrome 1.0+',
+                        'Internet Explorer 6.0+',
+                        'Opera 9.62+',
+                        'Safari 3.0.4+',
+                        'SeaMonkey 1.0+'
+               ],
+               /*
+                * This function gets called when the button was pressed.
+                *
+                * @param       object          editor: the editor instance
+                * @param       string          id: the button id or the key
+                *
+                * @return      boolean         false if action is completed
+                */
+               onButtonPress: function (editor, id) {
+                               // Could be a button or its hotkey
+                       var buttonId = this.translateHotKey(id);
+                       buttonId = buttonId ? buttonId : id;
+                       this.openDialogue(
+                               buttonId,
+                               'About HTMLArea',
+                               this.getWindowDimensions({width:450, height:350}, buttonId),
+                               this.buildTabItems()
+                       );
+                       return false;
+               },
+               /*
+                * Open the dialogue window
+                *
+                * @param       string          buttonId: the button id
+                * @param       string          title: the window title
+                * @param       integer         dimensions: the opening width of the window
+                * @param       object          tabItems: the configuration of the tabbed panel
+                *
+                * @return      void
+                */
+               openDialogue: function (buttonId, title, dimensions, tabItems) {
+                       this.dialog = new Ext.Window({
+                               title: this.localize(title),
+                               cls: 'htmlarea-window',
+                               border: false,
+                               width: dimensions.width,
+                               height: 'auto',
+                               iconCls: this.getButton(buttonId).iconCls,
+                               listeners: {
+                                       close: {
+                                               fn: this.onClose,
+                                               scope: this
+                                       }
+                               },
+                               items: {
+                                       xtype: 'tabpanel',
+                                       activeTab: 0,
+                                       listeners: {
+                                               activate: {
+                                                       fn: this.resetFocus,
+                                                       scope: this
+                                               },
+                                               tabchange: {
+                                                       fn: this.syncHeight,
+                                                       scope: this
+                                               }
+                                       },
+                                       items: tabItems
+                               },
+                               buttons: [
+                                       this.buildButtonConfig('Close', this.onCancel)
+                               ]
+                       });
+                       this.show();
+               },
+               /*
+                * Build the configuration of the the tab items
+                *
+                * @return      array   the configuration array of tab items
+                */
+               buildTabItems: function () {
+                       var tabItems = [];
+                               // About tab
+                       tabItems.push({
+                               xtype: 'panel',
+                               cls: 'about',
+                               title: this.localize('About'),
+                               html: '<h1 id="version">htmlArea RTE ' +  RTEarea[0].version + '</h1>'
+                                       + '<p>' + this.localize('free_editor').replace('<', '&lt;').replace('>', '&gt;') + '</p>'
+                                       + '<p><br />' + this.localize('Browser support') + ': ' + this.browsers.join(', ') + '.</p>'
+                                       + '<p><br />' + this.localize('product_documentation') + '&nbsp;<a href="http://docs.typo3.org/typo3cms/extensions/rtehtmlarea/" target="_blank">typo3.org</a></p>'
+                                       + '<p style="text-align: center;">'
+                                               + '<br />'
+                                               + '&copy; 2002-2004 <a href="http://interactivetools.com" target="_blank">interactivetools.com, inc.</a><br />'
+                                               + '&copy; 2003-2004 <a href="http://dynarch.com" target="_blank">dynarch.com LLC.</a><br />'
+                                               + '&copy; 2004-2014 <a href="http://www.sjbr.ca" target="_blank">Stanislas Rolland</a><br />'
+                                               + this.localize('All rights reserved.')
+                                       + '</p>'
+                       });
+                               // Plugins tab
+                       if (!this.store) {
+                               this.store = new Ext.data.ArrayStore({
+                                       fields: [{ name: 'name'}, { name: 'developer'},  { name: 'sponsor'}],
+                                       sortInfo: {
+                                               field: 'name',
+                                               direction: 'ASC'
+                                       },
+                                       data: this.getPluginsInfo()
+                               });
+                       }
+                       tabItems.push({
+                               xtype: 'panel',
+                               cls: 'about-plugins',
+                               height: 200,
+                               title: this.localize('Plugins'),
+                               autoScroll: true,
+                               items: {
+                                       xtype: 'listview',
+                                       store: this.store,
+                                       reserveScrollOffset: true,
+                                       columns: [{
+                                               header: this.localize('Name'),
+                                               dataIndex: 'name',
+                                               width: .33
+                                           },{
+                                               header: this.localize('Developer'),
+                                               dataIndex: 'developer',
+                                               width: .33
+                                           },{
+                                               header: this.localize('Sponsored by'),
+                                               dataIndex: 'sponsor'
+                                       }]
+                               }
+                       });
+                       return tabItems;
+               },
+               /*
+                * Format an array of information on each configured plugin
+                *
+                * @return      array           array of data objects
+                */
+               getPluginsInfo: function () {
+                       var pluginsInfo = [];
+                       for (var pluginId in this.editor.plugins) {
+                               var plugin = this.editor.plugins[pluginId];
+                               pluginsInfo.push([
+                                       plugin.name + ' ' + plugin.version,
+                                       '<a href="' + plugin.developerUrl + '" target="_blank">' + plugin.developer + '</a>',
+                                       '<a href="' + plugin.sponsorUrl + '" target="_blank">' + plugin.sponsor + '</a>'
+                               ]);
+                       }
+                       return pluginsInfo;
+               }
+       });
+
+       return AboutEditor;
+
+});
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/BlockElements.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/BlockElements.js
new file mode 100644 (file)
index 0000000..7a6c835
--- /dev/null
@@ -0,0 +1,1211 @@
+/**
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+/**
+ * BlockElements Plugin for TYPO3 htmlArea RTE
+ */
+define('TYPO3/CMS/Rtehtmlarea/Plugins/BlockElements',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/DOM',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event'],
+       function (Plugin, UserAgent, Dom, Event) {
+
+       var BlockElements = Ext.extend(Plugin, {
+
+               /**
+                * This function gets called by the class constructor
+                */
+               configurePlugin: function (editor) {
+                       /**
+                        * Setting up some properties from PageTSConfig
+                        */
+                       this.buttonsConfiguration = this.editorConfiguration.buttons;
+                       if (this.buttonsConfiguration.blockstyle) {
+                               this.tags = this.editorConfiguration.buttons.blockstyle.tags;
+                       }
+                       this.useClass = {
+                               Indent          : "indent",
+                               JustifyLeft     : "align-left",
+                               JustifyCenter   : "align-center",
+                               JustifyRight    : "align-right",
+                               JustifyFull     : "align-justify"
+                       };
+                       this.useAlignAttribute = false;
+                       for (var buttonId in this.useClass) {
+                               if (this.useClass.hasOwnProperty(buttonId)) {
+                                       if (this.editorConfiguration.buttons[this.buttonList[buttonId][2]]) {
+                                               this.useClass[buttonId] = this.editorConfiguration.buttons[this.buttonList[buttonId][2]].useClass ? this.editorConfiguration.buttons[this.buttonList[buttonId][2]].useClass : this.useClass[buttonId];
+                                               if (buttonId === "Indent") {
+                                                       this.useBlockquote = this.editorConfiguration.buttons.indent.useBlockquote ? this.editorConfiguration.buttons.indent.useBlockquote : false;
+                                               } else {
+                                                       if (this.editorConfiguration.buttons[this.buttonList[buttonId][2]].useAlignAttribute) {
+                                                               this.useAlignAttribute = true;
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+                       this.allowedAttributes = new Array('id', 'title', 'lang', 'xml:lang', 'dir', 'class', 'itemscope', 'itemtype', 'itemprop');
+                       if (UserAgent.isIEBeforeIE9) {
+                               this.addAllowedAttribute('className');
+                       }
+                       this.indentedList = null;
+                               // Standard block formating items
+                       var standardElements = new Array('address', 'article', 'aside', 'blockquote', 'div', 'footer', 'header', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'nav', 'p', 'pre', 'section');
+                       this.standardBlockElements = new RegExp( '^(' + standardElements.join('|') + ')$', 'i');
+                               // Process block formating customization configuration
+                       this.formatBlockItems = {};
+                       if (this.buttonsConfiguration
+                               && this.buttonsConfiguration.formatblock
+                               && this.buttonsConfiguration.formatblock.items) {
+                                       this.formatBlockItems = this.buttonsConfiguration.formatblock.items;
+                       }
+                               // Build lists of mutually exclusive class names
+                       for (var tagName in this.formatBlockItems) {
+                               if (this.formatBlockItems.hasOwnProperty(tagName) && this.formatBlockItems[tagName].tagName && this.formatBlockItems[tagName].addClass) {
+                                       if (!this.formatBlockItems[this.formatBlockItems[tagName].tagName]) {
+                                               this.formatBlockItems[this.formatBlockItems[tagName].tagName] = {};
+                                       }
+                                       if (!this.formatBlockItems[this.formatBlockItems[tagName].tagName].classList) {
+                                               this.formatBlockItems[this.formatBlockItems[tagName].tagName].classList = new Array();
+                                       }
+                                       this.formatBlockItems[this.formatBlockItems[tagName].tagName].classList.push(this.formatBlockItems[tagName].addClass);
+                               }
+                       }
+                       for (var tagName in this.formatBlockItems) {
+                               if (this.formatBlockItems.hasOwnProperty(tagName) && this.formatBlockItems[tagName].classList) {
+                                       this.formatBlockItems[tagName].classList = new RegExp( "^(" + this.formatBlockItems[tagName].classList.join("|") + ")$");
+                               }
+                       }
+                       /*
+                        * Registering plugin "About" information
+                        */
+                       var pluginInformation = {
+                               version         : '3.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 dropdown list
+                        */
+                       var buttonId = "FormatBlock";
+                       var dropDownConfiguration = {
+                               id: buttonId,
+                               tooltip: this.localize(buttonId + "-Tooltip"),
+                               options: this.buttonsConfiguration.formatblock ? this.buttonsConfiguration.formatblock.options : [],
+                               action: "onChange"
+                       };
+                       if (this.buttonsConfiguration.formatblock) {
+                               dropDownConfiguration.width = this.buttonsConfiguration.formatblock.width ? parseInt(this.buttonsConfiguration.formatblock.width, 10) : 200;
+                               if (this.buttonsConfiguration.formatblock.listWidth) {
+                                       dropDownConfiguration.listWidth = parseInt(this.buttonsConfiguration.formatblock.listWidth, 10);
+                               }
+                               if (this.buttonsConfiguration.formatblock.maxHeight) {
+                                       dropDownConfiguration.maxHeight = parseInt(this.buttonsConfiguration.formatblock.maxHeight, 10);
+                               }
+                       }
+                       this.registerDropDown(dropDownConfiguration);
+                       /*
+                        * Establishing the list of allowed block elements
+                        */
+                       var blockElements = new Array(), option;
+                       for (var i = 0, n = dropDownConfiguration.options.length; i < n; i++) {
+                               option = dropDownConfiguration.options[i];
+                               if (option[1] != 'none') {
+                                       blockElements.push(option[1]);
+                               }
+                       }
+                       if (blockElements.length) {
+                               this.allowedBlockElements = new RegExp( "^(" + blockElements.join("|") + ")$", "i");
+                       } else {
+                               this.allowedBlockElements = this.standardBlockElements;
+                       }
+                       /*
+                        * Registering hot keys for the dropdown list items
+                        */
+                       var blockElement, configuredHotKey;
+                       for (var i = 0, n = blockElements.length; i < n; i++) {
+                               blockElement = blockElements[i];
+                               configuredHotKey = this.defaultHotKeys[blockElement];
+                               if (this.editorConfiguration.buttons.formatblock
+                                               && this.editorConfiguration.buttons.formatblock.items
+                                               && this.editorConfiguration.buttons.formatblock.items[blockElement]
+                                               && this.editorConfiguration.buttons.formatblock.items[blockElement].hotKey) {
+                                       configuredHotKey = this.editorConfiguration.buttons.formatblock.items[blockElement].hotKey;
+                               }
+                               if (configuredHotKey) {
+                                       var hotKeyConfiguration = {
+                                               id              : configuredHotKey,
+                                               cmd             : buttonId,
+                                               element         : blockElement
+                                       };
+                                       this.registerHotKey(hotKeyConfiguration);
+                               }
+                       }
+                       /*
+                        * 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 + '-Tooltip'),
+                                               iconCls         : 'htmlarea-action-' + button[3],
+                                               contextMenuTitle: this.localize(buttonId + '-contextMenuTitle'),
+                                               helpText        : this.localize(buttonId + '-helpText'),
+                                               action          : 'onButtonPress',
+                                               hotKey          : ((this.buttonsConfiguration[button[2]] && this.buttonsConfiguration[button[2]].hotKey) ? this.buttonsConfiguration[button[2]].hotKey : (button[1] ? button[1] : null))
+                                       };
+                                       this.registerButton(buttonConfiguration);
+                               }
+                       }
+                       return true;
+               },
+               /*
+                * The list of buttons added by this plugin
+                */
+               buttonList: {
+                       Indent                  : [null, 'TAB', 'indent', 'indent'],
+                       Outdent                 : [null, 'SHIFT-TAB', 'outdent', 'outdent'],
+                       Blockquote              : [null, null, 'blockquote', 'blockquote'],
+                       InsertParagraphBefore   : [null, null, 'insertparagraphbefore', 'paragraph-insert-before'],
+                       InsertParagraphAfter    : [null, null, 'insertparagraphafter', 'paragraph-insert-after'],
+                       JustifyLeft             : [null, 'l', 'left', 'justify-left'],
+                       JustifyCenter           : [null, 'e', 'center', 'justify-center'],
+                       JustifyRight            : [null, 'r', 'right', 'justify-right'],
+                       JustifyFull             : [null, 'j', 'justifyfull', 'justify-full'],
+                       InsertOrderedList       : [null, null, 'orderedlist', 'ordered-list'],
+                       InsertUnorderedList     : [null, null, 'unorderedlist', 'unordered-list'],
+                       InsertHorizontalRule    : [null, null, 'inserthorizontalrule', 'horizontal-rule-insert']
+               },
+               /*
+                * The list of hotkeys associated with block elements and registered by default by this plugin
+                */
+               defaultHotKeys: {
+                               'p'     : 'n',
+                               'h1'    : '1',
+                               'h2'    : '2',
+                               'h3'    : '3',
+                               'h4'    : '4',
+                               'h5'    : '5',
+                               'h6'    : '6'
+               },
+               /*
+                * The function returns true if the type of block element is allowed in the current configuration
+                */
+               isAllowedBlockElement: function (blockName) {
+                       return this.allowedBlockElements.test(blockName);
+               },
+               /*
+                * This function adds an attribute to the array of attributes allowed on block elements
+                *
+                * @param       string  attribute: the name of the attribute to be added to the array
+                *
+                * @return      void
+                */
+               addAllowedAttribute: function (attribute) {
+                       this.allowedAttributes.push(attribute);
+               },
+               /*
+                * This function gets called when some block element was selected in the drop-down list
+                */
+               onChange: function (editor, combo, record, index) {
+                       this.applyBlockElement(combo.itemId, combo.getValue());
+               },
+               applyBlockElement: function (buttonId, blockElement) {
+                       var tagName = blockElement;
+                       var className = null;
+                       if (this.formatBlockItems[tagName]) {
+                               if (this.formatBlockItems[tagName].addClass) {
+                                       className = this.formatBlockItems[tagName].addClass;
+                               }
+                               if (this.formatBlockItems[tagName].tagName) {
+                                       tagName = this.formatBlockItems[tagName].tagName;
+                               }
+                       }
+                       if (this.standardBlockElements.test(tagName) || tagName == "none") {
+                               switch (tagName) {
+                                       case 'blockquote':
+                                               this.onButtonPress(this.editor, 'Blockquote', null, className);
+                                               break;
+                                       case 'address':
+                                       case 'article':
+                                       case 'aside':
+                                       case 'div':
+                                       case 'footer':
+                                       case 'header':
+                                       case 'nav':
+                                       case 'section':
+                                       case 'none':
+                                               this.onButtonPress(this.editor, tagName, null, className);
+                                               break;
+                                       default :
+                                               var element = tagName;
+                                               if (UserAgent.isIE) {
+                                                       element = '<' + element + '>';
+                                               }
+                                               this.editor.focus();
+                                               if (UserAgent.isWebKit) {
+                                                       if (!this.editor.document.body.hasChildNodes()) {
+                                                               this.editor.document.body.appendChild((this.editor.document.createElement('br')));
+                                                       }
+                                                               // WebKit sometimes leaves empty block at the end of the selection
+                                                       this.editor.document.body.normalize();
+                                               }
+                                               try {
+                                                       this.editor.getSelection().execCommand(buttonId, false, element);
+                                               } catch(e) {
+                                                       this.appendToLog('applyBlockElement', e + '\n\nby execCommand(' + buttonId + ');', 'error');
+                                               }
+                                               this.addClassOnBlockElements(tagName, className);
+                               }
+                       }
+               },
+               /*
+                * This function gets called when a button 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
+                * @param       string          className: the className to be assigned to the element
+                *
+                * @return      boolean         false if action is completed
+                */
+               onButtonPress: function (editor, id, target, className) {
+                               // Could be a button or its hotkey
+                       var buttonId = this.translateHotKey(id);
+                       buttonId = buttonId ? buttonId : id;
+                       var range = editor.getSelection().createRange();
+                       var statusBarSelection = this.editor.statusBar ? this.editor.statusBar.getSelection() : null;
+                       var parentElement = statusBarSelection ? statusBarSelection : this.editor.getSelection().getParentElement();
+                       if (target) {
+                               parentElement = target;
+                       }
+                       while (parentElement && (!Dom.isBlockElement(parentElement) || /^li$/i.test(parentElement.nodeName))) {
+                               parentElement = parentElement.parentNode;
+                       }
+                       var blockAncestors = Dom.getBlockAncestors(parentElement);
+                       var tableCell = null;
+                       if (id === "TAB" || id === "SHIFT-TAB") {
+                               for (var i = blockAncestors.length; --i >= 0;) {
+                                       if (/^(td|th)$/i.test(blockAncestors[i].nodeName)) {
+                                               tableCell = blockAncestors[i];
+                                               break;
+                                       }
+                               }
+                       }
+                       var fullNodeTextSelected = (!UserAgent.isIEBeforeIE9 && parentElement.textContent === range.toString()) || (UserAgent.isIEBeforeIE9 && parentElement.innerText === range.text);
+                       switch (buttonId) {
+                               case "Indent" :
+                                       if (/^(ol|ul)$/i.test(parentElement.nodeName) && !(fullNodeTextSelected && !/^(li)$/i.test(parentElement.parentNode.nodeName))) {
+                                               if (UserAgent.isOpera) {
+                                                       try {
+                                                               this.editor.getSelection().execCommand(buttonId, false, null);
+                                                       } catch(e) {
+                                                               this.appendToLog('onButtonPress', e + '\n\nby execCommand(' + buttonId + ');', 'error');
+                                                       }
+                                                       this.indentedList = parentElement;
+                                                       this.makeNestedList(parentElement);
+                                                       this.editor.getSelection().selectNodeContents(this.indentedList.lastChild, false);
+                                               } else {
+                                                       this.indentSelectedListElements(parentElement, range);
+                                               }
+                                       } else if (tableCell) {
+                                               var tablePart = tableCell.parentNode.parentNode;
+                                                       // Get next cell in same table part
+                                               var nextCell = tableCell.nextSibling ? tableCell.nextSibling : (tableCell.parentNode.nextSibling ? tableCell.parentNode.nextSibling.cells[0] : null);
+                                                       // Next cell is in other table part
+                                               if (!nextCell) {
+                                                       switch (tablePart.nodeName.toLowerCase()) {
+                                                           case "thead":
+                                                               nextCell = tablePart.parentNode.tBodies[0].rows[0].cells[0];
+                                                               break;
+                                                           case "tbody":
+                                                               nextCell = tablePart.nextSibling ? tablePart.nextSibling.rows[0].cells[0] : null;
+                                                               break;
+                                                           case "tfoot":
+                                                               this.editor.getSelection().selectNodeContents(tablePart.parentNode.lastChild.lastChild.lastChild, true);
+                                                       }
+                                               }
+                                               if (!nextCell) {
+                                                       if (this.getPluginInstance('TableOperations')) {
+                                                               this.getPluginInstance('TableOperations').onButtonPress(this.editor, 'TO-row-insert-under');
+                                                       } else {
+                                                               nextCell = tablePart.parentNode.rows[0].cells[0];
+                                                       }
+                                               }
+                                               if (nextCell) {
+                                                       if (UserAgent.isOpera && !nextCell.hasChildNodes()) {
+                                                               nextCell.appendChild(this.editor.document.createElement('br'));
+                                                       }
+                                                       this.editor.getSelection().selectNodeContents(nextCell, true);
+                                               }
+                                       } else  if (this.useBlockquote) {
+                                               try {
+                                                       this.editor.getSelection().execCommand(buttonId, false, null);
+                                               } catch(e) {
+                                                       this.appendToLog('onButtonPress', e + '\n\nby execCommand(' + buttonId + ');', 'error');
+                                               }
+                                       } else if (this.isAllowedBlockElement("div")) {
+                                               if (/^div$/i.test(parentElement.nodeName) && !Dom.hasClass(parentElement, this.useClass[buttonId])) {
+                                                       Dom.addClass(parentElement, this.useClass[buttonId]);
+                                               } else if (!/^div$/i.test(parentElement.nodeName) && /^div$/i.test(parentElement.parentNode.nodeName) && !Dom.hasClass(parentElement.parentNode, this.useClass[buttonId])) {
+                                                       Dom.addClass(parentElement.parentNode, this.useClass[buttonId]);
+                                               } else {
+                                                       var bookmark = this.editor.getBookMark().get(range);
+                                                       var newBlock = this.wrapSelectionInBlockElement('div', this.useClass[buttonId], null, true);
+                                                       this.editor.getSelection().selectRange(this.editor.getBookMark().moveTo(bookmark));
+                                               }
+                                       } else {
+                                               this.addClassOnBlockElements(buttonId);
+                                       }
+                                       break;
+                               case "Outdent" :
+                                       if (/^(ol|ul)$/i.test(parentElement.nodeName) && !Dom.hasClass(parentElement, this.useClass.Indent)) {
+                                               if (/^(li)$/i.test(parentElement.parentNode.nodeName)) {
+                                                       if (UserAgent.isOpera) {
+                                                               try {
+                                                                       this.editor.getSelection().execCommand(buttonId, false, null);
+                                                               } catch(e) {
+                                                                       this.appendToLog('onButtonPress', e + '\n\nby execCommand(' + buttonId + ');', 'error');
+                                                               }
+                                                       } else {
+                                                               this.outdentSelectedListElements(parentElement, range);
+                                                       }
+                                               }
+                                       } else if (tableCell) {
+                                               var previousCell = tableCell.previousSibling ? tableCell.previousSibling : (tableCell.parentNode.previousSibling ? tableCell.parentNode.previousSibling.lastChild : null);
+                                               if (!previousCell) {
+                                                       var table = tableCell.parentNode.parentNode.parentNode;
+                                                       var tablePart = tableCell.parentNode.parentNode.nodeName.toLowerCase();
+                                                       switch (tablePart) {
+                                                               case "tbody":
+                                                                       if (table.tHead) {
+                                                                               previousCell = table.tHead.rows[table.tHead.rows.length-1].cells[table.tHead.rows[table.tHead.rows.length-1].cells.length-1];
+                                                                               break;
+                                                                       }
+                                                               case "thead":
+                                                                       if (table.tFoot) {
+                                                                               previousCell = table.tFoot.rows[table.tFoot.rows.length-1].cells[table.tFoot.rows[table.tFoot.rows.length-1].cells.length-1];
+                                                                               break;
+                                                                       }
+                                                               case "tfoot":
+                                                                       previousCell = table.tBodies[table.tBodies.length-1].rows[table.tBodies[table.tBodies.length-1].rows.length-1].cells[table.tBodies[table.tBodies.length-1].rows[table.tBodies[table.tBodies.length-1].rows.length-1].cells.length-1];
+                                                       }
+                                               }
+                                               if (previousCell) {
+                                                       if (UserAgent.isOpera && !previousCell.hasChildNodes()) {
+                                                               previousCell.appendChild(this.editor.document.createElement('br'));
+                                                       }
+                                                       this.editor.getSelection().selectNodeContents(previousCell, true);
+                                               }
+                                       } else  if (this.useBlockquote) {
+                                               try {
+                                                       this.editor.getSelection().execCommand(buttonId, false, null);
+                                               } catch(e) {
+                                                       this.appendToLog('onButtonPress', e + '\n\nby execCommand(' + buttonId + ');', 'error');
+                                               }
+                                       } else if (this.isAllowedBlockElement("div")) {
+                                               for (var i = blockAncestors.length; --i >= 0;) {
+                                                       if (Dom.hasClass(blockAncestors[i], this.useClass.Indent)) {
+                                                               var bookmark = this.editor.getBookMark().get(range);
+                                                               var newBlock = this.wrapSelectionInBlockElement('div', false, blockAncestors[i]);
+                                                                       // If not directly under the div, we need to backtrack
+                                                               if (newBlock.parentNode !== blockAncestors[i]) {
+                                                                       var parent = newBlock.parentNode;
+                                                                       this.editor.getDomNode().removeMarkup(newBlock);
+                                                                       while (parent.parentNode !== blockAncestors[i]) {
+                                                                               parent = parent.parentNode;
+                                                                       }
+                                                                       blockAncestors[i].insertBefore(newBlock, parent);
+                                                                       newBlock.appendChild(parent);
+                                                               }
+                                                               newBlock.className = blockAncestors[i].className;
+                                                               Dom.removeClass(newBlock, this.useClass.Indent);
+                                                               if (!newBlock.previousSibling) {
+                                                                       while (newBlock.hasChildNodes()) {
+                                                                               if (newBlock.firstChild.nodeType === Dom.ELEMENT_NODE) {
+                                                                                       newBlock.firstChild.className = newBlock.className;
+                                                                               }
+                                                                               blockAncestors[i].parentNode.insertBefore(newBlock.firstChild, blockAncestors[i]);
+                                                                       }
+                                                               } else if (!newBlock.nextSibling) {
+                                                                       if (blockAncestors[i].nextSibling) {
+                                                                               while (newBlock.hasChildNodes()) {
+                                                                                       if (newBlock.firstChild.nodeType === Dom.ELEMENT_NODE) {
+                                                                                               newBlock.lastChild.className = newBlock.className;
+                                                                                       }
+                                                                                       blockAncestors[i].parentNode.insertBefore(newBlock.lastChild, blockAncestors[i].nextSibling);
+                                                                               }
+                                                                       } else {
+                                                                               while (newBlock.hasChildNodes()) {
+                                                                                       if (newBlock.firstChild.nodeType === Dom.ELEMENT_NODE) {
+                                                                                               newBlock.firstChild.className = newBlock.className;
+                                                                                       }
+                                                                                       blockAncestors[i].parentNode.appendChild(newBlock.firstChild);
+                                                                               }
+                                                                       }
+                                                               } else {
+                                                                       var clone = blockAncestors[i].cloneNode(false);
+                                                                       if (blockAncestors[i].nextSibling) {
+                                                                               blockAncestors[i].parentNode.insertBefore(clone, blockAncestors[i].nextSibling);
+                                                                       } else {
+                                                                               blockAncestors[i].parentNode.appendChild(clone);
+                                                                       }
+                                                                       while (newBlock.nextSibling) {
+                                                                               clone.appendChild(newBlock.nextSibling);
+                                                                       }
+                                                                       while (newBlock.hasChildNodes()) {
+                                                                               if (newBlock.firstChild.nodeType === Dom.ELEMENT_NODE) {
+                                                                                       newBlock.firstChild.className = newBlock.className;
+                                                                               }
+                                                                               blockAncestors[i].parentNode.insertBefore(newBlock.firstChild, clone);
+                                                                       }
+                                                               }
+                                                               blockAncestors[i].removeChild(newBlock);
+                                                               if (!blockAncestors[i].hasChildNodes()) {
+                                                                       blockAncestors[i].parentNode.removeChild(blockAncestors[i]);
+                                                               }
+                                                               this.editor.getSelection().selectRange(this.editor.getBookMark().moveTo(bookmark));
+                                                               break;
+                                                       }
+                                               }
+                                       } else {
+                                               this.addClassOnBlockElements(buttonId);
+                                       }
+                                       break;
+                               case "InsertParagraphBefore" :
+                               case "InsertParagraphAfter"  :
+                                       this.insertParagraph(buttonId === "InsertParagraphAfter");
+                                       break;
+                               case "Blockquote" :
+                                       var commandState = false;
+                                       for (var i = blockAncestors.length; --i >= 0;) {
+                                               if (/^blockquote$/i.test(blockAncestors[i].nodeName)) {
+                                                       commandState = true;
+                                                       this.editor.getDomNode().removeMarkup(blockAncestors[i]);
+                                                       break;
+                                               }
+                                       }
+                                       if (!commandState) {
+                                               var bookmark = this.editor.getBookMark().get(range);
+                                               var newBlock = this.wrapSelectionInBlockElement('blockquote', className, null, true);
+                                               this.editor.getSelection().selectRange(this.editor.getBookMark().moveTo(bookmark));
+                                       }
+                                       break;
+                               case 'address':
+                               case 'article':
+                               case 'aside':
+                               case 'div':
+                               case 'footer':
+                               case 'header':
+                               case 'nav':
+                               case 'section':
+                                       var bookmark = this.editor.getBookMark().get(range);
+                                       var newBlock = this.wrapSelectionInBlockElement(buttonId, className, null, true);
+                                       this.editor.getSelection().selectRange(this.editor.getBookMark().moveTo(bookmark));
+                                       if (UserAgent.isWebKit || UserAgent.isOpera) {
+                                               this.editor.getDomNode().cleanAppleStyleSpans(newBlock);
+                                       }
+                                       break;
+                               case "JustifyLeft"   :
+                               case "JustifyCenter" :
+                               case "JustifyRight"  :
+                               case "JustifyFull"   :
+                                       if (this.useAlignAttribute) {
+                                               try {
+                                                       this.editor.getSelection().execCommand(buttonId, false, null);
+                                               } catch(e) {
+                                                       this.appendToLog('onButtonPress', e + '\n\nby execCommand(' + buttonId + ');', 'error');
+                                               }
+                                       } else {
+                                               this.addClassOnBlockElements(buttonId);
+                                       }
+                                       break;
+                               case "InsertOrderedList":
+                               case "InsertUnorderedList":
+                                       this.insertList(buttonId, parentElement);
+                                       break;
+                               case "InsertHorizontalRule":
+                                       this.insertHorizontalRule();
+                                       break;
+                               case "none" :
+                                       if (this.isAllowedBlockElement(parentElement.nodeName)) {
+                                               this.editor.getDomNode().removeMarkup(parentElement);
+                                       }
+                                       break;
+                               default :
+                                       break;
+                       }
+                       return false;
+               },
+
+               /**
+                * This function wraps the block elements intersecting the current selection in a block element of the given type
+                *
+                * @param       string          blockName: the type of element to be used as wrapping block
+                * @param       string          useClass: a class to be assigned to the wrapping block
+                * @param       object          withinBlock: only elements contained in this block will be wrapped
+                * @param       boolean         keepValid: make only valid wraps (working wraps may produce temporary invalid hierarchies)
+                *
+                * @return      object          the wrapping block
+                */
+               wrapSelectionInBlockElement: function (blockName, useClass, withinBlock, keepValid) {
+                       var endBlocks = this.editor.getSelection().getEndBlocks();
+                       var startAncestors = Dom.getBlockAncestors(endBlocks.start, withinBlock);
+                       var endAncestors = Dom.getBlockAncestors(endBlocks.end, withinBlock);
+                       var i = 0;
+                       while (i < startAncestors.length && i < endAncestors.length && startAncestors[i] === endAncestors[i]) {
+                               ++i;
+                       }
+                       if ((endBlocks.start === endBlocks.end && /^(body)$/i.test(endBlocks.start.nodeName)) || !startAncestors[i] || !endAncestors[i]) {
+                               --i;
+                       }
+                       if (keepValid) {
+                               if (endBlocks.start === endBlocks.end) {
+                                       while (i && /^(thead|tbody|tfoot|tr|dt)$/i.test(startAncestors[i].nodeName)) {
+                                               --i;
+                                       }
+                               } else {
+                                       while (i && (/^(thead|tbody|tfoot|tr|td|li|dd|dt)$/i.test(startAncestors[i].nodeName) || /^(thead|tbody|tfoot|tr|td|li|dd|dt)$/i.test(endAncestors[i].nodeName))) {
+                                               --i;
+                                       }
+                               }
+                       }
+                       var blockElement = this.editor.document.createElement(blockName);
+                       if (useClass) {
+                               Dom.addClass(blockElement, useClass);
+                       }
+                       var contextElement = endAncestors[0];
+                       if (i) {
+                               contextElement = endAncestors[i-1];
+                       }
+                       var nextElement = endAncestors[i].nextSibling;
+                       var block = startAncestors[i], sibling;
+                       if ((!/^(body|td|th|li|dd)$/i.test(block.nodeName) || /^(ol|ul|dl)$/i.test(blockName)) && block != withinBlock) {
+                               while (block && block != nextElement) {
+                                       sibling = block.nextSibling;
+                                       blockElement.appendChild(block);
+                                       block = sibling;
+                               }
+                               if (nextElement) {
+                                       blockElement = nextElement.parentNode.insertBefore(blockElement, nextElement);
+                               } else {
+                                       blockElement = contextElement.appendChild(blockElement);
+                               }
+                       } else {
+                               contextElement = block;
+                               block = block.firstChild;
+                               while (block) {
+                                       sibling = block.nextSibling;
+                                       blockElement.appendChild(block);
+                                       block = sibling;
+                               }
+                               blockElement = contextElement.appendChild(blockElement);
+                       }
+                               // Things go wrong in some browsers when the node is empty
+                       if (UserAgent.isWebKit && !blockElement.hasChildNodes()) {
+                               blockElement = blockElement.appendChild(this.editor.document.createElement('br'));
+                       }
+                       return blockElement;
+               },
+               /*
+                * This function adds a class attribute on blocks sibling of the block containing the start container of the selection
+                */
+               addClassOnBlockElements: function (buttonId, className) {
+                       var endBlocks = this.editor.getSelection().getEndBlocks();
+                       var startAncestors = Dom.getBlockAncestors(endBlocks.start);
+                       var endAncestors = Dom.getBlockAncestors(endBlocks.end);
+                       var index = 0;
+                       while (index < startAncestors.length && index < endAncestors.length && startAncestors[index] === endAncestors[index]) {
+                               ++index;
+                       }
+                       if (endBlocks.start === endBlocks.end) {
+                               --index;
+                       }
+                       if (!/^(body)$/i.test(startAncestors[index].nodeName)) {
+                               for (var block = startAncestors[index]; block; block = block.nextSibling) {
+                                       if (Dom.isBlockElement(block)) {
+                                               switch (buttonId) {
+                                                       case "Indent" :
+                                                               if (!Dom.hasClass(block, this.useClass[buttonId])) {
+                                                                       Dom.addClass(block, this.useClass[buttonId]);
+                                                               }
+                                                               break;
+                                                       case "Outdent" :
+                                                               if (Dom.hasClass(block, this.useClass["Indent"])) {
+                                                                       Dom.removeClass(block, this.useClass["Indent"]);
+                                                               }
+                                                               break;
+                                                       case "JustifyLeft"   :
+                                                       case "JustifyCenter" :
+                                                       case "JustifyRight"  :
+                                                       case "JustifyFull"   :
+                                                               this.toggleAlignmentClass(block, buttonId);
+                                                               break;
+                                                       default :
+                                                               if (this.standardBlockElements.test(buttonId.toLowerCase()) && buttonId.toLowerCase() == block.nodeName.toLowerCase()) {
+                                                                       this.cleanClasses(block);
+                                                                       if (className) {
+                                                                               Dom.addClass(block, className);
+                                                                       }
+                                                               }
+                                                               break;
+                                               }
+                                       }
+                                       if (block == endAncestors[index]) {
+                                               break;
+                                       }
+                               }
+                       }
+               },
+               /*
+                * This function toggles the alignment class on the given block
+                */
+               toggleAlignmentClass: function (block, buttonId) {
+                       for (var alignmentButtonId in this.useClass) {
+                               if (this.useClass.hasOwnProperty(alignmentButtonId) && alignmentButtonId !== "Indent") {
+                                       if (Dom.hasClass(block, this.useClass[alignmentButtonId])) {
+                                               Dom.removeClass(block, this.useClass[alignmentButtonId]);
+                                       } else if (alignmentButtonId === buttonId) {
+                                               Dom.addClass(block, this.useClass[alignmentButtonId]);
+                                       }
+                               }
+                       }
+                       if (/^div$/i.test(block.nodeName) && !Dom.hasAllowedAttributes(block, this.allowedAttributes)) {
+                               this.editor.getDomNode().removeMarkup(block);
+                       }
+               },
+       
+               insertList: function (buttonId, parentElement) {
+                       if (/^(dd)$/i.test(parentElement.nodeName)) {
+                               var list = parentElement.appendChild(this.editor.document.createElement((buttonId === 'OrderedList') ? 'ol' : 'ul'));
+                               var first = list.appendChild(this.editor.document.createElement('li'));
+                               first.innerHTML = '<br />';
+                               this.editor.getSelection().selectNodeContents(first, true);
+                       } else {
+                               try {
+                                       this.editor.getSelection().execCommand(buttonId, false, null);
+                               } catch(e) {
+                                       this.appendToLog('onButtonPress', e + '\n\nby execCommand(' + buttonId + ');', 'error');
+                               }
+                               if (UserAgent.isWebKit || UserAgent.isOpera) {
+                                       var parentElement = this.editor.getSelection().getParentElement();
+                                       var parentNode = parentElement.parentNode;
+                                       // If the parent of the selection is a span, remove it
+                                       if (/^(span)$/i.test(parentElement.nodeName)) {
+                                               this.editor.getDomNode().removeMarkup(parentElement);
+                                               parentElement = parentNode;
+                                       }
+                                       // The list might not be well-formed
+                                       while (/^(li)$/i.test(parentElement.nodeName)) {
+                                               parentElement = parentElement.parentNode;
+                                       }
+                                       if (/^(ol|ul)$/i.test(parentElement.nodeName)) {
+                                               // Make sure the list is well-formed
+                                               this.makeNestedList(parentElement);
+                                               // The list may be wrapped inside a paragraph
+                                               if (/^(p)$/i.test(parentElement.parentNode.nodeName)) {
+                                                       this.editor.getDomNode().removeMarkup(parentElement.parentNode);
+                                               }
+                                       }
+                                       // Content may be polluted with span and font tags
+                                       this.editor.getDomNode().cleanAppleStyleSpans(parentElement);
+                               }
+                       }
+               },
+               /*
+                * Indent selected list elements
+                */
+               indentSelectedListElements: function (list, range) {
+                       var bookmark = this.editor.getBookMark().get(range);
+                               // The selected elements are wrapped into a list element
+                       var indentedList = this.wrapSelectionInBlockElement(list.nodeName.toLowerCase(), null, list);
+                               // which breaks the range
+                       var range = this.editor.getBookMark().moveTo(bookmark);
+                       bookmark = this.editor.getBookMark().get(range);
+       
+                               // Check if the last element has children. If so, outdent those that do not intersect the selection
+                       var last = indentedList.lastChild.lastChild;
+                       if (last && /^(ol|ul)$/i.test(last.nodeName)) {
+                               var child = last.firstChild, next;
+                               while (child) {
+                                       next = child.nextSibling;
+                                       if (!Dom.rangeIntersectsNode(range, child)) {
+                                               indentedList.appendChild(child);
+                                       }
+                                       child = next;
+                               }
+                               if (!last.hasChildNodes()) {
+                                       Dom.removeFromParent(last);
+                               }
+                       }
+                       if (indentedList.previousSibling && indentedList.previousSibling.hasChildNodes()) {
+                                       // Indenting some elements not including the first one
+                               if (/^(ol|ul)$/i.test(indentedList.previousSibling.lastChild.nodeName)) {
+                                               // Some indented elements exist just above our selection
+                                               // Moving to regroup with these elements
+                                       while (indentedList.hasChildNodes()) {
+                                               indentedList.previousSibling.lastChild.appendChild(indentedList.firstChild);
+                                       }
+                                       list.removeChild(indentedList);
+                               } else {
+                                       indentedList = indentedList.previousSibling.appendChild(indentedList);
+                               }
+                       } else {
+                                       // Indenting the first element and possibly some more
+                               var first = this.editor.document.createElement("li");
+                               first.innerHTML = "&nbsp;";
+                               list.insertBefore(first, indentedList);
+                               indentedList = first.appendChild(indentedList);
+                       }
+                       this.editor.getSelection().selectRange(this.editor.getBookMark().moveTo(bookmark));
+               },
+
+               /**
+                * Outdent selected list elements
+                */
+               outdentSelectedListElements: function (list, range) {
+                               // We wrap the selected li elements and thereafter move them one level up
+                       var bookmark = this.editor.getBookMark().get(range);
+                       var wrappedList = this.wrapSelectionInBlockElement(list.nodeName.toLowerCase(), null, list);
+                               // which breaks the range
+                       var range = this.editor.getBookMark().moveTo(bookmark);
+                       bookmark = this.editor.getBookMark().get(range);
+
+                       if (!wrappedList.previousSibling) {
+                                       // Outdenting the first element(s) of an indented list
+                               var next = list.parentNode.nextSibling;
+                               var last = wrappedList.lastChild;
+                               while (wrappedList.hasChildNodes()) {
+                                       if (next) {
+                                               list.parentNode.parentNode.insertBefore(wrappedList.firstChild, next);
+                                       } else {
+                                               list.parentNode.parentNode.appendChild(wrappedList.firstChild);
+                                       }
+                               }
+                               list.removeChild(wrappedList);
+                               last.appendChild(list);
+                       } else if (!wrappedList.nextSibling) {
+                                       // Outdenting the last element(s) of the list
+                                       // This will break the gecko bookmark
+                               this.editor.getBookMark().moveTo(bookmark);
+                               while (wrappedList.hasChildNodes()) {
+                                       if (list.parentNode.nextSibling) {
+                                               list.parentNode.parentNode.insertBefore(wrappedList.firstChild, list.parentNode.nextSibling);
+                                       } else {
+                                               list.parentNode.parentNode.appendChild(wrappedList.firstChild);
+                                       }
+                               }
+                               list.removeChild(wrappedList);
+                               this.editor.getSelection().selectNodeContents(list.parentNode.nextSibling, true);
+                               bookmark = this.editor.getBookMark().get(this.editor.getSelection().createRange());
+                       } else {
+                                       // Outdenting the middle of a list
+                               var next = list.parentNode.nextSibling;
+                               var last = wrappedList.lastChild;
+                               var sibling = wrappedList.nextSibling;
+                               while (wrappedList.hasChildNodes()) {
+                                       if (next) {
+                                               list.parentNode.parentNode.insertBefore(wrappedList.firstChild, next);
+                                       } else {
+                                               list.parentNode.parentNode.appendChild(wrappedList.firstChild);
+                                       }
+                               }
+                               while (sibling) {
+                                       wrappedList.appendChild(sibling);
+                                       sibling = sibling.nextSibling;
+                               }
+                               last.appendChild(wrappedList);
+                       }
+                               // Remove the list if all its elements have been moved up
+                       if (!list.hasChildNodes()) {
+                               list.parentNode.removeChild(list);
+                       }
+                       this.editor.getSelection().selectRange(this.editor.getBookMark().moveTo(bookmark));
+               },
+               /*
+                * Make XHTML-compliant nested list
+                * We need this for Opera and Chrome
+                */
+               makeNestedList: function (el) {
+                       var previous;
+                       for (var i = el.firstChild; i; i = i.nextSibling) {
+                               if (/^li$/i.test(i.nodeName)) {
+                                       for (var j = i.firstChild; j; j = j.nextSibling) {
+                                               if (/^(ol|ul)$/i.test(j.nodeName)) {
+                                                       this.makeNestedList(j);
+                                               } else if (/^(li)$/i.test(j.nodeName)) {
+                                                       this.editor.getDomNode().removeMarkup(j);
+                                               }
+                                       }
+                               } else if (/^(ol|ul)$/i.test(i.nodeName)) {
+                                       previous = i.previousSibling;
+                                       this.indentedList = i.cloneNode(true);
+                                       if (!previous) {
+                                               previous = el.insertBefore(this.editor.document.createElement('li'), i);
+                                               this.indentedList = previous.appendChild(this.indentedList);
+                                       } else {
+                                               this.indentedList = previous.appendChild(this.indentedList);
+                                       }
+                                       Dom.removeFromParent(i);
+                                       this.makeNestedList(el);
+                                       break;
+                               }
+                       }
+               },
+               /*
+                * Insert a paragraph
+                */
+               insertParagraph: function (after) {
+                       var endBlocks = this.editor.getSelection().getEndBlocks();
+                       var ancestors = after ? Dom.getBlockAncestors(endBlocks.end) : Dom.getBlockAncestors(endBlocks.start);
+                       var endElement = ancestors[ancestors.length-1];
+                       for (var i = ancestors.length; --i >= 0;) {
+                               if (/^(table|div|ul|ol|dl|blockquote|address|pre)$/i.test(ancestors[i].nodeName) && !/^(li)$/i.test(ancestors[i].parentNode.nodeName)) {
+                                       endElement = ancestors[i];
+                                       break;
+                               }
+                       }
+                       if (endElement) {
+                               var parent = endElement.parentNode;
+                               var paragraph = this.editor.document.createElement('p');
+                               if (UserAgent.isIEBeforeIE9) {
+                                       paragraph.innerHTML = '&nbsp';
+                               } else {
+                                       paragraph.appendChild(this.editor.document.createElement('br'));
+                               }
+                               if (after && !endElement.nextSibling) {
+                                       parent.appendChild(paragraph);
+                               } else {
+                                       parent.insertBefore(paragraph, after ? endElement.nextSibling : endElement);
+                               }
+                               this.editor.getSelection().selectNodeContents(paragraph, true);
+                       }
+               },
+               /*
+                * Insert horizontal line
+                */
+               insertHorizontalRule: function () {
+                       this.editor.getSelection().execCommand('InsertHorizontalRule');
+                               // Apply enterParagraphs rule
+                       if (!UserAgent.isIE && !UserAgent.isOpera && !this.editor.config.disableEnterParagraphs) {
+                               var range = this.editor.getSelection().createRange();
+                               var startContainer = range.startContainer;
+                               if (/^body$/i.test(startContainer.nodeName)) {
+                                       startContainer.normalize();
+                                       var ruler = startContainer.childNodes[range.startOffset-1];
+                                       if (ruler.nextSibling) {
+                                               if (ruler.nextSibling.nodeType === Dom.TEXT_NODE) {
+                                                       if (/\S/.test(ruler.nextSibling.textContent)) {
+                                                               var paragraph = this.editor.document.createElement('p');
+                                                               paragraph = startContainer.appendChild(paragraph);
+                                                               paragraph = startContainer.insertBefore(paragraph, ruler.nextSibling);
+                                                               paragraph.appendChild(ruler.nextSibling);
+                                                       } else {
+                                                               Dom.removeFromParent(ruler.nextSibling);
+                                                               var paragraph = ruler.nextSibling;
+                                                       }
+                                               } else {
+                                                       var paragraph = ruler.nextSibling;
+                                               }
+                                                       // Cannot set the cursor on the hr element
+                                               if (/^hr$/i.test(paragraph.nodeName)) {
+                                                       var inBetweenParagraph = this.editor.document.createElement('p');
+                                                       inBetweenParagraph.innerHTML = '<br />';
+                                                       paragraph = startContainer.insertBefore(inBetweenParagraph, paragraph);
+                                               }
+                                       } else {
+                                               var paragraph = this.editor.document.createElement('p');
+                                               if (UserAgent.isWebKit) {
+                                                       paragraph.innerHTML = '<br />';
+                                               }
+                                               paragraph = startContainer.appendChild(paragraph);
+                                       }
+                                       this.editor.getSelection().selectNodeContents(paragraph, true);
+                               }
+                       }
+               },
+
+               /**
+                * This function gets called when the plugin is generated
+                */
+               onGenerate: function () {
+                       // Register the enter key handler for IE when the cursor is at the end of a dt or a dd element
+                       if (UserAgent.isIE) {
+                               var self = this;
+                               this.editor.iframe.keyMap.addBinding({
+                                       key: Event.ENTER,
+                                       shift: false,
+                                       handler: function (event) { return self.onKey(event); }
+                               });
+                       }
+               },
+
+               /**
+                * This function gets called when the enter key was pressed in IE
+                * It will process the enter key for IE when the cursor is at the end of a dt or a dd element
+                *
+                * @param object event: the Ext event object (keydown)
+                * @return boolean false, if the event was taken care of
+                */
+               onKey: function (event) {
+                       if (this.editor.getSelection().isEmpty()) {
+                               var range = this.editor.getSelection().createRange();
+                               var parentElement = this.editor.getSelection().getParentElement();
+                               while (parentElement && !Dom.isBlockElement(parentElement)) {
+                                       parentElement = parentElement.parentNode;
+                               }
+                               if (/^(dt|dd)$/i.test(parentElement.nodeName)) {
+                                       var nodeRange = this.editor.getSelection().createRange();
+                                       nodeRange.moveToElementText(parentElement);
+                                       range.setEndPoint("EndToEnd", nodeRange);
+                                       if (!range.text || range.text == "\x20") {
+                                               var item = parentElement.parentNode.insertBefore(this.editor.document.createElement((parentElement.nodeName.toLowerCase() === "dt") ? "dd" : "dt"), parentElement.nextSibling);
+                                               item.innerHTML = "\x20";
+                                               this.editor.getSelection().selectNodeContents(item, true);
+                                               Event.stopEvent(event);
+                                               return false;
+                                       }
+                               } else if (/^(li)$/i.test(parentElement.nodeName)
+                                               && !parentElement.innerText
+                                               && parentElement.parentNode.parentNode
+                                               && /^(dd|td|th)$/i.test(parentElement.parentNode.parentNode.nodeName)) {
+                                       var item = parentElement.parentNode.parentNode.insertBefore(this.editor.document.createTextNode("\x20"), parentElement.parentNode.nextSibling);
+                                       this.editor.getSelection().selectNodeContents(parentElement.parentNode.parentNode, false);
+                                       parentElement.parentNode.removeChild(parentElement);
+                                       Event.stopEvent(event);
+                                       return false;
+                               }
+                       }
+                       return true;
+               },
+
+               /**
+                * This function removes any disallowed class or mutually exclusive classes from the class attribute of the node
+                */
+               cleanClasses: function (node) {
+                       var classNames = node.className.trim().split(" ");
+                       var nodeName = node.nodeName.toLowerCase();
+                       for (var i = classNames.length; --i >= 0;) {
+                               if (!HTMLArea.reservedClassNames.test(classNames[i])) {
+                                       if (this.tags && this.tags[nodeName] && this.tags[nodeName].allowedClasses) {
+                                               if (!this.tags[nodeName].allowedClasses.test(classNames[i])) {
+                                                       Dom.removeClass(node, classNames[i]);
+                                               }
+                                       } else if (this.tags && this.tags.all && this.tags.all.allowedClasses) {
+                                               if (!this.tags.all.allowedClasses.test(classNames[i])) {
+                                                       Dom.removeClass(node, classNames[i]);
+                                               }
+                                       }
+                                       if (this.formatBlockItems[nodeName] && this.formatBlockItems[nodeName].classList && this.formatBlockItems[nodeName].classList.test(classNames[i])) {
+                                               Dom.removeClass(node, classNames[i]);
+                                       }
+                               }
+                       }
+               },
+               /*
+                * This function gets called when the toolbar is updated
+                */
+               onUpdateToolbar: function (button, mode, selectionEmpty, ancestors, endPointsInSameBlock) {
+                       if (mode === 'wysiwyg' && this.editor.isEditable()) {
+                               var statusBarSelection = this.editor.statusBar ? this.editor.statusBar.getSelection() : null;
+                               var parentElement = statusBarSelection ? statusBarSelection : this.editor.getSelection().getParentElement();
+                               if (!/^body$/i.test(parentElement.nodeName)) {
+                                       while (parentElement && !Dom.isBlockElement(parentElement) || /^li$/i.test(parentElement.nodeName)) {
+                                               parentElement = parentElement.parentNode;
+                                       }
+                                       var blockAncestors = Dom.getBlockAncestors(parentElement);
+                                       var endBlocks = this.editor.getSelection().getEndBlocks();
+                                       var startAncestors = Dom.getBlockAncestors(endBlocks.start);
+                                       var endAncestors = Dom.getBlockAncestors(endBlocks.end);
+                                       var index = 0;
+                                       while (index < startAncestors.length && index < endAncestors.length && startAncestors[index] === endAncestors[index]) {
+                                               ++index;
+                                       }
+                                       if (endBlocks.start === endBlocks.end || !startAncestors[index]) {
+                                               --index;
+                                       }
+                                       var commandState = false;
+                                       switch (button.itemId) {
+                                               case 'FormatBlock':
+                                                       this.updateDropDown(button, blockAncestors[blockAncestors.length-1], startAncestors[index]);
+                                                       break;
+                                               case "Outdent" :
+                                                       if (this.useBlockquote) {
+                                                               for (var j = blockAncestors.length; --j >= 0;) {
+                                                                       if (/^blockquote$/i.test(blockAncestors[j].nodeName)) {
+                                                                               commandState = true;
+                                                                               break;
+                                                                       }
+                                                               }
+                                                       } else if (/^(ol|ul)$/i.test(parentElement.nodeName)) {
+                                                               commandState = true;
+                                                       } else {
+                                                               for (var j = blockAncestors.length; --j >= 0;) {
+                                                                       if (Dom.hasClass(blockAncestors[j], this.useClass.Indent) || /^(td|th)$/i.test(blockAncestors[j].nodeName)) {
+                                                                               commandState = true;
+                                                                               break;
+                                                                       }
+                                                               }
+                                                       }
+                                                       button.setDisabled(!commandState);
+                                                       break;
+                                               case "Indent" :
+                                                       break;
+                                               case "InsertParagraphBefore" :
+                                               case "InsertParagraphAfter"  :
+                                                       button.setDisabled(/^(body)$/i.test(startAncestors[index].nodeName));
+                                                       break;
+                                               case "Blockquote" :
+                                                       for (var j = blockAncestors.length; --j >= 0;) {
+                                                               if (/^blockquote$/i.test(blockAncestors[j].nodeName)) {
+                                                                       commandState = true;
+                                                                       break;
+                                                               }
+                                                       }
+                                                       button.setInactive(!commandState);
+                                                       break;
+                                               case "JustifyLeft"   :
+                                               case "JustifyCenter" :
+                                               case "JustifyRight"  :
+                                               case "JustifyFull"   :
+                                                       if (this.useAlignAttribute) {
+                                                               try {
+                                                                       commandState = this.editor.document.queryCommandState(button.itemId);
+                                                               } catch(e) {
+                                                                       commandState = false;
+                                                               }
+                                                       } else {
+                                                               if (/^(body)$/i.test(startAncestors[index].nodeName)) {
+                                                                       button.setDisabled(true);
+                                                               } else {
+                                                                       button.setDisabled(false);
+                                                                       commandState = true;
+                                                                       for (var block = startAncestors[index]; block; block = block.nextSibling) {
+                                                                               commandState = commandState && Dom.hasClass(block, this.useClass[button.itemId]);
+                                                                               if (block == endAncestors[index]) {
+                                                                                       break;
+                                                                               }
+                                                                       }
+                                                               }
+                                                       }
+                                                       button.setInactive(!commandState);
+                                                       break;
+                                               case "InsertOrderedList":
+                                               case "InsertUnorderedList":
+                                                       try {
+                                                               commandState = this.editor.document.queryCommandState(button.itemId);
+                                                       } catch(e) {
+                                                               commandState = false;
+                                                       }
+                                                       button.setInactive(!commandState);
+                                                       break;
+                                               default :
+                                                       break;
+                                       }
+                               } else {
+                                               // The selection is not contained in any block
+                                       switch (button.itemId) {
+                                               case 'FormatBlock':
+                                                       this.updateDropDown(button);
+                                                       break;
+                                               case 'Outdent' :
+                                                       button.setDisabled(true);
+                                                       break;
+                                               case 'Indent' :
+                                                       break;
+                                               case 'InsertParagraphBefore' :
+                                               case 'InsertParagraphAfter'  :
+                                                       button.setDisabled(true);
+                                                       break;
+                                               case 'Blockquote' :
+                                                       button.setInactive(true);
+                                                       break;
+                                               case 'JustifyLeft'   :
+                                               case 'JustifyCenter' :
+                                               case 'JustifyRight'  :
+                                               case 'JustifyFull'   :
+                                                       button.setInactive(true);
+                                                       button.setDisabled(true);
+                                                       break;
+                                               case 'InsertOrderedList':
+                                               case 'InsertUnorderedList':
+                                                       button.setInactive(true);
+                                                       break;
+                                               default :
+                                                       break;
+                                       }
+                               }
+                       }
+               },
+               /*
+                * This function updates the drop-down list of block elements
+                */
+               updateDropDown: function(select, deepestBlockAncestor, startAncestor) {
+                       var store = select.getStore();
+                       store.removeAt(0);
+                       var index = -1;
+                       if (deepestBlockAncestor) {
+                               var nodeName = deepestBlockAncestor.nodeName.toLowerCase();
+                                       // Could be a custom item ...
+                               index = store.findBy(function(record, id) {
+                                       var item = this.formatBlockItems[record.get('value')];
+                                       return item && item.tagName == nodeName && item.addClass && Dom.hasClass(deepestBlockAncestor, item.addClass);
+                               }, this);
+                               if (index == -1) {
+                                               // ... or a standard one
+                                       index = store.findExact('value', nodeName);
+                               }
+                       }
+                       if (index == -1) {
+                               store.insert(0, new store.recordType({
+                                       text: this.localize('No block'),
+                                       value: 'none'
+                               }));
+                               select.setValue('none');
+                       } else {
+                               store.insert(0, new store.recordType({
+                                       text: this.localize('Remove block'),
+                                       value: 'none'
+                               }));
+                               select.setValue(store.getAt(index+1).get('value'));
+                       }
+               },
+               /*
+               * This function handles the hotkey events registered on elements of the dropdown list
+               */
+               onHotKey: function(editor, key) {
+                       var blockElement;
+                       var hotKeyConfiguration = this.getHotKeyConfiguration(key);
+                       if (hotKeyConfiguration) {
+                               var blockElement = hotKeyConfiguration.element;
+                       }
+                       if (blockElement && this.isAllowedBlockElement(blockElement)) {
+                               this.applyBlockElement(this.translateHotKey(key), blockElement);
+                               return false;
+                       }
+                       return true;
+               }
+       });
+
+       return BlockElements;
+
+});
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/BlockStyle.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/BlockStyle.js
new file mode 100644 (file)
index 0000000..48c797a
--- /dev/null
@@ -0,0 +1,326 @@
+/**
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+/**
+ * Block Style Plugin for TYPO3 htmlArea RTE
+ */
+define('TYPO3/CMS/Rtehtmlarea/Plugins/BlockStyle',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/DOM',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/CSS/Parser'],
+       function (Plugin, Dom, Event, Parser) {
+
+       var BlockStyle = Ext.extend(Plugin, {
+
+               /**
+                * This function gets called by the class constructor
+                */
+               configurePlugin: function (editor) {
+                       this.cssArray = {};
+                       this.classesUrl = this.editorConfiguration.classesUrl;
+                       this.pageTSconfiguration = this.editorConfiguration.buttons.blockstyle;
+                       this.tags = (this.pageTSconfiguration && this.pageTSconfiguration.tags) ? this.pageTSconfiguration.tags : {};
+                       var allowedClasses;
+                       for (var tagName in this.tags) {
+                               if (this.tags.hasOwnProperty(tagName)) {
+                                       if (this.tags[tagName].allowedClasses) {
+                                               allowedClasses = this.tags[tagName].allowedClasses.trim().split(",");
+                                               for (var cssClass in allowedClasses) {
+                                                       if (allowedClasses.hasOwnProperty(cssClass)) {
+                                                               allowedClasses[cssClass] = allowedClasses[cssClass].trim().replace(/\*/g, ".*");
+                                                       }
+                                               }
+                                               this.tags[tagName].allowedClasses = new RegExp( "^(" + allowedClasses.join("|") + ")$", "i");
+                                       }
+                               }
+                       }
+                       this.showTagFreeClasses = this.pageTSconfiguration ? this.pageTSconfiguration.showTagFreeClasses : false;
+                       this.prefixLabelWithClassName = this.pageTSconfiguration ? this.pageTSconfiguration.prefixLabelWithClassName : false;
+                       this.postfixLabelWithClassName = this.pageTSconfiguration ? this.pageTSconfiguration.postfixLabelWithClassName : false;
+                       /**
+                        * Registering plugin "About" information
+                        */
+                       var pluginInformation = {
+                               version         : '3.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 drop-down list
+                        */
+                       var dropDownId = 'BlockStyle';
+                       var fieldLabel = this.pageTSconfiguration ? this.pageTSconfiguration.fieldLabel : '';
+                       if ((typeof fieldLabel !== 'string' || !fieldLabel.length) && this.isButtonInToolbar('I[Block style label]')) {
+                               fieldLabel = this.localize('Block style label');
+                       }
+                       var dropDownConfiguration = {
+                               id: dropDownId,
+                               tooltip: this.localize(dropDownId + '-Tooltip'),
+                               fieldLabel: fieldLabel,
+                               options: [[this.localize('No style'), 'none']],
+                               action: 'onChange',
+                               storeFields: [ { name: 'text'}, { name: 'value'}, { name: 'style'} ],
+                               tpl: '<tpl for="."><div ext:qtip="{value}" style="{style}text-align:left;font-size:11px;" class="x-combo-list-item">{text}</div></tpl>'
+                       };
+                       if (this.pageTSconfiguration) {
+                               if (this.pageTSconfiguration.width) {
+                                       dropDownConfiguration.width = parseInt(this.pageTSconfiguration.width, 10);
+                               }
+                               if (this.pageTSconfiguration.listWidth) {
+                                       dropDownConfiguration.listWidth = parseInt(this.pageTSconfiguration.listWidth, 10);
+                               }
+                               if (this.pageTSconfiguration.maxHeight) {
+                                       dropDownConfiguration.maxHeight = parseInt(this.pageTSconfiguration.maxHeight, 10);
+                               }
+                       }
+                       this.registerDropDown(dropDownConfiguration);
+                       return true;
+               },
+
+               /**
+                * This handler gets called when some block style was selected in the drop-down list
+                */
+               onChange: function (editor, combo, record, index) {
+                       var className = combo.getValue();
+                       this.editor.focus();
+                       var blocks = this.editor.getSelection().getElements();
+                       for (var k = 0; k < blocks.length; ++k) {
+                               var parent = blocks[k];
+                               while (parent && !Dom.isBlockElement(parent) && !/^(img)$/i.test(parent.nodeName)) {
+                                       parent = parent.parentNode;
+                               }
+                               if (!k) {
+                                       var tagName = parent.tagName.toLowerCase();
+                               }
+                               if (parent.tagName.toLowerCase() == tagName) {
+                                       this.applyClassChange(parent, className);
+                               }
+                       }
+               },
+
+               /**
+                * This function applies the class change to the node
+                */
+               applyClassChange: function (node, className) {
+                       if (className == "none") {
+                               var classNames = node.className.trim().split(" ");
+                               for (var i = classNames.length; --i >= 0;) {
+                                       if (!HTMLArea.reservedClassNames.test(classNames[i])) {
+                                               Dom.removeClass(node, classNames[i]);
+                                               if (node.nodeName.toLowerCase() === "table" && this.getPluginInstance('TableOperations')) {
+                                                       this.getPluginInstance('TableOperations').removeAlternatingClasses(node, classNames[i]);
+                                                       this.getPluginInstance('TableOperations').removeCountingClasses(node, classNames[i]);
+                                               }
+                                               break;
+                                       }
+                               }
+                       } else {
+                               var nodeName = node.nodeName.toLowerCase();
+                               if (this.tags && this.tags[nodeName] && this.tags[nodeName].allowedClasses) {
+                                       if (this.tags[nodeName].allowedClasses.test(className)) {
+                                               Dom.addClass(node, className);
+                                       }
+                               } else if (this.tags && this.tags.all && this.tags.all.allowedClasses) {
+                                       if (this.tags.all.allowedClasses.test(className)) {
+                                               Dom.addClass(node, className);
+                                       }
+                               } else {
+                                       Dom.addClass(node, className);
+                               }
+                               if (nodeName === "table" && this.getPluginInstance('TableOperations')) {
+                                       this.getPluginInstance('TableOperations').reStyleTable(node);
+                               }
+                       }
+               },
+
+               /**
+                * This handler gets called when the editor is generated
+                */
+               onGenerate: function () {
+                       var self = this;
+                       // Monitor editor changing mode
+                       Event.on(this.editor, 'HTMLAreaEventModeChange', function (event, mode) { Event.stopEvent(event); self.onModeChange(mode); return false; });
+                       // Create CSS Parser object
+                       this.blockStyles = new Parser({
+                               prefixLabelWithClassName: this.prefixLabelWithClassName,
+                               postfixLabelWithClassName: this.postfixLabelWithClassName,
+                               showTagFreeClasses: this.showTagFreeClasses,
+                               tags: this.tags,
+                               editor: this.editor
+                       });
+                       // Disable the combo while initialization completes
+                       var dropDown = this.getButton('BlockStyle');
+                       if (dropDown) {
+                               dropDown.setDisabled(true);
+                       }
+                       // Monitor css parsing being completed
+                       Event.one(this.blockStyles, 'HTMLAreaEventCssParsingComplete', function (event) { Event.stopEvent(event); self.onCssParsingComplete(); return false; }); 
+                       this.blockStyles.parse();
+               },
+
+               /**
+                * This handler gets called when parsing of css classes is completed
+                */
+               onCssParsingComplete: function () {
+                       if (this.blockStyles.isReady()) {
+                               this.cssArray = this.blockStyles.getClasses();
+                               if (this.getEditorMode() === 'wysiwyg' && this.editor.isEditable()) {
+                                       this.updateValue('BlockStyle');
+                               }
+                       }
+               },
+
+               /**
+                * This handler gets called when the toolbar is being updated
+                */
+               onUpdateToolbar: function (button, mode, selectionEmpty, ancestors) {
+                       if (mode === 'wysiwyg' && this.editor.isEditable() && this.blockStyles.isReady()) {
+                               this.updateValue(button.itemId);
+                       }
+               },
+
+               /**
+                * This handler gets called when the editor has changed its mode to "wysiwyg"
+                */
+               onModeChange: function(mode) {
+                       if (mode === 'wysiwyg' && this.editor.isEditable()) {
+                               this.updateValue('BlockStyle');
+                       }
+               },
+
+               /**
+                * This function updates the current value of the dropdown list
+                */
+               updateValue: function(dropDownId) {
+                       var dropDown = this.getButton(dropDownId);
+                       if (dropDown) {
+                               var classNames = new Array();
+                               var nodeName = '';
+                               var statusBarSelection = this.editor.statusBar ? this.editor.statusBar.getSelection() : null;
+                               var parent = statusBarSelection ? statusBarSelection : this.editor.getSelection().getParentElement();
+                               while (parent && !Dom.isBlockElement(parent) && !/^(img)$/i.test(parent.nodeName)) {
+                                       parent = parent.parentNode;
+                               }
+                               if (parent) {
+                                       nodeName = parent.nodeName.toLowerCase();
+                                       classNames = Dom.getClassNames(parent);
+                               }
+                               if (nodeName && nodeName !== 'body'){
+                                       this.buildDropDownOptions(dropDown, nodeName);
+                                       this.setSelectedOption(dropDown, classNames);
+                               } else {
+                                       this.initializeDropDown(dropDown);
+                                       dropDown.setDisabled(true);
+                               }
+                       }
+               },
+
+               /**
+                * This function reinitializes the options of the dropdown
+                */
+               initializeDropDown: function (dropDown) {
+                       var store = dropDown.getStore();
+                       store.removeAll(false);
+                       store.insert(0, new store.recordType({
+                               text: this.localize('No style'),
+                               value: 'none'
+                       }));
+                       dropDown.setValue('none');
+               },
+
+               /**
+                * This function builds the options to be displayed in the dropDown box
+                */
+               buildDropDownOptions: function (dropDown, nodeName) {
+                       var store = dropDown.getStore();
+                       this.initializeDropDown(dropDown);
+                       if (this.blockStyles.isReady()) {
+                               var allowedClasses = {};
+                               if (typeof this.cssArray[nodeName] !== 'undefined') {
+                                       allowedClasses = this.cssArray[nodeName];
+                               } else if (this.showTagFreeClasses && typeof this.cssArray['all'] !== 'undefined') {
+                                       allowedClasses = this.cssArray['all'];
+                               }
+                               for (var cssClass in allowedClasses) {
+                                       if (typeof HTMLArea.classesSelectable[cssClass] === 'undefined' || HTMLArea.classesSelectable[cssClass]) {
+                                               var style = null;
+                                               if (!this.pageTSconfiguration || !this.pageTSconfiguration.disableStyleOnOptionLabel) {
+                                                       if (HTMLArea.classesValues[cssClass] && !HTMLArea.classesNoShow[cssClass]) {
+                                                               style = HTMLArea.classesValues[cssClass];
+                                                       } else if (/-[0-9]+$/.test(cssClass) && HTMLArea.classesValues[RegExp.leftContext + '-'])  {
+                                                               style = HTMLArea.classesValues[RegExp.leftContext + '-'];
+                                                       }
+                                               }
+                                               store.add(new store.recordType({
+                                                       text: allowedClasses[cssClass],
+                                                       value: cssClass,
+                                                       style: style
+                                               }));
+                                       }
+                               }
+                       }
+               },
+
+               /**
+                * This function sets the selected option of the dropDown box
+                */
+               setSelectedOption: function (dropDown, classNames, noUnknown, defaultClass) {
+                       var store = dropDown.getStore();
+                       dropDown.setValue('none');
+                       if (classNames.length) {
+                               var index = store.findExact('value', classNames[classNames.length-1]);
+                               if (index !== -1) {
+                                       dropDown.setValue(classNames[classNames.length-1]);
+                                       if (!defaultClass) {
+                                               store.getAt(0).set('text', this.localize('Remove style'));
+                                       }
+                               }
+                               if (index === -1 && !noUnknown) {
+                                       var text = this.localize('Unknown style');
+                                       var value = classNames[classNames.length-1];
+                                       if (typeof HTMLArea.classesSelectable[value] !== 'undefined' && !HTMLArea.classesSelectable[value] && typeof HTMLArea.classesLabels[value] !== 'undefined') {
+                                               text = HTMLArea.classesLabels[value];
+                                       }
+                                       store.add(new store.recordType({
+                                               text: text,
+                                               value: value,
+                                               style: (!(this.pageTSconfiguration && this.pageTSconfiguration.disableStyleOnOptionLabel) && HTMLArea.classesValues && HTMLArea.classesValues[value] && !HTMLArea.classesNoShow[value]) ? HTMLArea.classesValues[value] : null
+                                       }));
+                                       dropDown.setValue(value);
+                                       if (!defaultClass) {
+                                               store.getAt(0).set('text', this.localize('Remove style'));
+                                       }
+                               }
+                               // Remove already assigned classes from the dropDown box
+                               var classNamesString = ',' + classNames.join(',') + ',';
+                               var selectedValue = dropDown.getValue(), optionValue;
+                               store.each(function (option) {
+                                       optionValue = option.get('value');
+                                       if (classNamesString.indexOf(',' + optionValue + ',') !== -1 && optionValue !== selectedValue) {
+                                               store.removeAt(store.indexOf(option));
+                                       }
+                                       return true;
+                               });
+                       }
+                       dropDown.setDisabled(!store.getCount() || (store.getCount() == 1 && dropDown.getValue() == 'none'));
+               }
+       });
+
+       return BlockStyle;
+
+});
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/CharacterMap.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/CharacterMap.js
new file mode 100644 (file)
index 0000000..d33a7ca
--- /dev/null
@@ -0,0 +1,507 @@
+/**
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+/**
+ * Character Map Plugin for TYPO3 htmlArea RTE
+ */
+define('TYPO3/CMS/Rtehtmlarea/Plugins/CharacterMap',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event'],
+       function (Plugin, UserAgent, Event) {
+
+       var CharacterMap = Ext.extend(Plugin, {
+
+               /**
+                * This function gets called by the class constructor
+                */
+               configurePlugin: function(editor) {
+                       /**
+                        * Registering plugin "About" information
+                        */
+                       var pluginInformation = {
+                               version         : '4.0',
+                               developer       : 'Holger Hees, Bernhard Pfeifer, Stanislas Rolland',
+                               developerUrl    : 'http://www.sjbr.ca/',
+                               copyrightOwner  : 'Holger Hees, Bernhard Pfeifer, Stanislas Rolland',
+                               sponsor         : 'System Concept GmbH, Bernhard Pfeifer, SJBR, BLE',
+                               sponsorUrl      : 'http://www.sjbr.ca/',
+                               license         : 'GPL'
+                       };
+                       this.registerPluginInformation(pluginInformation);
+
+                       /**
+                        * Registering the buttons
+                        */
+                       for (var i = 0, n = this.buttons.length; i < n; ++i) {
+                               var button = this.buttons[i];
+                               buttonId = button[0];
+                               var buttonConfiguration = {
+                                       id: buttonId,
+                                       tooltip: this.localize(buttonId + '-Tooltip'),
+                                       action: 'onButtonPress',
+                                       context: button[1],
+                                       dialog: false,
+                                       iconCls: 'htmlarea-action-' + button[2]
+                               };
+                               this.registerButton(buttonConfiguration);
+                       }
+
+                &nb