--- /dev/null
+/**\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> 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 \/> /, '<br />')\r
+ .replace(/<|>/g, ' ')\r
+ .replace(/&/g,"&")\r
+ .replace(/\n/g, '<br /> ') \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