# Yahoo! UI Library

### resize 3.3.0

Yahoo! UI Library > resize > resize-base.js (source view)
Search:
Filters Show Private Show Protected Show Deprecated

    /**
     * The Resize Utility allows you to make an HTML element resizable.
     *
     * @module resize
     */

    var Lang = Y.Lang,
    	isArray = Lang.isArray,
    	isBoolean = Lang.isBoolean,
    	isNumber = Lang.isNumber,
    	isString = Lang.isString,

    	YArray  = Y.Array,
    	trim = Lang.trim,
    	indexOf = YArray.indexOf,

    	COMMA = ',',
    	DOT = '.',
    	EMPTY_STR = '',
    	HANDLE_SUB = '{handle}',
    	SPACE = ' ',

    	ACTIVE = 'active',
    	ACTIVE_HANDLE = 'activeHandle',
    	ACTIVE_HANDLE_NODE = 'activeHandleNode',
    	ALL = 'all',
    	AUTO_HIDE = 'autoHide',
    	BORDER = 'border',
    	BOTTOM = 'bottom',
    	CLASS_NAME = 'className',
    	COLOR = 'color',
    	DEF_MIN_HEIGHT = 'defMinHeight',
    	DEF_MIN_WIDTH = 'defMinWidth',
    	HANDLE = 'handle',
    	HANDLES = 'handles',
    	HIDDEN = 'hidden',
    	INNER = 'inner',
    	LEFT = 'left',
    	MARGIN = 'margin',
    	NODE = 'node',
    	NODE_NAME = 'nodeName',
    	NONE = 'none',
    	OFFSET_HEIGHT = 'offsetHeight',
    	OFFSET_WIDTH = 'offsetWidth',
    	PADDING = 'padding',
    	PARENT_NODE = 'parentNode',
    	POSITION = 'position',
    	RELATIVE = 'relative',
    	RESIZE = 'resize',
    	RESIZING = 'resizing',
    	RIGHT = 'right',
    	STATIC = 'static',
    	STYLE = 'style',
    	TOP = 'top',
    	WIDTH = 'width',
    	WRAP = 'wrap',
    	WRAPPER = 'wrapper',
    	WRAP_TYPES = 'wrapTypes',

    	EV_MOUSE_UP = 'resize:mouseUp',
    	EV_RESIZE = 'resize:resize',
    	EV_RESIZE_ALIGN = 'resize:align',
    	EV_RESIZE_END = 'resize:end',
    	EV_RESIZE_START = 'resize:start',

    	T = 't',
    	TR = 'tr',
    	R = 'r',
    	BR = 'br',
    	B = 'b',
    	BL = 'bl',
    	L = 'l',
    	TL = 'tl',

    	concat = function() {
    		return Array.prototype.slice.call(arguments).join(SPACE);
    	},

    	// round the passed number to get rid of pixel-flickering
    	toRoundNumber = function(num) {
    		return Math.round(parseFloat(num)) || 0;
    	},

    	getCompStyle = function(node, val) {
    		return node.getComputedStyle(val);
    	},

    	handleAttrName = function(handle) {
    		return HANDLE + handle.toUpperCase();
    	},

    	isNode = function(v) {
    		return (v instanceof Y.Node);
    	},

    	toInitialCap = Y.cached(
    		function(str) {
    			return str.substring(0, 1).toUpperCase() + str.substring(1);
    		}
    	),

    	capitalize = Y.cached(function() {
    		var out = [],
    			args = YArray(arguments, 0, true);

    		YArray.each(args, function(part, i) {
    			if (i > 0) {
    				part = toInitialCap(part);
    			}
    			out.push(part);
    		});

    		return out.join(EMPTY_STR);
    	}),

    	getCN = Y.ClassNameManager.getClassName,

    	CSS_RESIZE = getCN(RESIZE),
    	CSS_RESIZE_HANDLE = getCN(RESIZE, HANDLE),
    	CSS_RESIZE_HANDLE_ACTIVE = getCN(RESIZE, HANDLE, ACTIVE),
    	CSS_RESIZE_HANDLE_INNER = getCN(RESIZE, HANDLE, INNER),
    	CSS_RESIZE_HANDLE_INNER_PLACEHOLDER = getCN(RESIZE, HANDLE, INNER, HANDLE_SUB),
    	CSS_RESIZE_HANDLE_PLACEHOLDER = getCN(RESIZE, HANDLE, HANDLE_SUB),
    	CSS_RESIZE_HIDDEN_HANDLES = getCN(RESIZE, HIDDEN, HANDLES),
    	CSS_RESIZE_WRAPPER = getCN(RESIZE, WRAPPER);

    /**
     * A base class for Resize, providing:
     * <ul>
     *    <li>Basic Lifecycle (initializer, renderUI, bindUI, syncUI, destructor)</li>
     *    <li>Applies drag handles to an element to make it resizable</li>
     *    <li>Here is the list of valid resize handles:
     *        <code>[ 't', 'tr', 'r', 'br', 'b', 'bl', 'l', 'tl' ]</code>. You can
     *        read this list as top, top-right, right, bottom-right, bottom,
     *        bottom-left, left, top-left.</li>
     *    <li>The drag handles are inserted into the element and positioned
     *        absolute. Some elements, such as a textarea or image, don't support
     *        children. To overcome that, set wrap:true in your config and the
     *        element willbe wrapped for you automatically.</li>
     * </ul>
     *
     * Quick Example:
     *
     * <pre><code>var instance = new Y.Resize({
     *  node: '#resize1',
     *  preserveRatio: true,
     *  wrap: true,
     *  maxHeight: 170,
     *  maxWidth: 400,
     *  handles: 't, tr, r, br, b, bl, l, tl'
     * });
     * </code></pre>
     *
     * Check the list of <a href="Resize.html#configattributes">Configuration Attributes</a> available for
     * Resize.
     *
     * @class Resize
     * @param config {Object} Object literal specifying widget configuration properties.
     * @constructor
     * @extends Base
     */
    function Resize() {
        Resize.superclass.constructor.apply(this, arguments);
    }

    Y.mix(Resize, {
    	/**
    	 * Static property provides a string to identify the class.
    	 *
    	 * @property Resize.NAME
    	 * @type String
    	 * @static
    	 */
    	NAME: RESIZE,

    	/**
    	 * Static property used to define the default attribute
    	 * configuration for the Resize.
    	 *
    	 * @property Resize.ATTRS
    	 * @type Object
    	 * @static
    	 */
    	ATTRS: {
    		/**
    		 * Stores the active handle during the resize.
    		 *
    		 * @attribute activeHandle
    		 * @default null
    		 * @private
    		 * @type String
    		 */
    		activeHandle: {
    			value: null,
    			validator: function(v) {
                    return Y.Lang.isString(v) || Y.Lang.isNull(v);
                }
    		},

    		/**
    		 * Stores the active handle element during the resize.
    		 *
    		 * @attribute activeHandleNode
    		 * @default null
    		 * @private
    		 * @type Node
    		 */
    		activeHandleNode: {
    			value: null,
    			validator: isNode
    		},

    		/**
             * False to ensure that the resize handles are always visible, true to
             * display them only when the user mouses over the resizable borders.
    		 *
    		 * @attribute autoHide
    		 * @default false
    		 * @type boolean
    		 */
    		autoHide: {
    			value: false,
    			validator: isBoolean
    		},

    		/**
             * The default minimum height of the element. Only used when
    		 * ResizeConstrained is not plugged.
             *
             * @attribute defMinHeight
             * @default 15
             * @type Number
             */
    		defMinHeight: {
    			value: 15,
    			validator: isNumber
    		},

    		/**
             * The default minimum width of the element. Only used when
    		 * ResizeConstrained is not plugged.
             *
             * @attribute defMinWidth
             * @default 15
             * @type Number
             */
    		defMinWidth: {
    			value: 15,
    			validator: isNumber
    		},

            /**
             * The handles to use (any combination of): 't', 'b', 'r', 'l', 'bl',
             * 'br', 'tl', 'tr'. Can use a shortcut of All.
             *
             * @attribute handles
             * @default all
             * @type Array | String
             */
    		handles: {
    			setter: '_setHandles',
    			value: ALL
    		},

    		/**
             * The selector or element to resize. Required.
             *
             * @attribute node
             * @type Node
             */
    		node: {
    			setter: Y.one
    		},

    		/**
             * True when the element is being Resized.
             *
             * @attribute resizing
             * @default false
             * @type boolean
             */
    		resizing: {
    			value: false,
    			validator: isBoolean
    		},

    		/**
    		 * True to wrap an element with a div if needed (required for textareas
             * and images, defaults to false) in favor of the handles config option.
             * The wrapper element type (default div) could be over-riden passing the
             * <code>wrapper</code> attribute.
    		 *
    		 * @attribute wrap
    		 * @default false
    		 * @type boolean
    		 */
    		wrap: {
    			setter: '_setWrap',
    			value: false,
    			validator: isBoolean
    		},

    		/**
    		 * Elements that requires a wrapper by default. Normally are elements
             * which cannot have children elements.
    		 *
    		 * @attribute wrapTypes
    		 * @default /canvas|textarea|input|select|button|img/i
    		 * @readOnly
    		 * @type Regex
    		 */
    		wrapTypes: {
    			readOnly: true,
    			value: /^canvas|textarea|input|select|button|img|iframe|table|embed$/i
    		},

    		/**
    		 * Element to wrap the <code>wrapTypes</code>. This element will house
             * the handles elements.
    		 *
    		 * @attribute wrapper
    		 * @default div
    		 * @type String | Node
    		 * @writeOnce
    		 */
    		wrapper: {
    			readOnly: true,
    			valueFn: '_valueWrapper',
    			writeOnce: true
    		}
    	},

    	RULES: {
    		b: function(instance, dx, dy) {
    			var info = instance.info,
    				originalInfo = instance.originalInfo;

    			info.offsetHeight = originalInfo.offsetHeight + dy;
    		},

    		l: function(instance, dx, dy) {
    			var info = instance.info,
    				originalInfo = instance.originalInfo;

    			info.left = originalInfo.left + dx;
    			info.offsetWidth = originalInfo.offsetWidth - dx;
    		},

    		r: function(instance, dx, dy) {
    			var info = instance.info,
    				originalInfo = instance.originalInfo;

    			info.offsetWidth = originalInfo.offsetWidth + dx;
    		},

    		t: function(instance, dx, dy) {
    			var info = instance.info,
    				originalInfo = instance.originalInfo;

    			info.top = originalInfo.top + dy;
    			info.offsetHeight = originalInfo.offsetHeight - dy;
    		},

    		tr: function(instance, dx, dy) {
    			this.t.apply(this, arguments);
    			this.r.apply(this, arguments);
    		},

    		bl: function(instance, dx, dy) {
    			this.b.apply(this, arguments);
    			this.l.apply(this, arguments);
    		},

    		br: function(instance, dx, dy) {
    			this.b.apply(this, arguments);
    			this.r.apply(this, arguments);
    		},

    		tl: function(instance, dx, dy) {
    			this.t.apply(this, arguments);
    			this.l.apply(this, arguments);
    		}
    	},

    	capitalize: capitalize
    });

    Y.Resize = Y.extend(
    	Resize,
    	Y.Base,
    	{
    		/**
    	     * Array containing all possible resizable handles.
    	     *
    	     * @property ALL_HANDLES
    	     * @type {String}
    	     */
    		ALL_HANDLES: [ T, TR, R, BR, B, BL, L, TL ],

    		/**
    	     * Regex which matches with the handles that could change the height of
    		 * the resizable element.
    	     *
    	     * @property REGEX_CHANGE_HEIGHT
    	     * @type {String}
    	     */
    		REGEX_CHANGE_HEIGHT: /^(t|tr|b|bl|br|tl)$/i,

    		/**
    	     * Regex which matches with the handles that could change the left of
    		 * the resizable element.
    	     *
    	     * @property REGEX_CHANGE_LEFT
    	     * @type {String}
    	     */
    		REGEX_CHANGE_LEFT: /^(tl|l|bl)$/i,

    		/**
    	     * Regex which matches with the handles that could change the top of
    		 * the resizable element.
    	     *
    	     * @property REGEX_CHANGE_TOP
    	     * @type {String}
    	     */
    		REGEX_CHANGE_TOP: /^(tl|t|tr)$/i,

    		/**
    	     * Regex which matches with the handles that could change the width of
    		 * the resizable element.
    	     *
    	     * @property REGEX_CHANGE_WIDTH
    	     * @type {String}
    	     */
    		REGEX_CHANGE_WIDTH: /^(bl|br|l|r|tl|tr)$/i,

    		/**
    	     * Template used to create the resize wrapper node when needed.
    	     *
    	     * @property WRAP_TEMPLATE
    	     * @type {String}
    	     */
    		WRAP_TEMPLATE: '<div class="'+CSS_RESIZE_WRAPPER+'"></div>',

    		/**
    	     * Template used to create each resize handle.
    	     *
    	     * @property HANDLE_TEMPLATE
    	     * @type {String}
    	     */
    		HANDLE_TEMPLATE: '<div class="'+concat(CSS_RESIZE_HANDLE, CSS_RESIZE_HANDLE_PLACEHOLDER)+'">' +
    							'<div class="'+concat(CSS_RESIZE_HANDLE_INNER, CSS_RESIZE_HANDLE_INNER_PLACEHOLDER)+'">&nbsp;</div>' +
    						'</div>',

    		/**
    		 * Each box has a content area and optional surrounding padding and
    		 * border areas. This property stores the sum of all horizontal
    		 * surrounding * information needed to adjust the node height.
    		 *
    		 * @property totalHSurrounding
    		 * @default 0
    		 * @type number
    		 */
    		totalHSurrounding: 0,

    		/**
    		 * Each box has a content area and optional surrounding padding and
    		 * border areas. This property stores the sum of all vertical
    		 * surrounding * information needed to adjust the node height.
    		 *
    		 * @property totalVSurrounding
    		 * @default 0
    		 * @type number
    		 */
    		totalVSurrounding: 0,

    		/**
    		 * Stores the <a href="Resize.html#config_node">node</a>
    		 * surrounding information retrieved from
    		 * <a href="Resize.html#method__getBoxSurroundingInfo">_getBoxSurroundingInfo</a>.
    		 *
    		 * @property nodeSurrounding
    		 * @type Object
    		 * @default null
    		 */
    		nodeSurrounding: null,

    		/**
    		 * Stores the <a href="Resize.html#config_wrapper">wrapper</a>
    		 * surrounding information retrieved from
    		 * <a href="Resize.html#method__getBoxSurroundingInfo">_getBoxSurroundingInfo</a>.
    		 *
    		 * @property wrapperSurrounding
    		 * @type Object
    		 * @default null
    		 */
    		wrapperSurrounding: null,

    		/**
    		 * Whether the handle being dragged can change the height.
    		 *
    		 * @property changeHeightHandles
    		 * @default false
    		 * @type boolean
    		 */
    		changeHeightHandles: false,

    		/**
    		 * Whether the handle being dragged can change the left.
    		 *
    		 * @property changeLeftHandles
    		 * @default false
    		 * @type boolean
    		 */
    		changeLeftHandles: false,

    		/**
    		 * Whether the handle being dragged can change the top.
    		 *
    		 * @property changeTopHandles
    		 * @default false
    		 * @type boolean
    		 */
    		changeTopHandles: false,

    		/**
    		 * Whether the handle being dragged can change the width.
    		 *
    		 * @property changeWidthHandles
    		 * @default false
    		 * @type boolean
    		 */
    		changeWidthHandles: false,

    		/**
    		 * Store DD.Delegate reference for the respective Resize instance.
    		 *
    		 * @property delegate
    		 * @default null
    		 * @type Object
    		 */
    		delegate: null,

    	    /**
    	     * Stores the current values for the height, width, top and left. You are
    	     * able to manipulate these values on resize in order to change the resize
    	     * behavior.
    	     *
    	     * @property info
    	     * @type Object
    	     * @protected
    	     */
    		info: null,

    		/**
    	     * Stores the last values for the height, width, top and left.
    	     *
    	     * @property lastInfo
    	     * @type Object
    	     * @protected
    	     */
    		lastInfo: null,

    	    /**
    	     * Stores the original values for the height, width, top and left, stored
    	     * on resize start.
    	     *
    	     * @property originalInfo
    	     * @type Object
    	     * @protected
    	     */
    		originalInfo: null,

    	    /**
    	     * Construction logic executed during Resize instantiation. Lifecycle.
    	     *
    	     * @method initializer
    	     * @protected
    	     */
    		initializer: function() {
    			this.renderer();
    		},

    	    /**
    	     * Create the DOM structure for the Resize. Lifecycle.
    	     *
    	     * @method renderUI
    	     * @protected
    	     */
    		renderUI: function() {
    			var instance = this;

    			instance._renderHandles();
    		},

    	    /**
    	     * Bind the events on the Resize UI. Lifecycle.
    	     *
    	     * @method bindUI
    	     * @protected
    	     */
    		bindUI: function() {
    			var instance = this;

    			instance._createEvents();
    			instance._bindDD();
    			instance._bindHandle();
    		},

    	    /**
    	     * Sync the Resize UI.
    	     *
    	     * @method syncUI
    	     * @protected
    	     */
    		syncUI: function() {
    			var instance = this;

    			this.get(NODE).addClass(CSS_RESIZE);

    			// hide handles if AUTO_HIDE is true
    			instance._setHideHandlesUI(
    				instance.get(AUTO_HIDE)
    			);
    		},

    	    /**
    	     * Descructor lifecycle implementation for the Resize class. Purges events attached
    	     * to the node (and all child nodes) and removes the Resize handles.
    	     *
    	     * @method destructor
    	     * @protected
    	     */
    		destructor: function() {
    			var instance = this,
    				node = instance.get(NODE),
    				wrapper = instance.get(WRAPPER),
    				pNode = wrapper.get(PARENT_NODE);

    			// purgeElements on boundingBox
    			Y.Event.purgeElement(wrapper, true);

    			// destroy handles dd and remove them from the dom
    			instance.eachHandle(function(handleEl) {
    				instance.delegate.dd.destroy();

    				// remove handle
    				handleEl.remove(true);
    			});

    			// unwrap node
    			if (instance.get(WRAP)) {
    				instance._copyStyles(wrapper, node);

    				if (pNode) {
    					pNode.insertBefore(node, wrapper);
    				}

    				wrapper.remove(true);
    			}

    			node.removeClass(CSS_RESIZE);
    			node.removeClass(CSS_RESIZE_HIDDEN_HANDLES);
    		},

    	    /**
    	     * Creates DOM (or manipulates DOM for progressive enhancement)
    	     * This method is invoked by initializer(). It's chained automatically for
    	     * subclasses if required.
    	     *
    	     * @method renderer
    	     * @protected
    	     */
    	    renderer: function() {
    	        this.renderUI();
    	        this.bindUI();
    	        this.syncUI();
    	    },

    	    /**
    	     * <p>Loop through each handle which is being used and executes a callback.</p>
    	     * <p>Example:</p>
    	     * <pre><code>instance.eachHandle(
    		 *      function(handleName, index) { ... }
    		 *  );</code></pre>
    	     *
    	     * @method eachHandle
    	     * @param {function} fn Callback function to be executed for each handle.
    	     */
    		eachHandle: function(fn) {
    			var instance = this;

    			Y.each(
    				instance.get(HANDLES),
    				function(handle, i) {
    					var handleEl = instance.get(
    						handleAttrName(handle)
    					);

    					fn.apply(instance, [handleEl, handle, i]);
    				}
    			);
    		},

    	    /**
    	     * Bind the handles DragDrop events to the Resize instance.
    	     *
    	     * @method _bindDD
    	     * @private
    	     */
    		_bindDD: function() {
    			var instance = this;

    			instance.delegate = new Y.DD.Delegate(
    				{
    					bubbleTargets: instance,
    					container: instance.get(WRAPPER),
    					dragConfig: {
    						clickPixelThresh: 0,
    						clickTimeThresh: 0,
    						useShim: true,
    						move: false
    					},
    					nodes: DOT+CSS_RESIZE_HANDLE,
    					target: false
    				}
    			);

    			instance.on('drag:drag', instance._handleResizeEvent);
    			instance.on('drag:dropmiss', instance._handleMouseUpEvent);
    			instance.on('drag:end', instance._handleResizeEndEvent);
    			instance.on('drag:start', instance._handleResizeStartEvent);
    		},

    	    /**
    	     * Bind the events related to the handles (_onHandleMouseEnter, _onHandleMouseLeave).
    	     *
    	     * @method _bindHandle
    	     * @private
    	     */
    		_bindHandle: function() {
    			var instance = this,
    				wrapper = instance.get(WRAPPER);

    			wrapper.on('mouseenter', Y.bind(instance._onWrapperMouseEnter, instance));
    			wrapper.on('mouseleave', Y.bind(instance._onWrapperMouseLeave, instance));
    			wrapper.delegate('mouseenter', Y.bind(instance._onHandleMouseEnter, instance), DOT+CSS_RESIZE_HANDLE);
    			wrapper.delegate('mouseleave', Y.bind(instance._onHandleMouseLeave, instance), DOT+CSS_RESIZE_HANDLE);
    		},

    	    /**
    	     * Create the custom events used on the Resize.
    	     *
    	     * @method _createEvents
    	     * @private
    	     */
    		_createEvents: function() {
    			var instance = this,
    				// create publish function for kweight optimization
    				publish = function(name, fn) {
    					instance.publish(name, {
    						defaultFn: fn,
    						queuable: false,
    						emitFacade: true,
    						bubbles: true,
    						prefix: RESIZE
    					});
    				};

    			/**
    			 * Handles the resize start event. Fired when a handle starts to be
    	         * dragged.
    			 *
    	         * @event resize:start
    	         * @preventable _defResizeStartFn
    	         * @param {Event.Facade} event The resize start event.
    	         * @bubbles Resize
    	         * @type {Event.Custom}
    	         */
    			publish(EV_RESIZE_START, this._defResizeStartFn);

    			/**
    			 * Handles the resize event. Fired on each pixel when the handle is
    	         * being dragged.
    			 *
    	         * @event resize:resize
    	         * @preventable _defResizeFn
    	         * @param {Event.Facade} event The resize event.
    	         * @bubbles Resize
    	         * @type {Event.Custom}
    	         */
    			publish(EV_RESIZE, this._defResizeFn);

    			/**
    			 * Handles the resize align event.
    			 *
    	         * @event resize:align
    	         * @preventable _defResizeAlignFn
    	         * @param {Event.Facade} event The resize align event.
    	         * @bubbles Resize
    	         * @type {Event.Custom}
    	         */
    			publish(EV_RESIZE_ALIGN, this._defResizeAlignFn);

    			/**
    			 * Handles the resize end event. Fired when a handle stop to be
    	         * dragged.
    			 *
    	         * @event resize:end
    	         * @preventable _defResizeEndFn
    	         * @param {Event.Facade} event The resize end event.
    	         * @bubbles Resize
    	         * @type {Event.Custom}
    	         */
    			publish(EV_RESIZE_END, this._defResizeEndFn);

    			/**
    			 * Handles the resize mouseUp event. Fired when a mouseUp event happens on a
    	         * handle.
    			 *
    	         * @event resize:mouseUp
    	         * @preventable _defMouseUpFn
    	         * @param {Event.Facade} event The resize mouseUp event.
    	         * @bubbles Resize
    	         * @type {Event.Custom}
    	         */
    			publish(EV_MOUSE_UP, this._defMouseUpFn);
    		},

    	    /**
    	      * Responsible for loop each handle element and append to the wrapper.
    	      *
    	      * @method _renderHandles
    	      * @protected
    	      */
    		_renderHandles: function() {
    			var instance = this,
    				wrapper = instance.get(WRAPPER);

    			instance.eachHandle(function(handleEl) {
    				wrapper.append(handleEl);
    			});
    		},

    	    /**
    	     * Creates the handle element based on the handle name and initialize the
    	     * DragDrop on it.
    	     *
    	     * @method _buildHandle
    	     * @param {String} handle Handle name ('t', 'tr', 'b', ...).
    	     * @protected
    	     */
    		_buildHandle: function(handle) {
    			var instance = this;

    			return Y.Node.create(
    				Y.substitute(instance.HANDLE_TEMPLATE, {
    					handle: handle
    				})
    			);
    		},

    	    /**
    	     * Basic resize calculations.
    	     *
    	     * @method _calcResize
    	     * @protected
    	     */
    		_calcResize: function() {
    			var instance = this,
    				handle = instance.handle,
    				info = instance.info,
    				originalInfo = instance.originalInfo,

    				dx = info.actXY[0] - originalInfo.actXY[0],
    				dy = info.actXY[1] - originalInfo.actXY[1];

                if (handle && Y.Resize.RULES[handle]) {
    			    Y.Resize.RULES[handle](instance, dx, dy);
                }
    			else {
    				Y.log('Handle rule not found: ' + handle, 'warn', 'resize');
    			}
    		},

    		/**
    	     * Helper method to update the current size value on
    	     * <a href="Resize.html#property_info">info</a> to respect the
    	     * min/max values and fix the top/left calculations.
    		 *
    		 * @method _checkSize
    		 * @param {String} offset 'offsetHeight' or 'offsetWidth'
    		 * @param {number} size Size to restrict the offset
    		 * @protected
    		 */
    		_checkSize: function(offset, size) {
    			var instance = this,
    				info = instance.info,
    				originalInfo = instance.originalInfo,
    				axis = (offset == OFFSET_HEIGHT) ? TOP : LEFT;

    			// forcing the offsetHeight/offsetWidth to be the passed size
    			info[offset] = size;

    			// predicting, based on the original information, the last left valid in case of reach the min/max dimension
    			// this calculation avoid browser event leaks when user interact very fast
    			if (((axis == LEFT) && instance.changeLeftHandles) ||
    				((axis == TOP) && instance.changeTopHandles)) {

    				info[axis] = originalInfo[axis] + originalInfo[offset] - size;
    			}
    		},

    	    /**
    	     * Copy relevant styles of the <a href="Resize.html#config_node">node</a>
    	     * to the <a href="Resize.html#config_wrapper">wrapper</a>.
    	     *
    	     * @method _copyStyles
    	     * @param {Node} node Node from.
    	     * @param {Node} wrapper Node to.
    	     * @protected
    	     */
    		_copyStyles: function(node, wrapper) {
    			var position = node.getStyle(POSITION).toLowerCase(),
    				surrounding = this._getBoxSurroundingInfo(node),
    				wrapperStyle;

    			// resizable wrapper should be positioned
    			if (position == STATIC) {
    				position = RELATIVE;
    			}

    			wrapperStyle = {
    				position: position,
    				left: getCompStyle(node, LEFT),
    				top: getCompStyle(node, TOP)
    			};

    			Y.mix(wrapperStyle, surrounding.margin);
    			Y.mix(wrapperStyle, surrounding.border);

    			wrapper.setStyles(wrapperStyle);

    			// remove margin and border from the internal node
    			node.setStyles({ border: 0, margin: 0 });

    			wrapper.sizeTo(
    				node.get(OFFSET_WIDTH) + surrounding.totalHBorder,
    				node.get(OFFSET_HEIGHT) + surrounding.totalVBorder
    			);
    		},

    		// extract handle name from a string
    		// using Y.cached to memoize the function for performance
    		_extractHandleName: Y.cached(
    			function(node) {
    				var className = node.get(CLASS_NAME),

    					match = className.match(
    						new RegExp(
    							getCN(RESIZE, HANDLE, '(\\w{1,2})\\b')
    						)
    					);

    				return match ? match[1] : null;
    			}
    		),

    	    /**
    	     * <p>Generates metadata to the <a href="Resize.html#property_info">info</a>
    	     * and <a href="Resize.html#property_originalInfo">originalInfo</a></p>
    	     * <pre><code>bottom, actXY, left, top, offsetHeight, offsetWidth, right</code></pre>
    	     *
    	     * @method _getInfo
    	     * @param {Node} node
    	     * @param {EventFacade} event
    	     * @private
    	     */
    		_getInfo: function(node, event) {
    			var actXY = [0,0],
    				drag = event.dragEvent.target,
    				nodeXY = node.getXY(),
    				nodeX = nodeXY[0],
    				nodeY = nodeXY[1],
    				offsetHeight = node.get(OFFSET_HEIGHT),
    				offsetWidth = node.get(OFFSET_WIDTH);

    			if (event) {
    				// the xy that the node will be set to. Changing this will alter the position as it's dragged.
    				actXY = (drag.actXY.length ? drag.actXY : drag.lastXY);
    			}

    			return {
    				actXY: actXY,
    				bottom: (nodeY + offsetHeight),
    				left: nodeX,
    				offsetHeight: offsetHeight,
    				offsetWidth: offsetWidth,
    				right: (nodeX + offsetWidth),
    				top: nodeY
    			};
    		},

    		/**
    		 * Each box has a content area and optional surrounding margin,
    		 * padding and * border areas. This method get all this information from
    		 * the passed node. For more reference see
    		 * <a href="http://www.w3.org/TR/CSS21/box.html#box-dimensions">
    		 * http://www.w3.org/TR/CSS21/box.html#box-dimensions</a>.
    		 *
    		 * @method _getBoxSurroundingInfo
    		 * @param {Node} node
    		 * @private
    		 * @return {Object}
    		 */
    		_getBoxSurroundingInfo: function(node) {
    			var	info = {
    				padding: {},
    				margin: {},
    				border: {}
    			};

    			if (isNode(node)) {
    				Y.each([ TOP, RIGHT, BOTTOM, LEFT ], function(dir) {
    					var paddingProperty = capitalize(PADDING, dir),
    						marginProperty = capitalize(MARGIN, dir),
    						borderWidthProperty = capitalize(BORDER, dir, WIDTH),
    						borderColorProperty = capitalize(BORDER, dir, COLOR),
    						borderStyleProperty = capitalize(BORDER, dir, STYLE);

    					info.border[borderColorProperty] = getCompStyle(node, borderColorProperty);
    					info.border[borderStyleProperty] = getCompStyle(node, borderStyleProperty);
    					info.border[borderWidthProperty] = getCompStyle(node, borderWidthProperty);
    					info.margin[marginProperty] = getCompStyle(node, marginProperty);
    					info.padding[paddingProperty] = getCompStyle(node, paddingProperty);
    				});
    			}

    			info.totalHBorder = (toRoundNumber(info.border.borderLeftWidth) + toRoundNumber(info.border.borderRightWidth));
    			info.totalHPadding = (toRoundNumber(info.padding.paddingLeft) + toRoundNumber(info.padding.paddingRight));
    			info.totalVBorder = (toRoundNumber(info.border.borderBottomWidth) + toRoundNumber(info.border.borderTopWidth));
    			info.totalVPadding = (toRoundNumber(info.padding.paddingBottom) + toRoundNumber(info.padding.paddingTop));

    			return info;
    		},

    		/**
    	     * Sync the Resize UI with internal values from
    	     * <a href="Resize.html#property_info">info</a>.
    	     *
    	     * @method _syncUI
    	     * @protected
    	     */
    		_syncUI: function() {
    			var instance = this,
    				info = instance.info,
    				wrapperSurrounding = instance.wrapperSurrounding,
    				wrapper = instance.get(WRAPPER),
    				node = instance.get(NODE);

    			wrapper.sizeTo(info.offsetWidth, info.offsetHeight);

    			if (instance.changeLeftHandles || instance.changeTopHandles) {
    				wrapper.setXY([info.left, info.top]);
    			}

    			// if a wrap node is being used
    			if (!wrapper.compareTo(node)) {
    				// the original internal node borders were copied to the wrapper on _copyStyles, to compensate that subtract the borders from the internal node
    				node.sizeTo(
    					info.offsetWidth - wrapperSurrounding.totalHBorder,
    					info.offsetHeight - wrapperSurrounding.totalVBorder
    				);
    			}

    			// prevent webkit textarea resize
    			if (Y.UA.webkit) {
    				node.setStyle(RESIZE, NONE);
    			}
    		},

    		/**
    	     * Update <code>instance.changeHeightHandles,
    		 * instance.changeLeftHandles, instance.changeTopHandles,
    		 * instance.changeWidthHandles</code> information.
    	     *
    	     * @method _updateChangeHandleInfo
    	     * @private
    	     */
    		_updateChangeHandleInfo: function(handle) {
    			var instance = this;

    			instance.changeHeightHandles = instance.REGEX_CHANGE_HEIGHT.test(handle);
    			instance.changeLeftHandles = instance.REGEX_CHANGE_LEFT.test(handle);
    			instance.changeTopHandles = instance.REGEX_CHANGE_TOP.test(handle);
    			instance.changeWidthHandles = instance.REGEX_CHANGE_WIDTH.test(handle);
    		},

    		/**
    	     * Update <a href="Resize.html#property_info">info</a> values (bottom, actXY, left, top, offsetHeight, offsetWidth, right).
    	     *
    	     * @method _updateInfo
    	     * @private
    	     */
    		_updateInfo: function(event) {
    			var instance = this;

    			instance.info = instance._getInfo(instance.get(WRAPPER), event);
    		},

    		/**
    	     * Update properties
    	     * <a href="Resize.html#property_nodeSurrounding">nodeSurrounding</a>,
    	     * <a href="Resize.html#property_nodeSurrounding">wrapperSurrounding</a>,
    	     * <a href="Resize.html#property_nodeSurrounding">totalVSurrounding</a>,
    	     * <a href="Resize.html#property_nodeSurrounding">totalHSurrounding</a>.
    	     *
    	     * @method _updateSurroundingInfo
    	     * @private
    	     */
    		_updateSurroundingInfo: function() {
    			var instance = this,
    				node = instance.get(NODE),
    				wrapper = instance.get(WRAPPER),
    				nodeSurrounding = instance._getBoxSurroundingInfo(node),
    				wrapperSurrounding = instance._getBoxSurroundingInfo(wrapper);

    			instance.nodeSurrounding = nodeSurrounding;
    			instance.wrapperSurrounding = wrapperSurrounding;

    			instance.totalVSurrounding = (nodeSurrounding.totalVPadding + wrapperSurrounding.totalVBorder);
    			instance.totalHSurrounding = (nodeSurrounding.totalHPadding + wrapperSurrounding.totalHBorder);
    		},

    	    /**
    	     * Set the active state of the handles.
    	     *
    	     * @method _setActiveHandlesUI
    	     * @param {boolean} val True to activate the handles, false to deactivate.
    	     * @protected
    	     */
    		_setActiveHandlesUI: function(val) {
    			var instance = this,
    				activeHandleNode = instance.get(ACTIVE_HANDLE_NODE);

    			if (activeHandleNode) {
    				if (val) {
    					// remove CSS_RESIZE_HANDLE_ACTIVE from all handles before addClass on the active
    					instance.eachHandle(
    						function(handleEl) {
    							handleEl.removeClass(CSS_RESIZE_HANDLE_ACTIVE);
    						}
    					);

    					activeHandleNode.addClass(CSS_RESIZE_HANDLE_ACTIVE);
    				}
    				else {
    					activeHandleNode.removeClass(CSS_RESIZE_HANDLE_ACTIVE);
    				}
    			}
    		},

    	    /**
    	     * Setter for the handles attribute
    	     *
    	     * @method _setHandles
    	     * @protected
    	     * @param {String} val
    	     */
    		_setHandles: function(val) {
    			var instance = this,
    				handles = [];

    			// handles attr accepts both array or string
    			if (isArray(val)) {
    				handles = val;
    			}
    			else if (isString(val)) {
    				// if the handles attr passed in is an ALL string...
    				if (val.toLowerCase() == ALL) {
    					handles = instance.ALL_HANDLES;
    				}
    				// otherwise, split the string to extract the handles
    				else {
    					Y.each(
    						val.split(COMMA),
    						function(node, i) {
    							var handle = trim(node);

    							// if its a valid handle, add it to the handles output
    							if (indexOf(instance.ALL_HANDLES, handle) > -1) {
    								handles.push(handle);
    							}
    						}
    					);
    				}
    			}

    			return handles;
    		},

    	    /**
    	     * Set the visibility of the handles.
    	     *
    	     * @method _setHideHandlesUI
    	     * @param {boolean} val True to hide the handles, false to show.
    	     * @protected
    	     */
    		_setHideHandlesUI: function(val) {
    			var instance = this,
    				wrapper = instance.get(WRAPPER);

    			if (!instance.get(RESIZING)) {
    				if (val) {
    					wrapper.addClass(CSS_RESIZE_HIDDEN_HANDLES);
    				}
    				else {
    					wrapper.removeClass(CSS_RESIZE_HIDDEN_HANDLES);
    				}
    			}
    		},

    	    /**
    	     * Setter for the wrap attribute
    	     *
    	     * @method _setWrap
    	     * @protected
    	     * @param {boolean} val
    	     */
    		_setWrap: function(val) {
    			var instance = this,
    				node = instance.get(NODE),
    				nodeName = node.get(NODE_NAME),
    				typeRegex = instance.get(WRAP_TYPES);

    			// if nodeName is listed on WRAP_TYPES force use the wrapper
    			if (typeRegex.test(nodeName)) {
    				val = true;
    			}

    			return val;
    		},

    	    /**
    	     * Default resize:mouseUp handler
    	     *
    	     * @method _defMouseUpFn
    	     * @param {EventFacade} event The Event object
    	     * @protected
    	     */
    		_defMouseUpFn: function(event) {
    			var instance = this;

    			instance.set(RESIZING, false);
    		},

    	    /**
    	     * Default resize:resize handler
    	     *
    	     * @method _defResizeFn
    	     * @param {EventFacade} event The Event object
    	     * @protected
    	     */
    		_defResizeFn: function(event) {
    			var instance = this;

    			instance._resize(event);
    		},

    		/**
    	     * Logic method for _defResizeFn. Allow AOP.
    	     *
    	     * @method _resize
    	     * @param {EventFacade} event The Event object
    	     * @protected
    	     */
    		_resize: function(event) {
    			var instance = this;

    			instance._handleResizeAlignEvent(event.dragEvent);

    			// _syncUI of the wrapper, not using proxy
    			instance._syncUI();
    		},

    		/**
    	     * Default resize:align handler
    	     *
    	     * @method _defResizeAlignFn
    	     * @param {EventFacade} event The Event object
    	     * @protected
    	     */
    		_defResizeAlignFn: function(event) {
    			var instance = this;

    			instance._resizeAlign(event);
    		},

    		/**
    	     * Logic method for _defResizeAlignFn. Allow AOP.
    	     *
    	     * @method _resizeAlign
    	     * @param {EventFacade} event The Event object
    	     * @protected
    	     */
    		_resizeAlign: function(event) {
    			var instance = this,
    				info,
    				defMinHeight,
    				defMinWidth;

    			instance.lastInfo = instance.info;

    			// update the instance.info values
    			instance._updateInfo(event);

    			info = instance.info;

    			// basic resize calculations
    			instance._calcResize();

    			// if Y.Plugin.ResizeConstrained is not plugged, check for min dimension
    			if (!instance.con) {
    				defMinHeight = (instance.get(DEF_MIN_HEIGHT) + instance.totalVSurrounding);
    				defMinWidth = (instance.get(DEF_MIN_WIDTH) + instance.totalHSurrounding);

    				if (info.offsetHeight <= defMinHeight) {
    					instance._checkSize(OFFSET_HEIGHT, defMinHeight);
    				}

    				if (info.offsetWidth <= defMinWidth) {
    					instance._checkSize(OFFSET_WIDTH, defMinWidth);
    				}
    			}
    		},

    	    /**
    	     * Default resize:end handler
    	     *
    	     * @method _defResizeEndFn
    	     * @param {EventFacade} event The Event object
    	     * @protected
    	     */
    		_defResizeEndFn: function(event) {
    			var instance = this;

    			instance._resizeEnd(event);
    		},

    		/**
    	     * Logic method for _defResizeEndFn. Allow AOP.
    	     *
    	     * @method _resizeEnd
    	     * @param {EventFacade} event The Event object
    	     * @protected
    	     */
    		_resizeEnd: function(event) {
    			var instance = this,
    				drag = event.dragEvent.target;

    			// reseting actXY from drag when drag end
    			drag.actXY = [];

    			// syncUI when resize end
    			instance._syncUI();

    			instance._setActiveHandlesUI(false);

    			instance.set(ACTIVE_HANDLE, null);
    			instance.set(ACTIVE_HANDLE_NODE, null);

    			instance.handle = null;
    		},

    	    /**
    	     * Default resize:start handler
    	     *
    	     * @method _defResizeStartFn
    	     * @param {EventFacade} event The Event object
    	     * @protected
    	     */
    		_defResizeStartFn: function(event) {
    			var instance = this;

    			instance._resizeStart(event);
    		},

    		/**
    	     * Logic method for _defResizeStartFn. Allow AOP.
    	     *
    	     * @method _resizeStart
    	     * @param {EventFacade} event The Event object
    	     * @protected
    	     */
    		_resizeStart: function(event) {
    			var instance = this,
    				wrapper = instance.get(WRAPPER);

    			instance.handle = instance.get(ACTIVE_HANDLE);

    			instance.set(RESIZING, true);

    			instance._updateSurroundingInfo();

    			// create an originalInfo information for reference
    			instance.originalInfo = instance._getInfo(wrapper, event);

    			instance._updateInfo(event);
    		},

    	    /**
    	     * Fires the resize:mouseUp event.
    	     *
    	     * @method _handleMouseUpEvent
    	     * @param {EventFacade} event resize:mouseUp event facade
    	     * @protected
    	     */
    		_handleMouseUpEvent: function(event) {
    			this.fire(EV_MOUSE_UP, { dragEvent: event, info: this.info });
    		},

    	    /**
    	     * Fires the resize:resize event.
    	     *
    	     * @method _handleResizeEvent
    	     * @param {EventFacade} event resize:resize event facade
    	     * @protected
    	     */
    		_handleResizeEvent: function(event) {
    			this.fire(EV_RESIZE, { dragEvent: event, info: this.info });
    		},

    	    /**
    	     * Fires the resize:align event.
    	     *
    	     * @method _handleResizeAlignEvent
    	     * @param {EventFacade} event resize:resize event facade
    	     * @protected
    	     */
    		_handleResizeAlignEvent: function(event) {
    			this.fire(EV_RESIZE_ALIGN, { dragEvent: event, info: this.info });
    		},

    	    /**
    	     * Fires the resize:end event.
    	     *
    	     * @method _handleResizeEndEvent
    	     * @param {EventFacade} event resize:end event facade
    	     * @protected
    	     */
    		_handleResizeEndEvent: function(event) {
    			this.fire(EV_RESIZE_END, { dragEvent: event, info: this.info });
    		},

    	    /**
    	     * Fires the resize:start event.
    	     *
    	     * @method _handleResizeStartEvent
    	     * @param {EventFacade} event resize:start event facade
    	     * @protected
    	     */
    		_handleResizeStartEvent: function(event) {
                if (!this.get(ACTIVE_HANDLE)) {
                    //This handles the "touch" case
    			    this._setHandleFromNode(event.target.get('node'));
                }
    			this.fire(EV_RESIZE_START, { dragEvent: event, info: this.info });
    		},

    		/**
    		 * Mouseenter event handler for the <a href="Resize.html#config_wrapper">wrapper</a>.
    		 *
    		 * @method _onWrapperMouseEnter
    	     * @param {EventFacade} event
    		 * @protected
    		 */
    		_onWrapperMouseEnter: function(event) {
    			var instance = this;

    			if (instance.get(AUTO_HIDE)) {
    				instance._setHideHandlesUI(false);
    			}
    		},

    		/**
    		 * Mouseleave event handler for the <a href="Resize.html#config_wrapper">wrapper</a>.
    		 *
    		 * @method _onWrapperMouseLeave
    	     * @param {EventFacade} event
    		 * @protected
    		 */
    		_onWrapperMouseLeave: function(event) {
    			var instance = this;

    			if (instance.get(AUTO_HIDE)) {
    				instance._setHideHandlesUI(true);
    			}
    		},

    		/**
    		 * Handles setting the activeHandle from a node, used from startDrag (for touch) and mouseenter (for mouse).
    		 *
    		 * @method _setHandleFromNode
    	     * @param {Node} node
    		 * @protected
    		 */
            _setHandleFromNode: function(node) {
    			var instance = this,
    				handle = instance._extractHandleName(node);

    			if (!instance.get(RESIZING)) {
    				instance.set(ACTIVE_HANDLE, handle);
    				instance.set(ACTIVE_HANDLE_NODE, node);

    				instance._setActiveHandlesUI(true);
    				instance._updateChangeHandleInfo(handle);
    			}
            },

    		/**
    		 * Mouseenter event handler for the handles.
    		 *
    		 * @method _onHandleMouseEnter
    	     * @param {EventFacade} event
    		 * @protected
    		 */
    		_onHandleMouseEnter: function(event) {
    			this._setHandleFromNode(event.currentTarget);
    		},

    		/**
    		 * Mouseout event handler for the handles.
    		 *
    		 * @method _onHandleMouseLeave
    	     * @param {EventFacade} event
    		 * @protected
    		 */
    		_onHandleMouseLeave: function(event) {
    			var instance = this;

    			if (!instance.get(RESIZING)) {
    				instance._setActiveHandlesUI(false);
    			}
    		},

    		/**
    	     * Default value for the wrapper attribute
    	     *
    	     * @method _valueWrapper
    	     * @protected
    	     * @readOnly
    	     */
    		_valueWrapper: function() {
    			var instance = this,
    				node = instance.get(NODE),
    				pNode = node.get(PARENT_NODE),
    				// by deafult the wrapper is always the node
    				wrapper = node;

    			// if the node is listed on the wrapTypes or wrap is set to true, create another wrapper
    			if (instance.get(WRAP)) {
    				wrapper = Y.Node.create(instance.WRAP_TEMPLATE);

    				if (pNode) {
    					pNode.insertBefore(wrapper, node);
    				}

    				wrapper.append(node);

    				instance._copyStyles(node, wrapper);

    				// remove positioning of wrapped node, the WRAPPER take care about positioning
    				node.setStyles({
    					position: STATIC,
    					left: 0,
    					top: 0
    				});
    			}

    			return wrapper;
    		}
    	}
    );

    Y.each(Y.Resize.prototype.ALL_HANDLES, function(handle, i) {
    	// creating ATTRS with the handles elements
    	Y.Resize.ATTRS[handleAttrName(handle)] = {
    		setter: function() {
    			return this._buildHandle(handle);
    		},
    		value: null,
    		writeOnce: true
    	};
    });

Copyright © 2011 Yahoo! Inc. All rights reserved.