2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
8 YUI.add('resize-constrain', function(Y) {
11 isBoolean = Lang.isBoolean,
12 isNumber = Lang.isNumber,
13 isString = Lang.isString,
14 capitalize = Y.Resize.capitalize,
16 isNode = function(v) {
17 return (v instanceof Y.Node);
20 toNumber = function(num) {
21 return parseFloat(num) || 0;
24 BORDER_BOTTOM_WIDTH = 'borderBottomWidth',
25 BORDER_LEFT_WIDTH = 'borderLeftWidth',
26 BORDER_RIGHT_WIDTH = 'borderRightWidth',
27 BORDER_TOP_WIDTH = 'borderTopWidth',
31 CONSTRAIN = 'constrain',
34 MAX_HEIGHT = 'maxHeight',
35 MAX_WIDTH = 'maxWidth',
36 MIN_HEIGHT = 'minHeight',
37 MIN_WIDTH = 'minWidth',
39 OFFSET_HEIGHT = 'offsetHeight',
40 OFFSET_WIDTH = 'offsetWidth',
41 PRESEVE_RATIO = 'preserveRatio',
43 RESIZE_CONTRAINED = 'resizeConstrained',
50 VIEWPORT_REGION = 'viewportRegion';
52 function ResizeConstrained() {
53 ResizeConstrained.superclass.constructor.apply(this, arguments);
56 Y.mix(ResizeConstrained, {
57 NAME: RESIZE_CONTRAINED,
63 * Will attempt to constrain the resize node to the boundaries. Arguments:<br>
64 * 'view': Contrain to Viewport<br>
65 * '#selector_string': Constrain to this node<br>
66 * '{Region Object}': An Object Literal containing a valid region (top, right, bottom, left) of page positions
68 * @attribute constrain
69 * @type {String/Object/Node}
73 if (v && (isNode(v) || isString(v) || v.nodeType)) {
82 * The minimum height of the element
84 * @attribute minHeight
94 * The minimum width of the element
106 * The maximum height of the element
108 * @attribute maxHeight
118 * The maximum width of the element
120 * @attribute maxWidth
130 * Maintain the element's ratio when resizing.
132 * @attribute preserveRatio
142 * The number of x ticks to span the resize to.
146 * @type Number | false
153 * The number of y ticks to span the resize to.
157 * @type Number | false
165 Y.extend(ResizeConstrained, Y.Plugin.Base, {
167 * Stores the <code>constrain</code>
168 * surrounding information retrieved from
169 * <a href="Resize.html#method__getBoxSurroundingInfo">_getBoxSurroundingInfo</a>.
171 * @property constrainSurrounding
175 constrainSurrounding: null,
177 initializer: function() {
179 host = instance.get(HOST);
181 host.delegate.dd.plug(
182 Y.Plugin.DDConstrained,
184 tickX: instance.get(TICK_X),
185 tickY: instance.get(TICK_Y)
189 host.after('resize:align', Y.bind(instance._handleResizeAlignEvent, instance));
190 host.on('resize:start', Y.bind(instance._handleResizeStartEvent, instance));
194 * Helper method to update the current values on
195 * <a href="Resize.html#property_info">info</a> to respect the
198 * @method _checkConstrain
199 * @param {String} axis 'top' or 'left'
200 * @param {String} axisConstrain 'bottom' or 'right'
201 * @param {String} offset 'offsetHeight' or 'offsetWidth'
204 _checkConstrain: function(axis, axisConstrain, offset) {
210 host = instance.get(HOST),
212 constrainBorders = instance.constrainSurrounding.border,
213 region = instance._getConstrainRegion();
216 point1 = info[axis] + info[offset];
217 point1Constrain = region[axisConstrain] - toNumber(constrainBorders[capitalize(BORDER, axisConstrain, WIDTH)]);
219 if (point1 >= point1Constrain) {
220 info[offset] -= (point1 - point1Constrain);
224 point2Constrain = region[axis] + toNumber(constrainBorders[capitalize(BORDER, axis, WIDTH)]);
226 if (point2 <= point2Constrain) {
227 info[axis] += (point2Constrain - point2);
228 info[offset] -= (point2Constrain - point2);
234 * Update the current values on <a href="Resize.html#property_info">info</a>
235 * to respect the maxHeight and minHeight.
237 * @method _checkHeight
240 _checkHeight: function() {
242 host = instance.get(HOST),
244 maxHeight = (instance.get(MAX_HEIGHT) + host.totalVSurrounding),
245 minHeight = (instance.get(MIN_HEIGHT) + host.totalVSurrounding);
247 instance._checkConstrain(TOP, BOTTOM, OFFSET_HEIGHT);
249 if (info.offsetHeight > maxHeight) {
250 host._checkSize(OFFSET_HEIGHT, maxHeight);
253 if (info.offsetHeight < minHeight) {
254 host._checkSize(OFFSET_HEIGHT, minHeight);
259 * Update the current values on <a href="Resize.html#property_info">info</a>
260 * calculating the correct ratio for the other values.
262 * @method _checkRatio
265 _checkRatio: function() {
267 host = instance.get(HOST),
269 originalInfo = host.originalInfo,
270 oWidth = originalInfo.offsetWidth,
271 oHeight = originalInfo.offsetHeight,
272 oTop = originalInfo.top,
273 oLeft = originalInfo.left,
274 oBottom = originalInfo.bottom,
275 oRight = originalInfo.right,
276 // wRatio/hRatio functions keep the ratio information always synced with the current info information
277 // RETURN: percentage how much width/height has changed from the original width/height
278 wRatio = function() {
279 return (info.offsetWidth/oWidth);
281 hRatio = function() {
282 return (info.offsetHeight/oHeight);
284 isClosestToHeight = host.changeHeightHandles,
292 // check whether the resizable node is closest to height or not
293 if (instance.get(CONSTRAIN) && host.changeHeightHandles && host.changeWidthHandles) {
294 constrainRegion = instance._getConstrainRegion();
295 constrainBorders = instance.constrainSurrounding.border;
296 bottomDiff = (constrainRegion.bottom - toNumber(constrainBorders[BORDER_BOTTOM_WIDTH])) - oBottom;
297 leftDiff = oLeft - (constrainRegion.left + toNumber(constrainBorders[BORDER_LEFT_WIDTH]));
298 rightDiff = (constrainRegion.right - toNumber(constrainBorders[BORDER_RIGHT_WIDTH])) - oRight;
299 topDiff = oTop - (constrainRegion.top + toNumber(constrainBorders[BORDER_TOP_WIDTH]));
301 if (host.changeLeftHandles && host.changeTopHandles) {
302 isClosestToHeight = (topDiff < leftDiff);
304 else if (host.changeLeftHandles) {
305 isClosestToHeight = (bottomDiff < leftDiff);
307 else if (host.changeTopHandles) {
308 isClosestToHeight = (topDiff < rightDiff);
311 isClosestToHeight = (bottomDiff < rightDiff);
315 // when the height of the resizable element touch the border of the constrain first
316 // force the offsetWidth to be calculated based on the height ratio
317 if (isClosestToHeight) {
318 info.offsetWidth = oWidth*hRatio();
319 instance._checkWidth();
320 info.offsetHeight = oHeight*wRatio();
323 info.offsetHeight = oHeight*wRatio();
324 instance._checkHeight();
325 info.offsetWidth = oWidth*hRatio();
328 // fixing the top on handles which are able to change top
329 // the idea here is change the top based on how much the height has changed instead of follow the dy
330 if (host.changeTopHandles) {
331 info.top = oTop + (oHeight - info.offsetHeight);
334 // fixing the left on handles which are able to change left
335 // the idea here is change the left based on how much the width has changed instead of follow the dx
336 if (host.changeLeftHandles) {
337 info.left = oLeft + (oWidth - info.offsetWidth);
340 // rounding values to avoid pixel jumpings
341 Y.each(info, function(value, key) {
342 if (isNumber(value)) {
343 info[key] = Math.round(value);
349 * Check whether the resizable node is inside the constrain region.
351 * @method _checkRegion
355 _checkRegion: function() {
357 host = instance.get(HOST),
358 region = instance._getConstrainRegion();
360 return Y.DOM.inRegion(null, region, true, host.info);
364 * Update the current values on <a href="Resize.html#property_info">info</a>
365 * to respect the maxWidth and minWidth.
367 * @method _checkWidth
370 _checkWidth: function() {
372 host = instance.get(HOST),
374 maxWidth = (instance.get(MAX_WIDTH) + host.totalHSurrounding),
375 minWidth = (instance.get(MIN_WIDTH) + host.totalHSurrounding);
377 instance._checkConstrain(LEFT, RIGHT, OFFSET_WIDTH);
379 if (info.offsetWidth < minWidth) {
380 host._checkSize(OFFSET_WIDTH, minWidth);
383 if (info.offsetWidth > maxWidth) {
384 host._checkSize(OFFSET_WIDTH, maxWidth);
389 * Get the constrain region based on the <code>constrain</code>
392 * @method _getConstrainRegion
394 * @return {Object Region}
396 _getConstrainRegion: function() {
398 host = instance.get(HOST),
399 node = host.get(NODE),
400 constrain = instance.get(CONSTRAIN),
404 if (constrain == VIEW) {
405 region = node.get(VIEWPORT_REGION);
407 else if (isNode(constrain)) {
408 region = constrain.get(REGION);
418 _handleResizeAlignEvent: function(event) {
420 host = instance.get(HOST);
422 // check the max/min height and locking top when these values are reach
423 instance._checkHeight();
425 // check the max/min width and locking left when these values are reach
426 instance._checkWidth();
428 // calculating the ratio, for proportionally resizing
429 if (instance.get(PRESEVE_RATIO)) {
430 instance._checkRatio();
433 if (instance.get(CONSTRAIN) && !instance._checkRegion()) {
434 host.info = host.lastInfo;
438 _handleResizeStartEvent: function(event) {
440 constrain = instance.get(CONSTRAIN),
441 host = instance.get(HOST);
443 instance.constrainSurrounding = host._getBoxSurroundingInfo(constrain);
447 Y.namespace('Plugin');
448 Y.Plugin.ResizeConstrained = ResizeConstrained;
451 }, '3.3.0' ,{requires:['resize-base', 'plugin'], skinnable:false});