[TASK] RTE: Migrate toolbar button to plain JavaScript 32/35532/4
authorStanislas Rolland <typo3@sjbr.ca>
Wed, 17 Dec 2014 01:25:31 +0000 (20:25 -0500)
committerStanislas Rolland <typo3@sjbr.ca>
Wed, 17 Dec 2014 02:07:12 +0000 (03:07 +0100)
Releases: master
Resolves: #63946
Change-Id: Ib9230c9b9317ced27f6b293514eacd98db1ba842
Reviewed-on: http://review.typo3.org/35532
Reviewed-by: Stanislas Rolland <typo3@sjbr.ca>
Tested-by: Stanislas Rolland <typo3@sjbr.ca>
typo3/sysext/rtehtmlarea/Classes/RteHtmlAreaBase.php
typo3/sysext/rtehtmlarea/Resources/Public/Css/Skin/htmlarea.css
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Configuration/Config.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Editor/Iframe.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Editor/StatusBar.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Editor/Toolbar.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Button.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Toolbar/Button.js [new file with mode: 0644]
typo3/sysext/t3skin/rtehtmlarea/htmlarea.css

index 6bd50df..90680e9 100644 (file)
@@ -822,7 +822,7 @@ class RteHtmlAreaBase extends \TYPO3\CMS\Backend\Rte\AbstractRte {
                        'DOM/Selection',
                        'DOM/Walker',
                        'Configuration/Config',
-                       'Extjs/ux/Button',
+                       'Toolbar/Button',
                        'Toolbar/ToolbarText',
                        'Extjs/ux/Combo',
                        'Extjs/ColorPalette',
@@ -1560,5 +1560,4 @@ class RteHtmlAreaBase extends \TYPO3\CMS\Backend\Rte\AbstractRte {
                }
                return implode('; ', $nStyle);
        }
-
 }
index f85abe3..979ffc7 100644 (file)
        padding-bottom: 10px;
 }
 /* Selectors for the editor toolbar */
+.htmlarea .unselectable,
+.htmlarea .unselectable * {
+       user-select: none;
+       -o-user-select: none;
+       -ms-user-select: none;
+       -moz-user-select: -moz-none;
+       -webkit-user-select: none;
+       cursor: default;
+}
 .htmlarea .toolbar {
        cursor: default;
        border-bottom: 0;
        border-color: transparent;
        border-style: solid;
        border-width: 1px;
+       color: ButtonText;
+       cursor: pointer;
        float: left;
        height: 23px;
        margin: 0 0 3px 0;
        padding: 0;
 }
-.htmlarea .toolbar .x-btn-text {
-       background-image: url("../../Images/Sprites/actions.png");
+.htmlarea .toolbar .button button {
+       background-color: transparent;
+       border: 0 none;
+       cursor: pointer;
+       margin: 0;
+       -moz-outline: 0 none;
+       outline: 0 none;
+       overflow: visible;
+       padding-left: 3px;
+       padding-right: 3px;
+}
+.htmlarea .toolbar .btn-text {
+       background-image: url("images/sprites/actions.png");
+       background-position: center;
        background-repeat: no-repeat;
        width: 20px;
        height: 18px;
        padding: 0;
        margin: 1px 0 1px 2px;
 }
+.htmlarea .toolbar .buttonHover {
+       background-color: #DCDCDC;
+}
 .htmlarea .toolbar .buttonActive,
 .htmlarea .toolbar .buttonPressed,
 .htmlarea-context-menu .buttonActive {
index de4f68a..589e46c 100644 (file)
@@ -79,8 +79,7 @@ define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Configuration/Config',
                                textMode: false,
                                selection: false,
                                dialog: false,
-                               hidden: false,
-                               hideMode: 'display'
+                               hidden: false
                        },
                        htmlareabutton: {
                                cls: 'button',
index d4a740f..c802103 100644 (file)
@@ -776,8 +776,8 @@ define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Editor/Iframe',
                        Event.off(this.document.documentElement);
                        // Cleaning references to DOM in order to avoid IE memory leaks
                        this.document = null;
-                       this.el = null;
                        delete this.el;
+                       this.el = null;
                }
        };
 
index 2732a13..6d11634 100644 (file)
@@ -342,8 +342,8 @@ define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Editor/StatusBar',
                        }
                        this.statusBarTree = null;
                        this.statusBarWordCount = null;
-                       this.el = null;
                        delete this.el;
+                       this.el = null;
                }
        };
 
index f6e1af9..bf4d60f 100644 (file)
@@ -19,7 +19,7 @@ define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Editor/Toolbar',
        'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/DOM',
        'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event',
        'TYPO3/CMS/Rtehtmlarea/HTMLArea/Extjs/ux/Combo',
-       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Extjs/ux/Button',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Toolbar/Button',
        'TYPO3/CMS/Rtehtmlarea/HTMLArea/Toolbar/ToolbarText'],
        function (Util, Dom, Event, Combo, Button, ToolbarText) {
 
@@ -166,14 +166,8 @@ define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Editor/Toolbar',
                                                this.el.insertBefore(textDiv, wrapDiv);
                                        }
                                        break;
-                               case 'htmlareabutton':
+                               default:
                                        item.render(this.el);
-                                       var itemDiv = this.el.appendChild(item.getEl().dom);
-                                       Dom.addClass(item.getEl().dom, 'x-form-item');
-                                       break;
-                               case 'htmlareatoolbartext':
-                                       item.render(this.el);
-                                       break;
                        }
                },
 
@@ -290,6 +284,9 @@ define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Editor/Toolbar',
                                                this.items[itemId].destroy();
                                        } catch (e) {}
                                }
+                               if (typeof this.items[itemId].onBeforeDestroy === 'function') {
+                                       this.items[itemId].onBeforeDestroy();
+                               }
                        }
                }
        };
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Button.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Extjs/ux/Button.js
deleted file mode 100644 (file)
index c42099f..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * Ext.ux.HTMLAreaButton extends Ext.Button
- */
-define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Extjs/ux/Button',
-       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
-       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event'],
-       function (UserAgent, Event) {
-
-       var Button = Ext.extend(Ext.Button, {
-
-               /**
-                * Component initialization
-                */
-               initComponent: function () {
-                       this.template = new Ext.Template(
-                               '<div id="{4}" class="x-btn {3}">',
-                               '<em class="{2} x-unselectable" unselectable="on"><button type="{0}"></button></em>',
-                               '</div>');
-                       this.template.compile();
-                       Button.superclass.initComponent.call(this);
-                       this.addListener({
-                               afterrender: {
-                                       fn: this.initEventListeners,
-                                       single: true
-                               }
-                       });
-               },
-
-               /**
-                * Initialize listeners
-                */
-               initEventListeners: function () {
-                       var self = this;
-                       Event.on(this, 'HTMLAreaEventHotkey', function (event, key, keyEvent) { return self.onHotKey(key, keyEvent); });
-                       Event.on(this, 'HTMLAreaEventContextMenu', function (event, button) { return self.onButtonClick(button, event); });
-                       this.setHandler(this.onButtonClick, this);
-                       // Monitor toolbar updates in order to refresh the state of the button
-                       Event.on(this.getToolbar(), 'HTMLAreaEventToolbarUpdate', function (event, mode, selectionEmpty, ancestors, endPointsInSameBlock) { Event.stopEvent(event); self.onUpdateToolbar(mode, selectionEmpty, ancestors, endPointsInSameBlock); return false; });
-               },
-
-               /**
-                * Get a reference to the editor
-                */
-               getEditor: function() {
-                       return RTEarea[this.toolbar.editorId].editor;
-               },
-
-               /**
-                * Get a reference to the toolbar
-                */
-               getToolbar: function() {
-                       return this.toolbar;
-               },
-
-               /**
-                * Add properties and function to set button active or not depending on current selection
-                */
-               inactive: true,
-               activeClass: 'buttonActive',
-               setInactive: function (inactive) {
-                       this.inactive = inactive;
-                       return inactive ? this.removeClass(this.activeClass) : this.addClass(this.activeClass);
-               },
-
-               /**
-                * Determine if the button should be enabled based on the current selection and context configuration property
-                */
-               isInContext: function (mode, selectionEmpty, ancestors) {
-                       var editor = this.getEditor();
-                       var inContext = true;
-                       if (mode === 'wysiwyg' && this.context) {
-                               var attributes = [],
-                                       contexts = [];
-                               if (/(.*)\[(.*?)\]/.test(this.context)) {
-                                       contexts = RegExp.$1.split(',');
-                                       attributes = RegExp.$2.split(',');
-                               } else {
-                                       contexts = this.context.split(',');
-                               }
-                               contexts = new RegExp( '^(' + contexts.join('|') + ')$', 'i');
-                               var matchAny = contexts.test('*');
-                               var i, j, n;
-                               for (i = 0, n = ancestors.length; i < n; i++) {
-                                       var ancestor = ancestors[i];
-                                       inContext = matchAny || contexts.test(ancestor.nodeName);
-                                       if (inContext) {
-                                               for (j = attributes.length; --j >= 0;) {
-                                                       inContext = eval("ancestor." + attributes[j]);
-                                                       if (!inContext) {
-                                                               break;
-                                                       }
-                                               }
-                                       }
-                                       if (inContext) {
-                                               break;
-                                       }
-                               }
-                       }
-                       return inContext && (!this.selection || !selectionEmpty);
-               },
-
-               /**
-                * Handler invoked when the button is clicked
-                */
-               onButtonClick: function (button, event, key) {
-                       if (!this.disabled) {
-                               if (!this.plugins[this.action](this.getEditor(), key || this.itemId) && event) {
-                                       Event.stopEvent(event);
-                               }
-                               if (UserAgent.isOpera) {
-                                       this.getEditor().focus();
-                               }
-                               if (this.dialog) {
-                                       this.setDisabled(true);
-                               } else {
-                                       this.getToolbar().update();
-                               }
-                       }
-                       return false;
-               },
-
-               /**
-                * Handler invoked when the hotkey configured for this button is pressed
-                */
-               onHotKey: function (key, event) {
-                       return this.onButtonClick(this, event, key);
-               },
-
-               /**
-                * Handler invoked when the toolbar is updated
-                */
-               onUpdateToolbar: function (mode, selectionEmpty, ancestors, endPointsInSameBlock) {
-                       this.setDisabled(mode === 'textmode' && !this.textMode);
-                       if (!this.disabled) {
-                               if (!this.noAutoUpdate) {
-                                       this.setDisabled(!this.isInContext(mode, selectionEmpty, ancestors));
-                               }
-                               this.plugins['onUpdateToolbar'](this, mode, selectionEmpty, ancestors, endPointsInSameBlock);
-                       }
-               },
-
-               /**
-                * Update the tooltip text
-                */
-               setTooltip: function (text) {
-                       this.tooltip = text;
-                       this.getEl().dom.firstChild.firstChild.title = text;
-               }
-       });
-
-       return Button;
-
-});
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Toolbar/Button.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/Toolbar/Button.js
new file mode 100644 (file)
index 0000000..30b1bba
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * 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!
+ */
+
+/**
+ * A button in the toolbar
+ */
+define('TYPO3/CMS/Rtehtmlarea/HTMLArea/Toolbar/Button',
+       ['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/DOM',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util',
+       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event'],
+       function (UserAgent, Dom, Util, Event) {
+
+       /**
+        * Button constructor
+        */
+       var Button = function (config) {
+               Util.apply(this, config);               
+       };
+
+       Button.prototype = {
+
+               render: function () {
+                       this.el = document.createElement('div');
+                       Dom.addClass(this.el, 'x-form-item');
+                       Dom.addClass(this.el, 'button');
+                       Dom.addClass(this.el, 'unselectable');
+                       this.el.setAttribute('unselectable', 'on');
+                       if (this.id) {
+                               this.el.setAttribute('id', this.id);
+                       }
+                       if (typeof this.cls === 'string') {
+                               Dom.addClass(this.el, this.cls);
+                       }
+                       if (typeof this.text === 'string') {
+                               this.el.innerHTML = this.text;
+                       }
+                       if (typeof this.tooltip === 'string') {
+                               this.el.setAttribute('title', this.tooltip);
+                       }
+                       if (this.hidden) {
+                               Dom.setStyle(this.el, { display: 'none' } );
+                       }
+                       this.buttonElement = document.createElement('button');
+                       this.buttonElement.setAttribute('type', 'button');
+                       Dom.addClass(this.buttonElement, 'btn-text');
+                       if (typeof this.iconCls === 'string') {
+                               Dom.addClass(this.buttonElement, this.iconCls);
+                       }
+                       this.buttonElement.innerHTML = '&nbsp;';
+                       this.el.appendChild(this.buttonElement);
+                       this.getToolbar().getEl().appendChild(this.el);
+                       this.initEventListeners();
+               },
+
+               /**
+                * Get the element to which the item is rendered
+                */
+               getEl: function () {
+                       return this.el;
+               },
+
+               /**
+                * Initialize listeners
+                */
+               initEventListeners: function () {
+                       var self = this;
+                       Event.on(this, 'HTMLAreaEventHotkey', function (event, key, keyEvent) { return self.onHotKey(key, keyEvent); });
+                       Event.on(this, 'HTMLAreaEventContextMenu', function (event, button) { return self.onButtonClick(button, event); });
+                       Event.on(this.el, this.clickEvent, function (event) { return self.onButtonClick(self, event); });
+                       Event.on(this.el, 'mouseover', function (event) { return self.onMouseOver(event); });
+                       Event.on(this.el, 'mouseout', function (event) { return self.onMouseOut(event); });
+                       // Monitor toolbar updates in order to refresh the state of the button
+                       Event.on(this.getToolbar(), 'HTMLAreaEventToolbarUpdate', function (event, mode, selectionEmpty, ancestors, endPointsInSameBlock) { Event.stopEvent(event); self.onUpdateToolbar(mode, selectionEmpty, ancestors, endPointsInSameBlock); return false; });
+               },
+
+               /**
+                * Get a reference to the editor
+                */
+               getEditor: function() {
+                       return RTEarea[this.toolbar.editorId].editor;
+               },
+
+               /**
+                * Get a reference to the toolbar
+                */
+               getToolbar: function() {
+                       return this.toolbar;
+               },
+
+               /**
+                * Get the itemId of the button
+                */
+               getItemId: function() {
+                       return this.itemId;
+               },
+
+               /**
+                * Add properties and function to set button active or not depending on current selection
+                */
+               inactive: true,
+               activeClass: 'buttonActive',
+               setInactive: function (inactive) {
+                       this.inactive = inactive;
+                       return inactive ? Dom.removeClass(this.el, this.activeClass) : Dom.addClass(this.el, this.activeClass);
+               },
+
+               /**
+                * Determine if the button should be enabled based on the current selection and context configuration property
+                */
+               isInContext: function (mode, selectionEmpty, ancestors) {
+                       var editor = this.getEditor();
+                       var inContext = true;
+                       if (mode === 'wysiwyg' && this.context) {
+                               var attributes = [],
+                                       contexts = [];
+                               if (/(.*)\[(.*?)\]/.test(this.context)) {
+                                       contexts = RegExp.$1.split(',');
+                                       attributes = RegExp.$2.split(',');
+                               } else {
+                                       contexts = this.context.split(',');
+                               }
+                               contexts = new RegExp( '^(' + contexts.join('|') + ')$', 'i');
+                               var matchAny = contexts.test('*');
+                               var i, j, n;
+                               for (i = 0, n = ancestors.length; i < n; i++) {
+                                       var ancestor = ancestors[i];
+                                       inContext = matchAny || contexts.test(ancestor.nodeName);
+                                       if (inContext) {
+                                               for (j = attributes.length; --j >= 0;) {
+                                                       inContext = eval("ancestor." + attributes[j]);
+                                                       if (!inContext) {
+                                                               break;
+                                                       }
+                                               }
+                                       }
+                                       if (inContext) {
+                                               break;
+                                       }
+                               }
+                       }
+                       return inContext && (!this.selection || !selectionEmpty);
+               },
+
+               /**
+                * Handler invoked when the button is clicked
+                */
+               onButtonClick: function (button, event, key) {
+                       if (!this.disabled) {
+                               if (!this.plugins[this.action](this.getEditor(), key || this.itemId) && event) {
+                                       Event.stopEvent(event);
+                               }
+                               if (UserAgent.isOpera) {
+                                       this.getEditor().focus();
+                               }
+                               if (this.dialog) {
+                                       this.setDisabled(true);
+                               } else {
+                                       this.getToolbar().update();
+                               }
+                       }
+                       return false;
+               },
+
+               /**
+                * Handler invoked when the mouse goes over the button
+                */
+               onMouseOver: function (event) {
+                       if (!this.buttonElement.disabled && this.inactive) {
+                               Dom.addClass(this.el, 'buttonHover');
+                       }
+               },
+
+               /**
+                * Handler invoked when the mouse moves out of the button
+                */
+               onMouseOut: function (event) {
+                       Dom.removeClass(this.el, 'buttonHover');
+               },
+
+               /**
+                * Handler invoked when the hotkey configured for this button is pressed
+                */
+               onHotKey: function (key, event) {
+                       return this.onButtonClick(this, event, key);
+               },
+
+               /**
+                * Handler invoked when the toolbar is updated
+                */
+               onUpdateToolbar: function (mode, selectionEmpty, ancestors, endPointsInSameBlock) {
+                       this.setDisabled(mode === 'textmode' && !this.textMode);
+                       if (!this.disabled) {
+                               if (!this.noAutoUpdate) {
+                                       this.setDisabled(!this.isInContext(mode, selectionEmpty, ancestors));
+                               }
+                               this.plugins['onUpdateToolbar'](this, mode, selectionEmpty, ancestors, endPointsInSameBlock);
+                       }
+               },
+
+               /**
+                * Update the tooltip text
+                */
+               setTooltip: function (text) {
+                       this.tooltip = text;
+                       this.buttonElement.title = text;
+               },
+
+               /**
+                * Setting disabled/enabled by boolean.
+                * @param boolean disabled
+                * @return void
+                */
+               disabledClass: 'buttonDisabled',
+               setDisabled: function(disabled){
+                       this.buttonElement.disabled = disabled;
+                       if (disabled) {
+                               Dom.addClass(this.el, this.disabledClass);
+                       } else {
+                               Dom.removeClass(this.el, this.disabledClass);
+                       }
+               },
+
+               /**
+                * Cleanup (called by toolbar onBeforeDestroy)
+                */
+               onBeforeDestroy: function () {
+                       Event.off(this);
+                       Event.off(this.el);
+                       while (node = this.el.firstChild) {
+                               this.el.removeChild(node);
+                       }
+                       delete this.e;
+                       this.el = null;
+               }
+       };
+
+       return Button;
+
+});
index af04125..cfb9697 100644 (file)
        padding-bottom: 10px;
 }
 /* Selectors for the editor toolbar */
+.htmlarea .unselectable,
+.htmlarea .unselectable * {
+       user-select: none;
+       -o-user-select: none;
+       -ms-user-select: none;
+       -moz-user-select: -moz-none;
+       -webkit-user-select: none;
+       cursor: default;
+}
 .htmlarea .toolbar {
        cursor: default;
        margin: 0;
 }
 .htmlarea .toolbar .button {
        background-color: transparent;
-       color: ButtonText;
        border-style: solid;
        border-width: 1px;
        border-color: transparent;
        border-collapse: separate;
+       color: ButtonText;
+       cursor: pointer;
+       float: left;
        height: 23px;
        margin: 0 0 3px 0;
        padding: 0;
-       float: left;
 }
-.htmlarea .toolbar .x-btn-text {
+.htmlarea .toolbar .button button {
+       background-color: transparent;
+       border: 0 none;
+       cursor: pointer;
+       margin: 0;
+       -moz-outline: 0 none;
+       outline: 0 none;
+       overflow: visible;
+       padding-left: 3px;
+       padding-right: 3px;
+}
+.htmlarea .toolbar .btn-text {
        background-image: url("images/sprites/actions.png");
+       background-position: center;
        background-repeat: no-repeat;
        width: 20px;
        height: 18px;
        margin: 1px 0 1px 2px;
 }
 .htmlarea .toolbar .buttonHover {
-       opacity: 1.00;
+       background-color: #DCDCDC;
 }
 .htmlarea .toolbar .buttonActive,
 .htmlarea .toolbar .buttonPressed,