Added feature #11812: Resizable Textareas for TCEforms
authorSteffen Kamper <info@sk-typo3.de>
Sat, 5 Sep 2009 14:23:29 +0000 (14:23 +0000)
committerSteffen Kamper <info@sk-typo3.de>
Sat, 5 Sep 2009 14:23:29 +0000 (14:23 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@5897 709f56b5-9817-0410-a4d7-c38de5d9e867

ChangeLog
t3lib/class.t3lib_tceforms.php
t3lib/js/extjs/tceforms.js
t3lib/js/extjs/ux/ext.resizable.js [new file with mode: 0644]
t3lib/js/extjs/ux/resize.css [new file with mode: 0644]
t3lib/js/extjs/ux/resize.gif [new file with mode: 0644]

index d6a8090..1ed6dc1 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,6 @@
 2009-09-05  Steffen Kamper  <info@sk-typo3.de>
 
+       * Added feature #11812: Resizable Textareas for TCEforms
        * Added feature #11407: Allow type="user" in user setup
 
 2009-09-04  Rupert Germann  <rupi@gmx.li>
index 9596fd0..bf86f61 100644 (file)
@@ -5234,7 +5234,9 @@ class t3lib_TCEforms      {
 
                        $GLOBALS['SOBE']->doc->loadPrototype();
                        $GLOBALS['SOBE']->doc->loadExtJS();
+                       $GLOBALS['SOBE']->doc->addStyleSheet('ext.resizable', $this->backPath . '../t3lib/js/extjs/ux/resize.css');
                        $this->loadJavascriptLib('../t3lib/jsfunc.evalfield.js');
+                       $this->loadJavascriptLib('../t3lib/js/extjs/ux/ext.resizable.js');
                        // @TODO: Change to loadJavascriptLib(), but fix "TS = new typoScript()" issue first - see bug #9494
                        $jsFile[] = '<script type="text/javascript" src="'.$this->backPath.'jsfunc.tbe_editor.js"></script>';
 
index 6e22875..819ea4a 100644 (file)
@@ -34,6 +34,7 @@ TYPO3.TCEFORMS = {
                Ext.QuickTips.init();
 
                this.convertDateFieldsToDatePicker();
+               this.convertTextareasResizable();
        },
 
        convertDateFieldsToDatePicker: function() {
@@ -69,6 +70,22 @@ TYPO3.TCEFORMS = {
                                menu.show(datepicker);
                        });
                });
+       },
+       
+       convertTextareasResizable: function() {
+               var textAreas = Ext.select("*[id^=tceforms-textarea-]");
+               textAreas.each(function(element) {
+                       element.addClass('resizable');
+                       var elasticTextarea = new Ext.ux.elasticTextArea().applyTo(element.dom.id, {
+                               minHeight: 50
+                       });
+                       var dwrapped = new Ext.Resizable(element.dom.id, {
+                       minWidth:  300,
+                       minHeight: 50,
+                       dynamic:   true
+                   });
+               });
        }
+       
 }
 Ext.onReady(TYPO3.TCEFORMS.init, TYPO3.TCEFORMS);
\ No newline at end of file
diff --git a/t3lib/js/extjs/ux/ext.resizable.js b/t3lib/js/extjs/ux/ext.resizable.js
new file mode 100644 (file)
index 0000000..ec18442
--- /dev/null
@@ -0,0 +1,1020 @@
+/**\r
+ *\r
+ * Ext.ux.EventZone Extension Class for Ext 3.x Library\r
+ *\r
+ * @author  Nigel White\r
+ *\r
+ * @license Ext.ux.EventZone is licensed under the terms of\r
+ * the Open Source LGPL 3.0 license.  Commercial use is permitted to the extent\r
+ * that the code/component(s) do NOT become part of another Open Source or Commercially\r
+ * licensed development library or toolkit without explicit permission.\r
+ * \r
+ * License details: http://www.gnu.org/licenses/lgpl.html\r
+ *\r
+ * @class Ext.ux.EventZone\r
+ * <p>This class implements a "virtual element" at a relative size and position\r
+ * <i>within</i> an existing element. It provides mouse events from a zone of an element of\r
+ * defined dimensions.</p>\r
+ * <p>The zone is defined using <code>top</code>, <code>right</code>, <code>bottom</code>,\r
+ * <code>left</code>, <code>width</code> and <code>height</code> options which specify\r
+ * the bounds of the zone in a similar manner to the CSS style properties of those names.</p>\r
+ * @cfg {String|HtmlElement} el The element in which to create the zone.\r
+ * @cfg {Array} points An Array of points within the element defining the event zone.\r
+ * @cfg {Number} top The top of the zone. If negative means an offset from the bottom.\r
+ * @cfg {Number} right The right of the zone. If negative means an offset from the right.\r
+ * @cfg {Number} left The left of the zone. If negative means an offset from the right.\r
+ * @cfg {Number} bottom The bottom of the zone. If negative means an offset from the bottom.\r
+ * @cfg {Number} width The width of the zone.\r
+ * @cfg {Number} height The height of the zone.\r
+ * @constructor\r
+ * Create a new EventZone\r
+ * @param {Object} config The config object.\r
+ */\r
+Ext.ux.EventZone = Ext.extend(Ext.util.Observable, {\r
+\r
+    constructor: function(config) {\r
+        this.initialConfig = config;\r
+        this.addEvents(\r
+            /**\r
+             * @event mouseenter\r
+             * This event fires when the mouse enters the zone.\r
+             * @param {EventObject} e the underlying mouse event.\r
+             * @param {EventZone} this\r
+             */\r
+            'mouseenter',\r
+            /**\r
+             * @event mousedown\r
+             * This event fires when the mouse button is depressed within the zone.\r
+             * @param {EventObject} e the underlying mouse event.\r
+             * @param {EventZone} this\r
+             */\r
+            'mousedown',\r
+            /**\r
+             * @event mousemove\r
+             * This event fires when the mouse moves within the zone.\r
+             * @param {EventObject} e the underlying mouse event.\r
+             * @param {EventZone} this\r
+             */\r
+            'mousemove',\r
+            /**\r
+             * @event mouseup\r
+             * This event fires when the mouse button is released within the zone.\r
+             * @param {EventObject} e the underlying mouse event.\r
+             * @param {EventZone} this\r
+             */\r
+            'mouseup',\r
+            /**\r
+             * @event mouseenter\r
+             * This event fires when the mouse is clicked within the zone.\r
+             * @param {EventObject} e the underlying mouse event.\r
+             * @param {EventZone} this\r
+             */\r
+            'click',\r
+            /**\r
+             * @event mouseleave\r
+             * This event fires when the mouse leaves the zone.\r
+             * @param {EventObject} e the underlying mouse event.\r
+             * @param {EventZone} this\r
+             */\r
+            'mouseleave'\r
+        );\r
+        Ext.apply(this, config);\r
+        this.el = Ext.get(this.el);\r
+\r
+//      If a polygon within the element is specified...\r
+        if (this.points) {\r
+            this.polygon = new Ext.lib.Polygon(this.points);\r
+            this.points = this.polygon.points;\r
+        }\r
+\r
+        Ext.ux.EventZone.superclass.constructor.call(this);\r
+        this.el.on({\r
+            mouseenter: this.handleMouseEvent,\r
+            mousedown: this.handleMouseEvent,\r
+            mousemove: this.handleMouseEvent,\r
+            mouseup: this.handleMouseEvent,\r
+            click: this.handleMouseEvent,\r
+            mouseleave: this.handleMouseEvent,\r
+            scope: this\r
+        });\r
+    },\r
+\r
+    handleMouseEvent: function(e) {\r
+        var r = this.polygon ? this.getPolygon() : this.getRegion();\r
+        var inBounds = r.contains(e.getPoint());\r
+\r
+        switch (e.type) {\r
+            // mouseenter fires this\r
+            case 'mouseover':\r
+               if (inBounds) {\r
+                   this.mouseIn = true;\r
+                   this.fireEvent('mouseenter', e, this);\r
+               }\r
+               break;\r
+            // mouseleave fires this\r
+            case 'mouseout':\r
+               this.mouseIn = false;\r
+               this.fireEvent('mouseleave', e, this);\r
+               break;\r
+           case 'mousemove':\r
+               if (inBounds) {\r
+                   if (this.mouseIn) {\r
+                       this.fireEvent('mousemove', e, this);\r
+                   } else {\r
+                       this.mouseIn = true;\r
+                       this.fireEvent('mouseenter', e, this);\r
+                   }\r
+               } else {\r
+                   if (this.mouseIn) {\r
+                       this.mouseIn = false;\r
+                       this.fireEvent('mouseleave', e, this);\r
+                   }\r
+               }\r
+               break;\r
+           default:\r
+               if (inBounds) {\r
+                   this.fireEvent(e.type, e, this);\r
+               }\r
+        }\r
+    },\r
+\r
+    getPolygon: function() {\r
+        var xy = this.el.getXY();\r
+        return this.polygon.translate(xy[0], xy[1]);\r
+    },\r
+\r
+    getRegion: function() {\r
+        var r = this.el.getRegion();\r
+\r
+//      Adjust left boundary of region\r
+        if (Ext.isNumber(this.left)) {\r
+            if (this.left < 0) {\r
+                r.left = r.right + this.left;\r
+            } else {\r
+                r.left += this.left;\r
+            }\r
+        }\r
+\r
+//      Adjust right boundary of region\r
+        if (Ext.isNumber(this.width)) {\r
+            r.right = r.left + this.width;\r
+        } else if (Ext.isNumber(this.right)) {\r
+            r.right = (this.right < 0) ? r.right + this.right : r.left + this.right;\r
+        }\r
+\r
+//      Adjust top boundary of region\r
+        if (Ext.isNumber(this.top)) {\r
+            if (this.top < 0) {\r
+                r.top = r.bottom + this.top;\r
+            } else {\r
+                r.top += this.top;\r
+            }\r
+        }\r
+\r
+//      Adjust bottom boundary of region\r
+        if (Ext.isNumber(this.height)) {\r
+            r.bottom = r.top + this.height;\r
+        } else if (Ext.isNumber(this.bottom)) {\r
+            r.bottom = (this.bottom < 0) ? r.bottom + this.bottom : r.top + this.bottom;\r
+        }\r
+\r
+        return r;\r
+    }\r
+});\r
+\r
+/**\r
+ * @class Ext.lib.Polygon\r
+ * <p>This class encapsulates an absolute area of the document bounded by a list of points.</p>\r
+ * @constructor\r
+ * Create a new Polygon\r
+ * @param {Object} points An Array of <code>[n,n]</code> point specification Arrays, or\r
+ * an Array of Ext.lib.Points, or an HtmlElement, or an Ext.lib.Region.\r
+ */\r
+Ext.lib.Polygon = Ext.extend(Ext.lib.Region, {\r
+    constructor: function(points) {\r
+        var i, l, el;\r
+        if (l = points.length) {\r
+            if (points[0].x) {\r
+                for (i = 0; i < l; i++) {\r
+                    points[i] = [ points[i].x, points[i].y ];\r
+                }\r
+            }\r
+            this.points = points;\r
+        } else {\r
+            if (el = Ext.get(points)) {\r
+                points = Ext.lib.Region.getRegion(el.dom);\r
+            }\r
+            if (points instanceof Ext.lib.Region) {\r
+                this.points = [\r
+                    [points.left, points.top],\r
+                    [points.right, points.top],\r
+                    [points.right, points.bottom],\r
+                    [points.left, points.bottom]\r
+                ];\r
+            }\r
+        }\r
+    },\r
+\r
+    /**\r
+     * Returns a new Polygon translated by the specified <code>X</code> and <code>Y</code> increments.\r
+     * @param xDelta {Number} The <code>X</code> translation increment.\r
+     * @param xDelta {Number} The <code>Y</code> translation increment.\r
+     * @return {Polygon} The resulting Polygon.\r
+     */\r
+    translate: function(xDelta, yDelta) {\r
+        var r = [], p = this.points, l = p.length, i;\r
+        for (i = 0; i < l; i++) {\r
+            r[i] = [ p[i][0] + xDelta, p[i][1] + yDelta ];\r
+        }\r
+        return new Ext.lib.Polygon(r);\r
+    },\r
+\r
+    /**\r
+     * Returns the area of this Polygon.\r
+     */\r
+    getArea: function() {\r
+        var p = this.points, l = p.length, area = 0, i, j = 0;\r
+        for (i = 0; i < l; i++) {\r
+            j++;\r
+            if (j == l) {\r
+                j = 0;\r
+            }\r
+            area += (p[i][0] + p[j][0]) * (p[i][1] - p[j][1]);\r
+        }\r
+        return area * 0.5;\r
+    },\r
+\r
+    /**\r
+     * Returns <code>true</code> if this Polygon contains the specified point. Thanks\r
+     * to http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html for the algorithm.\r
+     * @param pt {Point|Number} Either an Ext.lib.Point object, or the <code>X</code> coordinate to test.\r
+     * @param py {Number} <b>Optional.</b> If the first parameter was an <code>X</code> coordinate, this is the <code>Y</code> coordinate.\r
+     */\r
+    contains: function(pt, py) {\r
+        var f = (arguments.length == 1),\r
+            testX = f ? pt.x : pt,\r
+            testY = f ? pt.y : py,\r
+            p = this.points,\r
+            nvert = p.length,\r
+            j = nvert - 1,\r
+            i, j, c = false;\r
+        for (i = 0; i < nvert; j = i++) {\r
+            if ( ((p[i][1] > testY) != (p[j][1] > testY)) &&\r
+             (testX < (p[j][0]-p[i][0]) * (testY-p[i][1]) / (p[j][1]-p[i][1]) + p[i][0])) {\r
+                c = !c;\r
+            }\r
+        }\r
+        return c;\r
+    }\r
+});\r
+\r
+/**\r
+ * @class Ext.Resizable\r
+ * @extends Ext.util.Observable\r
+ * This is an override of Ext.Resizable to make usage of the Ex.ux.EventZone\r
+ * <p>Applies virtual drag handles to an element to make it resizable.</p>\r
+ * <p>Here is the list of valid resize handles:</p>\r
+ * <pre>\r
+Value   Description\r
+------  -------------------\r
+ 'n'     north\r
+ 's'     south\r
+ 'e'     east\r
+ 'w'     west\r
+ 'nw'    northwest\r
+ 'sw'    southwest\r
+ 'se'    southeast\r
+ 'ne'    northeast\r
+ 'all'   all\r
+</pre>\r
+ * <p>Here's an example showing the creation of a typical Resizable:</p>\r
+ * <pre><code>\r
+var resizer = new Ext.Resizable('element-id', {\r
+    handles: 'all',\r
+    minWidth: 200,\r
+    minHeight: 100,\r
+    maxWidth: 500,\r
+    maxHeight: 400,\r
+    pinned: true\r
+});\r
+resizer.on('resize', myHandler);\r
+</code></pre>\r
+ * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>\r
+ * resizer.east.setDisplayed(false);</p>\r
+ * @constructor\r
+ * Create a new resizable component\r
+ * @param {Mixed} el The id or element to resize\r
+ * @param {Object} config configuration options\r
+  */\r
+Ext.Resizable = function(el, config){\r
+    this.el = Ext.get(el);\r
+\r
+    /**\r
+     * The proxy Element that is resized in place of the real Element during the resize operation.\r
+     * This may be queried using {@link Ext.Element#getBox} to provide the new area to resize to.\r
+     * Read only.\r
+     * @type Ext.Element.\r
+     * @property proxy\r
+     */\r
+    this.proxy = this.el.createProxy({tag: 'div', cls: 'x-resizable-proxy', id: this.el.id + '-rzproxy'}, Ext.getBody());\r
+    this.proxy.unselectable();\r
+    this.proxy.enableDisplayMode('block');\r
+\r
+    Ext.apply(this, config);\r
+    \r
+    if(this.pinned){\r
+        this.disableTrackOver = true;\r
+        this.el.addClass('x-resizable-pinned');\r
+    }\r
+    // if the element isn't positioned, make it relative\r
+    var position = this.el.getStyle('position');\r
+    if(position != 'absolute' && position != 'fixed'){\r
+        this.el.setStyle('position', 'relative');\r
+    }\r
+    if(!this.handles){ // no handles passed, must be legacy style\r
+        this.handles = 's,e,se';\r
+        if(this.multiDirectional){\r
+            this.handles += ',n,w';\r
+        }\r
+    }\r
+    if(this.handles == 'all'){\r
+        this.handles = 'n s e w ne nw se sw';\r
+    }\r
+    var hs = this.handles.split(/\s*?[,;]\s*?| /);\r
+    var ps = Ext.Resizable.positions;\r
+    for(var i = 0, len = hs.length; i < len; i++){\r
+        if(hs[i] && ps[hs[i]]){\r
+            var pos = ps[hs[i]];\r
+            this[pos] = new Ext.Resizable.Handle(this, pos);\r
+        }\r
+    }\r
+    // legacy\r
+    this.corner = this.southeast;\r
+    \r
+    if(this.handles.indexOf('n') != -1 || this.handles.indexOf('w') != -1){\r
+        this.updateBox = true;\r
+    }   \r
+    this.activeHandle = null;\r
+\r
+    if(this.adjustments == 'auto'){\r
+        var hw = this.west, he = this.east, hn = this.north, hs = this.south;\r
+        this.adjustments = [\r
+            (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),\r
+            (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1 \r
+        ];\r
+    }\r
+\r
+    if(this.draggable){\r
+        this.dd = this.dynamic ? \r
+            this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});\r
+        this.dd.setHandleElId(this.el.id);\r
+    }\r
+\r
+    this.addEvents(\r
+        /**\r
+         * @event beforeresize\r
+         * Fired before resize is allowed. Set {@link #enabled} to false to cancel resize.\r
+         * @param {Ext.Resizable} this\r
+         * @param {Ext.EventObject} e The mousedown event\r
+         */\r
+        'beforeresize',\r
+        /**\r
+         * @event resize\r
+         * Fired after a resize.\r
+         * @param {Ext.Resizable} this\r
+         * @param {Number} width The new width\r
+         * @param {Number} height The new height\r
+         * @param {Ext.EventObject} e The mouseup event\r
+         */\r
+        'resize'\r
+    );\r
+    \r
+    if(this.width !== null && this.height !== null){\r
+        this.resizeTo(this.width, this.height);\r
+    }\r
+    if(Ext.isIE){\r
+        this.el.dom.style.zoom = 1;\r
+    }\r
+    Ext.Resizable.superclass.constructor.call(this);\r
+};\r
+\r
+Ext.extend(Ext.Resizable, Ext.util.Observable, {\r
+\r
+    /**\r
+     * @cfg {Array/String} adjustments String 'auto' or an array [width, height] with values to be <b>added</b> to the\r
+     * resize operation's new size (defaults to <tt>[0, 0]</tt>)\r
+     */\r
+    adjustments : [0, 0],\r
+    /**\r
+     * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)\r
+     */\r
+    animate : false,\r
+    /**\r
+     * @cfg {Mixed} constrainTo Constrain the resize to a particular element\r
+     */\r
+    /**\r
+     * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)\r
+     */\r
+    draggable: false,\r
+    /**\r
+     * @cfg {Number} duration Animation duration if animate = true (defaults to 0.35)\r
+     */\r
+    duration : 0.35,\r
+    /**\r
+     * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)\r
+     */\r
+    dynamic : false,\r
+    /**\r
+     * @cfg {String} easing Animation easing if animate = true (defaults to <tt>'easingOutStrong'</tt>)\r
+     */\r
+    easing : 'easeOutStrong',\r
+    /**\r
+     * @cfg {Boolean} enabled False to disable resizing (defaults to true)\r
+     */\r
+    enabled : true,\r
+    /**\r
+     * @property enabled Writable. False if resizing is disabled.\r
+     * @type Boolean \r
+     */\r
+    /**\r
+     * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined).\r
+     * Specify either <tt>'all'</tt> or any of <tt>'n s e w ne nw se sw'</tt>.\r
+     */\r
+    handles : false,\r
+    /**\r
+     * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  Deprecated style of adding multi-direction resize handles.\r
+     */\r
+    multiDirectional : false,\r
+    /**\r
+     * @cfg {Number} height The height of the element in pixels (defaults to null)\r
+     */\r
+    height : null,\r
+    /**\r
+     * @cfg {Number} width The width of the element in pixels (defaults to null)\r
+     */\r
+    width : null,\r
+    /**\r
+     * @cfg {Number} heightIncrement The increment to snap the height resize in pixels\r
+     * (only applies if <code>{@link #dynamic}==true</code>). Defaults to <tt>0</tt>.\r
+     */\r
+    heightIncrement : 0,\r
+    /**\r
+     * @cfg {Number} widthIncrement The increment to snap the width resize in pixels\r
+     * (only applies if <code>{@link #dynamic}==true</code>). Defaults to <tt>0</tt>.\r
+     */\r
+    widthIncrement : 0,\r
+    /**\r
+     * @cfg {Number} minHeight The minimum height for the element (defaults to 5)\r
+     */\r
+    minHeight : 5,\r
+    /**\r
+     * @cfg {Number} minWidth The minimum width for the element (defaults to 5)\r
+     */\r
+    minWidth : 5,\r
+    /**\r
+     * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)\r
+     */\r
+    maxHeight : 10000,\r
+    /**\r
+     * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)\r
+     */\r
+    maxWidth : 10000,\r
+    /**\r
+     * @cfg {Number} minX The minimum x for the element (defaults to 0)\r
+     */\r
+    minX: 0,\r
+    /**\r
+     * @cfg {Number} minY The minimum x for the element (defaults to 0)\r
+     */\r
+    minY: 0,\r
+    /**\r
+     * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the\r
+     * user mouses over the resizable borders. This is only applied at config time. (defaults to false)\r
+     */\r
+    pinned : false,\r
+    /**\r
+     * @cfg {Boolean} preserveRatio True to preserve the original ratio between height\r
+     * and width during resize (defaults to false)\r
+     */\r
+    preserveRatio : false,\r
+    /**\r
+     * @cfg {Ext.lib.Region} resizeRegion Constrain the resize to a particular region\r
+     */\r
+\r
+    \r
+    /**\r
+     * Perform a manual resize and fires the 'resize' event.\r
+     * @param {Number} width\r
+     * @param {Number} height\r
+     */\r
+    resizeTo : function(width, height){\r
+        this.el.setSize(width, height);\r
+        this.fireEvent('resize', this, width, height, null);\r
+    },\r
+\r
+    // private\r
+    startSizing : function(e, handle){\r
+        this.fireEvent('beforeresize', this, e);\r
+        if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler\r
+            e.stopEvent();\r
+\r
+            Ext.getDoc().on({\r
+                scope: this,\r
+                mousemove: this.onMouseMove,\r
+                mouseup: {\r
+                    fn: this.onMouseUp,\r
+                    single: true,\r
+                    scope: this\r
+                }\r
+            });\r
+            Ext.getBody().addClass('ux-resizable-handle-' + handle.position);\r
+\r
+            this.resizing = true;\r
+            this.startBox = this.el.getBox();\r
+            this.startPoint = e.getXY();\r
+            this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],\r
+                            (this.startBox.y + this.startBox.height) - this.startPoint[1]];\r
+\r
+            if(this.constrainTo) {\r
+                var ct = Ext.get(this.constrainTo);\r
+                this.resizeRegion = ct.getRegion().adjust(\r
+                    ct.getFrameWidth('t'),\r
+                    ct.getFrameWidth('l'),\r
+                    -ct.getFrameWidth('b'),\r
+                    -ct.getFrameWidth('r')\r
+                );\r
+            }\r
+\r
+            this.proxy.setStyle('visibility', 'hidden'); // workaround display none\r
+            this.proxy.show();\r
+            this.proxy.setBox(this.startBox);\r
+            if(!this.dynamic){\r
+                this.proxy.setStyle('visibility', 'visible');\r
+            }\r
+        }\r
+    },\r
+\r
+    // private\r
+    onMouseDown : function(handle, e){\r
+        if(this.enabled && !this.activeHandle){\r
+            e.stopEvent();\r
+            this.activeHandle = handle;\r
+            this.startSizing(e, handle);\r
+        }\r
+    },\r
+\r
+    // private\r
+    onMouseUp : function(e){\r
+        Ext.getBody().removeClass('ux-resizable-handle-' + this.activeHandle.position)\r
+            .un('mousemove', this.onMouseMove, this);\r
+        var size = this.resizeElement();\r
+        this.resizing = false;\r
+        this.handleOut(this.activeHandle);\r
+        this.proxy.hide();\r
+        this.fireEvent('resize', this, size.width, size.height, e);\r
+        this.activeHandle = null;\r
+    },\r
+\r
+    // private\r
+    snap : function(value, inc, min){\r
+        if(!inc || !value){\r
+            return value;\r
+        }\r
+        var newValue = value;\r
+        var m = value % inc;\r
+        if(m > 0){\r
+            if(m > (inc/2)){\r
+                newValue = value + (inc-m);\r
+            }else{\r
+                newValue = value - m;\r
+            }\r
+        }\r
+        return Math.max(min, newValue);\r
+    },\r
+\r
+    /**\r
+     * <p>Performs resizing of the associated Element. This method is called internally by this\r
+     * class, and should not be called by user code.</p>\r
+     * <p>If a Resizable is being used to resize an Element which encapsulates a more complex UI\r
+     * component such as a Panel, this method may be overridden by specifying an implementation\r
+     * as a config option to provide appropriate behaviour at the end of the resize operation on\r
+     * mouseup, for example resizing the Panel, and relaying the Panel's content.</p>\r
+     * <p>The new area to be resized to is available by examining the state of the {@link #proxy}\r
+     * Element. Example:\r
+<pre><code>\r
+new Ext.Panel({\r
+    title: 'Resize me',\r
+    x: 100,\r
+    y: 100,\r
+    renderTo: Ext.getBody(),\r
+    floating: true,\r
+    frame: true,\r
+    width: 400,\r
+    height: 200,\r
+    listeners: {\r
+        render: function(p) {\r
+            new Ext.Resizable(p.getEl(), {\r
+                handles: 'all',\r
+                pinned: true,\r
+                transparent: true,\r
+                resizeElement: function() {\r
+                    var box = this.proxy.getBox();\r
+                    p.updateBox(box);\r
+                    if (p.layout) {\r
+                        p.doLayout();\r
+                    }\r
+                    return box;\r
+                }\r
+           });\r
+       }\r
+    }\r
+}).show();\r
+</code></pre>\r
+     */\r
+    resizeElement : function(){\r
+        var box = this.proxy.getBox();\r
+        if(this.updateBox){\r
+            this.el.setBox(box, false, this.animate, this.duration, null, this.easing);\r
+        }else{\r
+            this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);\r
+        }\r
+        if(!this.dynamic){\r
+            this.proxy.hide();\r
+        }\r
+        return box;\r
+    },\r
+\r
+    // private\r
+    constrain : function(v, diff, m, mx){\r
+        if(v - diff < m){\r
+            diff = v - m;    \r
+        }else if(v - diff > mx){\r
+            diff = v - mx; \r
+        }\r
+        return diff;                \r
+    },\r
+\r
+    // private\r
+    onMouseMove : function(e){\r
+        if(this.enabled && this.activeHandle){\r
+            try{// try catch so if something goes wrong the user doesn't get hung\r
+\r
+            if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {\r
+                return;\r
+            }\r
+\r
+            //var curXY = this.startPoint;\r
+            var curSize = this.curSize || this.startBox,\r
+                x = this.startBox.x, y = this.startBox.y,\r
+                ox = x, \r
+                oy = y,\r
+                w = curSize.width, \r
+                h = curSize.height,\r
+                ow = w, \r
+                oh = h,\r
+                mw = this.minWidth, \r
+                mh = this.minHeight,\r
+                mxw = this.maxWidth, \r
+                mxh = this.maxHeight,\r
+                wi = this.widthIncrement,\r
+                hi = this.heightIncrement,\r
+                eventXY = e.getXY(),\r
+                diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0])),\r
+                diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1])),\r
+                pos = this.activeHandle.position,\r
+                tw,\r
+                th;\r
+            \r
+            switch(pos){\r
+                case 'east':\r
+                    w += diffX; \r
+                    w = Math.min(Math.max(mw, w), mxw);\r
+                    break;\r
+                case 'south':\r
+                    h += diffY;\r
+                    h = Math.min(Math.max(mh, h), mxh);\r
+                    break;\r
+                case 'southeast':\r
+                    w += diffX; \r
+                    h += diffY;\r
+                    w = Math.min(Math.max(mw, w), mxw);\r
+                    h = Math.min(Math.max(mh, h), mxh);\r
+                    break;\r
+                case 'north':\r
+                    diffY = this.constrain(h, diffY, mh, mxh);\r
+                    y += diffY;\r
+                    h -= diffY;\r
+                    break;\r
+                case 'west':\r
+                    diffX = this.constrain(w, diffX, mw, mxw);\r
+                    x += diffX;\r
+                    w -= diffX;\r
+                    break;\r
+                case 'northeast':\r
+                    w += diffX; \r
+                    w = Math.min(Math.max(mw, w), mxw);\r
+                    diffY = this.constrain(h, diffY, mh, mxh);\r
+                    y += diffY;\r
+                    h -= diffY;\r
+                    break;\r
+                case 'northwest':\r
+                    diffX = this.constrain(w, diffX, mw, mxw);\r
+                    diffY = this.constrain(h, diffY, mh, mxh);\r
+                    y += diffY;\r
+                    h -= diffY;\r
+                    x += diffX;\r
+                    w -= diffX;\r
+                    break;\r
+               case 'southwest':\r
+                    diffX = this.constrain(w, diffX, mw, mxw);\r
+                    h += diffY;\r
+                    h = Math.min(Math.max(mh, h), mxh);\r
+                    x += diffX;\r
+                    w -= diffX;\r
+                    break;\r
+            }\r
+            \r
+            var sw = this.snap(w, wi, mw);\r
+            var sh = this.snap(h, hi, mh);\r
+            if(sw != w || sh != h){\r
+                switch(pos){\r
+                    case 'northeast':\r
+                        y -= sh - h;\r
+                    break;\r
+                    case 'north':\r
+                        y -= sh - h;\r
+                        break;\r
+                    case 'southwest':\r
+                        x -= sw - w;\r
+                    break;\r
+                    case 'west':\r
+                        x -= sw - w;\r
+                        break;\r
+                    case 'northwest':\r
+                        x -= sw - w;\r
+                        y -= sh - h;\r
+                    break;\r
+                }\r
+                w = sw;\r
+                h = sh;\r
+            }\r
+            \r
+            if(this.preserveRatio){\r
+                switch(pos){\r
+                    case 'southeast':\r
+                    case 'east':\r
+                        h = oh * (w/ow);\r
+                        h = Math.min(Math.max(mh, h), mxh);\r
+                        w = ow * (h/oh);\r
+                       break;\r
+                    case 'south':\r
+                        w = ow * (h/oh);\r
+                        w = Math.min(Math.max(mw, w), mxw);\r
+                        h = oh * (w/ow);\r
+                        break;\r
+                    case 'northeast':\r
+                        w = ow * (h/oh);\r
+                        w = Math.min(Math.max(mw, w), mxw);\r
+                        h = oh * (w/ow);\r
+                    break;\r
+                    case 'north':\r
+                        tw = w;\r
+                        w = ow * (h/oh);\r
+                        w = Math.min(Math.max(mw, w), mxw);\r
+                        h = oh * (w/ow);\r
+                        x += (tw - w) / 2;\r
+                        break;\r
+                    case 'southwest':\r
+                        h = oh * (w/ow);\r
+                        h = Math.min(Math.max(mh, h), mxh);\r
+                        tw = w;\r
+                        w = ow * (h/oh);\r
+                        x += tw - w;\r
+                        break;\r
+                    case 'west':\r
+                        th = h;\r
+                        h = oh * (w/ow);\r
+                        h = Math.min(Math.max(mh, h), mxh);\r
+                        y += (th - h) / 2;\r
+                        tw = w;\r
+                        w = ow * (h/oh);\r
+                        x += tw - w;\r
+                       break;\r
+                    case 'northwest':\r
+                        tw = w;\r
+                        th = h;\r
+                        h = oh * (w/ow);\r
+                        h = Math.min(Math.max(mh, h), mxh);\r
+                        w = ow * (h/oh);\r
+                        y += th - h;\r
+                        x += tw - w;\r
+                        break;\r
+                        \r
+                }\r
+            }\r
+            this.proxy.setBounds(x, y, w, h);\r
+            if(this.dynamic){\r
+                this.resizeElement();\r
+            }\r
+            }catch(ex){}\r
+        }\r
+    },\r
+\r
+    // private\r
+    handleOver : function(handle){\r
+        if(this.enabled){\r
+            Ext.getBody().addClass('ux-resizable-handle-' + handle.position);\r
+        }\r
+    },\r
+\r
+    // private\r
+    handleOut : function(handle){\r
+        if(!this.resizing){\r
+            Ext.getBody().removeClass('ux-resizable-handle-' + handle.position);\r
+        }\r
+    },\r
+    \r
+    /**\r
+     * Returns the element this component is bound to.\r
+     * @return {Ext.Element}\r
+     */\r
+    getEl : function(){\r
+        return this.el;\r
+    },\r
+\r
+    /**\r
+     * Destroys this resizable. If the element was wrapped and \r
+     * removeEl is not true then the element remains.\r
+     * @param {Boolean} removeEl (optional) true to remove the element from the DOM\r
+     */\r
+    destroy : function(removeEl){\r
+        Ext.destroy(this.dd, this.proxy);\r
+        this.proxy = null;\r
+        \r
+        var ps = Ext.Resizable.positions;\r
+        for(var k in ps){\r
+            if(typeof ps[k] != 'function' && this[ps[k]]){\r
+                this[ps[k]].destroy();\r
+            }\r
+        }\r
+        if(removeEl){\r
+            this.el.update('');\r
+            Ext.destroy(this.el);\r
+            this.el = null;\r
+        }\r
+        this.purgeListeners();\r
+    },\r
+\r
+    syncHandleHeight : function(){\r
+        var h = this.el.getHeight(true);\r
+        if(this.west){\r
+            this.west.el.setHeight(h);\r
+        }\r
+        if(this.east){\r
+            this.east.el.setHeight(h);\r
+        }\r
+    }\r
+});\r
+\r
+// private\r
+// hash to map config positions to true positions\r
+Ext.Resizable.positions = {\r
+    n: 'north', s: 'south', e: 'east', w: 'west', se: 'southeast', sw: 'southwest', nw: 'northwest', ne: 'northeast'\r
+};\r
+Ext.Resizable.cfg = {\r
+    north: {left: 7, right: -7, height: 7},\r
+    south: {left: 7, right: -7, top: -7},\r
+    east: {top: 7, bottom: -7, left: -7},\r
+    west: {top: 7, bottom: -7, width: 7},\r
+    southeast: {top: -7, left: -7},\r
+    southwest: {top: -7, width: 7},\r
+    northwest: {height: 7, width: 7},\r
+    northeast: {left: -7, height: 7}\r
+};\r
+\r
+// private\r
+Ext.Resizable.Handle = function(rz, pos){\r
+    this.position = pos;\r
+    this.rz = rz;\r
+    var cfg = Ext.Resizable.cfg[pos] || Ext.Resizable.cfg[Ext.Resizable.positions[pos]];\r
+    this.ez = new Ext.ux.EventZone(Ext.apply({\r
+        position: pos,\r
+        el: rz.el\r
+    }, cfg));\r
+    this.ez.on({\r
+        mousedown: this.onMouseDown,\r
+        mouseenter: this.onMouseOver,\r
+        mouseleave: this.onMouseOut,\r
+        scope: this\r
+    });\r
+};\r
+\r
+// private\r
+Ext.Resizable.Handle.prototype = {\r
+    cursor: 'move',\r
+\r
+    // private\r
+    afterResize : function(rz){\r
+        // do nothing    \r
+    },\r
+    // private\r
+    onMouseDown : function(e){\r
+        this.rz.onMouseDown(this, e);\r
+    },\r
+    // private\r
+    onMouseOver : function(e){\r
+        this.rz.handleOver(this, e);\r
+    },\r
+    // private\r
+    onMouseOut : function(e){\r
+        this.rz.handleOut(this, e);\r
+    },\r
+    // private\r
+    destroy : function(){\r
+        Ext.destroy(this.el);\r
+        this.el = null;\r
+    }\r
+};\r
+\r
+/**\r
+*\r
+* Ext.ux.elasticTextArea Extension Class for Ext 3.x Library\r
+*\r
+* @author  Steffen Kamper\r
+*\r
+* @license Ext.ux.elasticTextArea is licensed under the terms of\r
+* the Open Source LGPL 3.0 license.  Commercial use is permitted to the extent\r
+* that the code/component(s) do NOT become part of another Open Source or Commercially\r
+* licensed development library or toolkit without explicit permission.\r
+* \r
+* License details: http://www.gnu.org/licenses/lgpl.html\r
+*\r
+*/\r
+Ext.ux.elasticTextArea = function(){\r
+    \r
+    var defaultConfig = function(){\r
+        return {\r
+            minHeight : 0\r
+            ,maxHeight : 0\r
+            ,growBy: 20\r
+        }\r
+    }\r
+    \r
+    var processOptions = function(config){\r
+        var o = defaultConfig();\r
+        var options = {};\r
+        Ext.apply(options, config, o);\r
+        \r
+        return options ;\r
+    }\r
+    \r
+    return {\r
+        div : null\r
+        ,applyTo: function(elementId, options){\r
+        \r
+            var el = Ext.get(elementId);\r
+            var width = el.getWidth();\r
+            var height = el.getHeight();\r
+            \r
+            var styles = el.getStyles('padding-top', 'padding-bottom', 'padding-left', 'padding-right', 'line-height', 'font-size', 'font-family', 'font-weight');\r
+\r
+            if(! this.div){\r
+                var options = processOptions(options);\r
+                \r
+                this.div = Ext.DomHelper.append(Ext.getBody() || document.body, {\r
+                    'id':elementId + '-preview-div'\r
+                    ,'tag' : 'div'\r
+                    ,'background': 'red'\r
+                    ,'style' : 'position: absolute; top: -100000px; left: -100000px;'\r
+                }, true)\r
+                Ext.DomHelper.applyStyles(this.div, styles);\r
+                \r
+                el.on('keyup', function() {\r
+                        this.applyTo(elementId, options);\r
+                }, this);\r
+            }\r
+            \r
+            //replace \n with <br>&nbsp; so that the enter key can trigger and height increase\r
+            //but first remove all previous entries, so that the height mesurement can be as accurate as possible\r
+            this.div.update( \r
+                    el.dom.value.replace(/<br \/>&nbsp;/, '<br />')\r
+                                .replace(/<|>/g, ' ')\r
+                                .replace(/&/g,"&amp;")\r
+                                .replace(/\n/g, '<br />&nbsp;') \r
+                    );\r
+            \r
+            var textHeight = this.div.getHeight() + 12;\r
+            \r
+            if ( (textHeight > options.maxHeight ) && (options.maxHeight > 0) ){\r
+                textHeight = options.maxHeight ;\r
+                el.setStyle('overflow', 'auto');\r
+            }\r
+            if ( (textHeight < options.minHeight ) && (options.minHeight > 0) ) {\r
+                textHeight = options.minHeight ;\r
+                el.setStyle('overflow', 'auto');\r
+            }\r
+            \r
+            el.setHeight(textHeight + options.growBy , true);\r
+        }\r
+    }\r
+}\r
diff --git a/t3lib/js/extjs/ux/resize.css b/t3lib/js/extjs/ux/resize.css
new file mode 100644 (file)
index 0000000..f2d0851
--- /dev/null
@@ -0,0 +1,68 @@
+.ux-resizable-handle-east, .ux-resizable-handle-east textarea {\r
+    cursor: e-resize;\r
+}\r
+\r
+.ux-resizable-handle-south, .ux-resizable-handle-south textarea {\r
+    cursor: s-resize;\r
+}\r
+\r
+.ux-resizable-handle-west, .ux-resizable-handle-west textarea {\r
+    cursor: w-resize;\r
+}\r
+\r
+.ux-resizable-handle-north, .ux-resizable-handle-north textarea {\r
+    cursor: n-resize;\r
+}\r
+\r
+.ux-resizable-handle-southeast, .ux-resizable-handle-southeast textarea {\r
+    cursor: se-resize;\r
+}\r
+\r
+.ux-resizable-handle-northwest, .ux-resizable-handle-northwest textarea {\r
+    cursor: nw-resize;\r
+}\r
+\r
+.ux-resizable-handle-northeast, .ux-resizable-handle-northeast textarea {\r
+    cursor: ne-resize;\r
+}\r
+\r
+.ux-resizable-handle-southwest, .ux-resizable-handle-southwest textarea {\r
+    cursor: sw-resize;\r
+}\r
+.ux-resizable-handle-east, .ux-resizable-handle-east textarea {\r
+    cursor: e-resize;\r
+}\r
+\r
+.ux-resizable-handle-south, .ux-resizable-handle-south textarea {\r
+    cursor: s-resize;\r
+}\r
+\r
+.ux-resizable-handle-west, .ux-resizable-handle-west textarea {\r
+    cursor: w-resize;\r
+}\r
+\r
+.ux-resizable-handle-north, .ux-resizable-handle-north textarea {\r
+    cursor: n-resize;\r
+}\r
+\r
+.ux-resizable-handle-southeast, .ux-resizable-handle-southeast textarea {\r
+    cursor: se-resize;\r
+}\r
+\r
+.ux-resizable-handle-northwest, .ux-resizable-handle-northwest textarea {\r
+    cursor: nw-resize;\r
+}\r
+\r
+.ux-resizable-handle-northeast, .ux-resizable-handle-northeast textarea {\r
+    cursor: ne-resize;\r
+}\r
+\r
+.ux-resizable-handle-southwest, .ux-resizable-handle-southwest textarea {\r
+    cursor: sw-resize;\r
+}\r
+\r
+textarea.resizable {\r
+       background-image: url(resize.gif);\r
+       background-position: bottom right;\r
+       background-repeat: no-repeat;\r
+}
\ No newline at end of file
diff --git a/t3lib/js/extjs/ux/resize.gif b/t3lib/js/extjs/ux/resize.gif
new file mode 100644 (file)
index 0000000..89b82d7
Binary files /dev/null and b/t3lib/js/extjs/ux/resize.gif differ