3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @class Ext.resizer.Resizer
17 * <p>Applies drag handles to an element or component to make it resizable. The
18 * drag handles are inserted into the element (or component's element) and
19 * positioned absolute.</p>
21 * <p>Textarea and img elements will be wrapped with an additional div because
22 * these elements do not support child nodes. The original element can be accessed
23 * through the originalTarget property.</p>
25 * <p>Here is the list of valid resize handles:</p>
28 ------ -------------------
39 * {@img Ext.resizer.Resizer/Ext.resizer.Resizer.png Ext.resizer.Resizer component}
40 * <p>Here's an example showing the creation of a typical Resizer:</p>
42 <div id="elToResize" style="width:200px; height:100px; background-color:#000000;"></div>
44 Ext.create('Ext.resizer.Resizer', {
55 Ext.define('Ext.resizer.Resizer', {
57 observable: 'Ext.util.Observable'
59 uses: ['Ext.resizer.ResizeTracker', 'Ext.Component'],
61 alternateClassName: 'Ext.Resizable',
63 handleCls: Ext.baseCSSPrefix + 'resizable-handle',
64 pinnedCls: Ext.baseCSSPrefix + 'resizable-pinned',
65 overCls: Ext.baseCSSPrefix + 'resizable-over',
66 proxyCls: Ext.baseCSSPrefix + 'resizable-proxy',
67 wrapCls: Ext.baseCSSPrefix + 'resizable-wrap',
70 * @cfg {Boolean} dynamic
71 * <p>Specify as true to update the {@link #target} (Element or {@link Ext.Component Component}) dynamically during dragging.
72 * This is <code>true</code> by default, but the {@link Ext.Component Component} class passes <code>false</code> when it
73 * is configured as {@link Ext.Component#resizable}.</p>
74 * <p>If specified as <code>false</code>, a proxy element is displayed during the resize operation, and the {@link #target}
75 * is updated on mouseup.</p>
80 * @cfg {String} handles String consisting of the resize handles to display. Defaults to 's e se' for
81 * Elements and fixed position Components. Defaults to 8 point resizing for floating Components (such as Windows).
82 * Specify either <code>'all'</code> or any of <code>'n s e w ne nw se sw'</code>.
87 * @cfg {Number} height Optional. The height to set target to in pixels (defaults to null)
92 * @cfg {Number} width Optional. The width to set the target to in pixels (defaults to null)
97 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels.
98 * Defaults to <code>0</code>.
103 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels
104 * Defaults to <code>0</code>.
109 * @cfg {Number} minHeight The minimum height for the element (defaults to 20)
114 * @cfg {Number} minWidth The minimum width for the element (defaults to 20)
119 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
124 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
129 * @cfg {Boolean} pinned True to ensure that the resize handles are always
130 * visible, false indicates resizing by cursor changes only (defaults to false)
135 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height
136 * and width during resize (defaults to false)
138 preserveRatio: false,
141 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
146 * @cfg {Mixed} constrainTo Optional. An element, or a {@link Ext.util.Region} into which the resize operation
147 * must be constrained.
162 * @cfg {Mixed} target The Element or Component to resize.
166 * Outer element for resizing behavior.
167 * @type Ext.core.Element
171 constructor: function(config) {
175 handles = me.handles,
184 * @event beforeresize
185 * Fired before resize is allowed. Return false to cancel resize.
186 * @param {Ext.resizer.Resizer} this
187 * @param {Number} width The start width
188 * @param {Number} height The start height
189 * @param {Ext.EventObject} e The mousedown event
194 * Fires during resizing. Return false to cancel resize.
195 * @param {Ext.resizer.Resizer} this
196 * @param {Number} width The new width
197 * @param {Number} height The new height
198 * @param {Ext.EventObject} e The mousedown event
203 * Fired after a resize.
204 * @param {Ext.resizer.Resizer} this
205 * @param {Number} width The new width
206 * @param {Number} height The new height
207 * @param {Ext.EventObject} e The mouseup event
212 if (Ext.isString(config) || Ext.isElement(config) || config.dom) {
214 config = arguments[1] || {};
215 config.target = target;
217 // will apply config to this
218 me.mixins.observable.constructor.call(me, config);
220 // If target is a Component, ensure that we pull the element out.
221 // Resizer must examine the underlying Element.
224 if (target.isComponent) {
225 me.el = target.getEl();
226 if (target.minWidth) {
227 me.minWidth = target.minWidth;
229 if (target.minHeight) {
230 me.minHeight = target.minHeight;
232 if (target.maxWidth) {
233 me.maxWidth = target.maxWidth;
235 if (target.maxHeight) {
236 me.maxHeight = target.maxHeight;
238 if (target.floating) {
239 if (!this.hasOwnProperty('handles')) {
240 this.handles = 'n ne e se s sw w nw';
244 me.el = me.target = Ext.get(target);
247 // Backwards compatibility with Ext3.x's Resizable which used el as a config.
249 me.target = me.el = Ext.get(me.el);
252 // Tags like textarea and img cannot
253 // have children and therefore must
255 tag = me.el.dom.tagName;
256 if (tag == 'TEXTAREA' || tag == 'IMG') {
258 * Reference to the original resize target if the element of the original
259 * resize target was an IMG or a TEXTAREA which must be wrapped in a DIV.
261 * @property originalTarget
263 me.originalTarget = me.target;
264 me.target = me.el = me.el.wrap({
266 id: me.el.id + '-rzwrap'
269 // Transfer originalTarget's positioning/sizing
270 me.el.setPositioning(me.originalTarget.getPositioning());
271 me.originalTarget.clearPositioning();
272 var box = me.originalTarget.getBox();
276 // Position the element, this enables us to absolute position
277 // the handles within this.el
280 me.el.addCls(me.pinnedCls);
284 * @type Ext.resizer.ResizeTracker
285 * @property resizeTracker
287 me.resizeTracker = Ext.create('Ext.resizer.ResizeTracker', {
288 disabled: me.disabled,
290 constrainTo: me.constrainTo,
292 throttle: me.throttle,
293 originalTarget: me.originalTarget,
294 delegate: '.' + me.handleCls,
296 preserveRatio: me.preserveRatio,
297 heightIncrement: me.heightIncrement,
298 widthIncrement: me.widthIncrement,
299 minHeight: me.minHeight,
300 maxHeight: me.maxHeight,
301 minWidth: me.minWidth,
302 maxWidth: me.maxWidth
305 // Relay the ResizeTracker's superclass events as our own resize events
306 me.resizeTracker.on('mousedown', me.onBeforeResize, me);
307 me.resizeTracker.on('drag', me.onResize, me);
308 me.resizeTracker.on('dragend', me.onResizeEnd, me);
310 if (me.handles == 'all') {
311 me.handles = 'n s e w ne nw se sw';
314 handles = me.handles = me.handles.split(/ |\s*?[,;]\s*?/);
315 possibles = me.possiblePositions;
316 len = handles.length;
317 handleCls = me.handleCls + ' ' + (this.target.isComponent ? (me.target.baseCls + '-handle ') : '') + me.handleCls + '-';
320 // if specified and possible, create
321 if (handles[i] && possibles[handles[i]]) {
322 pos = possibles[handles[i]];
323 // store a reference in this.east, this.west, etc
325 me[pos] = Ext.create('Ext.Component', {
328 cls: handleCls + pos,
331 me[pos].el.unselectable();
332 if (me.transparent) {
333 me[pos].el.setOpacity(0);
338 // Constrain within configured maxima
339 if (Ext.isNumber(me.width)) {
340 me.width = Ext.Number.constrain(me.width, me.minWidth, me.maxWidth);
342 if (Ext.isNumber(me.height)) {
343 me.height = Ext.Number.constrain(me.height, me.minHeight, me.maxHeight);
347 if (me.width != null || me.height != null) {
348 if (me.originalTarget) {
349 me.originalTarget.setWidth(me.width);
350 me.originalTarget.setHeight(me.height);
352 me.resizeTo(me.width, me.height);
355 me.forceHandlesHeight();
358 disable: function() {
359 this.resizeTracker.disable();
363 this.resizeTracker.enable();
367 * @private Relay the Tracker's mousedown event as beforeresize
368 * @param tracker The Resizer
371 onBeforeResize: function(tracker, e) {
372 var b = this.target.getBox();
373 return this.fireEvent('beforeresize', this, b.width, b.height, e);
377 * @private Relay the Tracker's drag event as resizedrag
378 * @param tracker The Resizer
381 onResize: function(tracker, e) {
383 b = me.target.getBox();
384 me.forceHandlesHeight();
385 return me.fireEvent('resizedrag', me, b.width, b.height, e);
389 * @private Relay the Tracker's dragend event as resize
390 * @param tracker The Resizer
393 onResizeEnd: function(tracker, e) {
395 b = me.target.getBox();
396 me.forceHandlesHeight();
397 return me.fireEvent('resize', me, b.width, b.height, e);
401 * Perform a manual resize and fires the 'resize' event.
402 * @param {Number} width
403 * @param {Number} height
405 resizeTo : function(width, height){
406 this.target.setSize(width, height);
407 this.fireEvent('resize', this, width, height, null);
411 * <p>Returns the element that was configured with the el or target config property.
412 * If a component was configured with the target property then this will return the
413 * element of this component.<p>
414 * <p>Textarea and img elements will be wrapped with an additional div because
415 * these elements do not support child nodes. The original element can be accessed
416 * through the originalTarget property.</p>
417 * @return {Element} element
424 * <p>Returns the element or component that was configured with the target config property.<p>
425 * <p>Textarea and img elements will be wrapped with an additional div because
426 * these elements do not support child nodes. The original element can be accessed
427 * through the originalTarget property.</p>
428 * @return {Element/Component}
430 getTarget: function() {
434 destroy: function() {
436 for (var i = 0, l = this.handles.length; i < l; i++) {
437 h = this[this.possiblePositions[this.handles[i]]];
445 * Fix IE6 handle height issue.
447 forceHandlesHeight : function() {
453 handle.setHeight(me.el.getHeight());
457 handle.setHeight(me.el.getHeight());