API Docs for: 3.8.0
Show:

File: resize/js/resize-constrain.js

  1. var Lang = Y.Lang,
  2.     isBoolean = Lang.isBoolean,
  3.     isNumber = Lang.isNumber,
  4.     isString = Lang.isString,
  5.     capitalize = Y.Resize.capitalize,

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

  9.     toNumber = function(num) {
  10.         return parseFloat(num) || 0;
  11.     },

  12.     BORDER_BOTTOM_WIDTH = 'borderBottomWidth',
  13.     BORDER_LEFT_WIDTH = 'borderLeftWidth',
  14.     BORDER_RIGHT_WIDTH = 'borderRightWidth',
  15.     BORDER_TOP_WIDTH = 'borderTopWidth',
  16.     BORDER = 'border',
  17.     BOTTOM = 'bottom',
  18.     CON = 'con',
  19.     CONSTRAIN = 'constrain',
  20.     HOST = 'host',
  21.     LEFT = 'left',
  22.     MAX_HEIGHT = 'maxHeight',
  23.     MAX_WIDTH = 'maxWidth',
  24.     MIN_HEIGHT = 'minHeight',
  25.     MIN_WIDTH = 'minWidth',
  26.     NODE = 'node',
  27.     OFFSET_HEIGHT = 'offsetHeight',
  28.     OFFSET_WIDTH = 'offsetWidth',
  29.     PRESEVE_RATIO = 'preserveRatio',
  30.     REGION = 'region',
  31.     RESIZE_CONTRAINED = 'resizeConstrained',
  32.     RIGHT = 'right',
  33.     TICK_X = 'tickX',
  34.     TICK_Y = 'tickY',
  35.     TOP = 'top',
  36.     WIDTH = 'width',
  37.     VIEW = 'view',
  38.     VIEWPORT_REGION = 'viewportRegion';

  39. /**
  40. A Resize plugin that will attempt to constrain the resize node to the boundaries.
  41. @module resize
  42. @submodule resize-contrain
  43. @class ResizeConstrained
  44. @param config {Object} Object literal specifying widget configuration properties.
  45. @constructor
  46. @extends Plugin.Base
  47. @namespace Plugin
  48. */

  49. function ResizeConstrained() {
  50.     ResizeConstrained.superclass.constructor.apply(this, arguments);
  51. }

  52. Y.mix(ResizeConstrained, {
  53.     NAME: RESIZE_CONTRAINED,

  54.     NS: CON,

  55.     ATTRS: {
  56.         /**
  57.         * Will attempt to constrain the resize node to the boundaries. Arguments:<br>
  58.         * 'view': Contrain to Viewport<br>
  59.         * '#selector_string': Constrain to this node<br>
  60.         * '{Region Object}': An Object Literal containing a valid region (top, right, bottom, left) of page positions
  61.         *
  62.         * @attribute constrain
  63.         * @type {String|Object|Node}
  64.         */
  65.         constrain: {
  66.             setter: function(v) {
  67.                 if (v && (isNode(v) || isString(v) || v.nodeType)) {
  68.                     v = Y.one(v);
  69.                 }

  70.                 return v;
  71.             }
  72.         },

  73.         /**
  74.          * The minimum height of the element
  75.          *
  76.          * @attribute minHeight
  77.          * @default 15
  78.          * @type Number
  79.          */
  80.         minHeight: {
  81.             value: 15,
  82.             validator: isNumber
  83.         },

  84.         /**
  85.          * The minimum width of the element
  86.          *
  87.          * @attribute minWidth
  88.          * @default 15
  89.          * @type Number
  90.          */
  91.         minWidth: {
  92.             value: 15,
  93.             validator: isNumber
  94.         },

  95.         /**
  96.          * The maximum height of the element
  97.          *
  98.          * @attribute maxHeight
  99.          * @default Infinity
  100.          * @type Number
  101.          */
  102.         maxHeight: {
  103.             value: Infinity,
  104.             validator: isNumber
  105.         },

  106.         /**
  107.          * The maximum width of the element
  108.          *
  109.          * @attribute maxWidth
  110.          * @default Infinity
  111.          * @type Number
  112.          */
  113.         maxWidth: {
  114.             value: Infinity,
  115.             validator: isNumber
  116.         },

  117.         /**
  118.          * Maintain the element's ratio when resizing.
  119.          *
  120.          * @attribute preserveRatio
  121.          * @default false
  122.          * @type boolean
  123.          */
  124.         preserveRatio: {
  125.             value: false,
  126.             validator: isBoolean
  127.         },

  128.         /**
  129.          * The number of x ticks to span the resize to.
  130.          *
  131.          * @attribute tickX
  132.          * @default false
  133.          * @type Number | false
  134.          */
  135.         tickX: {
  136.             value: false
  137.         },

  138.         /**
  139.          * The number of y ticks to span the resize to.
  140.          *
  141.          * @attribute tickY
  142.          * @default false
  143.          * @type Number | false
  144.          */
  145.         tickY: {
  146.             value: false
  147.         }
  148.     }
  149. });

  150. Y.extend(ResizeConstrained, Y.Plugin.Base, {
  151.     /**
  152.      * Stores the <code>constrain</code>
  153.      * surrounding information retrieved from
  154.      * <a href="Resize.html#method__getBoxSurroundingInfo">_getBoxSurroundingInfo</a>.
  155.      *
  156.      * @property constrainSurrounding
  157.      * @type Object
  158.      * @default null
  159.      */
  160.     constrainSurrounding: null,

  161.     initializer: function() {
  162.         var instance = this,
  163.             host = instance.get(HOST);

  164.         host.delegate.dd.plug(
  165.             Y.Plugin.DDConstrained,
  166.             {
  167.                 tickX: instance.get(TICK_X),
  168.                 tickY: instance.get(TICK_Y)
  169.             }
  170.         );

  171.         host.after('resize:align', Y.bind(instance._handleResizeAlignEvent, instance));
  172.         host.on('resize:start', Y.bind(instance._handleResizeStartEvent, instance));
  173.     },

  174.     /**
  175.      * Helper method to update the current values on
  176.      * <a href="Resize.html#property_info">info</a> to respect the
  177.      * constrain node.
  178.      *
  179.      * @method _checkConstrain
  180.      * @param {String} axis 'top' or 'left'
  181.      * @param {String} axisConstrain 'bottom' or 'right'
  182.      * @param {String} offset 'offsetHeight' or 'offsetWidth'
  183.      * @protected
  184.      */
  185.     _checkConstrain: function(axis, axisConstrain, offset) {
  186.         var instance = this,
  187.             point1,
  188.             point1Constrain,
  189.             point2,
  190.             point2Constrain,
  191.             host = instance.get(HOST),
  192.             info = host.info,
  193.             constrainBorders = instance.constrainSurrounding.border,
  194.             region = instance._getConstrainRegion();

  195.         if (region) {
  196.             point1 = info[axis] + info[offset];
  197.             point1Constrain = region[axisConstrain] - toNumber(constrainBorders[capitalize(BORDER, axisConstrain, WIDTH)]);

  198.             if (point1 >= point1Constrain) {
  199.                 info[offset] -= (point1 - point1Constrain);
  200.             }

  201.             point2 = info[axis];
  202.             point2Constrain = region[axis] + toNumber(constrainBorders[capitalize(BORDER, axis, WIDTH)]);

  203.             if (point2 <= point2Constrain) {
  204.                 info[axis] += (point2Constrain - point2);
  205.                 info[offset] -= (point2Constrain - point2);
  206.             }
  207.         }
  208.     },

  209.     /**
  210.      * Update the current values on <a href="Resize.html#property_info">info</a>
  211.      * to respect the maxHeight and minHeight.
  212.      *
  213.      * @method _checkHeight
  214.      * @protected
  215.      */
  216.     _checkHeight: function() {
  217.         var instance = this,
  218.             host = instance.get(HOST),
  219.             info = host.info,
  220.             maxHeight = (instance.get(MAX_HEIGHT) + host.totalVSurrounding),
  221.             minHeight = (instance.get(MIN_HEIGHT) + host.totalVSurrounding);

  222.         instance._checkConstrain(TOP, BOTTOM, OFFSET_HEIGHT);

  223.         if (info.offsetHeight > maxHeight) {
  224.             host._checkSize(OFFSET_HEIGHT, maxHeight);
  225.         }

  226.         if (info.offsetHeight < minHeight) {
  227.             host._checkSize(OFFSET_HEIGHT, minHeight);
  228.         }
  229.     },

  230.     /**
  231.      * Update the current values on <a href="Resize.html#property_info">info</a>
  232.      * calculating the correct ratio for the other values.
  233.      *
  234.      * @method _checkRatio
  235.      * @protected
  236.      */
  237.     _checkRatio: function() {
  238.         var instance = this,
  239.             host = instance.get(HOST),
  240.             info = host.info,
  241.             originalInfo = host.originalInfo,
  242.             oWidth = originalInfo.offsetWidth,
  243.             oHeight = originalInfo.offsetHeight,
  244.             oTop = originalInfo.top,
  245.             oLeft = originalInfo.left,
  246.             oBottom = originalInfo.bottom,
  247.             oRight = originalInfo.right,
  248.             // wRatio/hRatio functions keep the ratio information always synced with the current info information
  249.             // RETURN: percentage how much width/height has changed from the original width/height
  250.             wRatio = function() {
  251.                 return (info.offsetWidth/oWidth);
  252.             },
  253.             hRatio = function() {
  254.                 return (info.offsetHeight/oHeight);
  255.             },
  256.             isClosestToHeight = host.changeHeightHandles,
  257.             bottomDiff,
  258.             constrainBorders,
  259.             constrainRegion,
  260.             leftDiff,
  261.             rightDiff,
  262.             topDiff;

  263.         // check whether the resizable node is closest to height or not
  264.         if (instance.get(CONSTRAIN) && host.changeHeightHandles && host.changeWidthHandles) {
  265.             constrainRegion = instance._getConstrainRegion();
  266.             constrainBorders = instance.constrainSurrounding.border;
  267.             bottomDiff = (constrainRegion.bottom - toNumber(constrainBorders[BORDER_BOTTOM_WIDTH])) - oBottom;
  268.             leftDiff = oLeft - (constrainRegion.left + toNumber(constrainBorders[BORDER_LEFT_WIDTH]));
  269.             rightDiff = (constrainRegion.right - toNumber(constrainBorders[BORDER_RIGHT_WIDTH])) - oRight;
  270.             topDiff = oTop - (constrainRegion.top + toNumber(constrainBorders[BORDER_TOP_WIDTH]));

  271.             if (host.changeLeftHandles && host.changeTopHandles) {
  272.                 isClosestToHeight = (topDiff < leftDiff);
  273.             }
  274.             else if (host.changeLeftHandles) {
  275.                 isClosestToHeight = (bottomDiff < leftDiff);
  276.             }
  277.             else if (host.changeTopHandles) {
  278.                 isClosestToHeight = (topDiff < rightDiff);
  279.             }
  280.             else {
  281.                 isClosestToHeight = (bottomDiff < rightDiff);
  282.             }
  283.         }

  284.         // when the height of the resizable element touch the border of the constrain first
  285.         // force the offsetWidth to be calculated based on the height ratio
  286.         if (isClosestToHeight) {
  287.             info.offsetWidth = oWidth*hRatio();
  288.             instance._checkWidth();
  289.             info.offsetHeight = oHeight*wRatio();
  290.         }
  291.         else {
  292.             info.offsetHeight = oHeight*wRatio();
  293.             instance._checkHeight();
  294.             info.offsetWidth = oWidth*hRatio();
  295.         }

  296.         // fixing the top on handles which are able to change top
  297.         // the idea here is change the top based on how much the height has changed instead of follow the dy
  298.         if (host.changeTopHandles) {
  299.             info.top = oTop + (oHeight - info.offsetHeight);
  300.         }

  301.         // fixing the left on handles which are able to change left
  302.         // the idea here is change the left based on how much the width has changed instead of follow the dx
  303.         if (host.changeLeftHandles) {
  304.             info.left = oLeft + (oWidth - info.offsetWidth);
  305.         }

  306.         // rounding values to avoid pixel jumpings
  307.         Y.each(info, function(value, key) {
  308.             if (isNumber(value)) {
  309.                 info[key] = Math.round(value);
  310.             }
  311.         });
  312.     },

  313.     /**
  314.      * Check whether the resizable node is inside the constrain region.
  315.      *
  316.      * @method _checkRegion
  317.      * @protected
  318.      * @return {boolean}
  319.      */
  320.     _checkRegion: function() {
  321.         var instance = this,
  322.             host = instance.get(HOST),
  323.             region = instance._getConstrainRegion();

  324.         return Y.DOM.inRegion(null, region, true, host.info);
  325.     },

  326.     /**
  327.      * Update the current values on <a href="Resize.html#property_info">info</a>
  328.      * to respect the maxWidth and minWidth.
  329.      *
  330.      * @method _checkWidth
  331.      * @protected
  332.      */
  333.     _checkWidth: function() {
  334.         var instance = this,
  335.             host = instance.get(HOST),
  336.             info = host.info,
  337.             maxWidth = (instance.get(MAX_WIDTH) + host.totalHSurrounding),
  338.             minWidth = (instance.get(MIN_WIDTH) + host.totalHSurrounding);

  339.         instance._checkConstrain(LEFT, RIGHT, OFFSET_WIDTH);

  340.         if (info.offsetWidth < minWidth) {
  341.             host._checkSize(OFFSET_WIDTH, minWidth);
  342.         }

  343.         if (info.offsetWidth > maxWidth) {
  344.             host._checkSize(OFFSET_WIDTH, maxWidth);
  345.         }
  346.     },

  347.     /**
  348.      * Get the constrain region based on the <code>constrain</code>
  349.      * attribute.
  350.      *
  351.      * @method _getConstrainRegion
  352.      * @protected
  353.      * @return {Object Region}
  354.      */
  355.     _getConstrainRegion: function() {
  356.         var instance = this,
  357.             host = instance.get(HOST),
  358.             node = host.get(NODE),
  359.             constrain = instance.get(CONSTRAIN),
  360.             region = null;

  361.         if (constrain) {
  362.             if (constrain === VIEW) {
  363.                 region = node.get(VIEWPORT_REGION);
  364.             }
  365.             else if (isNode(constrain)) {
  366.                 region = constrain.get(REGION);
  367.             }
  368.             else {
  369.                 region = constrain;
  370.             }
  371.         }

  372.         return region;
  373.     },

  374.     _handleResizeAlignEvent: function() {
  375.         var instance = this,
  376.             host = instance.get(HOST);

  377.         // check the max/min height and locking top when these values are reach
  378.         instance._checkHeight();

  379.         // check the max/min width and locking left when these values are reach
  380.         instance._checkWidth();

  381.         // calculating the ratio, for proportionally resizing
  382.         if (instance.get(PRESEVE_RATIO)) {
  383.             instance._checkRatio();
  384.         }

  385.         if (instance.get(CONSTRAIN) && !instance._checkRegion()) {
  386.             host.info = host.lastInfo;
  387.         }
  388.     },

  389.     _handleResizeStartEvent: function() {
  390.         var instance = this,
  391.             constrain = instance.get(CONSTRAIN),
  392.             host = instance.get(HOST);

  393.         instance.constrainSurrounding = host._getBoxSurroundingInfo(constrain);
  394.     }
  395. });

  396. Y.namespace('Plugin');
  397. Y.Plugin.ResizeConstrained = ResizeConstrained;

  398.