[!!!][TASK] Remove ExtJS from RTE 45/48245/37
authorAndreas Fernandez <a.fernandez@scripting-base.de>
Mon, 23 May 2016 16:07:55 +0000 (18:07 +0200)
committerBenni Mack <benni@typo3.org>
Thu, 21 Jul 2016 08:44:00 +0000 (10:44 +0200)
The RTE user interface has been rewritten to use jQuery and Bootstrap,
ExtJS is not used anymore. Due to internal changes, existing plugins
most likely need migration as well.
The option `RTEsetup.properties.colors` has been removed in favor
of using the jQuery color picker already used in the TYPO3 core.

Resolves: #77137
Releases: master
Change-Id: Ibd84d030614f7bd538d79f2e95ff062d896221f9
Reviewed-on: https://review.typo3.org/48245
Tested-by: Bamboo TYPO3com <info@typo3.com>
Tested-by: Jigal van Hemert <jigal.van.hemert@typo3.org>
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: Benni Mack <benni@typo3.org>
39 files changed:
typo3/sysext/backend/Classes/Utility/BackendUtility.php
typo3/sysext/core/Documentation/Changelog/master/Breaking-77137-JavaScriptAPIOfRTEChanged.rst [new file with mode: 0644]
typo3/sysext/core/Documentation/Changelog/master/Breaking-77137-RTEOptionColorsRemoved.rst [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Classes/Extension/Typo3Color.php
typo3/sysext/rtehtmlarea/Classes/Form/Element/RichTextElement.php
typo3/sysext/rtehtmlarea/Documentation/Configuration/PageTsconfig/Index.rst
typo3/sysext/rtehtmlarea/Documentation/Configuration/PageTsconfig/colors/Index.rst [deleted file]
typo3/sysext/rtehtmlarea/Documentation/Configuration/PageTsconfig/interfaceConfiguration/Index.rst
typo3/sysext/rtehtmlarea/Resources/Public/Css/Skin/htmlarea.css
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Components/Select.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Editor/Editor.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ColorPalette.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/ColorMenu.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/ColorPaletteField.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/HTMLArea.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/NameSpace/NameSpace.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Plugin/Plugin.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Toolbar/Select.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/Abbreviation.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/AboutEditor.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/BlockStyle.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/CharacterMap.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/ContextMenu.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/DefaultImage.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/DefaultLink.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/EditElement.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/FindReplace.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/InsertSmiley.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/MicrodataSchema.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/PlainText.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/QuickTag.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/RemoveFormat.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/SpellChecker.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/TYPO3Color.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/TYPO3Image.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/TYPO3Link.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/TableOperations.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/UserElements.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/SelectImage.js

index 41e5a02..95d66d6 100755 (executable)
@@ -3473,20 +3473,20 @@ class BackendUtility
                 switch ($set) {
                     case 'updatePageTree':
                         $signals[] = '
-                                                               if (top && top.TYPO3.Backend.NavigationContainer.PageTree) {
+                                                               if (top && top.TYPO3.Backend && top.TYPO3.Backend.NavigationContainer.PageTree) {
                                                                        top.TYPO3.Backend.NavigationContainer.PageTree.refreshTree();
                                                                }
                                                        ';
                         break;
                     case 'updateFolderTree':
                         $signals[] = '
-                                                               if (top && top.TYPO3.Backend.NavigationIframe) {
+                                                               if (top && top.TYPO3.Backend && top.TYPO3.Backend.NavigationIframe) {
                                                                        top.TYPO3.Backend.NavigationIframe.refresh();
                                                                }';
                         break;
                     case 'updateModuleMenu':
                         $signals[] = '
-                                                               if (top && top.TYPO3.ModuleMenu.App) {
+                                                               if (top && top.TYPO3.ModuleMenu && top.TYPO3.ModuleMenu.App) {
                                                                        top.TYPO3.ModuleMenu.App.refreshMenu();
                                                                }';
                 }
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-77137-JavaScriptAPIOfRTEChanged.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-77137-JavaScriptAPIOfRTEChanged.rst
new file mode 100644 (file)
index 0000000..6e3280e
--- /dev/null
@@ -0,0 +1,47 @@
+================================================
+Breaking: #77137 - JavaScript API of RTE changed
+================================================
+
+Description
+===========
+
+Due to the migration of the RTE from ExtJS to Bootstrap and jQuery, some API methods have been changed or removed.
+
+
+Impact
+======
+
+ExtJS-based plugins will throw JavaScript errors.
+
+The following methods have been removed:
+* onContainerResize
+* getWindowDimensions
+* setTabPanelHeight
+* syncHeight
+
+The following methods have been changed:
+* openContainerWindow
+* buildButtonConfig
+
+
+Affected Installations
+======================
+
+All installations using custom RTE plugins are affected.
+
+
+Migration
+=========
+
+The former ``Ext.Window`` objects are replaced by Bootstrap modals.
+
+See the list below for a migration of the changed methods:
+
+openContainerWindow
+   The third parameter ``dimensions`` which was an array has been changed to ``height``, containing an integer
+
+buildButtonConfig
+   The method takes now two additional arguments: ``active`` and ``severity``. The parameter ``active`` is a boolean
+   value and declares the button being either active or not. The parameter ``severity`` is an integer representing the
+   severity of the button. This should always represent the severity of the modal, use one of the severities defined in
+   :js:`TYPO3/CMS/Backend/Severity`.
\ No newline at end of file
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-77137-RTEOptionColorsRemoved.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-77137-RTEOptionColorsRemoved.rst
new file mode 100644 (file)
index 0000000..3bf712a
--- /dev/null
@@ -0,0 +1,26 @@
+==============================================
+Breaking: #77137 - RTE option "colors" removed
+==============================================
+
+Description
+===========
+
+The RTE configuration option ``RTEsetup.properties.colors`` has been removed.
+
+
+Impact
+======
+
+The color picker is streamlined with all occurrences in the backend, it shows all available colors.
+
+
+Affected Installations
+======================
+
+All installations configuring ``RTEsetup.properties.colors`` are affected.
+
+
+Migration
+=========
+
+There is no migration available, the obsolete configuration can be removed.
\ No newline at end of file
index 7cb5f08..94098f6 100644 (file)
@@ -14,7 +14,6 @@ namespace TYPO3\CMS\Rtehtmlarea\Extension;
  * The TYPO3 project - inspiring people to share!
  */
 
-use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Rtehtmlarea\RteHtmlAreaApi;
 
 /**
@@ -67,26 +66,7 @@ class Typo3Color extends RteHtmlAreaApi
     {
         $jsArray = array();
         $jsArray[] = 'RTEarea[editornumber].disableColorPicker = ' . (trim($this->configuration['thisConfig']['disableColorPicker']) ? 'true' : 'false') . ';';
-        // Building the array of configured colors
-        $HTMLAreaColorName = array();
-        if (is_array($this->configuration['RTEsetup']['properties']['colors.'])) {
-            foreach ($this->configuration['RTEsetup']['properties']['colors.'] as $colorName => $conf) {
-                $colorName = substr($colorName, 0, -1);
-                $colorLabel = $this->getPageConfigLabel($conf['name']);
-                $HTMLAreaColorName[$colorName] = array($colorLabel, strtoupper(substr($conf['value'], 1, 6)));
-            }
-        }
-        // Setting the list of colors if specified in the RTE config
-        if ($this->configuration['thisConfig']['colors']) {
-            $HTMLAreaColors = GeneralUtility::trimExplode(',', $this->cleanList($this->configuration['thisConfig']['colors']));
-            $HTMLAreaJSColors = array();
-            foreach ($HTMLAreaColors as $colorName) {
-                if ($HTMLAreaColorName[$colorName]) {
-                    $HTMLAreaJSColors[] = $HTMLAreaColorName[$colorName];
-                }
-            }
-            $jsArray[] = 'RTEarea[editornumber].colors = ' . json_encode($HTMLAreaJSColors) . ';';
-        }
+
         return implode(LF, $jsArray);
     }
 }
index 047e76e..2e40d6e 100644 (file)
@@ -608,7 +608,7 @@ class RichTextElement extends AbstractFormElement
         // Editing area style sheet
         $editedContentCSS = GeneralUtility::createVersionNumberedFilename($skinDirectory . '/htmlarea-edited-content.css');
 
-        return 'require(["TYPO3/CMS/Rtehtmlarea/HTMLArea/HTMLArea"], function (HTMLArea) {
+        return 'require(["TYPO3/CMS/Rtehtmlarea/HTMLArea/HTMLArea", "jquery"], function (HTMLArea, $) {
                        if (typeof RTEarea === "undefined") {
                                RTEarea = new Object();
                                RTEarea[0] = new Object();
@@ -617,13 +617,7 @@ class RichTextElement extends AbstractFormElement
                                RTEarea[0].editorSkin = "' . $skinDirectory . '/";
                                RTEarea[0].editedContentCSS = "' . $editedContentCSS . '";
                                RTEarea.init = function() {
-                                       if (typeof HTMLArea === "undefined" || !Ext.isReady) {
-                                               window.setTimeout(function () {
-                                                       RTEarea.init();
-                                               }, 10);
-                                       } else {
-                                               HTMLArea.init();
-                                       }
+                                   HTMLArea.init();
                                };
                                RTEarea.initEditor = function(editorNumber) {
                                        if (typeof HTMLArea === "undefined" || !HTMLArea.isReady) {
index 34e5b1e..78e7656 100644 (file)
@@ -29,7 +29,6 @@ this is most often done on the root page of your site.
 
    classes/Index
    mutuallyExclusiveClasses/Index
-   colors/Index
    fonts/Index
    fontSizes/Index
    interfaceConfiguration/Index
diff --git a/typo3/sysext/rtehtmlarea/Documentation/Configuration/PageTsconfig/colors/Index.rst b/typo3/sysext/rtehtmlarea/Documentation/Configuration/PageTsconfig/colors/Index.rst
deleted file mode 100644 (file)
index 48a9c7d..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-.. ==================================================
-.. FOR YOUR INFORMATION
-.. --------------------------------------------------
-.. -*- coding: utf-8 -*- with BOM.
-
-.. include:: ../../../Includes.txt
-
-
-.. _colors-configuration:
-
-colors:
-"""""""
-
-Properties of each color available in the RTE.
-
-
-.. _colors-id-string:
-
-colors.[ *id-string* ]
-~~~~~~~~~~~~~~~~~~~~~~
-
-.. container:: table-row
-
-   Property
-         colors.[ *id-string* ]
-
-   Description
-         Defines the colors available in the RTE.
-
-         Properties:
-
-         ::
-
-            .name = Label of the color in menu
-            .value = The HTML-color value
-
-         Example:
-
-         ::
-
-            # General configuration of the available colors:
-            RTE.colors {
-              color1 {
-                name = Background color
-                value = blue
-              }
-              color2 {
-                name = Another color I like!
-                value = #775533
-              }
-              noColor {
-                name = No color
-                value =
-              }
-            }
-            # Specific setting for the font color selector:
-            RTE.default.colors = color1, color2, noColor
-
-
-[page:RTE]
-
index 630469a..217014c 100644 (file)
@@ -1978,32 +1978,6 @@ buttons.acronym.lockBeUserToDBmounts
 
 
 
-.. _colors:
-
-colors
-~~~~~~
-
-.. container:: table-row
-
-   Property
-         colors
-
-   Data type
-         list of id-strings
-
-   Description
-         Defines the specific colors generally available in the color
-         selectors. The id-strings must be configured in the RTE.colors array
-         (see description earlier).
-
-         **Example:** ::
-
-            RTE.default {
-              colors = color1, color2,noColor
-            }
-
-
-
 .. _disablecolorpicker:
 
 disableColorPicker
index fced7a3..3ae4c6d 100644 (file)
@@ -46,7 +46,6 @@
        padding: 0;
 }
 .htmlarea .btn-toolbar .btn-sm {
-       /*background-color: transparent;*/
        height: 24px;
        margin: 1px 0;
        padding: 2px 0 0 2px;
        float: right;
        padding-right: 8px;
 }
-/* Selectors for dialogue windows */
-.htmlarea-window .x-panel-icon {
-       background-image: url("../../Images/Sprites/actions.png");
-       background-repeat: no-repeat;
-       margin-top: 2px;
-       height: 22px;
-}
-.htmlarea-window .x-window-header-text {
-       display: inline-block;
-       margin-top: -4px !important;
-       vertical-align: top;
-}
-.htmlarea-window .x-window-header .t3-help-link {
-       color: white;
-}
-.htmlarea-window ul.x-tab-strip {
-       list-style: none;
-}
-.htmlarea-window .x-tab-panel-body {
-       background-color: #EFEFF4;
-       padding-bottom: 10px;
-       padding-left: 3px;
-}
-.htmlarea-window .x-tab-panel-body h1 {
-       padding-left: 3px;
-}
-.htmlarea-window .x-fieldset {
-       margin: 3px;
-}
-.htmlarea-window .x-fieldset legend {
-       border: none;
-       font-size: 12px;
-       margin: 0;
-       padding: 0 2px;
-       text-align: left;
-}
-/* IE legend positioning bug */
-.ext-ie .htmlarea-window .x-fieldset legend,
-.ext-ie .htmlarea-window .x-fieldset legend span {
-       display: block;
-       float: left;
-}
-.ext-ie .htmlarea-window .x-fieldset-bwrap {
-       clear: left;
-}
 /* Selector htmlarea-window-table is used in the element browsers */
 #typo3-curUrl {
        margin-left: 10px;
        padding-bottom: 3px;
        padding-top: 3px;
 }
-.htmlarea-window .x-form-item-label {
-       font-size: 10px;
-       text-align: right;
-       margin: 0;
-       padding: 3px 5px 0 5px;
-}
-.htmlarea-window .x-btn-text {
-       font-weight: normal;
-       color: black;
-}
-.htmlarea-window .x-form-check-wrap {
-       padding-top: 3px;
-}
 .htmlarea-window iframe {
        background-color: white;
        color: black;
 .htmlarea-window iframe.content-iframe {
        background-color: #E4E0DB;
 }
-.htmlarea-window .show-color {
-       margin-top: 10px;
-       border-style: solid;
-       border-width: 1px;
-       border-color: white;
-}
-.htmlarea-custom-colors a {
-       border-color: transparent;
-       float: none;
-       display: block;
-       width: 100%;
-}
-.htmlarea-custom-colors {
-       height: auto;
-}
-.htmlarea-custom-colors em {
-       display: block;
-       border: 1px solid;
-       border-color: #C5C5D5;
-       width: 100%;
-}
-.htmlarea-custom-colors em span {
-       width: 100%;
-}
 /* Selectors for the About dialogue */
 .htmlarea-window .about h1 {
        font-size: 14px !important;
        font-size: 12px;
        font-style: normal;
 }
-.htmlarea-window .about-plugins .x-list-body dt {
-       overflow: auto;
-       white-space: normal;
-}
 .htmlarea-window .about a,
 .htmlarea-window .about-plugins a {
        font-weight: bold;
 /* Selectors for the default image dialogue */
 .htmlarea-window .image-preview {
     height: 300px;
-    width: 99%;
-    float: right;
-}
-/* Selectors for the InsertSmiley plugin */
-.htmlarea-window .emoticon-array {
-       padding: 10px;
-}
-.htmlarea-window .emoticon {
-       display: block;
-       float: left;
-       padding: 0 5px 10px 5px;
-       width: 20px;
-       height: 28px;
-       vertical-align: middle;
+    width: 100%;
 }
 /* Selectors for the CharacterMap dialogue */
-.htmlarea-window .character-map .character {
-       display: block;
-       float: left;
-       height: 26px;
-       width: 20px;
-       font-size: 14px;
-       font-weight: bold;
-       text-align: center;
-       padding-bottom: 3px;
-       border: 1px solid transparent;
+.htmlarea-character-map .character {
+       display: inline-block;
+       width: 30px;
+       height: 30px;
 }
 .htmlarea-window .character-map .character:hover {
        background-color: white;
        width: 175px;
        margin: 3px;
 }
-.htmlarea-window .spell-check .controls .x-btn {
-       margin-top: 3px;
-       margin-left: 8px;
-}
 .htmlarea-window .spell-check .contentframe {
        float: right;
        height: 450px;
        padding-top: 1px;
        border-color: #A2AAB8;
 }
-.htmlarea-context-menu .x-menu-scroller {
+.htmlarea-context-menu .ctx-menu-direction {
        background: #F5F5F5;
        height: 20px;
        line-height: 20px;
        position: relative;
+       border-color: #D7D7D7;
+       border-style: solid;
+       outline: none;
 }
-.htmlarea-context-menu .x-menu-scroller.x-menu-scroller-top {
-       border-bottom: 1px solid #D7D7D7;
+.htmlarea-context-menu .ctx-menu-direction.ctx-menu-direction-top {
+       border-width: 1px 1px 0 1px;
 }
-.htmlarea-context-menu .x-menu-scroller.x-menu-scroller-bottom {
-       border-top: 1px solid #D7D7D7;
+.htmlarea-context-menu .ctx-menu-direction.ctx-menu-direction-bottom {
+       border-width: 0 1px 1px 1px;
 }
-.htmlarea-context-menu .x-menu-scroller.x-menu-scroller-top:after,
-.htmlarea-context-menu .x-menu-scroller.x-menu-scroller-bottom:after {
+.htmlarea-context-menu .ctx-menu-direction.ctx-menu-direction-top:after,
+.htmlarea-context-menu .ctx-menu-direction.ctx-menu-direction-bottom:after {
        font-family: 'FontAwesome';
        font-size: 12px;
-       display: block;
-       position: absolute;
-       margin-left: 50%;
        top: 0;
 }
-.htmlarea-context-menu .x-menu-scroller.x-menu-scroller-top:after {
+.htmlarea-context-menu .ctx-menu-direction.ctx-menu-direction-top:after {
        content: "\f0d8";
 }
-.htmlarea-context-menu .x-menu-scroller.x-menu-scroller-bottom:after {
+.htmlarea-context-menu .ctx-menu-direction.ctx-menu-direction-bottom:after {
        content: "\f0d7";
 }
-.htmlarea-context-menu .x-menu-item-active,
-.htmlarea-context-menu .x-menu-item.hover {
-       background-color: #D7D7D7;
-}
-.htmlarea-context-menu .x-menu-item-icon {
+.htmlarea-context-menu .ctx-menu-item-icon {
        background-image: url("../../Images/Sprites/actions.png");
        background-repeat: no-repeat;
        margin-top: -2px;
        width: 18px;
        height: 18px;
 }
-/* Window status bar selectors */
-.htmlarea-window .status-ready {
-       padding-left: 21px !important;
-       background-repeat: no-repeat;
-       background-image: url("../../Images/dialog-ok.png");
-       background-position: 0px 2px;
-}
-.htmlarea-window .status-info {
-       padding-left: 21px !important;
-       background-repeat: no-repeat;
-       background-image: url("../../Images/dialog-information.png");
-       background-position: 0px 2px;
-}
-.htmlarea-window .status-wait {
-       padding-left: 45px !important;
-       background-repeat: no-repeat;
-       background-image:url("../../Images/loading-balls.gif");
-       background-position: 0px 6px;
-}
-.htmlarea-window .x-toolbar .x-btn {
-       margin-left: 5px;
-}
-.htmlarea-window .x-toolbar .xtb-text {
-       cursor: default;
-}
-/* Button background positioning in window status bar*/
-.htmlarea-window .x-toolbar .x-btn-tl{
-       background-position: 0 0;
-}
-.htmlarea-window .x-toolbar .x-btn-tr{
-       background-position: -3px 0;
-}
-.htmlarea-window .x-toolbar .x-btn-tc{
-       background-position: 0 -6px;
-}
-.htmlarea-window .x-toolbar .x-btn-ml{
-       background-position: 0 -24px;
-}
-.htmlarea-window .x-toolbar .x-btn-mr{
-       background-position: -3px -24px;
-}
-.htmlarea-window .x-toolbar .x-btn-mc{
-       background-position: 0 -1096px;
-}
-.htmlarea-window .x-toolbar .x-btn-bl{
-       background-position: 0 -3px;
-}
-.htmlarea-window .x-toolbar .x-btn-br{
-       background-position: -3px -3px;
-}
-.htmlarea-window .x-toolbar .x-btn-bc{
-       background-position: 0 -15px;
-}
 /* Action icon selectors for toolbar, context menu and window headers */
 .htmlarea-action-abbreviation-edit { background-position: 0 0 !important; }
 .htmlarea-action-bidi-override { background-position: 0 -58px !important; }
 .htmlarea-action-unlink { background-position: 0 -4698px !important; }
 .htmlarea-action-unordered-list { background-position: 0 -4756px !important; }
 .htmlarea-action-user-element-edit { background-position: 0 -4814px !important; }
-.htmlarea-action-variable { background-position: 0 -4872px !important; }
-
-/* ExtJs combo boxes */
-.x-combo-list {
-       border-color:#bcbcbc;
-       background-color:#eaeaea;
-}
-.x-combo-list-inner {
-       background-color:#fff;
-}
-.x-combo-list-hd {
-       color:#55545E;
-       background-image: url(../../Images/panel-title-light-bg.gif);
-       border-bottom-color:#bcbcbc;
-}
-.x-combo-list-item {
-       border-color:#fff;
-}
-.x-combo-list .x-combo-selected {
-       border-color:#b9b9b9 !important;
-       background-color:#e7e7e7;
-}
-.x-combo-list .x-toolbar {
-       border-top-color:#bcbcbc;
-}
+.htmlarea-action-variable { background-position: 0 -4872px !important; }
\ No newline at end of file
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Components/Select.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Components/Select.js
new file mode 100644 (file)
index 0000000..e390e9c
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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!
+ */
+
+/**
+ * Module: TYPO3/CMS/Rtehtmlarea/HTMLArea/Components/Select
+ * A select field used in dialog windows
+ */
+define([
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/DOM',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Toolbar/Select'
+], function (Util, Dom, ToolbarSelect) {
+
+       /**
+        * Select constructor
+        *
+        * @param {Object} config
+        * @constructor
+        * @exports TYPO3/CMS/Rtehtmlarea/HTMLArea/Components/Select
+        */
+       var Select = function (config) {
+               this.constructor.super.call(this, config);
+
+               this.selectElement = document.createElement('select');
+       };
+       Util.inherit(Select, ToolbarSelect);
+       Util.apply(Select.prototype, {
+               /**
+                * Render the select item (called by the toolbar)
+                *
+                * @param {Object} container The container of the select (the toolbar object)
+                */
+               render: function (container) {
+                       this.el = document.createElement('div');
+                       Dom.addClass(this.el, 'form-group');
+                       this.selectElement = document.createElement('select');
+                       Dom.addClass(this.selectElement, 'form-control');
+                       if (this.id) {
+                               this.selectElement.setAttribute('id', this.id);
+                       }
+                       if (typeof this.cls === 'string') {
+                               Dom.addClass(this.selectElement, this.cls);
+                       }
+                       if (typeof this.tooltip === 'string') {
+                               this.selectElement.setAttribute('title', this.tooltip);
+                       }
+                       if (this.options) {
+                               for (var i = 0, n = this.options.length; i < n; i++) {
+                                       this.addOption(this.options[i][0], this.options[i][1], this.options[i][1], this.options[i][2]);
+                               }
+                       }
+                       var fieldWrapper = document.createElement('div');
+                       Dom.addClass(fieldWrapper, 'col-sm-10');
+                       this.selectElement = fieldWrapper.appendChild(this.selectElement);
+                       fieldWrapper = this.el.appendChild(fieldWrapper);
+                       this.el = container.appendChild(this.el);
+                       if (this.fieldLabel) {
+                               var label = document.createElement('label');
+                               label.innerHTML = this.fieldLabel;
+                               Dom.addClass(label, 'col-sm-2');
+                               label.setAttribute('for', this.selectElement.id);
+                               this.el.insertBefore(label, fieldWrapper);
+                       } else if (typeof this.tooltip === 'string') {
+                               this.selectElement.setAttribute('aria-label', this.tooltip);
+                       }
+               }
+       });
+
+       return Select;
+});
index be68561..972a6c7 100644 (file)
@@ -242,7 +242,11 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                this.statusBar = this.htmlArea.getStatusBar();
                // Get triggered when the framework becomes ready
                var self = this;
-               Event.one(this.htmlArea, 'HTMLAreaEventFrameworkReady', function (event) { Event.stopEvent(event); self.onFrameworkReady(); return false; });
+               Event.one(this.htmlArea, 'HTMLAreaEventFrameworkReady', function (event) {
+                       Event.stopEvent(event);
+                       self.onFrameworkReady();
+                       return false;
+               });
        };
 
        /**
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ColorPalette.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ColorPalette.js
deleted file mode 100644 (file)
index 96654bd..0000000
+++ /dev/null
@@ -1,34 +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!
- */
-
-/**
- * Module: TYPO3/CMS/Rtehtmlarea/HTMLArea/Extjs/ColorPalette
- * Intercept Ext.ColorPalette.prototype.select
- * @exports TYPO3/CMS/Rtehtmlarea/HTMLArea/Extjs/ColorPalette
- */
-define(['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/ux/ColorMenu.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/ColorMenu.js
deleted file mode 100644 (file)
index 46708bf..0000000
+++ /dev/null
@@ -1,96 +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!
- */
-
-/**
- * Module: TYPO3/CMS/Rtehtmlarea/HTMLArea/Extjs/ColorPalette/ColorMenu
- * Color menu
- * @exports TYPO3/CMS/Rtehtmlarea/HTMLArea/Extjs/ColorPalette/ColorMenu
- */
-define(['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
deleted file mode 100644 (file)
index 3d73ab3..0000000
+++ /dev/null
@@ -1,133 +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!
- */
-
-/**
- * Module: TYPO3/CMS/Rtehtmlarea/HTMLArea/Extjs/ColorPalette/ColorPaletteField
- * Color palette trigger field
- * Based on http://www.extjs.com/forum/showthread.php?t=89312
- * @exports TYPO3/CMS/Rtehtmlarea/HTMLArea/Extjs/ColorPalette/ColorPaletteField
- */
-define(['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);
-});
index 1398ace..6fe2248 100644 (file)
@@ -113,7 +113,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                        appendToLog: function (editorId, objectName, functionName, text, type) {
                                var str = 'RTE[' + editorId + '][' + objectName + '::' + functionName + ']: ' + text;
                                if (typeof type === 'undefined') {
-                                       var type = 'info';
+                                       type = 'info';
                                }
                                // IE may not have any console
                                if (typeof console === 'object' && console !== null && typeof console[type] !== 'undefined') {
index 8bef035..a8a7b8e 100644 (file)
 var HTMLArea = HTMLArea || {};
 HTMLArea.jQuery = TYPO3.jQuery;
 HTMLArea.CSS = {};
-
-/**
- * ExtJS namespacing
- */
-Ext.ux.form = {};
-Ext.ux.menu = {};
-Ext.ux.Toolbar = {};
index 2d696c7..0dcffb6 100644 (file)
  * Every plugin should be a subclass of this class
  *
  */
-define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
+define([
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
        'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util',
-       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event'],
-       function (UserAgent, Util, Event) {
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event',
+       'jquery',
+       'TYPO3/CMS/Backend/Modal',
+       'TYPO3/CMS/Backend/Severity'
+], function (UserAgent, Util, Event, $, Modal, Severity) {
 
        /**
         * Constructor method
@@ -54,9 +58,9 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                 *      - registering any buttons with method registerButton;
                 *      - registering any drop-down lists with method registerDropDown.
                 *
-                * @param       object          editor: instance of RTE
+                * @param {Object} editor Instance of RTE
                 *
-                * @return      boolean         true if the plugin was configured
+                * @return {Boolean} True if the plugin was configured
                 */
                configurePlugin: function (editor) {
                        return false;
@@ -65,16 +69,16 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                /**
                 * Registers the plugin "About" information
                 *
-                * @param       object          pluginInformation:
-                *                                      version         : the version,
-                *                                      developer       : the name of the developer,
-                *                                      developerUrl    : the url of the developer,
-                *                                      copyrightOwner  : the name of the copyright owner,
-                *                                      sponsor         : the name of the sponsor,
-                *                                      sponsorUrl      : the url of the sponsor,
-                *                                      license         : the type of license (should be "GPL")
+                * @param {Object} pluginInformation
+                *                     version: the version,
+                *                     developer: the name of the developer,
+                *                     developerUrl: the url of the developer,
+                *                     copyrightOwner: the name of the copyright owner,
+                *                     sponsor: the name of the sponsor,
+                *                     sponsorUrl: the url of the sponsor,
+                *                     license: the type of license (should be "GPL")
                 *
-                * @return      boolean         true if the information was registered
+                * @return {Boolean} True if the information was registered
                 */
                registerPluginInformation: function (pluginInformation) {
                        if (typeof pluginInformation !== 'object' || pluginInformation === null) {
@@ -90,7 +94,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                /**
                 * Returns the plugin information
                 *
-                * @return      object          the plugin information object
+                * @return {Object} The plugin information object
                 */
                getPluginInformation: function () {
                        return this.pluginInformation;
@@ -99,8 +103,8 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                /**
                 * Returns a plugin object
                 *
-                * @param       string          pluinName: the name of some plugin
-                * @return      object          the plugin object or null
+                * @param {String} pluginName The name of some plugin
+                * @return {Object} The plugin object or null
                 */
                getPluginInstance: function (pluginName) {
                        return this.editor.getPlugin(pluginName);
@@ -109,7 +113,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                /**
                 * Returns a current editor mode
                 *
-                * @return      string          editor mode
+                * @return {String} Editor mode
                 */
                getEditorMode: function () {
                        return this.editor.getMode();
@@ -118,9 +122,9 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                /**
                 * Returns true if the button is enabled in the toolbar configuration
                 *
-                * @param       string          buttonId: identification of the button
+                * @param {String} buttonId Identification of the button
                 *
-                * @return      boolean         true if the button is enabled in the toolbar configuration
+                * @return {Boolean} True if the button is enabled in the toolbar configuration
                 */
                isButtonInToolbar: function (buttonId) {
                        var index = -1;
@@ -144,9 +148,9 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                /**
                 * Returns the button object from the toolbar
                 *
-                * @param       string          buttonId: identification of the button
+                * @param {String} buttonId Identification of the button
                 *
-                * @return      object          the toolbar button object
+                * @return {Object} The toolbar button object
                 */
                getButton: function (buttonId) {
                        return this.editor.toolbar.getButton(buttonId);
@@ -155,20 +159,20 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                /**
                 * Registers a button for inclusion in the toolbar
                 *
-                * @param       object          buttonConfiguration: the configuration object of the button:
-                *                                      id              : unique id for the button
-                *                                      tooltip         : tooltip for the button
-                *                                      textMode        : enable in text mode
-                *                                      action          : name of the function invoked when the button is pressed
-                *                                      context         : will be disabled if not inside one of listed elements
-                *                                      hide            : hide in menu and show only in context menu (deprecated, use hidden)
-                *                                      hidden          : synonym of hide
-                *                                      selection       : will be disabled if there is no selection
-                *                                      hotkey          : hotkey character
-                *                                      dialog          : if true, the button opens a dialogue
-                *                                      dimensions      : the opening dimensions object of the dialogue window
-                *
-                * @return      boolean         true if the button was successfully registered
+                * @param {Object} buttonConfiguration The configuration object of the button:
+                *                     id: unique id for the button
+                *                     tooltip: tooltip for the button
+                *                     textMode : enable in text mode
+                *                     action: name of the function invoked when the button is pressed
+                *                     context: will be disabled if not inside one of listed elements
+                *                     hide: hide in menu and show only in context menu (deprecated, use hidden)
+                *                     hidden: synonym of hide
+                *                     selection: will be disabled if there is no selection
+                *                     hotkey: hotkey character
+                *                     dialog: if true, the button opens a dialogue
+                *                     dimensions: the opening dimensions object of the dialogue window
+                *
+                * @return {Boolean} True if the button was successfully registered
                 */
                registerButton: function (buttonConfiguration) {
                        if (this.isButtonInToolbar(buttonConfiguration.id)) {
@@ -213,13 +217,13 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                /**
                 * Registers a drop-down list for inclusion in the toolbar
                 *
-                * @param       object          dropDownConfiguration: the configuration object of the drop-down:
-                *                                      id              : unique id for the drop-down
-                *                                      tooltip         : tooltip for the drop-down
-                *                                      action          : name of function to invoke when an option is selected
-                *                                      textMode        : enable in text mode
+                * @param {Object} dropDownConfiguration: the configuration object of the drop-down:
+                *                     id: unique id for the drop-down
+                *                     tooltip: tooltip for the drop-down
+                *                     action: name of function to invoke when an option is selected
+                *                     textMode: enable in text mode
                 *
-                * @return      boolean         true if the drop-down list was successfully registered
+                * @return {Boolean} True if the drop-down list was successfully registered
                 */
                registerDropDown: function (dropDownConfiguration) {
                        if (this.isButtonInToolbar(dropDownConfiguration.id)) {
@@ -241,13 +245,13 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                /**
                 * Registers a text element for inclusion in the toolbar
                 *
-                * @param       object          textConfiguration: the configuration object of the text element:
-                *                                      id              : unique id for the text item
-                *                                      text            : the text litteral
-                *                                      tooltip         : tooltip for the text item
-                *                                      cls             : a css class to be assigned to the text element
+                * @param {Object} textConfiguration: the configuration object of the text element:
+                *                     id: unique id for the text item
+                *                     text: the text litteral
+                *                     tooltip: tooltip for the text item
+                *                     cls: a css class to be assigned to the text element
                 *
-                * @return      boolean         true if the drop-down list was successfully registered
+                * @return {Boolean} true if the drop-down list was successfully registered
                 */
                registerText: function (textConfiguration) {
                        if (this.isButtonInToolbar(textConfiguration.id)) {
@@ -261,23 +265,23 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                /**
                 * Returns the drop-down configuration
                 *
-                * @param       string          dropDownId: the unique id of the drop-down
+                * @param {String} dropDownId The unique id of the drop-down
                 *
-                * @return      object          the drop-down configuration object
+                * @return {Object} The drop-down configuration object
                 */
                getDropDownConfiguration: function(dropDownId) {
                        return this.editorConfiguration.buttonsConfig[dropDownId];
                },
 
                /**
-                * Registors a hotkey
+                * Registers a hotkey
                 *
-                * @param       object          hotKeyConfiguration: the configuration object of the hotkey:
-                *                                      id              : the key
-                *                                      cmd             : name of the button corresponding to the hot key
-                *                                      element         : value of the record to be selected in the dropDown item
+                * @param {Object} hotKeyConfiguration The configuration object of the hotkey:
+                *                     id: the key
+                *                     cmd: name of the button corresponding to the hot key
+                *                     element: value of the record to be selected in the dropDown item
                 *
-                * @return      boolean         true if the hotkey was successfully registered
+                * @return {Boolean} True if the hotkey was successfully registered
                 */
                registerHotKey: function (hotKeyConfiguration) {
                        return this.editorConfiguration.registerHotKey(hotKeyConfiguration);
@@ -286,44 +290,40 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                /**
                 * Returns the buttonId corresponding to the hotkey, if any
                 *
-                * @param       string          key: the hotkey
+                * @param {String} key The hotkey
                 *
-                * @return      string          the buttonId or ""
+                * @return {string} The buttonId or ""
                 */
                translateHotKey: function(key) {
+                       var returnValue = '';
                        if (typeof this.editorConfiguration.hotKeyList[key] !== 'undefined') {
                                var buttonId = this.editorConfiguration.hotKeyList[key].cmd;
                                if (typeof buttonId !== 'undefined') {
-                                       return buttonId;
-                               } else {
-                                       return "";
+                                       returnValue = buttonId;
                                }
                        }
-                       return "";
+                       return returnValue;
                },
 
                /**
                 * Returns the hotkey configuration
                 *
-                * @param       string          key: the hotkey
+                * @param {String} key The hotkey
                 *
-                * @return      object          the hotkey configuration object
+                * @return {Object} The hotkey configuration object
                 */
                getHotKeyConfiguration: function(key) {
                        if (typeof this.editorConfiguration.hotKeyList[key] !== 'undefined') {
                                return this.editorConfiguration.hotKeyList[key];
-                       } else {
-                               return null;
                        }
+                       return null;
                },
 
                /**
                 * Initializes the plugin
                 * Is invoked when the toolbar component is created (subclass of Ext.ux.HTMLAreaButton or Ext.ux.form.HTMLAreaCombo)
                 *
-                * @param       object          button: the component
-                *
-                * @return      void
+                * @param {Object} button The component
                 */
                init: Util.emptyFunction,
 
@@ -332,7 +332,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                 * This function may be defined by the plugin subclass.
                 * If defined, the function will be invoked whenever the toolbar state is refreshed.
                 *
-                * @return      boolean
+                * @return {Boolean}
                 */
                onUpdateToolbar: Util.emptyFunction,
 
@@ -341,13 +341,15 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                 * This function may be redefined by the plugin subclass.
                 * The function is invoked whenever the editor changes mode.
                 *
-                * @param       string          mode: "wysiwyg" or "textmode"
+                * @param {String} mode "wysiwyg" or "textmode"
                 *
-                * @return      boolean
+                * @return {Boolean}
                 */
                onMode: function(mode) {
                        if (mode === "textmode" && this.dialog && !(this.dialog.buttonId && this.editorConfiguration.buttons[this.dialog.buttonId] && this.editorConfiguration.buttons[this.dialog.buttonId].textMode)) {
-                               this.dialog.close();
+                               if (typeof Modal.currentModal !== 'undefined') {
+                                       Modal.currentModal.trigger('modal-dismiss');
+                               }
                        }
                },
 
@@ -356,16 +358,17 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                 * This function may be defined by the plugin subclass.
                 * The function is invoked when the editor is initialized
                 *
-                * @return      boolean
+                * @return {Boolean}
                 */
                onGenerate: Util.emptyFunction,
 
                /**
                 * Localize a string
                 *
-                * @param       string          label: the name of the label to localize
+                * @param {String} label The name of the label to localize
+                * @param {Integer} plural
                 *
-                * @return      string          the localization of the label
+                * @return {String} The localization of the label
                 */
                localize: function (label, plural) {
                        var i = plural || 0;
@@ -381,31 +384,30 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                /**
                 * Get localized label wrapped with contextual help markup when available
                 *
-                * @param       string          fieldName: the name of the field in the CSH file
-                * @param       string          label: the name of the label to localize
-                * @param       string          pluginName: overrides this.name
+                * @param {String} fieldName The name of the field in the CSH file
+                * @param {String} label The name of the label to localize
+                * @param {String} pluginName Overrides this.name
                 *
-                * @return      string          localized label with CSH markup
+                * @return {String} Localized label with CSH markup
                 */
                getHelpTip: function (fieldName, label, pluginName) {
                        if (typeof TYPO3.ContextHelp !== 'undefined' && typeof fieldName === 'string') {
-                               var pluginName = typeof pluginName !== 'undefined' ? pluginName : this.name;
+                               pluginName = typeof pluginName !== 'undefined' ? pluginName : this.name;
                                if (fieldName.length > 0) {
                                        fieldName = fieldName.replace(/-|\s/gi, '_');
                                }
                                return '<span class="t3-help-link" href="#" data-table="xEXT_rtehtmlarea_' + pluginName + '" data-field="' + fieldName + '"><abbr class="t3-help-teaser">' + (this.localize(label) || label) + '</abbr></span>';
-                       } else {
-                               return this.localize(label) || label;
                        }
+                       return this.localize(label) || label;
                },
 
                /**
                 * Load a Javascript file asynchronously
                 *
-                * @param       string          url: url of the file to load
-                * @param       function        callBack: the callBack function
+                * @param {String} url URL of the file to load
+                * @param {Function} callback The callBack function
                 *
-                * @return      boolean         true on success of the request submission
+                * @return {Boolean} True on success of the request submission
                 */
                getJavascriptFile: function (url, callback) {
                        return this.editor.ajax.getJavascriptFile(url, callback, this);
@@ -414,11 +416,11 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                /**
                 * Post data to the server
                 *
-                * @param       string          url: url to post data to
-                * @param       object          data: data to be posted
-                * @param       function        callback: function that will handle the response returned by the server
+                * @param {String} url URL to post data to
+                * @param {Object} data Data to be posted
+                * @param {Function} callback Function that will handle the response returned by the server
                 *
-                * @return      boolean         true on success
+                * @return {Boolean} True on success
                 */
                postData: function (url, data, callback) {
                        return this.editor.ajax.postData(url, data, callback, this);
@@ -427,96 +429,32 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                /**
                 * Open a window with container iframe
                 *
-                * @param       string          buttonId: the id of the button
-                * @param       string          title: the window title (will be localized here)
-                * @param       object          dimensions: the opening dimensions od the window
-                * @param       string          url: the url to load ino the iframe
-                *
-                * @ return     void
-                */
-               openContainerWindow: function (buttonId, title, dimensions, url) {
-                       this.dialog = new Ext.Window({
-                               id: this.editor.editorId + buttonId,
-                               title: this.localize(title) || title,
-                               cls: 'htmlarea-window',
-                               width: dimensions.width,
-                               border: false,
-                               iconCls: this.getButton(buttonId).iconCls,
-                               listeners: {
-                                       afterrender: {
-                                               fn: this.onContainerResize
-                                       },
-                                       resize: {
-                                               fn: this.onContainerResize
-                                       },
-                                       close: {
-                                               fn: this.onClose,
-                                               scope: this
-                                       }
-                               },
-                               items: {
-                                               // The content iframe
-                                       xtype: 'box',
-                                       height: dimensions.height-20,
-                                       itemId: 'content-iframe',
-                                       autoEl: {
-                                               tag: 'iframe',
-                                               cls: 'content-iframe',
-                                               src: url
-                                       }
-                               },
-                               maximizable: true
-                       });
-                       this.show();
-               },
-
-               /**
-                * Handler invoked when the container window is rendered or resized in order to resize the content iframe to maximum size
+                * @param {String} buttonId The id of the button
+                * @param {String} title The window title (will be localized here)
+                * @param {Integer} height The height of the containing iframe
+                * @param {String} url The url to load ino the iframe
                 */
-               onContainerResize: function (panel) {
-                       var iframe = panel.getComponent('content-iframe');
-                       if (iframe.rendered) {
-                               iframe.getEl().setSize(panel.getInnerWidth(), panel.getInnerHeight());
-                       }
-               },
+               openContainerWindow: function (buttonId, title, height, url) {
+                       var self = this,
+                               $iframe = $('<iframe />', {src: url, 'class': 'content-iframe', style: 'border: 0; height: ' + height * 1 + 'px;'}),
+                               $content = $('<div />', {'class': 'htmlarea-window', id: this.editor.editorId + buttonId}).append($iframe);
 
-               /**
-                * Get the opening diment=sions of the window
-                *
-                * @param       object          dimensions: default opening width and height set by the plugin
-                * @param       string          buttonId: the id of the button that is triggering the opening of the window
-                *
-                * @return      object          opening width and height of the window
-                */
-               getWindowDimensions: function (dimensions, buttonId) {
-                       // Apply default dimensions
-                       this.dialogueWindowDimensions = {
-                               width: 250,
-                               height: 250
-                       };
-                       // Apply default values as per PageTSConfig
-                       Util.apply(this.dialogueWindowDimensions, this.editorConfiguration.dialogueWindows);
-                       // Apply dimensions as per button registration
-                       if (typeof this.editorConfiguration.buttonsConfig[buttonId] === 'object' && this.editorConfiguration.buttonsConfig[buttonId] !== null) {
-                               Util.apply(this.dialogueWindowDimensions, this.editorConfiguration.buttonsConfig[buttonId].dimensions);
-                       }
-                       // Apply dimensions as per call
-                       Util.apply(this.dialogueWindowDimensions, dimensions);
-                       // Overrride dimensions as per PageTSConfig
-                       var buttonConfiguration = this.editorConfiguration.buttons[this.editorConfiguration.convertButtonId[buttonId]];
-                       if (buttonConfiguration) {
-                               Util.apply(this.dialogueWindowDimensions, buttonConfiguration.dialogueWindow);
-                       }
-                       return this.dialogueWindowDimensions;
+                       this.dialog = Modal.show(this.localize(title) || title, $content, Severity.notice);
+
+                       // TODO: dirty CSS hack - provide an API instead?
+                       this.dialog.find('.modal-body').css('padding', 0);
+                       this.dialog.on('modal-dismiss', function() {
+                               self.onCancel();
+                       });
                },
 
                /**
                 * Make url from module path
                 *
-                * @param       string          modulePath: module path
-                * @param       string          parameters: additional parameters
+                * @param {String} modulePath Module path
+                * @param {String} parameters Additional parameters
                 *
-                * @return      string          the url
+                * @return {String} The url
                 */
                makeUrlFromModulePath: function (modulePath, parameters) {
                        return modulePath + (modulePath.indexOf("?") === -1 ? "?" : "&") + this.editorConfiguration.RTEtsConfigParams + '&editorNo=' + this.editor.editorId + '&sys_language_content=' + this.editorConfiguration.sys_language_content + '&contentTypo3Language=' + this.editorConfiguration.typo3ContentLanguage + (parameters?parameters:'');
@@ -525,11 +463,9 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                /**
                 * Append an entry at the end of the troubleshooting log
                 *
-                * @param       string          functionName: the name of the plugin function writing to the log
-                * @param       string          text: the text of the message
-                * @param       string          type: the typeof of message: 'log', 'info', 'warn' or 'error'
-                *
-                * @return      void
+                * @param {String} functionName The name of the plugin function writing to the log
+                * @param {String} text The text of the message
+                * @param {String} type The typeof of message: 'log', 'info', 'warn' or 'error'
                 */
                appendToLog: function (functionName, text, type) {
                        this.editor.appendToLog(this.name, functionName, text, type);
@@ -538,10 +474,8 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                /**
                 * Add a config element to config array if not empty
                 *
-                * @param       object          configElement: the config element
-                * @param       array           configArray: the config array
-                *
-                * @return      void
+                * @param {Object} configElement The config element
+                * @param {Array} configArray The config array
                 */
                addConfigElement: function (configElement, configArray) {
                        if (typeof configElement === 'object'  && configElement !== null) {
@@ -550,32 +484,6 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                },
 
                /**
-                * Handler for Ext.TabPanel afterrender and tabchange events
-                * Set height of the tabpanel (miscalculated when the brower zoom is in use)
-                * Working around ExtJS 3.1 bug
-                */
-               setTabPanelHeight: function (tabpanel, tab) {
-                       var components = tab.findByType('fieldset');
-                       var height = 0;
-                       for (var i = components.length; --i >= 0;) {
-                               height += components[i].getEl().dom.offsetHeight;
-                       }
-                       tabpanel.setHeight(tabpanel.getFrameHeight() + height + tabpanel.findParentByType('window').footer.getHeight());
-               },
-
-               /**
-                * Handler for Ext.TabPanel tabchange event
-                * Force window ghost height synchronization
-                * Working around ExtJS 3.1 bug
-                */
-               syncHeight: function (tabPanel, tab) {
-                       var position = this.dialog.getPosition();
-                       if (position[0] > 0) {
-                               this.dialog.setPosition(position);
-                       }
-               },
-
-               /**
                 * Show the dialogue window
                 */
                show: function () {
@@ -594,7 +502,6 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                 * Remove listeners
                 * This function may be defined by the plugin subclass.
                 * The function is invoked when a plugin dialog is closed
-                * @return void
                 */
                removeListeners: Util.emptyFunction,
 
@@ -604,7 +511,9 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                close: function () {
                        this.removeListeners();
                        this.saveSelection();
-                       this.dialog.close();
+                       if (typeof Modal.currentModal !== 'undefined') {
+                               Modal.currentModal.trigger('modal-dismiss');
+                       }
                },
 
                /**
@@ -621,8 +530,9 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                 * Handler for window cancel
                 */
                onCancel: function () {
+                       Modal.currentModal.trigger('modal-dismiss');
+
                        this.removeListeners();
-                       this.dialog.close();
                        this.editor.focus();
                },
 
@@ -654,24 +564,96 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                /**
                 * Build the configuration object of a button
                 *
-                * @param       string          button: the text of the button
-                * @param       function        handler: button handler
+                * @param {String} button The text of the button
+                * @param {Function} handler Button handler
+                * @param {Boolean} active Whether the button should be active or not
+                * @param {Integer} severity The severity the button is representing
                 *
-                * @return      object          the button configuration object
+                * @return {Object} The button configuration object
                 */
-               buildButtonConfig: function (button, handler) {
+               buildButtonConfig: function (button, handler, active, severity) {
                        return {
-                               xtype: 'button',
                                text: this.localize(button),
-                               listeners: {
-                                       click: {
-                                               fn: handler,
-                                               scope: this
-                                       }
-                               }
+                               active: active,
+                               btnClass: 'btn-' + (typeof severity !== 'undefined' ? Severity.getCssClass(severity) : 'default'),
+                               trigger: handler
                        };
-               }
-       }
+               },
+
+               /**
+                * Helper method to generate the select boxes with predefined values
+                *
+                * @param {Object} $fieldset The jQuery object of the current fieldset
+                * @param {String} fieldLabel The label of the form field
+                * @param {String} selectName The name of the select field
+                * @param {Array} availableOptions Nested options array used for the select field. 0: label, 1: value
+                * @param {String} selectedValue The selected value. Used to set the `selected` property
+                * @param {Function} onChangeHandler Callback function triggered on change
+                * @param {Boolean} isDisabled Whether the field should be disabled or not
+                * @return {Object}
+                */
+               attachSelectMarkup: function($fieldset, fieldLabel, selectName, availableOptions, selectedValue, onChangeHandler, isDisabled) {
+                       var $select = $('<select />', {'class': 'form-control', name: selectName}),
+                               attributeConfiguration = {}
+
+                       for (var i = 0; i < availableOptions.length; ++i) {
+                               attributeConfiguration = {
+                                       value: availableOptions[i][1]
+                               };
+
+                               if (selectedValue && availableOptions[i][1] === selectedValue) {
+                                       attributeConfiguration.selected = 'selected';
+                               }
+
+                               $select.append(
+                                       $('<option />', attributeConfiguration).text(availableOptions[i][0])
+                               );
+                       }
+
+                       if (onChangeHandler && typeof onChangeHandler === 'function') {
+                               $select.on('change', onChangeHandler);
+                       }
+
+                       if (typeof isDisabled === 'boolean' && isDisabled) {
+                               $select.prop('disabled', isDisabled);
+                       }
+
+                       $fieldset.append(
+                               $('<div />', {'class': 'form-group'}).append(
+                                       $('<label />', {'class': 'col-sm-2'}).html(fieldLabel),
+                                       $('<div />', {'class': 'col-sm-10'}).append($select)
+                               )
+                       );
+
+                       return $fieldset;
+               },
+
+               /**
+                * Helper method that creates the necessary markup for a new tab
+                *
+                * @param {Object} $tabs
+                * @param {Object} $container
+                * @param {String} identifier
+                * @param {Object} elements
+                * @param {String} label
+                */
+               buildTabMarkup: function($tabs, $container, identifier, elements, label) {
+                       var $newTabPanel = $('<div />', {role: 'tabpanel', 'class': 'tab-pane', id: identifier});
+                       $tabs.append(
+                               $('<li />').append(
+                                       $('<a />', {href: '#' + identifier, 'aria-controls': identifier, role: 'tab', 'data-toggle': 'tab'}).text(label)
+                               )
+                       );
+                       for (var item in elements) {
+                               if (elements.hasOwnProperty(item)) {
+                                       $newTabPanel.append(
+                                               $('<div />', {'class': 'form-section'}).append(elements[item])
+                                       );
+                               }
+                       }
+                       $container.append($newTabPanel);
+               },
+       };
 
        return Plugin;
 
index 26f6e4a..478f1f1 100644 (file)
@@ -321,11 +321,11 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                /**
                 * Remove the option at the specified index
                 *
-                * @param int index: the index of the option to be removed
+                * @param {Integer} index: the index of the option to be removed
                 * @return void
                 */
                removeAt: function (index) {
-                       this.selectElement.remove(index);
+                       this.selectElement.removeChild(this.selectElement.childNodes[index]);
                },
 
                /**
@@ -334,9 +334,8 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                 * @return void
                 */
                removeAll: function () {
-                       var index, options = this.getOptions();
-                       while (index = options.length) {
-                               this.selectElement.remove(0);
+                       while (this.selectElement.firstChild) {
+                               this.selectElement.removeChild(this.selectElement.firstChild);
                        }
                },
 
index d93a7f7..a53e7e6 100644 (file)
 /**
  * Abbreviation plugin for htmlArea RTE
  */
-define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
-       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util'],
-       function (Plugin, Util) {
+define([
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util',
+       'jquery',
+       'TYPO3/CMS/Backend/Modal',
+       'TYPO3/CMS/Backend/Severity'
+], function (Plugin, Util, $, Modal, Severity) {
 
        var Abbreviation = function (editor, pluginName) {
                this.constructor.super.call(this, editor, pluginName);
@@ -66,28 +70,14 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                 },
 
                /**
-                * Sets of default configuration values for dialogue form fields
-                */
-               configDefaults: {
-                       combo: {
-                               editable: true,
-                               selectOnFocus: true,
-                               typeAhead: true,
-                               triggerAction: 'all',
-                               forceSelection: true,
-                               mode: 'local'
-                       }
-               },
-
-               /**
                 * 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
+                * @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
+                       // Could be a button or its hotkey
                        var buttonId = this.translateHotKey(id);
                        buttonId = buttonId ? buttonId : id;
                        var abbr = this.getCurrentAbbrElement();
@@ -97,13 +87,16 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                title: typeof abbr === 'object' && abbr !== null ? abbr.title : '',
                                text: typeof abbr === 'object' && abbr !== null ? abbr.innerHTML : this.editor.getSelection().getHtml()
                        };
-                               // Open the dialogue window
+                       // Open the dialogue window
                        this.openDialogue(
                                this.getButton(buttonId).tooltip,
                                buttonId,
-                               this.getWindowDimensions({ width: 580}, buttonId),
                                this.buildTabItemsConfig(abbr),
-                               this.buildButtonsConfig(abbr, this.okHandler, this.deleteHandler),
+                               [
+                                       this.buildButtonConfig(this.localize('Cancel'), $.proxy(this.onCancel, this), true),
+                                       this.buildButtonConfig(this.localize('Delete'), $.proxy(this.deleteHandler, this)),
+                                       this.buildButtonConfig(this.localize('OK'), $.proxy(this.okHandler, this), false, Severity.notice)
+                               ],
                                type
                        );
                        return false;
@@ -112,7 +105,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                /**
                 * Get the current abbr or aconym element, if any is selected
                 *
-                * @return object the element or null
+                * @return {Object} The element or null
                 */
                getCurrentAbbrElement: function() {
                        var abbr = this.editor.getSelection().getParentElement();
@@ -129,64 +122,31 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                /**
                 * Open the dialogue window
                 *
-                * @param string title: the window title
-                * @param string buttonId: the itemId of the button that was pressed
-                * @param integer dimensions: the opening width of the window
-                * @param object tabItems: the configuration of the tabbed panel
-                * @param object buttonsConfig: the configuration of the buttons
-                * @param string activeTab: itemId of the opening tab
-                * @return      void
+                * @param {String} title: the window title
+                * @param {String} buttonId: the itemId of the button that was pressed
+                * @param {Object} tabItems: the configuration of the tabbed panel
+                * @param {Object} buttonsConfig: the configuration of the buttons
+                * @param {String} activeTab: itemId of the opening tab
                 */
-               openDialogue: function (title, buttonId, dimensions, tabItems, buttonsConfig, activeTab) {
-                       this.dialog = new Ext.Window({
-                               title: this.getHelpTip('', 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: activeTab ? activeTab : 0,
-                                       defaults: {
-                                               xtype: 'container',
-                                               layout: 'form',
-                                               defaults: {
-                                                       labelWidth: 150
-                                               }
-                                       },
-                                       listeners: {
-                                               tabchange: {
-                                                       fn: function (tabpanel, tab) {
-                                                               this.setTabPanelHeight(tabpanel, tab);
-                                                               this.syncHeight(tabpanel, tab);
-                                                       },
-                                                       scope: this
-                                               }
-                                       },
-                                       items: tabItems
-                               },
-                               buttons: buttonsConfig
-                       });
-                       this.show();
+               openDialogue: function (title, buttonId, tabItems, buttonsConfig, activeTab) {
+                       this.dialog = Modal.show(title, tabItems, Severity.notice, buttonsConfig);
+                       this.dialog.on('modal-dismiss', $.proxy(this.onClose, this));
                },
 
                /**
                 * Build the dialogue tab items config
                 *
-                * @param object element: the element being edited, if any
-                * @return object the tab items configuration
+                * @param {Object} element: the element being edited, if any
+                * @return {Object} the tab items configuration
                 */
                buildTabItemsConfig: function (element) {
-                       var type = typeof element === 'object' && element !== null ? element.nodeName.toLowerCase() : '';
-                       var tabItems = [];
-                       var abbrTabItems = [];
+                       var type = typeof element === 'object' && element !== null ? element.nodeName.toLowerCase() : '',
+                               abbrTabItems = [],
+                               acronymTabItems = [],
+                               $finalMarkup,
+                               $tabs = $('<ul />', {'class': 'nav nav-tabs', role: 'tablist'}),
+                               $tabContent = $('<div />', {'class': 'tab-content'});
+
                        // abbreviation tab not shown if the current selection is an acronym
                        if (type !== 'acronym') {
                                // definedAbbreviation fieldset not shown if no pre-defined abbreviation exists
@@ -198,14 +158,11 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                        this.addConfigElement(this.buildUseTermFieldsetConfig((type === 'abbr') ? element : null, 'abbr'), abbrTabItems);
                                }
                        }
+
                        if (abbrTabItems.length > 0) {
-                               tabItems.push({
-                                       title: this.localize('Abbreviation'),
-                                       itemId: 'abbr',
-                                       items: abbrTabItems
-                               });
+                               this.buildTabMarkup($tabs, $tabContent, 'abbr', abbrTabItems, this.localize('Abbreviation'));
                        }
-                       var acronymTabItems = [];
+
                        // acronym tab not shown if the current selection is an abbreviation
                        if (type !== 'abbr') {
                                // definedAcronym fieldset not shown if no pre-defined acronym exists
@@ -218,191 +175,161 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                }
                        }
                        if (acronymTabItems.length > 0) {
-                               tabItems.push({
-                                       title: this.localize('Acronym'),
-                                       itemId: 'acronym',
-                                       items: acronymTabItems
-                               });
+                               this.buildTabMarkup($tabs, $tabContent, 'acronym', acronymTabItems, this.localize('Acronym'));
                        }
-                       return tabItems;
-               },
 
-               /**
-                * Build the dialogue buttons config
-                *
-                * @param       object          element: the element being edited, if any
-                * @param       function        okHandler: the handler for the ok button
-                * @param       function        deleteHandler: the handler for the delete button
-                *
-                * @return      object          the buttons configuration
-                */
-               buildButtonsConfig: function (element, okHandler, deleteHandler) {
-                       var buttonsConfig = [this.buildButtonConfig('OK', okHandler)];
-                       if (element) {
-                               buttonsConfig.push(this.buildButtonConfig('Delete', deleteHandler));
-                       }
-                       buttonsConfig.push(this.buildButtonConfig('Cancel', this.onCancel));
-                       return buttonsConfig;
+                       $tabs.find('li:first').addClass('active');
+                       $tabContent.find('.tab-pane:first').addClass('active');
+
+                       $finalMarkup = $('<form />', {'class': 'form-horizontal'}).append($tabs, $tabContent);
+
+                       return $finalMarkup;
                },
 
                /**
                 * This function builds the configuration object for the defined Abbreviation or Acronym fieldset
                 *
-                * @param object element: the element being edited, if any
-                * @param string type: 'abbr' or 'acronym'
-                *
-                * @return object the fieldset configuration object
+                * @param {Object} element: the element being edited, if any
+                * @param {String} type: 'abbr' or 'acronym'
+                * @return {Object} the fieldset configuration object
                 */
                buildDefinedTermFieldsetConfig: function (element, type) {
-                       var itemsConfig = [];
-                       itemsConfig.push(Util.apply({
-                               xtype: 'combo',
-                               displayField: 'term',
-                               valueField: 'term',
-                               fieldLabel: this.getHelpTip('unabridgedTerm', 'Unabridged_term'),
-                               itemId: 'termSelector',
-                               tpl: '<tpl for="."><div ext:qtip="{abbr}" style="text-align:left;font-size:11px;" class="x-combo-list-item">{term}</div></tpl>',
-                               store: new Ext.data.JsonStore({
-                                       autoDestroy:  true,
-                                       autoLoad: false,
-                                       root: type,
-                                       fields: [ { name: 'term'}, { name: 'abbr'},  { name: 'language'}],
-                                       url: this.pageTSConfiguration.abbreviationUrl
-                               }),
-                               width: 350,
-                               listeners: {
-                                       afterrender: {
-                                               fn: function (combo) {
-                                                       // Ensure the store is loaded
-                                                       combo.getStore().load({
-                                                               callback: function () { this.onSelectorRender(combo); },
-                                                               scope: this
-                                                       });
-                                               },
-                                               scope: this
-                                       },
-                                       select: {
-                                               fn: this.onTermSelect,
-                                               scope: this
-                                       }
-                               }
-                       }, this.configDefaults['combo']));
-                       itemsConfig.push(Util.apply({
-                               xtype: 'combo',
-                               displayField: 'abbr',
-                               valueField: 'abbr',
-                               tpl: '<tpl for="."><div ext:qtip="{language}" style="text-align:left;font-size:11px;" class="x-combo-list-item">{abbr}</div></tpl>',
-                               fieldLabel: this.getHelpTip('abridgedTerm', 'Abridged_term'),
-                               itemId: 'abbrSelector',
-                               store: new Ext.data.JsonStore({
-                                       autoDestroy:  true,
-                                       autoLoad: false,
-                                       root: type,
-                                       fields: [ { name: 'term'}, { name: 'abbr'},  { name: 'language'}],
-                                       url: this.pageTSConfiguration.abbreviationUrl
-                               }),
-                               width: 100,
-                               listeners: {
-                                       afterrender: {
-                                               fn: function (combo) {
-                                                       // Ensure the store is loaded
-                                                       combo.getStore().load({
-                                                               callback: function () { this.onSelectorRender(combo); },
-                                                               scope: this
-                                                       });
-                                               },
-                                               scope: this
-                                       },
-                                       select: {
-                                               fn: this.onAbbrSelect,
-                                               scope: this
+                       var self = this,
+                               $fieldset = $('<fieldset />');
+
+                       $fieldset.append(
+                               $('<h4 />', {'class': 'form-section-headline'}).html(this.getHelpTip('preDefined' + ((type == 'abbr') ? 'Abbreviation' : 'Acronym'), 'Defined_' + type))
+                       );
+
+                       $fieldset.append(
+                               $('<div />', {'class': 'form-group'}).append(
+                                       $('<label />', {'class': 'col-sm-2'}).html(this.getHelpTip('unabridgedTerm', 'Unabridged_term')),
+                                       $('<div />', {'class': 'col-sm-10'}).append(
+                                               $('<select />', {name: 'termSelector', 'class': 'form-control'})
+                                                       .on('change', $.proxy(this.onTermSelect, this))
+                                       )
+                               ),
+                               $('<div />', {'class': 'form-group'}).append(
+                                       $('<label />', {'class': 'col-sm-2'}).html(this.getHelpTip('abridgedTerm', 'Abridged_term')),
+                                       $('<div />', {'class': 'col-sm-10'}).append(
+                                               $('<select />', {name: 'abbrSelector', 'class': 'form-control'})
+                                                       .on('change', $.proxy(this.onAbbrSelect, this))
+                                       )
+                               )
+                       );
+
+                       $.ajax({
+                               url: this.pageTSConfiguration.abbreviationUrl,
+                               dataType: 'json',
+                               success: function (response) {
+                                       var $termSelector = $fieldset.find('select[name="termSelector"]'),
+                                               $abbrSelector = $fieldset.find('select[name="abbrSelector"]');
+
+                                       for (var item in response.type) {
+                                               if (response.type.hasOwnProperty(item)) {
+                                                       var current = response.type[item],
+                                                               attributeConfiguration = {
+                                                                       value: current.term,
+                                                                       'data-abbr': current.abbr,
+                                                                       'data-language': current.language
+                                                               };
+                                                       $termSelector.append(
+                                                               $('<option />', attributeConfiguration).text(current.term)
+                                                       );
+
+                                                       attributeConfiguration = {
+                                                               value: current.abbr,
+                                                               'data-term': current.term,
+                                                               'data-language': current.language
+                                                       };
+                                                       $abbrSelector.append(
+                                                               $('<option />', attributeConfiguration).text(current.abbr)
+                                                       );
+                                               }
                                        }
+
+                                       self.onSelectorRender($termSelector);
+                                       self.onSelectorRender($abbrSelector);
                                }
-                       }, this.configDefaults['combo']));
+                       });
+
                        var languageObject = this.getPluginInstance('Language');
                        if (this.getButton('Language')) {
                                var selectedLanguage = typeof element === 'object' && element !== null ? languageObject.getLanguageAttribute(element) : 'none';
-                               itemsConfig.push(Util.apply({
-                                       xtype: 'combo',
-                                       fieldLabel: this.getHelpTip('language', 'Language'),
-                                       itemId: 'language',
-                                       valueField: 'value',
-                                       displayField: 'text',
-                                       tpl: '<tpl for="."><div ext:qtip="{value}" style="text-align:left;font-size:11px;" class="x-combo-list-item">{text}</div></tpl>',
-                                       store: new Ext.data.JsonStore({
-                                               autoDestroy:  true,
-                                               root: 'options',
-                                               fields: [ { name: 'text'}, { name: 'value'} ],
-                                               url: this.getDropDownConfiguration('Language').dataUrl,
-                                               listeners: {
-                                                       load: {
-                                                               fn: function (store) {
-                                                                       if (selectedLanguage !== 'none') {
-                                                                               store.removeAt(0);
-                                                                               store.insert(0, new store.recordType({
-                                                                                       text: languageObject.localize('Remove language mark'),
-                                                                                       value: 'none'
-                                                                               }));
-                                                                       }
+
+                               $fieldset.append(
+                                       $('<div />', {'class': 'form-group'}).append(
+                                               $('<label />', {'class': 'col-sm-2'}).html(this.getHelpTip('language', 'Language')),
+                                               $('<div />', {'class': 'col-sm-10'}).append(
+                                                       $('<select />', {name: 'language', 'class': 'form-control'})
+                                               )
+                                       )
+                               );
+
+                               $.ajax({
+                                       url: this.getDropDownConfiguration('Language').dataUrl,
+                                       dataType: 'json',
+                                       success: function (response) {
+                                               var $select = $fieldset.find('select[name="language"]');
+
+                                               for (var language in response.options) {
+                                                       if (response.options.hasOwnProperty(language)) {
+                                                               if (selectedLanguage !== 'none') {
+                                                                       response.options[language].value = 'none';
+                                                                       response.options[language].text = languageObject.localize('Remove language mark');
                                                                }
-                                                       }
-                                               }
-                                       }),
-                                       width: 200,
-                                       value: selectedLanguage,
-                                       listeners: {
-                                               beforerender: {
-                                                       fn: function (combo) {
-                                                               // Ensure the store is loaded
-                                                               combo.getStore().load({
-                                                                       callback: function () { combo.setValue(selectedLanguage); }
-                                                               });
+                                                               var attributeConfiguration = {value: response.options[language].value};
+                                                               if (selectedLanguage === response.options[language].value) {
+                                                                       attributeConfiguration.selected = 'selected';
+                                                               }
+                                                               $select.append(
+                                                                       $('<option />', attributeConfiguration).text(response.options[language].text)
+                                                               );
                                                        }
                                                }
                                        }
-                               }, this.configDefaults['combo']));
+                               });
                        }
-                       return {
-                               xtype: 'fieldset',
-                               title: this.getHelpTip('preDefined' + ((type == 'abbr') ? 'Abbreviation' : 'Acronym'), 'Defined_' + type),
-                               items: itemsConfig,
-                               listeners: {
-                                       render: {
-                                               fn: this.onDefinedTermFieldsetRender,
-                                               scope: this
-                                       }
-                               }
-                       };
+
+                       this.onDefinedTermFieldsetRender($fieldset);
+
+                       return $fieldset;
                },
 
                /**
                 * Handler on rendering the defined abbreviation fieldset
                 * If an abbr is selected but no term is selected, select any corresponding term with the correct language value, if any
+                *
+                * @param {Object} fieldset
                 */
                onDefinedTermFieldsetRender: function (fieldset) {
-                       var termSelector = fieldset.find('itemId', 'termSelector')[0];
-                       var term = termSelector.getValue();
-                       var abbrSelector = fieldset.find('itemId', 'abbrSelector')[0];
-                       var abbr = abbrSelector.getValue();
+                       var termSelector = fieldset.find('[name="termSelector"]');
+                       var term = termSelector.val();
+                       var abbrSelector = fieldset.find('[name="abbrSelector"]');
+                       var abbr = abbrSelector.val();
                        var language = '';
-                       var languageSelector = fieldset.find('itemId', 'language')[0];
+                       var languageSelector = fieldset.find('[name="language"]');
                        if (languageSelector) {
-                               var language = languageSelector.getValue();
-                               if (language == 'none') {
+                               language = languageSelector.val();
+                               if (language === 'none') {
                                        language = '';
                                }
                        }
                        if (abbr && !term) {
-                               var abbrStore = abbrSelector.getStore();
-                               var index = abbrStore.findBy(function (record) {
-                                       return record.get('abbr') == abbr && (!languageSelector || record.get('language') == language);
-                               }, this);
-                               if (index !== -1) {
-                                       term = abbrStore.getAt(index).get('term');
-                                       termSelector.setValue(term);
-                                       var useTermField = fieldset.ownerCt.find('itemId', 'useTerm');
+                               var $activeEl = null;
+                               abbrSelector.children().each(function(key) {
+                                       var $me = $(this);
+                                       if ($me.data('term') === abbr && (!languageSelector || $me.data('language') === language)) {
+                                               $activeEl = $me;
+                                               return false;
+                                       }
+                               });
+                               if ($activeEl !== null) {
+                                       term = $activeEl.data('term');
+                                       termSelector.val(term);
+                                       var useTermField = fieldset.closest('.tab-pane').find('[name="useTerm"]');
                                        if (useTermField.length) {
-                                               useTermField[0].setValue(term);
+                                               useTermField.val(term);
                                        }
                                }
                        }
@@ -414,141 +341,177 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                 * If there is already an abbr and the filtered list has only one or no element, hide the fieldset
                 */
                onSelectorRender: function (combo) {
-                       var store = combo.getStore();
-                       store.filterBy(function (record) {
-                               return !this.params.text || !this.params.title || this.params.text == record.get('term') || this.params.title == record.get('term') || this.params.title == record.get('abbr');
-                       }, this);
-                       // Make sure the combo list is filtered
-                       store.snapshot = store.data;
-                       var store = combo.getStore();
+                       var self = this,
+                               filteredStore = [],
+                               index = -1;
+
+                       combo.children().each(function() {
+                               var $me = $(this),
+                                       term = $me.data('term'),
+                                       abbr = $me.data('abbr');
+
+                               if (!self.params.text
+                                       || !self.params.title
+                                       || self.params.text === term
+                                       || self.params.title === term
+                                       || self.params.title === abbr
+                               ) {
+                                       filteredStore.push($me);
+                               }
+                       });
                        // Initialize the term and abbr combos
-                       if (combo.getItemId() == 'termSelector') {
+                       if (combo.attr('name') === 'termSelector') {
                                if (this.params.title) {
-                                       var index = store.findExact('term', this.params.title);
+                                       for (var i = 0; i < filteredStore.length; ++i) {
+                                               if (filteredStore[i].data('term') === this.params.title) {
+                                                       index = i;
+                                                       break;
+                                               }
+                                       }
+
                                        if (index !== -1) {
-                                               var record = store.getAt(index);
-                                               combo.setValue(record.get('term'));
-                                               this.onTermSelect(combo, record, index);
+                                               record = filteredStore[i];
+                                               combo.val(record.value);
                                        }
                                } else if (this.params.text) {
-                                       var index = store.findExact('term', this.params.text);
+                                       for (var i = 0; i < filteredStore.length; ++i) {
+                                               if (filteredStore[i].data('term') === this.params.text) {
+                                                       index = i;
+                                                       break;
+                                               }
+                                       }
                                        if (index !== -1) {
-                                               var record = store.getAt(index);
-                                               combo.setValue(record.get('term'));
-                                               this.onTermSelect(combo, record, index);
+                                               record = filteredStore[i];
+                                               combo.val(record.value);
+                                       }
+                               }
+                       } else if (combo.attr('name') === 'abbrSelector' && this.params.text) {
+                               for (var i = 0; i < filteredStore.length; ++i) {
+                                       if (filteredStore[i].data('abbr') === this.params.text) {
+                                               index = i;
+                                               break;
                                        }
                                }
-                       } else if (combo.getItemId() == 'abbrSelector' && this.params.text) {
-                               var index = store.findExact('abbr', this.params.text);
                                if (index !== -1) {
-                                       var record = store.getAt(index);
-                                       combo.setValue(record.get('abbr'));
-                                       this.onAbbrSelect(combo, record, index);
+                                       var record = filteredStore[index];
+                                       combo.val(record.value);
                                }
                        }
                },
 
                /**
                 * Handler when a term is selected
+                *
+                * @param {Event} event
                 */
-               onTermSelect: function (combo, record, index) {
-                       var fieldset = combo.findParentByType('fieldset');
-                       var tab = fieldset.findParentByType('container');
-                       var term = record.get('term');
-                       var abbr = record.get('abbr');
-                       var language = record.get('language');
+               onTermSelect: function (event) {
+                       var $me = $(event.currentTarget),
+                               fieldset = $me.closest('fieldset'),
+                               tab = fieldset.closest('.tab-pane'),
+                               term = $me.data('term'),
+                               abbr = $me.data('abbr'),
+                               language = $me.data('language');
+
                        // Update the abbreviation selector
-                       var abbrSelector = tab.find('itemId', 'abbrSelector')[0];
-                       abbrSelector.setValue(abbr);
+                       var abbrSelector = tab.find('[name="abbrSelector"]');
+                       abbrSelector.val(abbr);
+
                        // Update the language selector
-                       var languageSelector = tab.find('itemId', 'language');
+                       var languageSelector = tab.find('[name="language"]');
                        if (languageSelector.length > 0) {
                                if (language) {
-                                       languageSelector[0].setValue(language);
+                                       languageSelector.val(language);
                                } else {
-                                       languageSelector[0].setValue('none');
+                                       languageSelector.val('none');
                                }
                        }
+
                        // Update the term to use
-                       var useTermField = tab.find('itemId', 'useTerm');
+                       var useTermField = tab.find('[name="useTerm"]');
                        if (useTermField.length) {
-                               useTermField[0].setValue(term);
+                               useTermField.val(term);
                        }
                },
 
                /**
                 * Handler when an abbreviation or acronym is selected
+                *
+                * @param {Event} event
                 */
-               onAbbrSelect: function (combo, record, index) {
-                       var fieldset = combo.findParentByType('fieldset');
-                       var tab = fieldset.findParentByType('container');
-                       var term = record.get('term');
-                       var language = record.get('language');
+               onAbbrSelect: function (event) {
+                       var $me = $(event.currentTarget),
+                               fieldset = $me.closest('fieldset'),
+                               tab = fieldset.closest('.tab-pane'),
+                               term = $me.data('term'),
+                               language = $me.data('language');
+
                        // Update the term selector
-                       var termSelector = tab.find('itemId', 'termSelector')[0];
-                       termSelector.setValue(term);
+                       var termSelector = tab.find('[name="termSelector"]');
+                       termSelector.val(term);
+
                        // Update the language selector
-                       var languageSelector = tab.find('itemId', 'language');
+                       var languageSelector = tab.find('[name="language"]');
                        if (languageSelector.length > 0) {
                                if (language) {
-                                       languageSelector[0].setValue(language);
+                                       languageSelector.val(language);
                                } else {
-                                       languageSelector[0].setValue('none');
+                                       languageSelector.val('none');
                                }
                        }
+
                        // Update the term to use
-                       var useTermField = tab.find('itemId', 'useTerm');
+                       var useTermField = tab.find('[name="useTerm"]');
                        if (useTermField.length) {
-                               useTermField[0].setValue(term);
+                               useTermField.val(term);
                        }
                },
 
                /**
                 * This function builds the configuration object for the Abbreviation or Acronym to use fieldset
                 *
-                * @param       object          element: the element being edited, if any
-                *
-                * @return      object          the fieldset configuration object
+                * @param {Object} element The element being edited, if any
+                * @return {Object} The fieldset configuration object
                 */
-               buildUseTermFieldsetConfig: function (element, type) {
-                       var itemsConfig = [];
-                       itemsConfig.push({
-                               fieldLabel: this.getHelpTip('useThisTerm', 'Use_this_term'),
-                               labelSeparator: '',
-                               itemId: 'useTerm',
-                               value: element ? element.title : '',
-                               width: 300
-                       });
-                       return {
-                               xtype: 'fieldset',
-                               title: this.getHelpTip('termToAbridge', 'Term_to_abridge'),
-                               defaultType: 'textfield',
-                               items: itemsConfig
-                       };
+               buildUseTermFieldsetConfig: function (element) {
+                       var $fieldset = $('<fieldset />');
+
+                       $fieldset.append(
+                               $('<h4 />', {'class': 'form-section-headline'}).html(this.getHelpTip('termToAbridge', 'Term_to_abridge')),
+                               $('<div />', {'class': 'form-group'}).append(
+                                       $('<label />', {'class': 'col-sm-2'}).html(this.getHelpTip('useThisTerm', 'Use_this_term')),
+                                       $('<div />', {'class': 'col-sm-10'}).append(
+                                               $('<input />', {name: 'useTerm', 'class': 'form-control', value: element ? element.title : ''})
+                                       )
+                               )
+                       );
+
+                       return $fieldset;
                },
 
                /**
                 * Handler when the ok button is pressed
+                *
+                * @param {Event} event
                 */
-               okHandler: function (button, event) {
+               okHandler: function (event) {
                        this.restoreSelection();
-                       var tab = this.dialog.findByType('tabpanel')[0].getActiveTab();
-                       var type = tab.getItemId();
-                       var languageSelector = tab.find('itemId', 'language');
-                       var language = languageSelector && languageSelector.length > 0 ? languageSelector[0].getValue() : '';
-                       var termSelector = tab.find('itemId', 'termSelector');
-                       var term = termSelector && termSelector.length > 0 ? termSelector[0].getValue() : '';
-                       var abbrSelector = tab.find('itemId', 'abbrSelector');
-                       var useTermField = tab.find('itemId', 'useTerm');
+                       var tab = this.dialog.find('.tab-pane.active');
+                       var type = tab.attr('id');
+                       var languageSelector = tab.find('[name="language"]');
+                       var language = languageSelector && languageSelector.length > 0 ? languageSelector.val() : '';
+                       var termSelector = tab.find('[name="termSelector"]');
+                       var term = termSelector && termSelector.length > 0 ? termSelector.val() : '';
+                       var abbrSelector = tab.find('[name="abbrSelector"]');
+                       var useTermField = tab.find('[name="useTerm"]');
                        if (!this.params.abbr) {
                                var abbr = this.editor.document.createElement(type);
                                if (useTermField.length) {
-                                       abbr.title = useTermField[0].getValue();
+                                       abbr.title = useTermField.val();
                                } else {
                                        abbr.title = term;
                                }
                                if (term === abbr.title && abbrSelector && abbrSelector.length > 0) {
-                                       abbr.innerHTML = abbrSelector[0].getValue();
+                                       abbr.innerHTML = abbrSelector.val();
                                } else {
                                        abbr.innerHTML = this.params.text;
                                }
@@ -566,7 +529,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                        } else {
                                var abbr = this.params.abbr;
                                if (useTermField.length) {
-                                       abbr.title = useTermField[0].getValue();
+                                       abbr.title = useTermField.val();
                                } else {
                                        abbr.title = term;
                                }
@@ -574,31 +537,36 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                        this.getPluginInstance('Language').setLanguageAttributes(abbr, language);
                                }
                                if (term === abbr.title && abbrSelector && abbrSelector.length > 0) {
-                                       abbr.innerHTML = abbrSelector[0].getValue();
+                                       abbr.innerHTML = abbrSelector.val();
                                }
                        }
                        this.close();
-                       event.stopEvent();
+                       event.stopImmediatePropagation();
                },
 
                /**
                 * Handler when the delete button is pressed
+                *
+                * @param {Event} event
                 */
-               deleteHandler: function (button, event) {
+               deleteHandler: function (event) {
                        this.restoreSelection();
                        var abbr = this.params.abbr;
                        if (abbr) {
                                this.editor.getDomNode().removeMarkup(abbr);
                        }
                        this.close();
-                       event.stopEvent();
+                       event.stopImmediatePropagation();
                },
 
                /**
                 * This function gets called when the toolbar is updated
+                *
+                * @param {Object} button
+                * @param {String} mode
                 */
-               onUpdateToolbar: function (button, mode, selectionEmpty, ancestors) {
-                       if ((mode === 'wysiwyg') && this.editor.isEditable()) {
+               onUpdateToolbar: function (button, mode) {
+                       if (mode === 'wysiwyg' && this.editor.isEditable()) {
                                var el = this.getCurrentAbbrElement();
                                var nodeName = typeof el === 'object' && el !== null ? el.nodeName.toLowerCase() : '';
                                // Disable the button if the selection and not inside a abbr or acronym element
@@ -621,5 +589,4 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
        });
 
        return Abbreviation;
-
 });
index 4e95ffe..880f220 100644 (file)
 /**
  * About Plugin for TYPO3 htmlArea RTE
  */
-define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
-       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util'],
-       function (Plugin, Util) {
+define([
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util',
+       'jquery',
+       'TYPO3/CMS/Backend/Modal',
+       'TYPO3/CMS/Backend/Severity'
+], function (Plugin, Util, $, Modal, Severity) {
 
        var AboutEditor = function (editor, pluginName) {
                this.constructor.super.call(this, editor, pluginName);
@@ -57,7 +61,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                        this.registerButton(buttonConfiguration);
                        return true;
                 },
-               /*
+               /**
                 * Supported browsers
                 */
                browsers: [
@@ -68,146 +72,132 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                         '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
+                * @param {Object} editor The editor instance
+                * @param {String} id The button id or the key
                 *
-                * @return      boolean         false if action is completed
+                * @return {Boolean} false if action is completed
                 */
                onButtonPress: function (editor, id) {
-                               // Could be a button or its hotkey
+                       // 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()
+                               this.buildTabItems(),
+                               function () {
+                                       Modal.currentModal.trigger('modal-dismiss');
+                               }
                        );
                        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
+                * @param {String} buttonId The button id
+                * @param {String} title The window title
+                * @param {Object} tabItems The pre-rendered window content
+                * @param {Function} handler Handler when the OK button is clicked
                 */
-               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();
+               openDialogue: function (buttonId, title, tabItems, handler) {
+                       this.dialog = Modal.show(title, tabItems, Severity.info, [
+                               this.buildButtonConfig('Close', handler, true, Severity.info)
+                       ]);
+
+                       this.dialog.on('modal-dismiss', $.proxy(this.onClose, this));
                },
-               /*
+               /**
                 * Build the configuration of the the tab items
                 *
-                * @return      array   the configuration array of 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="https://docs.typo3.org/typo3cms/extensions/rtehtmlarea/" target="_blank">typo3.org</a></p>'
-                                       + '<p style="text-align: center;">'
-                                               + '<br />'
+                       var $finalMarkup,
+                               $tabs = $('<ul />', {'class': 'nav nav-tabs', role: 'tablist'}),
+                               $tabContent;
+
+                       $tabs.append(
+                               $('<li />', {'class': 'active'}).append(
+                                       $('<a />', {href: '#about', 'aria-controls': 'about', role: 'tab', 'data-toggle': 'tab'}).text(this.localize('About'))
+                               ),
+                               $('<li />').append(
+                                       $('<a />', {href: '#plugins', 'aria-controls': 'plugins', role: 'tab', 'data-toggle': 'tab'}).text(this.localize('Plugins'))
+                               )
+                       );
+
+                       // About tab
+                       var $aboutTab = $('<div />', {'class': 'panel panel-default'}).append(
+                               $('<div />', {'class': 'panel-heading'}).text('htmlArea RTE ' +  RTEarea[0].version),
+                               $('<div />', {'class': 'panel-body'}).append(
+                                       $('<p />').text(this.localize('free_editor')),
+                                       $('<p />').text(this.localize('Browser support') + ': ' + this.browsers.join(', ')),
+                                       $('<p />').text(this.localize('product_documentation')).append(
+                                               $('<a />', {href: 'https://docs.typo3.org/typo3cms/extensions/rtehtmlarea/', target: '_blank'}).text('typo3.org')
+                                       ),
+                                       $('<p />', {'class': 'text-center'}).html(
+                                               '<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-2016 <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()
-                               });
+                                       )
+                               )
+                       );
+
+                       // Plugins tab
+                       var $pluginTab = $('<div />', {'class': 'panel panel-default'}).append(
+                                       $('<div />', {'class': 'panel-heading'}).text(this.localize('Plugins'))
+                               ),
+                               $pluginTable = $('<table />', {'class': 'table'}).append(
+                                       $('<thead />').append(
+                                               $('<tr />').append(
+                                                       $('<th />').text(this.localize('Name')),
+                                                       $('<th />').text(this.localize('Developer')),
+                                                       $('<th />').text(this.localize('Sponsored by'))
+                                               )
+                                       )
+                               ),
+                               $pluginRows = $('<tbody />');
+
+
+                       for (var i = 0, plugins = this.getPluginsInfo(); i < plugins.length; ++i) {
+                               $pluginRows.append(
+                                       $('<tr />').append(
+                                               $('<td />').text(plugins[i][0]),
+                                               $('<td />').html(plugins[i][1]),
+                                               $('<td />').html(plugins[i][2])
+                                       )
+                               );
                        }
-                       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;
+                       $pluginTab.append($pluginTable.append($pluginRows));
+
+                       $tabContent = $('<div />', {'class': 'tab-content'}).append(
+                               $('<div />', {'class': 'tab-pane active', id: 'about'}).append($aboutTab),
+                               $('<div />', {'class': 'tab-pane', id: 'plugins'}).append($pluginTab)
+                       );
+                       $finalMarkup = $('<div />').append($tabs, $tabContent);
+
+                       return $finalMarkup
                },
-               /*
+               /**
                 * Format an array of information on each configured plugin
                 *
-                * @return      array           array of data objects
+                * @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>'
-                               ]);
+                               if (this.editor.plugins.hasOwnProperty(pluginId)) {
+                                       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;
                }
index 049c3ec..8466e4c 100644 (file)
 /**
  * Block Style Plugin for TYPO3 htmlArea RTE
  */
-define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
+define([
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
        'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/DOM',
        'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event',
        'TYPO3/CMS/Rtehtmlarea/HTMLArea/CSS/Parser',
-       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util'],
-       function (Plugin, Dom, Event, Parser, Util) {
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util',
+       'jquery'
+], function (Plugin, Dom, Event, Parser, Util, $) {
 
        var BlockStyle = function (editor, pluginName) {
                this.constructor.super.call(this, editor, pluginName);
@@ -88,6 +90,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                        dropDownConfiguration.maxHeight = parseInt(this.pageTSconfiguration.maxHeight, 10);
                                }
                        }
+                       dropDownConfiguration.xtype = 'htmlareaselect';
                        this.registerDropDown(dropDownConfiguration);
                        return true;
                },
@@ -169,7 +172,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                dropDown.setDisabled(true);
                        }
                        // Monitor css parsing being completed
-                       Event.one(this.blockStyles, 'HTMLAreaEventCssParsingComplete', function (event) { Event.stopEvent(event); self.onCssParsingComplete(); return false; }); 
+                       Event.one(this.blockStyles, 'HTMLAreaEventCssParsingComplete', function (event) { Event.stopEvent(event); self.onCssParsingComplete(); return false; });
                        this.blockStyles.parse();
                },
 
@@ -231,7 +234,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                },
 
                /**
-                * This function reinitializes the options of the dropdown
+                * This function re-initializes the options of the dropdown
                 */
                initializeDropDown: function (dropDown) {
                        switch (dropDown.xtype) {
@@ -240,15 +243,6 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                        dropDown.setFirstOption(this.localize('No style'), 'none', this.localize('No style'));
                                        dropDown.setValueByIndex(0);
                                        break;
-                               case 'combo':
-                                       var store = dropDown.getStore();
-                                       store.removeAll(false);
-                                       store.insert(0, new store.recordType({
-                                               text: this.localize('No style'),
-                                               value: 'none'
-                                       }));
-                                       dropDown.setValue('none');
-                                       break;
                        }
                },
 
index e4dfab5..623e986 100644 (file)
 /**
  * Character Map Plugin for TYPO3 htmlArea RTE
  */
-define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
+define([
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
        'TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
        'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event',
-       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util'],
-       function (Plugin, UserAgent, Event, Util) {
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util',
+       'jquery',
+       'TYPO3/CMS/Backend/Modal',
+       'TYPO3/CMS/Backend/Severity'
+], function (Plugin, UserAgent, Event, Util, $, Modal, Severity) {
 
        var CharacterMap = function (editor, pluginName) {
                this.constructor.super.call(this, editor, pluginName);
@@ -49,7 +53,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                         */
                        for (var i = 0, n = this.buttons.length; i < n; ++i) {
                                var button = this.buttons[i];
-                               buttonId = button[0];
+                               var buttonId = button[0];
                                var buttonConfiguration = {
                                        id: buttonId,
                                        tooltip: this.localize(buttonId + '-Tooltip'),
@@ -65,9 +69,11 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                         * Localizing the maps
                         */
                        for (var key in this.maps) {
-                               var map = this.maps[key];
-                               for (var i = map.length; --i >= 0;) {
-                                       this.maps[key][i].push(this.localize(map[i][1]));
+                               if (this.maps.hasOwnProperty(key)) {
+                                       var map = this.maps[key];
+                                       for (var i = map.length; --i >= 0;) {
+                                               this.maps[key][i].push(this.localize(map[i][1]));
+                                       }
                                }
                        }
                        return true;
@@ -325,26 +331,22 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                ['&lArr;', 'lArr'],
                                ['&rArr;', 'rArr'],
                                ['&hArr;', 'hArr'],
-                               ['&nbsp;', 'nbsp'],
-                               ['&nbsp;', 'nbsp'],
-                               ['&nbsp;', 'nbsp'],
-                               ['&nbsp;', 'nbsp'],
                                ['&clubs;', 'clubs'],
                                ['&diams;', 'diams'],
                                ['&hearts;', 'hearts'],
                                ['&spades;', 'spades']
                        ]
                },
-               /*
+               /**
                 * This function gets called when the button was pressed.
                 *
-                * @param       object          editor: the editor instance
-                * @param       string          id: the button id or the key
+                * @param {Object} editor The editor instance
+                * @param {String} id The button id or the key
                 *
-                * @return      boolean         false if action is completed
+                * @return {Boolean} false if action is completed
                 */
                onButtonPress: function (editor, id) {
-                               // Could be a button or its hotkey
+                       // Could be a button or its hotkey
                        var buttonId = this.translateHotKey(id);
                        buttonId = buttonId ? buttonId : id;
                        switch (buttonId) {
@@ -352,14 +354,10 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                        this.openDialogue(
                                                buttonId,
                                                'Insert special character',
-                                               this.getWindowDimensions(
-                                                       {
-                                                               width: 434,
-                                                               height: 360
-                                                       },
-                                                       buttonId
-                                               ),
-                                               this.buildTabItems()
+                                               this.buildTabItems(),
+                                               function () {
+                                                       Modal.currentModal.trigger('modal-dismiss');
+                                               }
                                        );
                                        break;
                                case 'InsertSoftHyphen':
@@ -368,96 +366,81 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                        }
                        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
-                * @param       function        handler: handler when the OK button if clicked
-                *
-                * @return      void
+                * @param {String} buttonId The button id
+                * @param {String} title The window title
+                * @param {Object} tabItems The configuration of the tabbed panel
+                * @param {Function} handler Handler when the OK button is clicked
                 */
-               openDialogue: function (buttonId, title, dimensions, tabItems, handler) {
-                       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('Cancel', this.onCancel)
-                               ]
-                       });
-                       this.show();
+               openDialogue: function (buttonId, title, tabItems, handler) {
+                       this.dialog = Modal.show(title, tabItems, Severity.notice, [
+                               this.buildButtonConfig('Close', handler, true, Severity.notice)
+                       ]);
+
+                       this.resetFocus();
+
+                       this.dialog.on('modal-dismiss', $.proxy(this.onClose, this));
                },
-               /*
+               /**
                 * Build the configuration of the the tab items
                 *
-                * @return      array   the configuration array of tab items
+                * @return {Object} The configuration array of tab items
                 */
                buildTabItems: function () {
-                       var tabItems = [];
+                       var self = this,
+                               $finalMarkup,
+                               $tabs = $('<ul />', {'class': 'nav nav-tabs', role: 'tablist'}),
+                               $tabContent = $('<div />', {'class': 'tab-content'});
+
                        for (var id in this.maps) {
-                               tabItems.push({
-                                       xtype: 'box',
-                                       cls: 'character-map',
-                                       title: this.localize(id),
-                                       itemId: id,
-                                       tpl: new Ext.XTemplate(
-                                               '<tpl for="."><a href="#" class="character" hidefocus="on" ext:qtitle="<span>&</span>{1};" ext:qtip="{2}">{0}</a></tpl>'
-                                       ),
-                                       listeners: {
-                                               render: {
-                                                       fn: this.renderMap,
-                                                       scope: this
+                               if (this.maps.hasOwnProperty(id)) {
+                                       var isFirst = Object.keys(this.maps).indexOf(id) === 0;
+                                       $tabs.append(
+                                               $('<li />', {'class': (isFirst ? 'active' : '')}).append(
+                                                       $('<a />', {
+                                                               href: '#' + id,
+                                                               'aria-controls': id,
+                                                               role: 'tab',
+                                                               'data-toggle': 'tab'
+                                                       }).text(this.localize(id))
+                                               )
+                                       );
+
+                                       var $characters = $('<div />', {'class': 'form-section htmlarea-character-map', id: id});
+                                       for (var charDefinition in this.maps[id]) {
+                                               if (this.maps[id].hasOwnProperty(charDefinition)) {
+                                                       var char = this.maps[id][charDefinition];
+                                                       $characters.append(
+                                                               $('<a />', {
+                                                                       'class': 'character btn btn-default',
+                                                                       hidefocus: 'on',
+                                                                       title: char[2] + ' (' + char[1] + ')'
+                                                               }).html(char[0])
+                                                       );
                                                }
                                        }
-                               });
+                                       $characters.on('click', 'a.character', function (e) {
+                                               return self.insertCharacter(e);
+                                       });
+
+                                       $tabContent.append(
+                                               $('<div />', {'class': 'tab-pane ' + (isFirst ? 'active' : ''), id: id}).append($characters)
+                                       );
+                               }
                        }
-                       return tabItems;
-               },
 
-               /**
-                * Render an array of characters
-                *
-                * @param object component: the box containing the characters
-                * @return void
-                */
-               renderMap: function (component) {
-                       component.tpl.overwrite(component.el, this.maps[component.itemId]);
-                       var self = this;
-                       Event.on(component.el.dom, 'click', function (event) { return self.insertCharacter(event); }, {delegate: 'a'});
+                       $finalMarkup = $('<div />').append($tabs, $tabContent);
+
+                       return $finalMarkup;
                },
 
                /**
                 * Handle the click on an item of the map
                 *
-                * @param object event: the jQuery event
-                * @return boolean
+                * @param {Event} event The jQuery event
+                * @return {Boolean}
                 */
                insertCharacter: function (event) {
                        Event.stopEvent(event);
@@ -471,9 +454,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                /**
                 * Insert the selected entity
                 *
-                * @param       string          entity: the entity to insert at the current selection
-                *
-                * @return      void
+                * @param {String} entity The entity to insert at the current selection
                 */
                insertEntity: function (entity) {
                        // Firefox, WebKit and IE convert '&nbsp;' to '&amp;nbsp;'
@@ -488,18 +469,6 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                 */
                resetFocus: function () {
                        this.restoreSelection();
-               },
-
-               /**
-                * Remove listeners before closing the window
-                */             
-               removeListeners: function () {
-                       var components = this.dialog.findByType('box');
-                       for (var i = components.length; --i > 0;) {
-                               if (components[i].el) {
-                                       Event.off(components[i].el.dom);
-                               }
-                       }                       
                }
        });
 
index dce5904..d70f803 100644 (file)
 /**
  * Context Menu Plugin for TYPO3 htmlArea RTE
  */
-define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
+define([
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
        '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 (Plugin, UserAgent, Util, Dom, Event) {
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event',
+       'jquery'
+], function (Plugin, UserAgent, Util, Dom, Event, $) {
 
        var ContextMenu = function (editor, pluginName) {
                this.constructor.super.call(this, editor, pluginName);
@@ -61,33 +63,53 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                 * This function gets called when the editor gets generated
                 */
                onGenerate: function() {
-                       var self = this;
+                       var self = this,
+                               $iframeDocument = $(this.editor.document.documentElement);
+
+                       this.menu =
+                               '<div id="contentMenu0" class="context-menu htmlarea-context-menu"></div>'
+                               + '<div id="contentMenu1" class="context-menu htmlarea-context-menu" style="display: block;"></div>'
+                       ;
+                       this.menuItems = {};
+
                        // Build the context menu
-                       this.menu = new Ext.menu.Menu(Util.applyIf({
-                               cls: 'htmlarea-context-menu',
-                               defaultType: 'menuitem',
-                               shadow: false,
-                               maxHeight: this.editor.iframe.height - this.editor.document.documentElement.clientHeight,
-                               listeners: {
-                                       itemClick: {
-                                               fn: this.onItemClick,
-                                               scope: this
-                                       },
-                                       show: {
-                                               fn: this.onShow,
-                                               scope: this
-                                       },
-                                       hide: {
-                                               fn: this.onHide,
-                                               scope: this
-                                       }
-                               },
-                               items: this.buildItemsConfig()
-                       }, this.pageTSConfiguration));
+                       this.buildContextMenu();
+
+                       $('body').on('click', '#contentMenu1 [data-type="menuitem"]', function(e) {
+                               e.preventDefault();
+
+                               self.onItemClick(self.menuItems[$(this).data('itemId')]);
+                               $('#contentMenu1').hide();
+                       }).on('click', '.t3js-ctx-menu-direction', function(e) {
+                               e.stopPropagation();
+
+                               var $me = $(this),
+                                       $firstGroupItem = $me.parent().find('.list-group-item:first'),
+                                       direction = $me.data('direction'),
+                                       itemHeight = $firstGroupItem.outerHeight(),
+                                       listGroup = $firstGroupItem.parent(),
+                                       scrollTop = direction === 'down'
+                                               ? listGroup.scrollTop() + itemHeight
+                                               : listGroup.scrollTop() - itemHeight;
+
+                               listGroup.scrollTop(scrollTop);
+                       });
+
+                       Event.on(this.editor.document.documentElement, 'click', function () {
+                               $('#contentMenu1').hide();
+                       });
+                       Event.on('body', 'click', function () {
+                               $('#contentMenu1').hide();
+                       });
                        // Monitor contextmenu clicks on the iframe
-                       Event.on(this.editor.document.documentElement, 'contextmenu', function (event) { return self.show(event, event.target); });
+                       Event.on(this.editor.document.documentElement, 'contextmenu', function (event) {
+                               return self.show(event, event.target);
+                       });
                        // Monitor editor being unloaded
-                       Event.one(this.editor.iframe.getIframeWindow(), 'unload', function (event) { self.onBeforeDestroy(event); return  true; });
+                       Event.one(this.editor.iframe.getIframeWindow(), 'unload', function () {
+                               $('#contentMenu1').remove();
+                               return true;
+                       });
 
                        this.mousePosition = {
                                x: 0,
@@ -97,54 +119,18 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                self.mousePosition.x = e.pageX;
                                self.mousePosition.y = e.pageY;
                        };
-                       Event.on(this.editor.document.documentElement, 'mousemove', onMouseUpdate);
-                       Event.on(this.editor.document.documentElement, 'mouseenter', onMouseUpdate);
-
-                       this.menu.constrainScroll = this.constrainScroll;
+                       $iframeDocument.on('mousemove mouseenter', this.editor.document.documentElement, onMouseUpdate);
                },
 
                /**
-                * This overrides the constrainScroll method of Ext.menu.Menu. The only difference here is that the Y position
-                * and the height is NOT recalculated even if maxHeight is set.
-                *
-                * @param {Number} y
-                * @returns {Number}
+                * Create the menu items config
                 */
-               constrainScroll: function(y) {
-                       var max, full = this.ul.setHeight('auto').getHeight(),
-                               returnY = y, normalY, parentEl, scrollTop, viewHeight;
-                       if (this.floating){
-                               parentEl = Ext.fly(this.el.dom.parentNode);
-                               scrollTop = parentEl.getScroll().top;
-                               viewHeight = parentEl.getViewSize().height;
-
-                               normalY = y - scrollTop;
-                               max = this.maxHeight ? this.maxHeight : viewHeight - normalY;
-                       } else {
-                               max = this.getHeight();
+               buildContextMenu: function () {
+                       // Initialize click menu container
+                       if ($('#contentMenu0').length === 0) {
+                               $('body').append(this.menu);
                        }
 
-                       if (this.maxHeight){
-                               max = Math.min(this.maxHeight, max);
-                       }
-                       if (full > max && max > 0){
-                               this.activeMax = max - this.scrollerHeight * 2 - this.el.getFrameWidth('tb') - Ext.num(this.el.shadowOffset, 0);
-                               this.ul.setHeight(this.activeMax);
-                               this.createScrollers();
-                               this.el.select('.x-menu-scroller').setDisplayed('');
-                       } else {
-                               this.ul.setHeight(full);
-                               this.el.select('.x-menu-scroller').setDisplayed('none');
-                       }
-                       this.ul.dom.scrollTop = 0;
-                       return returnY;
-               },
-
-               /**
-                * Create the menu items config
-                */
-               buildItemsConfig: function () {
-                       var itemsConfig = [];
                        // Walk through the editor toolbar configuration nested arrays: [ toolbar [ row [ group ] ] ]
                        var firstInGroup = true, convertedItemId;
                        var i, j ,k, n, m, p, row, group, itemId;
@@ -156,10 +142,9 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                        group = row[j];
                                        if (!firstInGroup) {
                                                // If a visible item was added to the line
-                                               itemsConfig.push({
-                                                               xtype: 'menuseparator',
-                                                               cls: 'separator'
-                                               });
+                                               this.menuItems['___' + j] = {
+                                                       type: 'menuseparator'
+                                               };
                                        }
                                        firstInGroup = true;
                                        // Add each item
@@ -172,15 +157,14 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                                        // xtype is set through applied button configuration
                                                        if (button && button.xtype === 'htmlareabutton' && !button.hideInContextMenu) {
                                                                itemId = button.getItemId();
-                                                               itemsConfig.push({
+                                                               this.menuItems[itemId] = {
+                                                                       type: 'menuitem',
                                                                        itemId: itemId,
-                                                                       cls: 'button',
-                                                                       overCls: 'hover',
                                                                        text: (button.contextMenuTitle ? button.contextMenuTitle : button.tooltip),
                                                                        iconCls: button.iconCls,
                                                                        helpText: (button.helpText ? button.helpText : this.localize(itemId + '-tooltip')),
                                                                        hidden: true
-                                                               });
+                                                               };
                                                                firstInGroup = false;
                                                        }
                                                }
@@ -189,21 +173,17 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                        }
                        // If a visible item was added
                        if (!firstInGroup) {
-                               itemsConfig.push({
-                                               xtype: 'menuseparator',
-                                               cls: 'separator'
-                               });
+                               this.menuItems['___9999'] = {
+                                       type: 'menuseparator'
+                               };
                        }
                         // Add special target delete item
-                       itemId = 'DeleteTarget';
-                       itemsConfig.push({
-                               itemId: itemId,
-                               cls: 'button',
-                               overCls: 'hover',
+                       this.menuItems['DeleteTarget'] = {
+                               type: 'menuitem',
+                               itemId: 'DeleteTarget',
                                iconCls: 'htmlarea-action-delete-item',
                                helpText: this.localize('Remove this node from the document')
-                       });
-                       return itemsConfig;
+                       };
                },
 
                /**
@@ -218,7 +198,6 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                 * Handler when the menu gets hidden
                 */
                onHide: function () {
-                       var self = this;
                        Event.off(this.editor.document.documentElement, 'mousedown.contextmeu');
                },
 
@@ -243,82 +222,123 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                        this.ranges = this.editor.getSelection().getRanges();
                        // Show the context menu
                        var iframePosition = Dom.getPosition(this.editor.iframe.getEl());
-                       this.menu.showAt([
-                               iframePosition.x + this.mousePosition.x,
-                               document.body.scrollTop + iframePosition.y + this.mousePosition.y
-                       ]);
+                       $('#contentMenu1').css({
+                               left: iframePosition.x + this.mousePosition.x + 'px',
+                               top: document.body.scrollTop + iframePosition.y + this.mousePosition.y + 'px'
+                       }).show();
                },
 
                /**
                 * Show items depending on context
                 */
                showContextItems: function (target) {
-                       var lastIsSeparator = false, lastIsButton = false, xtype, lastVisible;
-                       this.menu.cascade(function (menuItem) {
-                               xtype = menuItem.getXType();
-                               if (xtype === 'menuseparator') {
-                                       menuItem.setVisible(lastIsButton);
+                       var self = this,
+                               lastIsButton = false,
+                               lastVisible;
+
+                       $.each(this.menuItems, function(itemId, item) {
+                               if (item.type === 'menuseparator') {
+                                       item.hidden = !lastIsButton;
                                        lastIsButton = false;
-                               } else if (xtype === 'menuitem') {
-                                       var button = this.getButton(menuItem.getItemId());
+                               } else if (item.type === 'menuitem') {
+                                       var button = self.getButton(itemId);
                                        if (button) {
                                                var text = button.contextMenuTitle ? button.contextMenuTitle : button.tooltip;
-                                               if (menuItem.text != text) {
-                                                       menuItem.setText(text);
+                                               if (item.text !== text) {
+                                                       item.text = text;
                                                }
-                                               menuItem.helpText = button.helpText ? button.helpText : menuItem.helpText;
-                                               menuItem.setVisible(!button.disabled);
+                                               item.helpText = button.helpText ? button.helpText : item.helpText;
+                                               item.hidden = button.disabled;
                                                lastIsButton = lastIsButton || !button.disabled;
                                        } else {
                                                // Special target delete item
-                                               this.deleteTarget = target;
+                                               self.deleteTarget = target;
                                                if (/^(html|body)$/i.test(target.nodeName)) {
-                                                       this.deleteTarget = null;
+                                                       self.deleteTarget = null;
                                                } else if (/^(table|thead|tbody|tr|td|th|tfoot)$/i.test(target.nodeName)) {
-                                                       this.deleteTarget = Dom.getFirstAncestorOfType(target, 'table');
+                                                       self.deleteTarget = Dom.getFirstAncestorOfType(target, 'table');
                                                } else if (/^(ul|ol|dl|li|dd|dt)$/i.test(target.nodeName)) {
-                                                       this.deleteTarget = Dom.getFirstAncestorOfType(target, ['ul', 'ol', 'dl']);
+                                                       self.deleteTarget = Dom.getFirstAncestorOfType(target, ['ul', 'ol', 'dl']);
                                                }
-                                               if (this.deleteTarget) {
-                                                       menuItem.setVisible(true);
-                                                       menuItem.setText(this.localize('Remove the') + ' &lt;' + this.deleteTarget.nodeName.toLowerCase() + '&gt; ');
+                                               if (self.deleteTarget) {
+                                                       item.hidden = false;
+                                                       item.text = 'Remove the <' + self.deleteTarget.nodeName.toLowerCase() + '>';
                                                        lastIsButton = true;
                                                } else {
-                                                       menuItem.setVisible(false);
+                                                       item.hidden = true;
                                                }
                                        }
                                }
-                               if (!menuItem.hidden) {
-                                       lastVisible = menuItem;
+
+                               if (!item.hidden) {
+                                       lastVisible = item;
                                }
-                       }, this);
-                               // Hide the last item if it is a separator
-                       if (!lastIsButton) {
-                               lastVisible.setVisible(false);
+                       });
+
+                       if (!lastIsButton && typeof lastVisible) {
+                               lastVisible.hidden = true;
                        }
+
+                       // Render context menu items
+                       var $contentMenu = $('#contentMenu1'),
+                               maxHeight = Math.max(160, this.editor.iframe.height - this.editor.document.documentElement.clientHeight),
+                               $menuItems = $('<div />', {'class': 'list-group', style: 'max-height: ' + maxHeight + 'px; overflow: hidden;'});
+                       $.each(this.menuItems, function(_, item) {
+                               if (typeof item.hidden !== 'undefined' && item.hidden) {
+                                       return true;
+                               }
+
+                               if (item.type === 'menuseparator') {
+                                       $menuItems.append($('<span />', {'class': 'list-group-item list-group-item-divider', 'data-type': item.type}));
+                               } else if (item.type === 'menuitem') {
+                                       $menuItems.append(
+                                       $('<a />', {href: '#', 'class': 'list-group-item', 'data-type': item.type, 'data-item-id': item.itemId})
+                                               .text(item.text)
+                                               .prepend(
+                                                       $('<img />', {
+                                                               src: '/typo3/sysext/backend/Resources/Public/Images/clear.gif', 'class': 'ctx-menu-item-icon ' + item.iconCls
+                                                       })
+                                               )
+                                       );
+                               }
+                       });
+
+                       $contentMenu.empty().append(
+                               $('<button />', {
+                                       'class': 'btn-block ctx-menu-direction ctx-menu-direction-top t3js-ctx-menu-direction',
+                                       type: 'button',
+                                       'data-direction': 'up'
+                               }),
+                               $menuItems,
+                               $('<button />', {
+                                       'class': 'btn-block ctx-menu-direction ctx-menu-direction-bottom t3js-ctx-menu-direction',
+                                       type: 'button',
+                                       'data-direction': 'down'
+                               })
+                       );
                },
 
                /**
                 * Handler invoked when a menu item is clicked on
                 */
-               onItemClick: function (item, event) {
+               onItemClick: function (item) {
                        this.editor.getSelection().setRanges(this.ranges);
-                       var button = this.getButton(item.getItemId());
+                       var button = this.getButton(item.itemId);
                        if (button) {
                                /**
                                 * @event HTMLAreaEventContextMenu
                                 * Fires when the button is triggered from the context menu
                                 */
                                Event.trigger(button, 'HTMLAreaEventContextMenu', [button]);
-                       } else if (item.getItemId() === 'DeleteTarget') {
-                                       // Do not leave a non-ie table cell empty
+                       } else if (item.itemId === 'DeleteTarget') {
+                               // Do not leave a non-ie table cell empty
                                var parent = this.deleteTarget.parentNode;
                                parent.normalize();
                                if (!UserAgent.isIE && /^(td|th)$/i.test(parent.nodeName) && parent.childNodes.length == 1) {
-                                               // Do not leave a non-ie table cell empty
+                                       // Do not leave a non-ie table cell empty
                                        parent.appendChild(this.editor.document.createElement('br'));
                                }
-                                       // Try to find a reasonable replacement selection
+                               // Try to find a reasonable replacement selection
                                var nextSibling = this.deleteTarget.nextSibling;
                                var previousSibling = this.deleteTarget.previousSibling;
                                if (nextSibling) {
@@ -329,17 +349,8 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                Dom.removeFromParent(this.deleteTarget);
                                this.editor.updateToolbar();
                        }
-               },
-
-               /**
-                * Handler invoked when the editor is about to be destroyed
-                */
-               onBeforeDestroy: function (event) {
-                       this.menu.removeAll(true);
-                       this.menu.destroy();
                }
        });
 
        return ContextMenu;
-
-});
+});
\ No newline at end of file
index 650d0e1..de13c31 100644 (file)
 /**
  * Image Plugin for TYPO3 htmlArea RTE
  */
-define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
+define([
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
        'TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
-       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util'],
-       function (Plugin, UserAgent, Util) {
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util',
+       'jquery',
+       'TYPO3/CMS/Backend/Modal',
+       'TYPO3/CMS/Backend/Notification',
+       'TYPO3/CMS/Backend/Severity'
+], function (Plugin, UserAgent, Util, $, Modal, Notification, Severity) {
 
        var DefaultImage = function (editor, pluginName) {
                this.constructor.super.call(this, editor, pluginName);
@@ -44,17 +49,18 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                                ++padding;
                                        }
                                }
-                               if (layout == 3) {
+                               if (layout === 3) {
                                        this.removeItems.push('layout');
                                }
-                               if (layout == 4) {
+                               if (layout === 4) {
                                        this.removeItems.push('padding');
                                }
                                this.removeItems = new RegExp( '^(' + this.removeItems.join('|') + ')$', 'i');
                        } else {
                                this.removeItems = new RegExp( '^(none)$', 'i');
                        }
-                       /*
+
+                       /**
                         * Registering plugin "About" information
                         */
                        var pluginInformation = {
@@ -67,7 +73,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                license         : 'GPL'
                        };
                        this.registerPluginInformation(pluginInformation);
-                       /*
+                       /**
                         * Registering the button
                         */
                        var buttonId = 'InsertImage';
@@ -81,34 +87,17 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                        };
                        this.registerButton(buttonConfiguration);
                        return true;
-                },
-               /*
-                * Sets of default configuration values for dialogue form fields
-                */
-               configDefaults: {
-                       combo: {
-                               editable: true,
-                               selectOnFocus: true,
-                               typeAhead: true,
-                               triggerAction: 'all',
-                               forceSelection: true,
-                               mode: 'local',
-                               valueField: 'value',
-                               displayField: 'text',
-                               helpIcon: true,
-                               tpl: '<tpl for="."><div ext:qtip="{value}" style="text-align:left;font-size:11px;" class="x-combo-list-item">{text}</div></tpl>'
-                       }
                },
-               /*
+               /**
                 * This function gets called when the button was pressed.
                 *
-                * @param       object          editor: the editor instance
-                * @param       string          id: the button id or the key
+                * @param {Object} editor the editor instance
+                * @param {String} id: the button id or the key
                 *
-                * @return      boolean         false if action is completed
+                * @return {Boolean} false if action is completed
                 */
                onButtonPress: function(editor, id) {
-                               // Could be a button or its hotkey
+                       // Could be a button or its hotkey
                        var buttonId = this.translateHotKey(id);
                        buttonId = buttonId ? buttonId : id;
                        this.image = this.editor.getSelection().getParentElement();
@@ -142,306 +131,221 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                        cssFloat: ''
                                };
                        }
-                               // Open dialogue window
+                       // Open dialogue window
                        this.openDialogue(
                                buttonId,
-                               this.getButton(buttonId).tooltip,
-                               this.getWindowDimensions(
-                                       {
-                                               width: 460,
-                                               height:300
-                                       },
-                                       buttonId
-                               ),
-                               this.buildTabItems()
+                               this.getButton(buttonId).tooltip
                        );
                        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
+                * @param {String} buttonId: the button id
+                * @param {String} title: the window title
                 */
-               openDialogue: function (buttonId, title, dimensions, tabItems) {
-                       this.dialog = new Ext.Window({
-                               title: this.localize(title) || 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',
-                                       itemId: 'tabpanel',
-                                       activeTab: 0,
-                                       defaults: {
-                                               xtype: 'container',
-                                               layout: 'form',
-                                               defaults: {
-                                                       labelWidth: 100
-                                               }
-                                       },
-                                       listeners: {
-                                               tabchange: {
-                                                       fn: this.syncHeight,
-                                                       scope: this
-                                               }
-                                       },
-                                       items: tabItems
-                               },
-                               buttons: [
-                                       this.buildButtonConfig('OK', this.onOK),
-                                       this.buildButtonConfig('Cancel', this.onCancel)
-                               ]
-                       });
-                       this.show();
+               openDialogue: function (buttonId, title) {
+                       this.dialog = Modal.show(this.localize(title), this.buildTabItems(), Severity.notice, [
+                               this.buildButtonConfig('Next', $.proxy(this.onOK, this), true),
+                               this.buildButtonConfig('Done', $.proxy(this.onCancel, this), false, Severity.notice)
+                       ]);
+                       this.dialog.on('modal-dismiss', $.proxy(this.onClose, this));
                },
-               /*
+
+               /**
                 * Build the configuration of the the tab items
                 *
-                * @return      array   the configuration array of tab items
+                * @return {Array} the configuration array of tab items
                 */
                buildTabItems: function () {
-                       var tabItems = [];
-                               // General tab
-                       tabItems.push({
-                               title: this.localize('General'),
-                               items: [{
-                                               xtype: 'fieldset',
-                                               defaultType: 'textfield',
-                                               defaults: {
-                                                       helpIcon: true,
-                                                       width: 300,
-                                                       labelSeparator: ''
-                                               },
-                                               items: [{
-                                                               itemId: 'url',
-                                                               fieldLabel: this.localize('Image URL:'),
-                                                               value: this.parameters.url,
-                                                               helpTitle: this.localize('Enter the image URL here')
-                                                       },{
-                                                               itemId: 'alt',
-                                                               fieldLabel: this.localize('Alternate text:'),
-                                                               value: this.parameters.alt,
-                                                               helpTitle: this.localize('For browsers that dont support images')
-                                                       }
-                                               ]
-                                       },{
-                                               xtype: 'fieldset',
-                                               title: this.localize('Image Preview'),
-                                               items: [{
-                                                                       // The preview iframe
-                                                               xtype: 'box',
-                                                               itemId: 'image-preview',
-                                                               autoEl: {
-                                                                       name: 'ipreview',
-                                                                       tag: 'iframe',
-                                                                       cls: 'image-preview',
-                                                                       src: this.parameters.url
-                                                               }
-                                                       },{
-                                                               xtype: 'button',
-                                                               minWidth: 150,
-                                                               text: this.localize('Preview'),
-                                                               itemId: 'preview',
-                                                               style: {
-                                                                       marginTop: '5px',
-                                                                       'float': 'right'
-                                                               },
-                                                               listeners: {
-                                                                       click: {
-                                                                               fn: this.onPreviewClick,
-                                                                               scope: this
-                                                                       }
-                                                               }
-                                                       }
-                                               ]
-                                       }
-                               ]
-                       });
-                               // Layout tab
+                       var $finalMarkup,
+                               $tabs = $('<ul />', {'class': 'nav nav-tabs', role: 'tablist'}),
+                               $tabContent = $('<div />', {'class': 'tab-content'});
+
+                       $tabs.append(
+                               $('<li />', {'class': 'active'}).append(
+                                       $('<a />', {href: '#general', 'aria-controls': 'general', role: 'tab', 'data-toggle': 'tab'}).text(this.localize('General'))
+                               )
+                       );
+
+                       $tabContent.append(
+                               $('<div />', {'class': 'tab-pane active', id: 'general'}).append(
+                                       $('<fieldset />', {'class': 'form-section'}).append(
+                                               $('<div />', {'class': 'form-group'}).append(
+                                                       $('<label />', {'class': 'col-sm-2'}).text(this.localize('Image URL:')),
+                                                       $('<div />', {'class': 'col-sm-10'}).append(
+                                                               $('<input />', {name: 'url', 'class': 'form-control', value: this.parameters.url})
+                                                       )
+                                               ),
+                                               $('<div />', {'class': 'form-group'}).append(
+                                                       $('<label />', {'class': 'col-sm-2'}).text(this.localize('Alternate text:')),
+                                                       $('<div />', {'class': 'col-sm-10'}).append(
+                                                               $('<input />', {name: 'alt', 'class': 'form-control', value: this.parameters.alt})
+                                                       )
+                                               )
+                                       ),
+                                       $('<fieldset />', {'class': 'form-section'}).append(
+                                               $('<h4 />', {'class': 'form-section-headline'}).text(this.localize('Image Preview')),
+                                               $('<div />').append(
+                                                       $('<iframe />', {name: 'ipreview', 'class': 'image-preview', src: this.parameters.url})
+                                               ),
+                                               $('<button />', {'class': 'btn btn-block'})
+                                                       .text(this.localize('Preview'))
+                                                       .on('click', $.proxy(this.onPreviewClick, this))
+                                       )
+                               )
+                       );
+
+                       // Layout tab
                        if (!this.removeItems.test('layout')) {
-                               tabItems.push({
-                                       title: this.localize('Layout'),
-                                       items: [{
-                                                       xtype: 'fieldset',
-                                                       defaultType: 'textfield',
-                                                       defaults: {
-                                                               helpIcon: true,
-                                                               width: 250,
-                                                               labelSeparator: ''
-                                                       },
-                                                       items: [
-                                                               Util.apply({
-                                                                       xtype: 'combo',
-                                                                       fieldLabel: this.localize('Image alignment:'),
-                                                                       itemId: 'align',
-                                                                       value: this.parameters.align,
-                                                                       helpTitle: this.localize('Positioning of this image'),
-                                                                       store: new Ext.data.ArrayStore({
-                                                                               autoDestroy:  true,
-                                                                               fields: [ { name: 'text'}, { name: 'value'}],
-                                                                               data: [
-                                                                                       [this.localize('Not set'), ''],
-                                                                                       [this.localize('Bottom'), 'bottom'],
-                                                                                       [this.localize('Middle'), 'middle'],
-                                                                                       [this.localize('Top'), 'top']
-                                                                               ]
-                                                                       }),
-                                                                       hidden: this.removeItems.test('align'),
-                                                                       hideLabel: this.removeItems.test('align')
-                                                                       }, this.configDefaults['combo'])
-                                                               ,{
-                                                                       itemId: 'border',
-                                                                       fieldLabel: this.localize('Border thickness:'),
-                                                                       width: 100,
-                                                                       value: this.parameters.border,
-                                                                       helpTitle: this.localize('Leave empty for no border'),
-                                                                       hidden: this.removeItems.test('border'),
-                                                                       hideLabel: this.removeItems.test('border')
-                                                               },
-                                                               Util.apply({
-                                                                       xtype: 'combo',
-                                                                       fieldLabel: this.localize('Float:'),
-                                                                       itemId: 'cssFloat',
-                                                                       value: this.parameters.cssFloat,
-                                                                       helpTitle: this.localize('Where the image should float'),
-                                                                       store: new Ext.data.ArrayStore({
-                                                                               autoDestroy:  true,
-                                                                               fields: [ { name: 'text'}, { name: 'value'}],
-                                                                               data: [
-                                                                                       [this.localize('Not set'), ''],
-                                                                                       [this.localize('Non-floating'), 'none'],
-                                                                                       [this.localize('Left'), 'left'],
-                                                                                       [this.localize('Right'), 'right']
-                                                                               ]
-                                                                       }),
-                                                                       hidden: this.removeItems.test('float'),
-                                                                       hideLabel: this.removeItems.test('float')
-                                                                       }, this.configDefaults['combo'])
-                                                       ]
-                                       }]
-                               });
+                               $tabs.append(
+                                       $('<li />').append(
+                                               $('<a />', {href: '#layout', 'aria-controls': 'layout', role: 'tab', 'data-toggle': 'tab'}).text(this.localize('Layout'))
+                                       )
+                               );
+
+                               $tabContent.append(
+                                       $('<div />', {'class': 'tab-pane active', id: 'layout'}).append(
+                                               $('<fieldset />', {'class': 'form-section'}).append(
+                                                       $('<div />', {'class': 'form-group'}).append(
+                                                               $('<label />', {'class': 'col-sm-2'}).text(this.localize('Image alignment:')),
+                                                               $('<div />', {'class': 'col-sm-10'}).append(
+                                                                       $('<select />', {name: 'align', 'class': 'form-control'}).append(
+                                                                               $('<option />', {value: ''}).text(this.localize('Not set')),
+                                                                               $('<option />', {value: 'bottom'}).text(this.localize('Bottom')),
+                                                                               $('<option />', {value: 'middle'}).text(this.localize('Middle')),
+                                                                               $('<option />', {value: 'top'}).text(this.localize('Top'))
+                                                                       )
+                                                               )
+                                                       ).toggle(!this.removeItems.test('align')),
+                                                       $('<div />', {'class': 'form-group'}).append(
+                                                               $('<label />', {'class': 'col-sm-2'}).text(this.localize('Border thickness:')),
+                                                               $('<div />', {'class': 'col-sm-10'}).append(
+                                                                       $('<input />', {name: 'border', type: 'number', 'class': 'form-control', value: this.parameters.border})
+                                                               )
+                                                       ).toggle(!this.removeItems.test('border')),
+                                                       $('<div />', {'class': 'form-group'}).append(
+                                                               $('<label />', {'class': 'col-sm-2'}).text(this.localize('Float:')),
+                                                               $('<div />', {'class': 'col-sm-10'}).append(
+                                                                       $('<select />', {name: 'cssFloat', 'class': 'form-control'}).append(
+                                                                               $('<option />', {value: ''}).text(this.localize('Not set')),
+                                                                               $('<option />', {value: 'none'}).text(this.localize('Non-floating')),
+                                                                               $('<option />', {value: 'left'}).text(this.localize('Left')),
+                                                                               $('<option />', {value: 'right'}).text(this.localize('Right'))
+                                                                       )
+                                                               )
+                                                       )
+                                               )
+                                       )
+                               );
                        }
                                // Padding tab
                        if (!this.removeItems.test('padding')) {
-                               tabItems.push({
-                                       title: this.localize('Spacing and padding'),
-                                       items: [{
-                                                       xtype: 'fieldset',
-                                                       defaultType: 'textfield',
-                                                       defaults: {
-                                                               helpIcon: true,
-                                                               width: 100,
-                                                               labelSeparator: ''
-                                                       },
-                                                       items: [{
-                                                                       itemId: 'paddingTop',
-                                                                       fieldLabel: this.localize('Top:'),
-                                                                       value: this.parameters.paddingTop,
-                                                                       helpTitle: this.localize('Top padding'),
-                                                                       hidden: this.removeItems.test('paddingTop'),
-                                                                       hideLabel: this.removeItems.test('paddingTop')
-                                                               },{
-                                                                       itemId: 'paddingRight',
-                                                                       fieldLabel: this.localize('Right:'),
-                                                                       value: this.parameters.paddingRight,
-                                                                       helpTitle: this.localize('Right padding'),
-                                                                       hidden: this.removeItems.test('paddingRight'),
-                                                                       hideLabel: this.removeItems.test('paddingRight')
-                                                               },{
-                                                                       itemId: 'paddingBottom',
-                                                                       fieldLabel: this.localize('Bottom:'),
-                                                                       value: this.parameters.paddingBottom,
-                                                                       helpTitle: this.localize('Bottom padding'),
-                                                                       hidden: this.removeItems.test('paddingBottom'),
-                                                                       hideLabel: this.removeItems.test('paddingBottom')
-                                                               },{
-                                                                       itemId: 'paddingLeft',
-                                                                       fieldLabel: this.localize('Left:'),
-                                                                       value: this.parameters.paddingLeft,
-                                                                       helpTitle: this.localize('Left padding'),
-                                                                       hidden: this.removeItems.test('paddingLeft'),
-                                                                       hideLabel: this.removeItems.test('paddingLeft')
-                                                               }
-                                                       ]
-                                       }]
-                               });
+                               $tabs.append(
+                                       $('<li />').append(
+                                               $('<a />', {href: '#spacing', 'aria-controls': 'spacing', role: 'tab', 'data-toggle': 'tab'}).text(this.localize('Spacing and padding'))
+                                       )
+                               );
+
+                               $tabContent.append(
+                                       $('<div />', {'class': 'tab-pane active', id: 'spacing'}).append(
+                                               $('<fieldset />', {'class': 'form-section'}).append(
+                                                       $('<div />', {'class': 'form-group'}).append(
+                                                               $('<label />', {'class': 'col-sm-2'}).text(this.localize('Top:')),
+                                                               $('<div />', {'class': 'col-sm-10'}).append(
+                                                                       $('<input />', {name: 'paddingTop', 'class': 'form-control', value: this.parameters.paddingTop})
+                                                               )
+                                                       ).toggle(!this.removeItems.test('paddingTop')),
+                                                       $('<div />', {'class': 'form-group'}).append(
+                                                               $('<label />', {'class': 'col-sm-2'}).text(this.localize('Right:')),
+                                                               $('<div />', {'class': 'col-sm-10'}).append(
+                                                                       $('<input />', {name: 'paddingRight', 'class': 'form-control', value: this.parameters.paddingRight})
+                                                               )
+                                                       ).toggle(!this.removeItems.test('paddingRight')),
+                                                       $('<div />', {'class': 'form-group'}).append(
+                                                               $('<label />', {'class': 'col-sm-2'}).text(this.localize('Bottom:')),
+                                                               $('<div />', {'class': 'col-sm-10'}).append(
+                                                                       $('<input />', {name: 'paddingBottom', 'class': 'form-control', value: this.parameters.paddingBottom})
+                                                               )
+                                                       ).toggle(!this.removeItems.test('paddingBottom')),
+                                                       $('<div />', {'class': 'form-group'}).append(
+                                                               $('<label />', {'class': 'col-sm-2'}).text(this.localize('Left:')),
+                                                               $('<div />', {'class': 'col-sm-10'}).append(
+                                                                       $('<input />', {name: 'paddingLeft', 'class': 'form-control', value: this.parameters.paddingLeft})
+                                                               )
+                                                       ).toggle(!this.removeItems.test('paddingLeft'))
+                                               )
+                                       )
+                               );
                        }
-                       return tabItems;
+                       $finalMarkup = $('<div />').append($tabs, $tabContent);
+
+                       return $finalMarkup;
                },
-               /*
+               /**
                 * Handler invoked when the Preview button is clicked
                 */
                onPreviewClick: function () {
-                       var tabPanel = this.dialog.find('itemId', 'tabpanel')[0];
-                       var urlField = this.dialog.find('itemId', 'url')[0];
-                       var url = urlField.getValue().trim();
+                       var $urlField = this.dialog.find('[name="url"]');
+                       var url = $.trim($urlField.val());
                        if (url) {
                                try {
                                        window.ipreview.location.replace(url);
                                } catch (e) {
-                                       TYPO3.Dialog.InformationDialog({
-                                               title: this.localize('Image Preview'),
-                                               msg: this.localize('image_url_invalid'),
-                                               fn: function () { tabPanel.setActiveTab(0); urlField.focus(); }
-                                       });
+                                       Notification.info(
+                                               this.localize('Image Preview'),
+                                               this.localize('image_url_invalid')
+                                       );
+                                       this.dialog.find('.nav-tabs a[href="#general"]').tab('show');
+                                       $urlField.focus();
                                }
                        } else {
-                               TYPO3.Dialog.InformationDialog({
-                                       title: this.localize('Image Preview'),
-                                       msg: this.localize('image_url_first'),
-                                       fn: function () { tabPanel.setActiveTab(0); urlField.focus(); }
-                               });
+                               Notification.info(
+                                       this.localize('Image Preview'),
+                                       this.localize('image_url_first')
+                               );
+                               this.dialog.find('.nav-tabs a[href="#general"]').tab('show');
+                               $urlField.focus();
                        }
+
                        return false;
                },
-               /*
+               /**
                 * Handler invoked when the OK button is clicked
                 */
                onOK: function () {
-                       var urlField = this.dialog.find('itemId', 'url')[0];
-                       var url = urlField.getValue().trim();
+                       var $urlField = this.dialog.find('[name="url"]');
+                       var url = $.trim($urlField.val());
                        if (url) {
-                               var fieldNames = ['url', 'alt', 'align', 'border', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft', 'cssFloat'], fieldName;
+                               var fieldNames = ['url', 'alt', 'align', 'border', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft', 'cssFloat'],
+                                       fieldName;
                                for (var i = fieldNames.length; --i >= 0;) {
                                        fieldName = fieldNames[i];
-                                       var field = this.dialog.find('itemId', fieldName)[0];
-                                       if (field && !field.hidden) {
-                                               this.parameters[fieldName] = field.getValue();
+                                       var field = this.dialog.find('[name="' + fieldName + '"]');
+                                       if (field && field.is(':visible')) {
+                                               this.parameters[fieldName] = field.val();
                                        }
                                }
                                this.insertImage();
                                this.close();
                        } else {
-                               var tabPanel = this.dialog.find('itemId', 'tabpanel')[0];
-                               TYPO3.Dialog.InformationDialog({
-                                       title: this.localize('image_url'),
-                                       msg: this.localize('image_url_required'),
-                                       fn: function () { tabPanel.setActiveTab(0); urlField.focus(); }
-                               });
+                               Notification.info(
+                                       this.localize('image_url'),
+                                       this.localize('image_url_required')
+                               );
+                               this.dialog.find('.nav-tabs a[href="#general"]').tab('show');
+                               $urlField.focus();
                        }
                        return false;
                },
-               /*
+               /**
                 * Insert the image
                 */
                insertImage: function() {
                        this.restoreSelection();
                        var image = this.image;
                        if (!image) {
-                               var range = this.editor.getSelection().createRange();
+                               this.editor.getSelection().createRange();
                                this.editor.getSelection().execCommand('InsertImage', false, this.parameters.url);
                                if (UserAgent.isWebKit) {
                                        this.editor.getDomNode().cleanAppleStyleSpans(this.editor.document.body);
@@ -514,5 +418,4 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
        });
 
        return DefaultImage;
-
-});
+});
\ No newline at end of file
index 9e8d6b0..4e7421b 100644 (file)
 /**
  * Default Link Plugin for TYPO3 htmlArea RTE
  */
-define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
+define([
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
        'TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
        'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util',
-       'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/DOM'],
-       function (Plugin, UserAgent, Util, Dom) {
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/DOM',
+       'jquery',
+       'TYPO3/CMS/Backend/Modal',
+       'TYPO3/CMS/Backend/Notification',
+       'TYPO3/CMS/Backend/Severity'
+], function (Plugin, UserAgent, Util, Dom, $, Modal, Notification, Severity) {
 
        var DefaultLink = function (editor, pluginName) {
                this.constructor.super.call(this, editor, pluginName);
@@ -47,8 +52,8 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                license         : 'GPL'
                        };
                        this.registerPluginInformation(pluginInformation);
-                       /*
-                        * Registering the buttons
+                       /**
+                        * Registering the button
                         */
                        var buttonList = this.buttonList, buttonId;
                        for (var i = 0; i < buttonList.length; ++i) {
@@ -78,39 +83,21 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                ],
 
                /**
-                * Sets of default configuration values for dialogue form fields
-                */
-               configDefaults: {
-                       combo: {
-                               editable: true,
-                               selectOnFocus: true,
-                               typeAhead: true,
-                               triggerAction: 'all',
-                               forceSelection: true,
-                               mode: 'local',
-                               valueField: 'value',
-                               displayField: 'text',
-                               helpIcon: true,
-                               tpl: '<tpl for="."><div ext:qtip="{value}" style="text-align:left;font-size:11px;" class="x-combo-list-item">{text}</div></tpl>'
-                       }
-               },
-
-               /**
                 * This function gets called when the editor is generated
                 */
                onGenerate: function () {
                },
-               /*
+               /**
                 * This function gets called when the 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 {Object} editor: the editor instance
+                * @param {String} id: the button id or the key
+                * @param {Object} target: the target element of the contextmenu event, when invoked from the context menu
                 *
-                * @return      boolean         false if action is completed
+                * @return {Boolean} false if action is completed
                 */
                onButtonPress: function (editor, id, target) {
-                               // Could be a button or its hotkey
+                       // Could be a button or its hotkey
                        var buttonId = this.translateHotKey(id);
                        buttonId = buttonId ? buttonId : id;
                        this.link = this.editor.getSelection().getFirstAncestorOfType('a');
@@ -142,173 +129,132 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                                // Open dialogue window
                                        this.openDialogue(
                                                buttonId,
-                                               this.getButton(buttonId).tooltip,
-                                               this.getWindowDimensions(
-                                                       {
-                                                               width: 470,
-                                                               height:150
-                                                       },
-                                                       buttonId
-                                               )
+                                               this.getButton(buttonId).tooltip
                                        );
                                        break;
                        }
                        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 {String} buttonId: the button id
+                * @param {String} title: the window title
+                */
+               openDialogue: function (buttonId, title) {
+                       this.dialog = Modal.show(this.localize(title), this.generateDialogContent(), Severity.notice, [
+                               this.buildButtonConfig('Next', $.proxy(this.onOK, this), true),
<