From 29651b1d141d3e1ed8681fc08f71f41664a35d46 Mon Sep 17 00:00:00 2001
From: Andreas Fernandez <a.fernandez@scripting-base.de>
Date: Wed, 25 Mar 2020 22:15:07 +0100
Subject: [PATCH] [BUGFIX] Let t3editor consume as much space as possible

T3editor now respects the configured `rows` attribute of its textarea.
If the attribute isn't configured for the node in FormEngine, the
attribute is omitted. To be able to achieve this, the enforcement of a
value for "rows" has been removed.

If the "rows" attribute is missing, the T3editor instance may consume as
much space as available, which comes in handy in the filelist.

Resolves: #86699
Releases: master, 9.5
Change-Id: Iccb152b7a747918cfc4459721c7f69a8f67cb03d
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/63923
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Georg Ringer <georg.ringer@gmail.com>
Tested-by: Daniel Goerz <daniel.goerz@posteo.de>
Reviewed-by: Georg Ringer <georg.ringer@gmail.com>
Reviewed-by: Daniel Goerz <daniel.goerz@posteo.de>
---
 .../Resources/Public/TypeScript/T3editor.ts      | 16 ++++++++++++++--
 .../Classes/Form/Element/T3editorElement.php     | 13 +++++--------
 .../Resources/Public/JavaScript/T3editor.js      |  2 +-
 3 files changed, 20 insertions(+), 11 deletions(-)

diff --git a/Build/Sources/TypeScript/t3editor/Resources/Public/TypeScript/T3editor.ts b/Build/Sources/TypeScript/t3editor/Resources/Public/TypeScript/T3editor.ts
index c7d61962eb07..ed5412095bd4 100644
--- a/Build/Sources/TypeScript/t3editor/Resources/Public/TypeScript/T3editor.ts
+++ b/Build/Sources/TypeScript/t3editor/Resources/Public/TypeScript/T3editor.ts
@@ -116,13 +116,25 @@ class T3editor {
         FormEngine.Validation.markFieldAsChanged($textarea);
       });
 
+      const bottomPanel = T3editor.createPanelNode('bottom', $textarea.attr('alt'));
       cm.addPanel(
-        T3editor.createPanelNode('bottom', $textarea.attr('alt')),
+        bottomPanel,
         {
           position: 'bottom',
-          stable: true,
+          stable: false,
         },
       );
+
+      // cm.addPanel() changes the height of the editor, thus we have to override it here again
+      if ($textarea.attr('rows')) {
+        const lineHeight = 18;
+        const paddingBottom = 4;
+        cm.setSize(null, parseInt($textarea.attr('rows'), 10) * lineHeight + paddingBottom + bottomPanel.getBoundingClientRect().height);
+      } else {
+        // Textarea has no "rows" attribute configured, don't limit editor in space
+        cm.getWrapperElement().style.height = (document.body.getBoundingClientRect().height - cm.getWrapperElement().getBoundingClientRect().top - 80) + 'px';
+        cm.setOption('viewportMargin', Infinity);
+      }
     });
 
     $textarea.prop('is_t3editor', true);
diff --git a/typo3/sysext/t3editor/Classes/Form/Element/T3editorElement.php b/typo3/sysext/t3editor/Classes/Form/Element/T3editorElement.php
index 9462abcc9dbd..d50ec61e9344 100644
--- a/typo3/sysext/t3editor/Classes/Form/Element/T3editorElement.php
+++ b/typo3/sysext/t3editor/Classes/Form/Element/T3editorElement.php
@@ -115,19 +115,16 @@ class T3editorElement extends AbstractFormElement
 
         $parameterArray = $this->data['parameterArray'];
 
-        $rows = MathUtility::forceIntegerInRange($parameterArray['fieldConf']['config']['rows'] ?: 10, 1, 40);
-
         $attributes = [];
-        $attributes['rows'] = $rows;
+        if (isset($parameterArray['fieldConf']['config']['rows']) && MathUtility::canBeInterpretedAsInteger($parameterArray['fieldConf']['config']['rows'])) {
+            $attributes['rows'] = $parameterArray['fieldConf']['config']['rows'];
+        }
+
         $attributes['wrap'] = 'off';
         $attributes['style'] = 'width:100%;';
         $attributes['onchange'] = GeneralUtility::quoteJSvalue($parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged']);
 
-        $attributeString = '';
-        foreach ($attributes as $param => $value) {
-            $attributeString .= $param . '="' . htmlspecialchars((string)$value) . '" ';
-        }
-
+        $attributeString = GeneralUtility::implodeAttributes($attributes, true);
         $editorHtml = $this->getHTMLCodeForEditor(
             $parameterArray['itemFormElName'],
             'text-monospace enable-tab',
diff --git a/typo3/sysext/t3editor/Resources/Public/JavaScript/T3editor.js b/typo3/sysext/t3editor/Resources/Public/JavaScript/T3editor.js
index 61e0438d0a59..a58b8fe77291 100644
--- a/typo3/sysext/t3editor/Resources/Public/JavaScript/T3editor.js
+++ b/typo3/sysext/t3editor/Resources/Public/JavaScript/T3editor.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-define(["require","exports","cm/lib/codemirror","jquery","TYPO3/CMS/Backend/FormEngine"],(function(e,t,i,r,o){"use strict";class n{static createPanelNode(e,t){return r("<div />",{class:"CodeMirror-panel CodeMirror-panel-"+e,id:"panel-"+e}).append(r("<span />").text(t)).get(0)}constructor(){this.initialize()}initialize(){r(()=>{this.observeEditorCandidates()})}observeEditorCandidates(){const e={root:document.body};let t=new IntersectionObserver(e=>{e.forEach(e=>{if(e.intersectionRatio>0){const t=r(e.target);t.prop("is_t3editor")||this.initializeEditor(t)}})},e);document.querySelectorAll("textarea.t3editor").forEach(e=>{t.observe(e)})}initializeEditor(t){const a=t.data("codemirror-config"),s=a.mode.split("/"),l=r.merge([s.join("/")],JSON.parse(a.addons)),d=JSON.parse(a.options);e(l,()=>{const e=i.fromTextArea(t.get(0),{extraKeys:{"Ctrl-F":"findPersistent","Cmd-F":"findPersistent","Ctrl-Alt-F":e=>{e.setOption("fullScreen",!e.getOption("fullScreen"))},"Ctrl-Space":"autocomplete",Esc:e=>{e.getOption("fullScreen")&&e.setOption("fullScreen",!1)}},fullScreen:!1,lineNumbers:!0,lineWrapping:!0,mode:s[s.length-1]});r.each(d,(t,i)=>{e.setOption(t,i)}),e.on("change",()=>{o.Validation.markFieldAsChanged(t)}),e.addPanel(n.createPanelNode("bottom",t.attr("alt")),{position:"bottom",stable:!0})}),t.prop("is_t3editor",!0)}}return new n}));
\ No newline at end of file
+define(["require","exports","cm/lib/codemirror","jquery","TYPO3/CMS/Backend/FormEngine"],(function(e,t,i,n,r){"use strict";class o{static createPanelNode(e,t){return n("<div />",{class:"CodeMirror-panel CodeMirror-panel-"+e,id:"panel-"+e}).append(n("<span />").text(t)).get(0)}constructor(){this.initialize()}initialize(){n(()=>{this.observeEditorCandidates()})}observeEditorCandidates(){const e={root:document.body};let t=new IntersectionObserver(e=>{e.forEach(e=>{if(e.intersectionRatio>0){const t=n(e.target);t.prop("is_t3editor")||this.initializeEditor(t)}})},e);document.querySelectorAll("textarea.t3editor").forEach(e=>{t.observe(e)})}initializeEditor(t){const a=t.data("codemirror-config"),s=a.mode.split("/"),l=n.merge([s.join("/")],JSON.parse(a.addons)),d=JSON.parse(a.options);e(l,()=>{const e=i.fromTextArea(t.get(0),{extraKeys:{"Ctrl-F":"findPersistent","Cmd-F":"findPersistent","Ctrl-Alt-F":e=>{e.setOption("fullScreen",!e.getOption("fullScreen"))},"Ctrl-Space":"autocomplete",Esc:e=>{e.getOption("fullScreen")&&e.setOption("fullScreen",!1)}},fullScreen:!1,lineNumbers:!0,lineWrapping:!0,mode:s[s.length-1]});n.each(d,(t,i)=>{e.setOption(t,i)}),e.on("change",()=>{r.Validation.markFieldAsChanged(t)});const a=o.createPanelNode("bottom",t.attr("alt"));if(e.addPanel(a,{position:"bottom",stable:!1}),t.attr("rows")){const i=18,n=4;e.setSize(null,parseInt(t.attr("rows"),10)*i+n+a.getBoundingClientRect().height)}else e.getWrapperElement().style.height=document.body.getBoundingClientRect().height-e.getWrapperElement().getBoundingClientRect().top-80+"px",e.setOption("viewportMargin",1/0)}),t.prop("is_t3editor",!0)}}return new o}));
\ No newline at end of file
-- 
GitLab