| /**
 * FlexLayout.js
 *
 * Released under LGPL License.
 * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
 *
 * License: http://www.tinymce.com/license
 * Contributing: http://www.tinymce.com/contributing
 */
/**
 * This layout manager works similar to the CSS flex box.
 *
 * @setting {String} direction row|row-reverse|column|column-reverse
 * @setting {Number} flex A positive-number to flex by.
 * @setting {String} align start|end|center|stretch
 * @setting {String} pack start|end|justify
 *
 * @class tinymce.ui.FlexLayout
 * @extends tinymce.ui.AbsoluteLayout
 */
define(
  'tinymce.ui.FlexLayout',
  [
    "tinymce.ui.AbsoluteLayout"
  ],
  function (AbsoluteLayout) {
    "use strict";
    return AbsoluteLayout.extend({
      /**
       * Recalculates the positions of the controls in the specified container.
       *
       * @method recalc
       * @param {tinymce.ui.Container} container Container instance to recalc.
       */
      recalc: function (container) {
        // A ton of variables, needs to be in the same scope for performance
        var i, l, items, contLayoutRect, contPaddingBox, contSettings, align, pack, spacing, totalFlex, availableSpace, direction;
        var ctrl, ctrlLayoutRect, ctrlSettings, flex, maxSizeItems = [], size, maxSize, ratio, rect, pos, maxAlignEndPos;
        var sizeName, minSizeName, posName, maxSizeName, beforeName, innerSizeName, deltaSizeName, contentSizeName;
        var alignAxisName, alignInnerSizeName, alignSizeName, alignMinSizeName, alignBeforeName, alignAfterName;
        var alignDeltaSizeName, alignContentSizeName;
        var max = Math.max, min = Math.min;
        // Get container items, properties and settings
        items = container.items().filter(':visible');
        contLayoutRect = container.layoutRect();
        contPaddingBox = container.paddingBox;
        contSettings = container.settings;
        direction = container.isRtl() ? (contSettings.direction || 'row-reversed') : contSettings.direction;
        align = contSettings.align;
        pack = container.isRtl() ? (contSettings.pack || 'end') : contSettings.pack;
        spacing = contSettings.spacing || 0;
        if (direction == "row-reversed" || direction == "column-reverse") {
          items = items.set(items.toArray().reverse());
          direction = direction.split('-')[0];
        }
        // Setup axis variable name for row/column direction since the calculations is the same
        if (direction == "column") {
          posName = "y";
          sizeName = "h";
          minSizeName = "minH";
          maxSizeName = "maxH";
          innerSizeName = "innerH";
          beforeName = 'top';
          deltaSizeName = "deltaH";
          contentSizeName = "contentH";
          alignBeforeName = "left";
          alignSizeName = "w";
          alignAxisName = "x";
          alignInnerSizeName = "innerW";
          alignMinSizeName = "minW";
          alignAfterName = "right";
          alignDeltaSizeName = "deltaW";
          alignContentSizeName = "contentW";
        } else {
          posName = "x";
          sizeName = "w";
          minSizeName = "minW";
          maxSizeName = "maxW";
          innerSizeName = "innerW";
          beforeName = 'left';
          deltaSizeName = "deltaW";
          contentSizeName = "contentW";
          alignBeforeName = "top";
          alignSizeName = "h";
          alignAxisName = "y";
          alignInnerSizeName = "innerH";
          alignMinSizeName = "minH";
          alignAfterName = "bottom";
          alignDeltaSizeName = "deltaH";
          alignContentSizeName = "contentH";
        }
        // Figure out total flex, availableSpace and collect any max size elements
        availableSpace = contLayoutRect[innerSizeName] - contPaddingBox[beforeName] - contPaddingBox[beforeName];
        maxAlignEndPos = totalFlex = 0;
        for (i = 0, l = items.length; i < l; i++) {
          ctrl = items[i];
          ctrlLayoutRect = ctrl.layoutRect();
          ctrlSettings = ctrl.settings;
          flex = ctrlSettings.flex;
          availableSpace -= (i < l - 1 ? spacing : 0);
          if (flex > 0) {
            totalFlex += flex;
            // Flexed item has a max size then we need to check if we will hit that size
            if (ctrlLayoutRect[maxSizeName]) {
              maxSizeItems.push(ctrl);
            }
            ctrlLayoutRect.flex = flex;
          }
          availableSpace -= ctrlLayoutRect[minSizeName];
          // Calculate the align end position to be used to check for overflow/underflow
          size = contPaddingBox[alignBeforeName] + ctrlLayoutRect[alignMinSizeName] + contPaddingBox[alignAfterName];
          if (size > maxAlignEndPos) {
            maxAlignEndPos = size;
          }
        }
        // Calculate minW/minH
        rect = {};
        if (availableSpace < 0) {
          rect[minSizeName] = contLayoutRect[minSizeName] - availableSpace + contLayoutRect[deltaSizeName];
        } else {
          rect[minSizeName] = contLayoutRect[innerSizeName] - availableSpace + contLayoutRect[deltaSizeName];
        }
        rect[alignMinSizeName] = maxAlignEndPos + contLayoutRect[alignDeltaSizeName];
        rect[contentSizeName] = contLayoutRect[innerSizeName] - availableSpace;
        rect[alignContentSizeName] = maxAlignEndPos;
        rect.minW = min(rect.minW, contLayoutRect.maxW);
        rect.minH = min(rect.minH, contLayoutRect.maxH);
        rect.minW = max(rect.minW, contLayoutRect.startMinWidth);
        rect.minH = max(rect.minH, contLayoutRect.startMinHeight);
        // Resize container container if minSize was changed
        if (contLayoutRect.autoResize && (rect.minW != contLayoutRect.minW || rect.minH != contLayoutRect.minH)) {
          rect.w = rect.minW;
          rect.h = rect.minH;
          container.layoutRect(rect);
          this.recalc(container);
          // Forced recalc for example if items are hidden/shown
          if (container._lastRect === null) {
            var parentCtrl = container.parent();
            if (parentCtrl) {
              parentCtrl._lastRect = null;
              parentCtrl.recalc();
            }
          }
          return;
        }
        // Handle max size elements, check if they will become to wide with current options
        ratio = availableSpace / totalFlex;
        for (i = 0, l = maxSizeItems.length; i < l; i++) {
          ctrl = maxSizeItems[i];
          ctrlLayoutRect = ctrl.layoutRect();
          maxSize = ctrlLayoutRect[maxSizeName];
          size = ctrlLayoutRect[minSizeName] + ctrlLayoutRect.flex * ratio;
          if (size > maxSize) {
            availableSpace -= (ctrlLayoutRect[maxSizeName] - ctrlLayoutRect[minSizeName]);
            totalFlex -= ctrlLayoutRect.flex;
            ctrlLayoutRect.flex = 0;
            ctrlLayoutRect.maxFlexSize = maxSize;
          } else {
            ctrlLayoutRect.maxFlexSize = 0;
          }
        }
        // Setup new ratio, target layout rect, start position
        ratio = availableSpace / totalFlex;
        pos = contPaddingBox[beforeName];
        rect = {};
        // Handle pack setting moves the start position to end, center
        if (totalFlex === 0) {
          if (pack == "end") {
            pos = availableSpace + contPaddingBox[beforeName];
          } else if (pack == "center") {
            pos = Math.round(
              (contLayoutRect[innerSizeName] / 2) - ((contLayoutRect[innerSizeName] - availableSpace) / 2)
            ) + contPaddingBox[beforeName];
            if (pos < 0) {
              pos = contPaddingBox[beforeName];
            }
          } else if (pack == "justify") {
            pos = contPaddingBox[beforeName];
            spacing = Math.floor(availableSpace / (items.length - 1));
          }
        }
        // Default aligning (start) the other ones needs to be calculated while doing the layout
        rect[alignAxisName] = contPaddingBox[alignBeforeName];
        // Start laying out controls
        for (i = 0, l = items.length; i < l; i++) {
          ctrl = items[i];
          ctrlLayoutRect = ctrl.layoutRect();
          size = ctrlLayoutRect.maxFlexSize || ctrlLayoutRect[minSizeName];
          // Align the control on the other axis
          if (align === "center") {
            rect[alignAxisName] = Math.round((contLayoutRect[alignInnerSizeName] / 2) - (ctrlLayoutRect[alignSizeName] / 2));
          } else if (align === "stretch") {
            rect[alignSizeName] = max(
              ctrlLayoutRect[alignMinSizeName] || 0,
              contLayoutRect[alignInnerSizeName] - contPaddingBox[alignBeforeName] - contPaddingBox[alignAfterName]
            );
            rect[alignAxisName] = contPaddingBox[alignBeforeName];
          } else if (align === "end") {
            rect[alignAxisName] = contLayoutRect[alignInnerSizeName] - ctrlLayoutRect[alignSizeName] - contPaddingBox.top;
          }
          // Calculate new size based on flex
          if (ctrlLayoutRect.flex > 0) {
            size += ctrlLayoutRect.flex * ratio;
          }
          rect[sizeName] = size;
          rect[posName] = pos;
          ctrl.layoutRect(rect);
          // Recalculate containers
          if (ctrl.recalc) {
            ctrl.recalc();
          }
          // Move x/y position
          pos += size + spacing;
        }
      }
    });
  }
);
 |